# Render user video

<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

> 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`, or `IPreviewVideoRenderElement` - 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.

```cpp
#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.

```cpp
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()`.

```cpp
// 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.

```cpp
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;
}
```
