Render user video
The code on this page only works with the custom UI.
In custom UI mode, the SDK provides multiple options to render user video after a user subscribes to another user's video. After following the steps to render a user's video, each option contains a different type of video stream.
- Single attendee - A single user's video.
- Active speaker - A single user's video that automatically updates when the active speaker changes.
- Preview - A preview of the local user's video.
- Share - The content being shared by another user.
Regardless of which render option you are using, the SDK utilizes three main components.
INormalVideoRenderElement,IActiveVideoRenderElement, orIPreviewVideoRenderElement- Interface for video element rendering in custom UI.ICustomizedVideoContainer- An object that sets up the video view.ICustomizedVideoContainerEvent- A delegate that indicates if a video's subscription fails and more.
Subscribe to error data
Before subscribing to the video, implement ICustomizedVideoContainerEvent to know when a video subscription fails. To pass it into the SDK, you'll need a reference to the ICustomizedVideoContainer instance being set up.
#include "customized_ui/customized_ui_mgr.h"
#include "customized_ui/customized_video_container.h"
class MeetingVideoRenderer : public ZOOM_SDK_NAMESPACE::ICustomizedVideoContainerEvent {
public:
bool Initialize(
ZOOM_SDK_NAMESPACE::ICustomizedUIMgr* ui_mgr,
HWND parent_wnd,
RECT container_rect) {
if (!ui_mgr) {
return false;
}
const auto err = ui_mgr->CreateVideoContainer(
&video_container_,
parent_wnd,
container_rect);
if (err != ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS || !video_container_) {
return false;
}
video_container_->SetEvent(this);
return true;
}
void CleanupContainer(ZOOM_SDK_NAMESPACE::ICustomizedUIMgr* ui_mgr) {
if (!ui_mgr || !video_container_) {
return;
}
video_container_->SetEvent(nullptr);
ui_mgr->DestroyVideoContainer(video_container_);
video_container_ = nullptr;
}
void onSubscribeUserFail(
ZOOM_SDK_NAMESPACE::ZoomSDKVideoSubscribeFailReason fail_reason,
ZOOM_SDK_NAMESPACE::IVideoRenderElement* element) override {
}
void onRenderUserChanged(
ZOOM_SDK_NAMESPACE::IVideoRenderElement* element,
unsigned int user_id) override {
}
void onRenderDataTypeChanged(
ZOOM_SDK_NAMESPACE::IVideoRenderElement* element,
ZOOM_SDK_NAMESPACE::VideoRenderDataType data_type) override {
}
void onLayoutNotification(RECT wnd_client_rect) override {
}
void onVideoRenderElementDestroyed(
ZOOM_SDK_NAMESPACE::IVideoRenderElement* element) override {
}
void onWindowMsgNotification(
UINT u_msg,
WPARAM w_param,
LPARAM l_param) override {
}
ZOOM_SDK_NAMESPACE::ICustomizedVideoContainer* video_container_ = nullptr;
};
Define a video element
The primary user-facing video element is IVideoRenderElement and is inherited by the components VideoRenderElement_ACTIVE for active speaker video, VideoRenderElement_PRVIEW for local user preview video, and VideoRenderElement_NORMAL for local and remote participant video.
Create a new IVideoRenderElement instance based on which video element you need and cast it to the interface that matches the stream you want to display.
RECT video_rect = {0, 0, 640, 360};
ZOOM_SDK_NAMESPACE::IVideoRenderElement* element = nullptr;
video_container_->CreateVideoElement(
&element,
ZOOM_SDK_NAMESPACE::VideoRenderElement_NORMAL);
auto* normal_video =
dynamic_cast<ZOOM_SDK_NAMESPACE::INormalVideoRenderElement*>(element);
Subscribe to video streams
Each of the IVideoRenderElement instance created by the SDK is managed by the ICustomizedVideoContainer. Use these to manage the lifecycle of the video subscription rendered on each video view.
To subscribe to a single attendee's video, you must provide the userID of the user whose video is being subscribed to. Access this in IUserInfo::GetUserID().
// To subscribe a participant view
RECT video_rect = {0, 0, 640, 360};
ZOOM_SDK_NAMESPACE::IVideoRenderElement* element = nullptr;
video_container_->CreateVideoElement(
&element,
ZOOM_SDK_NAMESPACE::VideoRenderElement_NORMAL);
auto* attendee_video =
dynamic_cast<ZOOM_SDK_NAMESPACE::INormalVideoRenderElement*>(element);
if (attendee_video) {
attendee_video->SetPos(video_rect);
attendee_video->SetResolution(ZOOM_SDK_NAMESPACE::VideoRenderResolution_360p);
unsigned int user_id = user_info->GetUserID();
attendee_video->Subscribe(user_id);
attendee_video->Show();
}
// To subscribe to the active speaker view
RECT active_rect = {0, 0, 640, 360};
ZOOM_SDK_NAMESPACE::IVideoRenderElement* element = nullptr;
video_container_->CreateVideoElement(
&element,
ZOOM_SDK_NAMESPACE::VideoRenderElement_ACTIVE);
auto* active_video =
dynamic_cast<ZOOM_SDK_NAMESPACE::IActiveVideoRenderElement*>(element);
if (active_video) {
active_video->SetPos(active_rect);
active_video->Start();
}
// To subscribe to the local user preview view
RECT preview_rect = {0, 0, 320, 180};
ZOOM_SDK_NAMESPACE::IVideoRenderElement* element = nullptr;
video_container_->CreateVideoElement(
&element,
ZOOM_SDK_NAMESPACE::VideoRenderElement_PRVIEW);
auto* preview_video =
dynamic_cast<ZOOM_SDK_NAMESPACE::IPreviewVideoRenderElement*>(element);
if (preview_video) {
preview_video->SetPos(preview_rect);
preview_video->Start();
}
Manage existing video streams
To resize or set resolution to any of the video element, update its position with SetPos. To request a specific stream resolution, call SetResolution.
The SDK may call onLayoutNotification when the container layout changes. When that happens, recalculate your RECT values and reposition the render elements.
RECT new_rect = {20, 20, 420, 260};
video_element->SetPos(new_rect);
video_element->SetResolution(ZOOM_SDK_NAMESPACE::VideoRenderResolution_360p);
Finally, when a render element is no longer being used, end the subscription and remove the video view.
if (attendee_video) {
const unsigned int user_id = attendee_video->GetCurrentRenderUserId();
if (user_id != 0) {
attendee_video->Unsubscribe(user_id);
}
attendee_video->Hide();
video_container_->DestroyVideoElement(attendee_video);
attendee_video = nullptr;
}
if (active_video) {
active_video->Stop();
video_container_->DestroyVideoElement(active_video);
active_video = nullptr;
}
if (preview_video) {
preview_video->Stop();
video_container_->DestroyVideoElement(preview_video);
preview_video = nullptr;
}