# Video To render the video of each user who joins a session, your app must: 1. Retrieve the `IZoomVideoSDKUser` object related to each user as they join the session. 2. Obtain the `IZoomVideoSDKRawDataPipe` from each user whose stream you'd like to render. 3. Subscribe to their video pipe. 4. Listen for frames. ## Retrieve user To be notified when users join a session, use `onUserJoin` within the `ZoomVideoSDKDelegate`. ```cpp void CExampleListener::onUserJoin(IZoomVideoSDKUserHelper* pUserHelper, IVideoSDKVector* userList) { CString strInfo; IZoomVideoSDKUser* pUser; int count = userList->GetCount(); for (int i = 0; i < count; i++) { pUser = userList->GetItem(i); if (!pUser) continue; strInfo.Format(_T("A user joined the session: name=%s"), pUser->getUserName()); } } ``` See [callback events](/docs/video-sdk/linux/integrate/#callback-events) for details on how to add this to your callback listener delegate. ## Subscribe to user's video pipe Now that you have access to each user represented by a `IZoomVideoSDKUser` object, you must retrieve the `IZoomVideoSDKRawDataPipe` of each user whose video you would like to render and to subscribe to that user's video pipe. ```cpp // CExampleRenderer.h class CExampleRenderer : public IZoomVideoSDKRawDataPipeDelegate { // ... public: // IZoomVideoSDKRawDataPipeDelegate virtual void onRawDataFrameReceived(YUVRawDataI420* data_); virtual void onRawDataStatusChanged(RawDataStatus status); // ... } // CExampleRenderer.cpp ZoomVideoSDKErrors CExampleRenderer::Subscribe(IZoomVideoSDKUser* pUser, ZoomVideoSDKRawDataType dataType, int size) { // Set the resolution. ZoomVideoSDKResolution resolution = ZoomVideoSDKResolution_360P; // Get the video pipe for the user. IZoomVideoSDKRawDataPipe* pPipe = NULL; pPipe = pUser->GetVideoPipe(); if (!pPipe) return; // Call subscribe. err = pPipe->subscribe(resolution, this) return err; } ``` ## Receive video frames The `CExampleRenderer` class shown in the previous sample code inherits from `IZoomVideoSDKRawDataPipeDelegate`, which provides two callbacks. `onRawDataFrameReceived(YUVRawDataI420*data)` provides video data in YUV format. The data parameter `YUVRawDataI420` provides everything needed to be able to render the single video frame's data. ```cpp void CExampleRenderer::onRawDataFrameReceived(YUVRawDataI420* data_) { // Get frame data resolution. data_->GetStreamWidth(); data_->GetStreamHeight(); // Get frame buffer. data_->GetYBuffer(); data_->GetUBuffer(); data_->GetVBuffer(); // Get frame rotation data_->GetRotation(); } ``` The SDK calls `onRawDataStatusChanged(RawDataStatus status)` when there is a change in raw data status. ```cpp void CExampleRenderer::onRawDataStatusChanged(RawDataStatus status) { if (status == RawData_On) { // Now subscribed to the user's data. } else { // No longer subscribed to user's data. } } ``` ## Unsubscribe from user's video pipe To stop rendering a user's content, you must unsubscribe from the user's video by calling `unsubscribe()`. ```cpp void CExampleRenderer::unSubscribe(IZoomVideoSDKUser* pUser) { if (pUser->GetVideoPipe()) { pUser->GetVideoPipe()->unSubscribe(this); } } ``` ## Send raw video data You can send raw data, for example, if you want to process it before sending it. Here's an example. ```cpp // CExampleVideoSource.h class CExampleVideoSource : public IZoomVideoSDKVideoSource { // ... public: // IZoomVideoSDKVideoSource virtual void onInitialize(IZoomVideoSDKVideoSender* sender, IVideoSDKVector* support_cap_list, VideoSourceCapability& suggest_cap); virtual void onPropertyChange(IVideoSDKVector* support_cap_list, VideoSourceCapability suggest_cap); virtual void onStartSend(); virtual void onStopSend(); virtual void onUninitialized(); // ... private: IZoomVideoSDKVideoSender* m_pSender; } // CExampleVideoSource.cpp void CExampleVideoSource::onInitialize(IZoomVideoSDKVideoSender* sender, IVideoSDKVector* support_cap_list, VideoSourceCapability& suggest_cap) { // Store IZoomVideoSDKVideoSender. if (!sender) return; m_pSender = sender; // Inspect video capabilities. if (!support_cap_list) return; unsigned int nCount = support_cap_list->GetCount(); for(int i=0; iGetItem(i); } } // Call sendVideoFrame to send a frame buffer of raw data. char* pFrameData; int width; int height; int frameLength; int rotation; m_pSender->sendVideoFrame(pFrameData, width, height, frameLength, rotation); ``` The `support_cap_list` parameter provides a vector of current video capabilities for each frame to send. The `suggest_cap` parameter is the current suggested video capability. The SDK calls the `onPropertyChange` callback when this changes. ```cpp void CExampleVideoSource::onPropertyChange(IVideoSDKVector* support_cap_list, VideoSourceCapability suggest_cap) { if (!support_cap_list) return; unsigned int nCount = support_cap_list->GetCount(); for(int i=0; iGetItem(i); } // Example: Suggested video resolution changed to 1080p. if (suggest_cap.width == 1920 && suggest_cap.height == 1080) { } } ``` You can also preprocess raw video data using `onPreProcessRawData` within the `IZoomVideoSDKVideoSourcePreProcessor`: ```cpp void CExampleVideoSourcePreProcessor::onPreProcessRawData(YUVProcessDataI420* rawData) { // Use the rawData parameter to perform preprocessing actions. unsigned int frameWidth = rawData->GetWidth(); unsigned int frameHeight = rawData->GetHeight(); char* yBuffer = rawData->GetYBuffer(); char* uBuffer = rawData->GetUBuffer(); char* vBuffer = rawData->GetVBuffer(); } ``` ## Use virtual background Use [`IVirtualBackgroundItem`](https://marketplacefront.zoom.us/sdk/custom/linux/class_i_virtual_background_item.html) to add, get, and remove virtual backgrounds. ## Configure camera controls Configure camera functionality using `IZoomVideoSDKVideoHelper`. To get the current camera list, use `getCameraList`. ```cpp IZoomVideoSDKVideoHelper* pInsVideoHelper = m_pVideoSDK->getVideoHelper(); if (pInsVideoHelper->getNumberOfCameras() > 0) { IVideoSDKVector* pCameraList = pInsVideoHelper->getCameraList(); for (int i=0; iGetCount(); i++) { IZoomVideoSDKCameraDevice* pCamera = m_pCameraList->GetItem(i); CString strItem; strItem.Format(_T("%s***%s"), pCamera->getDeviceName(), pCamera->getDeviceId()); } } ``` To select a camera, use `selectCamera` on the `IZoomVideoSDKCameraDevice` object for the desired camera. ```cpp IVideoSDKVector* pCameraList = pInsVideoHelper->getCameraList(); IZoomVideoSDKCameraDevice* pCamera = m_pCameraList->GetItem(0); pVideoHelper->selectCamera(pCamera->getDeviceId()); ``` Switch to the next available camera with `switchCamera`. ```cpp IZoomVideoSDKVideoHelper* pVideoHelper = m_pVideoSDK->getVideoHelper(); if (!pVideoHelper) return; pVideoHelper->switchCamera(); ``` Control user video for an ongoing session using the `IZoomVideoSDKVideoHelper`. Session hosts can manage their own audio and video as well the audio and video of other users in the session. Continue reading for some video control examples. ## Display or hide a user's video You can control video on a per-user basis. First, check the current video status of the user. ```cpp // Get videoStatus for User. ZoomVideoSDKVideoStatus videoStatus = pUserInfo->getVideoStatus(); // Check if User's video is on. bool isVideoOn = pUserInfo->GetVideoPipe()->getVideoStatus().isOn(); ``` If the local user's video is not already enabled, start video using `startVideo` in the `ZoomVideoSDKVideoHelper`. ```cpp // Get the IZoomVideoSDKVideoHelper to perform video actions. IZoomVideoSDKVideoHelper* pVideoHelper = m_pVideoSDK->getVideoHelper(); if (pVideoHelper) { // Start local User's video. pVideoHelper->startVideo(); } ``` Use `stopVideo` to stop displaying local user's video. ```cpp // Get the IZoomVideoSDKVideoHelper to perform video actions. IZoomVideoSDKVideoHelper* pVideoHelper = m_pVideoSDK->getVideoHelper(); if (pVideoHelper) { // Stop local User's video. pVideoHelper->stopVideo(); } ``` ## Video callbacks Be sure that you have [set up a delegate for callback events](/docs/video-sdk/linux/integrate/#callback-events) to receive video callbacks. Get notified when a user's video status has changed. ```cpp void CExampleListener::onUserVideoStatusChanged(IZoomVideoSDKVideoHelper* pVideoHelper, IVideoSDKVector* userList) { CString strInfo; IZoomVideoSDKUser* pUser; int count = userList->GetCount(); for (int i = 0; i < count; i++) { pUser = userList->GetItem(i); strInfo.Format(_T("A user's video status changed: userid=%s, name=%s, status=%s"), pUser->getUserName(), pUser->getVideoStatus()); } } ``` ## Video quality preference When network bandwidth is limited, you can adjust video quality preferences between resolution and frame rate. Video quality preferences are useful when the primary focus is not the image of a person attending the session, but on other video images where smoothness or sharpness is preferred. For example, you can choose to preserve the video sharpness or smoothness. If bandwidth is not a concern, you can receive the best quality video using a high video resolution and maximum frame rate. Aside from video conferencing, you may want to set video quality preferences for: - Transmission of medical X-ray images. - Transmission of video captured by endoscopic cameras during a medical operation. - Live sporting event broadcasts, such as co-watching or broadcasting a football game. Set your video quality preferences with [`tagVideoPreferenceSetting`](https://marketplacefront.zoom.us/sdk/custom/linux/structtag_video_preference_setting.html). ### Video preference modes Choose from the following modes, depending on what you'd like to prioritize: - **Balance mode**. Zoom will do what is best under the current bandwidth situation and make adjustments as needed. You don't need to set any additional parameters for this mode. This mode is suitable for video conference usage. This is the default preference. - **Smoothness mode**. Preserves the frame rate as much as possible. If network bandwidth degrades, Zoom will sacrifice video resolution to preserve the frame rate. This prioritizes a smooth video frame transition. - **Sharpness mode**. Preserves the resolution as much as possible. If network bandwidth degrades, Zoom will sacrifice the frame rate to preserve video resolution. This prioritizes a sharp video image. - **Custom mode**. Allows you to provide the minimum and maximum frame rate. Use this mode if you have an understanding of your network behavior and a clear idea of how to adjust the frame rate to achieve the desired video quality. You can also use this mode to influence bandwidth usage by increasing or decreasing the maximum frame rate setting. Given a resolution, a lower maximum frame rate results in less bandwidth usage. Note that if the bandwidth cannot be sustained by following the minimum and maximum frame rates, the system will drop down to the next lower resolution. See the reference documentation for details.