# 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 four main components. - `ZoomSDKNormalVideoElement`, `ZoomSDKActiveVideoElement`, or `ZoomSDKPreViewVideoElement` - Interface for video element rendering in custom UI. - `ZoomSDKVideoContainer` - An object that sets up the video view. - `ZoomSDKVideoContainerDelegate` - A delegate that indicates if a video's subscription fails and more. ## Subscribe to error data Before subscribing to the video, conform your class to the `ZoomSDKVideoContainerDelegate` protocol to know when a video subscription fails. To pass it into the SDK, you'll need a reference to the `ZoomSDKVideoContainer` instance being set up. ```swift // Add delegate first. if let videoContainer = ZoomSDK.shared().getMeetingService()?.getVideoContainer() { videoContainer.delegate = self } // Also, conform your class to the ZoomSDKVideoContainerDelegate extension ViewController: ZoomSDKVideoContainerDelegate func onSubscribeUserFail(_ error: ZoomSDKVideoSubscribeFailReason, videoElement element: ZoomSDKVideoElement) { } } ``` ```objectivec // In your .h file, conform to the ZoomSDKVideoContainerDelegate protocol @interface ZMSDKWindow : NSWindowController { } // In your .m file, add delegate and add onSubscribeUserFail callback ZoomSDKVideoContainer *videoContainer = [[[ZoomSDK sharedSDK] getMeetingService] getVideoContainer]; if (videoContainer) { videoContainer.delegate = self; } - (void)onSubscribeUserFail:(ZoomSDKVideoSubscribeFailReason)error videoElement:(ZoomSDKVideoElement *)element { } ``` When you've finished using the video view, remove the listener to avoid leaks. ```swift videoContainer.delegate = nil ``` ```objectivec videoContainer.delegate = nil; ``` ## Define a video element The primary user-facing video element is `ZoomSDKVideoElement` and is inherited by the components `ZoomSDKActiveVideoElement` - for active speaker video, `ZoomSDKPreViewVideoElement` - for local user preview video, and `ZoomSDKNormalVideoElement` - for local and remote participant video. Create a new `ZoomSDKVideoElement` instance based on which video element you need and give it the frame of the `NSView` that you will be using to add the video view to. ```swift // To create the video element for a local/remote participant video let yourVideoView:NSView = NSView() // Get your NSView to show a user's video var normalVideoElement = ZoomSDKNormalVideoElement(frame: yourVideoView.frame) var videoElement: ZoomSDKVideoElement? = normalVideoElement // To create the video element for active speaker video let yourVideoView:NSView = NSView() // Get your NSView to show a user's video var activeVideoElement = ZoomSDKActiveVideoElement(frame: yourVideoView.frame) var videoElement: ZoomSDKVideoElement? = normalVideoElement // To create the video element for local user's preview video let yourVideoView:NSView = NSView() // Get your NSView to show a user's video previewVideoElement = ZoomSDKPreViewVideoElement(frame: yourVideoView.frame) var videoElement: ZoomSDKVideoElement? = normalVideoElement ``` ```objectivec // To create the video element for a local/remote participant video NSView *yourVideoView = [NSView alloc]; ZoomSDKNormalVideoElement* normalVideoElement = [[ZoomSDKNormalVideoElement alloc] initWithFrame:yourVideoView.frame]; // To create the video element for active speaker video NSView *yourVideoView = [NSView alloc]; ZoomSDKActiveVideoElement* activeVideoElement = [[ZoomSDKActiveVideoElement alloc] initWithFrame:yourVideoView.frame]; // To create the video element for local user's preview video NSView *yourVideoView = [NSView alloc]; ZoomSDKPreViewVideoElement* previewVideoElement = [[ZoomSDKPreViewVideoElement alloc] initWithFrame:yourVideoView.frame]; ``` ## Subscribe to video streams Each of the `ZoomSDKVideoElement` instance created by the SDK is managed by the `ZoomSDKVideoContainer`. 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 `ZoomSDKUserInfo.userID`. Learn how to retrieve a `userID` on [Access in-meeting user info](/docs/meeting-sdk/android/add-features/in-meeting-user-info/). ```swift videoContainer.createVideoElement(&videoElement) // To subscribe a participant view yourVideoView.addSubview(normalVideoElement.getVideoView()) normalVideoElement.userid = 123456789 // userID of the participant you will like to subscribe to normalVideoElement.subscribeVideo(true) // or false // To subscribe to the active speaker view yourVideoView.addSubview(activeVideoElement.getVideoView()) activeVideoElement.startActiveView(true) // or false // To subscribe to the local user preview view yourVideoView.addSubview(previewVideoElement.getVideoView()) previewVideoElement.startPreview(true) // or false ``` ```objectivec // To subscribe a participant view [videoContainer createVideoElement:&normalVideoElement]; [yourVideoView addSubview:[normalVideoElement getVideoView]];66 normalVideoElement.userid = 123456789 // userID of the participant you will like to subscribe to [normalVideoElement subscribeVideo:YES]; // or NO // To subscribe to the active speaker view [videoContainer createVideoElement:&activeVideoElement]; [yourVideoView addSubview:[activeVideoElement getVideoView]]; [activeVideoElement startActiveView:YES]; // or NO // To subscribe to the local user preview view [videoContainer createVideoElement:&previewVideoElement]; [yourVideoView addSubview:[previewVideoElement getVideoView]]; [previewVideoElement startPreview:YES]; // or NO ``` ## Manage existing video streams To resize or set resolution to any of the video element, use the `resize` or `setResolution` method respectively. ```swift videoElement?.resize(yourNewVideoView.frame) videoElement?.setResolution(ZoomSDKVideoRenderResolution_360p) // Check out ZoomSDKVideoRenderResolution for all resolution available. ``` ```objectivec [normalVideoElement resize:yourVideoView.frame]; [normalVideoElement setResolution:ZoomSDKVideoRenderResolution_360p]; ``` Finally, when a video unit is no longer being used, end the subscription with the `cleanVideoElement` method and remove the video view from its superview. ```swift videoContainer.clean(normalVideoElement) normalVideoElement.getVideoView().removeFromSuperview() ``` ```objectivec [videoContainer cleanVideoElement:normalVideoElement]; [[normalVideoElement getVideoView] removeFromSuperview]; ``` ---