Subchapter 150.12
examples/screen-share-subscription.mdMarkdown18 KBView on GitHub
Screen share subscription in the Zoom Video SDK is fundamentally different from video subscription. This guide explains why and provides complete working code for both Canvas API and Raw Data approaches.
| Aspect | Video | Screen Share |
|---|---|---|
| Streams per user | One video stream | Multiple share streams possible (multi-share) |
| Access method | user->GetVideoCanvas() | IZoomVideoSDKShareAction* from callback |
| Subscription timing | onUserVideoStatusChanged | onUserShareStatusChanged |
| Key object | IZoomVideoSDKUser* | IZoomVideoSDKShareAction* |
The critical difference: A user can have multiple active share actions simultaneously (e.g., sharing screen + sharing a whiteboard). The IZoomVideoSDKShareAction object in the callback represents a specific share stream.
// WRONG - This won't work for remote screen shares!
IZoomVideoSDKUser* sharingUser = ...;
IZoomVideoSDKCanvas* shareCanvas = sharingUser->GetShareCanvas();
shareCanvas->subscribeWithView(hwnd, aspect); // May fail or show nothing!Why it fails: GetShareCanvas() on the user object doesn’t give you access to the active share stream. You MUST use the IZoomVideoSDKShareAction* provided in the callback.
The Canvas API lets the SDK render the shared screen directly to your window handle.
class MyDelegate : public IZoomVideoSDKDelegate {
private:
HWND shareWindow_;
std::map<IZoomVideoSDKShareAction*, bool> activeShares_;
public:
MyDelegate(HWND shareWnd) : shareWindow_(shareWnd) {}
void onUserShareStatusChanged(
IZoomVideoSDKShareHelper* pShareHelper,
IZoomVideoSDKUser* pUser,
IZoomVideoSDKShareAction* pShareAction) override
{
if (!pShareAction) return;
ZoomVideoSDKShareStatus status = pShareAction->getShareStatus();
ZoomVideoSDKShareType type = pShareAction->getShareType();
// Get user name for logging
const zchar_t* userName = pUser ? pUser->getUserName() : L"Unknown";
switch (status) {
case ZoomVideoSDKShareStatus_Start:
case ZoomVideoSDKShareStatus_Resume:
SubscribeToShare(pShareAction, userName);
break;
case ZoomVideoSDKShareStatus_Pause:
// Share is paused - you may want to show a "Paused" overlay
// The subscription remains active
break;
case ZoomVideoSDKShareStatus_Stop:
UnsubscribeFromShare(pShareAction, userName);
break;
}
}
private:
void SubscribeToShare(IZoomVideoSDKShareAction* pShareAction,
const zchar_t* userName)
{
// Prevent duplicate subscriptions
if (activeShares_.find(pShareAction) != activeShares_.end()) {
return;
}
// Get the share canvas from the ShareAction (NOT from the user!)
IZoomVideoSDKCanvas* shareCanvas = pShareAction->getShareCanvas();
if (!shareCanvas) {
// Error: Share canvas not available
return;
}
// Subscribe with Canvas API
ZoomVideoSDKErrors ret = shareCanvas->subscribeWithView(
shareWindow_,
ZoomVideoSDKVideoAspect_Original // Show full content, letterbox if needed
);
if (ret == ZoomVideoSDKErrors_Success) {
activeShares_[pShareAction] = true;
// Successfully subscribed to share from [userName]
} else {
// Failed to subscribe: error code [ret]
}
}
void UnsubscribeFromShare(IZoomVideoSDKShareAction* pShareAction,
const zchar_t* userName)
{
auto it = activeShares_.find(pShareAction);
if (it == activeShares_.end()) {
return; // Not subscribed
}
IZoomVideoSDKCanvas* shareCanvas = pShareAction->getShareCanvas();
if (shareCanvas) {
shareCanvas->unSubscribeWithView(shareWindow_);
}
activeShares_.erase(it);
// Unsubscribed from share
}
};Use Raw Data when you need to process the shared screen frames yourself (recording, effects, computer vision).
class ShareRawDataDelegate : public IZoomVideoSDKRawDataPipeDelegate {
private:
std::function<void(YUVRawDataI420*)> frameCallback_;
public:
ShareRawDataDelegate(std::function<void(YUVRawDataI420*)> callback)
: frameCallback_(callback) {}
void onRawDataFrameReceived(
Here’s a complete example showing screen share subscription with proper lifecycle management:
#include <windows.h>
#include <map>
#include "zoom_video_sdk_api.h"
#include "zoom_video_sdk_interface.h"
#include "zoom_video_sdk_delegate_interface.h"
USING_ZOOM_VIDEO_SDK_NAMESPACE
class ScreenShareManager : public IZoomVideoSDKDelegate {
private:
IZoomVideoSDK* sdk_;
HWND mainShareWindow_;
// Track active share subscriptions
struct ShareSubscription {
IZoomVideoSDKShareAction* action;
IZoomVideoSDKUser
The IZoomVideoSDKShareAction::getShareType() returns:
| Type | Description |
|---|---|
ZoomVideoSDKShareType_Normal | Desktop/window share |
ZoomVideoSDKShareType_Camera | Camera share (second camera) |
User starts sharing
│
▼
onUserShareStatusChanged (status = Start)
│
├──► Subscribe to share canvas
│
▼
[Share is active and visible]
│
├──► User pauses share
│ │
│ ▼
│ onUserShareStatusChanged (status = Pause)
│ │
│ ├──► Show "paused" UI (optional)
│ │
│ ▼
│ [Share paused]
│ │
│ ├──► User resumes share
│ │ │
│ │ ▼
│ │ onUserShareStatusChanged (status = Resume)
│ │ │
│ │ └──► Re-subscribe if needed
│ │
▼ ▼
[Share continues...]
│
▼
User stops sharing
│
▼
onUserShareStatusChanged (status = Stop)
│
├──► Unsubscribe from canvas
├──► Clear share window
└──► Remove from tracking// CORRECT
void onUserShareStatusChanged(..., IZoomVideoSDKShareAction* pShareAction) {
pShareAction->getShareCanvas()->subscribeWithView(...);
}
// WRONG
user->GetShareCanvas()->subscribeWithView(...);std::map<IZoomVideoSDKShareAction*, bool> subscribedShares_;
// Subscribe
subscribedShares_[pShareAction] = true;
// On session leave - cleanup all
for (auto& pair : subscribedShares_) {
// Unsubscribe each
}IZoomVideoSDKUser* myself = session->getMyself();
if (pUser == myself) {
return; // Don't subscribe to our own share
}switch (status) {
case ZoomVideoSDKShareStatus_Start: // New share started
case ZoomVideoSDKShareStatus_Resume: // Paused share resumed
case ZoomVideoSDKShareStatus_Pause: // Share temporarily paused
case ZoomVideoSDKShareStatus_Stop: // Share ended
}// For screen share - show all content
ZoomVideoSDKVideoAspect_Original // Letterbox, no crop
// For camera share - fill window
ZoomVideoSDKVideoAspect_PanAndScan // May crop edges| Issue | Cause | Solution |
|---|---|---|
| Share not visible | Using user->GetShareCanvas() | Use pShareAction->getShareCanvas() from callback |
| Multiple shares not handled | Not tracking by ShareAction | Use map keyed by IZoomVideoSDKShareAction* |
| Share doesn’t update | Not handling Resume status | Subscribe on both Start and Resume |
| Crash on session leave | Not unsubscribing | Call unSubscribeWithView before cleanup |
| Can see own share | Not filtering self | Check pUser == session->getMyself() |
Key Takeaway: Always get the share canvas from IZoomVideoSDKShareAction* in the callback, never from the user object directly.