# Singleton Hierarchy: Navigation Guide ## Overview The Zoom Video SDK uses a **service locator pattern** - a tree of singletons where you navigate from the root SDK object down to specific features. You don't construct objects; you traverse to them. ``` You want to... You navigate to... ───────────────────────────────────────────────────── Start your camera IZoomVideoSDK → IZoomVideoSDKVideoHelper Mute a user IZoomVideoSDK → IZoomVideoSDKAudioHelper Subscribe to video IZoomVideoSDKUser → IZoomVideoSDKCanvas Get raw YUV frames IZoomVideoSDKUser → IZoomVideoSDKRawDataPipe Send chat message IZoomVideoSDK → IZoomVideoSDKChatHelper Start screen share IZoomVideoSDK → IZoomVideoSDKShareHelper Subscribe to remote share IZoomVideoSDKShareAction → subscribeWithView() ``` --- ## Complete Hierarchy (5 Levels Deep) ``` Level 0: Global Factory Function │ └─► CreateZoomVideoSDKObj() ──────────────────────────────────► IZoomVideoSDK* │ ├─► Level 1: Session & Lifecycle │ ├── initialize(params) → ZoomVideoSDKErrors │ ├── joinSession(context) → IZoomVideoSDKSession* │ ├── leaveSession(end) → ZoomVideoSDKErrors │ ├── addListener(delegate) → void │ ├── getSessionInfo() → IZoomVideoSDKSession* │ └── isInSession() → bool │ ├─► Level 1: Core Helpers (Control YOUR streams) │ ├── getVideoHelper() → IZoomVideoSDKVideoHelper* │ │ ├── startVideo() / stopVideo() │ │ ├── switchCamera(deviceId) │ │ ├── getCameraList() → IVideoSDKVector* │ │ │ └─► Level 4: IZoomVideoSDKCameraDevice │ │ │ ├── getDeviceId() │ │ │ ├── getDeviceName() │ │ │ └── isSelectedDevice() │ │ └── startVideoCanvasPreview(hwnd, aspect, resolution) │ │ │ ├── getAudioHelper() → IZoomVideoSDKAudioHelper* │ │ ├── startAudio() / stopAudio() │ │ ├── muteAudio(user) / unmuteAudio(user) │ │ ├── getMicList() → IVideoSDKVector* │ │ │ └─► Level 4: IZoomVideoSDKMicDevice │ │ ├── getSpeakerList() → IVideoSDKVector* │ │ │ └─► Level 4: IZoomVideoSDKSpeakerDevice │ │ └── selectMic() / selectSpeaker() │ │ │ ├── getShareHelper() → IZoomVideoSDKShareHelper* │ │ ├── startShareScreen(monitorId) │ │ ├── startShareView(hwnd) │ │ ├── startShareComputerAudio() │ │ ├── startSharingExternalSource(source) │ │ ├── stopShare() │ │ ├── isOtherSharing() / isSharingOut() │ │ ├── lockShare(lock) / isShareLocked() │ │ ├── enableMultiShare(enable) │ │ └── getWhiteboardHelper() → IZoomVideoSDKWhiteboardHelper* │ │ │ ├── getChatHelper() → IZoomVideoSDKChatHelper* │ │ ├── sendChatToAll(message) │ │ └── sendChatToUser(user, message) │ │ │ ├── getUserHelper() → IZoomVideoSDKUserHelper* │ │ ├── removeUser(user) │ │ ├── makeHost(user) │ │ ├── makeManager(user) │ │ └── changeName(user, name) │ │ │ ├── getRecordingHelper() → IZoomVideoSDKRecordingHelper* │ │ │ ├── getCmdChannel() → IZoomVideoSDKCmdChannel* │ │ ├── sendCommand(user, cmd) │ │ └── sendCommandToAll(cmd) │ │ │ ├── getLiveStreamHelper() → IZoomVideoSDKLiveStreamHelper* │ │ │ ├── getPhoneHelper() → IZoomVideoSDKPhoneHelper* │ │ │ └── getLiveTranscriptionHelper() → IZoomVideoSDKLiveTranscriptionHelper* │ ├─► Level 1: Settings Helpers (Configure devices & behavior) │ ├── getAudioSettingHelper() → IZoomVideoSDKAudioSettingHelper* │ │ ├── enableAutoAdjustMicVolume() │ │ ├── enableStereoAudio() │ │ └── setEchoCancellationLevel() │ │ │ ├── GetAudioDeviceTestHelper() → IZoomVideoSDKTestAudioDeviceHelper* │ │ ├── startMicTest() / stopMicTest() │ │ ├── startSpeakerTest() / stopSpeakerTest() │ │ └── playMicTest() │ │ │ ├── getVideoSettingHelper() → IZoomVideoSDKVideoSettingHelper* │ │ ├── enableHDVideo() │ │ ├── enableMirrorEffect() │ │ └── setVideoQualityPreference() │ │ │ └── getShareSettingHelper() → IZoomVideoSDKShareSettingHelper* │ ├── enableGreenBorderWhenSharing() │ └── setShareScreenSetting() │ ├─► Level 1: Advanced Helpers (Special features) │ ├── getNetworkConnectionHelper() → IZoomVideoSDKNetworkConnectionHelper* │ │ └── getNetworkType() │ │ │ ├── getCRCHelper() → IZoomVideoSDKCRCHelper* │ │ └── callCRCDevice(address, protocol) │ │ │ ├── getSubSessionHelper() → IZoomVideoSDKSubSessionHelper* │ │ ├── createSubSession(name) │ │ ├── joinSubSession(id) │ │ ├── leaveSubSession() │ │ └── getSubSessionList() │ │ │ ├── getIncomingLiveStreamHelper()→ IZoomVideoSDKIncomingLiveStreamHelper* │ │ ├── bindIncomingLiveStream(streamKeyId) │ │ ├── unbindIncomingLiveStream(streamKeyId) │ │ └── startIncomingLiveStream(streamKeyId) │ │ │ ├── getBroadcastStreamingController() → IZoomVideoSDKBroadcastStreamingController* │ │ ├── startBroadcast() │ │ └── stopBroadcast() │ │ │ ├── getBroadcastStreamingViewer()→ IZoomVideoSDKBroadcastStreamingViewer* │ │ └── joinBroadcast(channelId) │ │ │ └── getRealTimeMediaStreamsHelper() → IZoomVideoSDKRTMSHelper* (RTMS) │ ├── startRealTimeMediaStream() │ └── stopRealTimeMediaStream() │ └─► Level 1: Session Object │ └── getSessionInfo() → IZoomVideoSDKSession* ├── getSessionName() ├── getSessionID() ├── getSessionHost() → IZoomVideoSDKUser* ├── getMyself() → IZoomVideoSDKUser* │ │ │ └─► Level 3: IZoomVideoSDKUser (LOCAL - yourself) │ ├── getUserID() / getUserName() │ ├── isHost() / isManager() │ ├── getVideoStatus() → ZoomVideoSDKVideoStatus │ ├── getAudioStatus() → ZoomVideoSDKAudioStatus │ │ │ ├── GetVideoCanvas() → IZoomVideoSDKCanvas* [SDK RENDERING] │ │ └─► Level 4: Canvas API │ │ ├── subscribeWithView(hwnd, aspect, resolution) │ │ ├── unSubscribeWithView(hwnd) │ │ ├── setAspectMode(aspect) │ │ └── setResolution(resolution) │ │ │ ├── GetVideoPipe() → IZoomVideoSDKRawDataPipe* [RAW DATA] │ │ └─► Level 4: Raw Data Pipe │ │ ├── subscribe(resolution, delegate) │ │ ├── unSubscribe(delegate) │ │ ├── getVideoStatus() │ │ │ │ │ └─► Level 5: IZoomVideoSDKRawDataPipeDelegate (your callback) │ │ ├── onRawDataFrameReceived(YUVRawDataI420*) │ │ └── onRawDataStatusChanged(status) │ │ │ ├── getShareActionList() → IVideoSDKVector* │ │ └─► Level 4: IZoomVideoSDKShareAction (for share subscription) │ │ ├── getShareCanvas() → IZoomVideoSDKCanvas* │ │ ├── getSharePipe() → IZoomVideoSDKRawDataPipe* │ │ ├── getShareStatus() → ZoomVideoSDKShareStatus │ │ ├── getShareType() → ZoomVideoSDKShareType │ │ ├── getShareSourceId() │ │ ├── isAnnotationPrivilegeEnabled() │ │ └── getRemoteControlHelper() → IZoomVideoSDKRemoteControlHelper* (Win/Mac) │ │ │ └── getRemoteCameraControlHelper() → IZoomVideoSDKRemoteCameraControlHelper* │ └── getRemoteUsers() → IVideoSDKVector* │ └─► Level 3: IZoomVideoSDKUser (REMOTE - other participants) ├── [Same methods as local user] ├── GetVideoCanvas() → Subscribe to their video └── GetVideoPipe() → Get their raw frames ┌─────────────────────────────────────────────────────────────────────────┐ │ CALLBACK PATH (from IZoomVideoSDKDelegate) │ │ │ │ ⚠️ CRITICAL: Share subscription uses IZoomVideoSDKShareAction from │ │ callback, NOT user->GetShareCanvas()! │ │ │ │ onUserShareStatusChanged(pShareHelper, pUser, pShareAction) │ │ │ │ │ └─► IZoomVideoSDKShareAction* (received in callback) │ │ ├── getShareCanvas() → IZoomVideoSDKCanvas* │ │ │ └── subscribeWithView(hwnd, aspect) │ │ │ └── unSubscribeWithView(hwnd) │ │ ├── getSharePipe() → IZoomVideoSDKRawDataPipe* │ │ │ └── subscribe(resolution, delegate) │ │ │ └── unSubscribe(delegate) │ │ ├── getShareStatus() → ZoomVideoSDKShareStatus │ │ ├── getShareType() → ZoomVideoSDKShareType │ │ ├── getShareSourceId() │ │ ├── getShareSourceContentSize() → ZoomVideoSDKViewSize │ │ ├── isAnnotationPrivilegeEnabled() │ │ └── getRemoteControlHelper() → IZoomVideoSDKRemoteControlHelper│ │ │ │ Pattern: Subscribe to share in onUserShareStatusChanged callback │ │ void onUserShareStatusChanged(..., IZoomVideoSDKShareAction* action) { │ │ if (action->getShareStatus() == ZoomVideoSDKShareStatus_Start) { │ │ action->getShareCanvas()->subscribeWithView(hwnd, aspect); │ │ } │ │ } │ └─────────────────────────────────────────────────────────────────────────┘ ``` --- ## Key Difference from Meeting SDK | Aspect | Meeting SDK | Video SDK | |--------|-------------|-----------| | **Root Object** | `IMeetingService` | `IZoomVideoSDK` | | **Feature Access** | Controllers (`GetMeetingAudioController()`) | Helpers (`getAudioHelper()`) | | **Video Subscription** | Per-user renderers | Per-user Canvas/Pipe | | **Share Subscription** | Via callback's `IZoomVideoSDKShareAction` | Via callback's `IZoomVideoSDKShareAction` | | **Depth** | 4 levels max | 5 levels max | --- ## When to Use Each Level | Level | When | Example | |-------|------|---------| | **Level 1** | After SDK init, control YOUR streams | `sdk->getVideoHelper()->startVideo()` | | **Level 2** | After session join, get session info | `sdk->getSessionInfo()->getMyself()` | | **Level 3** | Get user objects | `session->getRemoteUsers()` | | **Level 4** | Subscribe to video/share | `user->GetVideoCanvas()->subscribeWithView()` | | **Level 5** | Receive raw frames | `pipe->subscribe(res, myDelegate)` | --- ## Two Rendering Paths The SDK provides **two distinct paths** for video rendering: ### Path A: Canvas API (SDK-Rendered) ``` IZoomVideoSDKUser └── GetVideoCanvas() └── IZoomVideoSDKCanvas └── subscribeWithView(HWND, aspect, resolution) └── SDK renders directly to your window ``` **Pros**: Best quality, no CPU overhead, automatic scaling **Use for**: Standard video conferencing UI ### Path B: Raw Data Pipe (Self-Rendered) ``` IZoomVideoSDKUser └── GetVideoPipe() └── IZoomVideoSDKRawDataPipe └── subscribe(resolution, delegate) └── Your IZoomVideoSDKRawDataPipeDelegate └── onRawDataFrameReceived(YUVRawDataI420*) └── You convert YUV→RGB and render ``` **Pros**: Full control over frames, can process/filter/record **Use for**: Custom effects, AI processing, recording --- ## Universal Pattern (3 Steps) Every feature follows the **same pattern**: ```cpp // Step 1: Navigate to the helper (singleton) IZoomVideoSDKVideoHelper* videoHelper = sdk->getVideoHelper(); // Step 2: Use it videoHelper->startVideo(); // For subscriptions, get user first: IZoomVideoSDKUser* user = sdk->getSessionInfo()->getMyself(); IZoomVideoSDKCanvas* canvas = user->GetVideoCanvas(); canvas->subscribeWithView(hwnd, aspect, resolution); ``` For **event-driven features**, implement `IZoomVideoSDKDelegate`: ```cpp // Step 1: Implement delegate class MyDelegate : public IZoomVideoSDKDelegate { void onUserVideoStatusChanged(...) override { // React to video status changes } // ... all 80+ callbacks }; // Step 2: Register sdk->addListener(new MyDelegate()); // Step 3: Events arrive automatically ``` --- ## Navigation by Feature | Feature | Navigation Path | |---------|-----------------| | **Start camera** | `sdk->getVideoHelper()->startVideo()` | | **Stop camera** | `sdk->getVideoHelper()->stopVideo()` | | **Switch camera** | `sdk->getVideoHelper()->switchCamera(deviceId)` | | **Camera list** | `sdk->getVideoHelper()->getCameraList()` | | **Mute audio** | `sdk->getAudioHelper()->muteAudio(user)` | | **Unmute audio** | `sdk->getAudioHelper()->unmuteAudio(user)` | | **Start audio** | `sdk->getAudioHelper()->startAudio()` | | **Mic list** | `sdk->getAudioHelper()->getMicList()` | | **Speaker list** | `sdk->getAudioHelper()->getSpeakerList()` | | **Test mic** | `sdk->GetAudioDeviceTestHelper()->startMicTest()` | | **Test speaker** | `sdk->GetAudioDeviceTestHelper()->startSpeakerTest()` | | **Send chat** | `sdk->getChatHelper()->sendChatToAll(msg)` | | **Start share** | `sdk->getShareHelper()->startShareScreen(monitorId)` | | **Stop share** | `sdk->getShareHelper()->stopShare()` | | **Subscribe video** | `user->GetVideoCanvas()->subscribeWithView(hwnd, ...)` | | **Get raw frames** | `user->GetVideoPipe()->subscribe(res, delegate)` | | **Subscribe share** | `shareAction->getShareCanvas()->subscribeWithView(hwnd, aspect)` ⚠️ From callback! | | **Get raw share** | `shareAction->getSharePipe()->subscribe(res, delegate)` ⚠️ From callback! | | **Kick user** | `sdk->getUserHelper()->removeUser(user)` | | **Make host** | `sdk->getUserHelper()->makeHost(user)` | | **Send command** | `sdk->getCmdChannel()->sendCommandToAll(cmd)` | | **Get myself** | `sdk->getSessionInfo()->getMyself()` | | **Get remote users** | `sdk->getSessionInfo()->getRemoteUsers()` | | **Join subsession** | `sdk->getSubSessionHelper()->joinSubSession(id)` | | **Start broadcast** | `sdk->getBroadcastStreamingController()->startBroadcast()` | | **Transcription** | `sdk->getLiveTranscriptionHelper()->startLiveTranscription()` | --- ## Critical Timing Rules ### 1. Helpers Control YOUR Streams Only ```cpp // videoHelper controls YOUR camera, not others' sdk->getVideoHelper()->startVideo(); // Starts YOUR camera sdk->getVideoHelper()->stopVideo(); // Stops YOUR camera // To SEE other users' video, subscribe via their Canvas/Pipe IZoomVideoSDKUser* remoteUser = ...; remoteUser->GetVideoCanvas()->subscribeWithView(hwnd, ...); ``` ### 2. Subscribe in onUserVideoStatusChanged, NOT onUserJoin ```cpp // WRONG - user's video may not be ready yet void onUserJoin(..., userList) { user->GetVideoCanvas()->subscribeWithView(hwnd, ...); // Error 2! } // CORRECT - wait for video status change void onUserVideoStatusChanged(..., userList) { for (auto user : userList) { if (user->GetVideoPipe()->getVideoStatus().isOn) { user->GetVideoCanvas()->subscribeWithView(hwnd, ...); // Works! } } } ``` ### 3. ShareAction Comes from Callback (CRITICAL!) ⚠️ **Share subscription is DIFFERENT from video subscription!** ```cpp // WRONG - Don't use user->GetShareCanvas() for remote share! user->GetShareCanvas()->subscribeWithView(hwnd, ...); // Won't work! // CORRECT - Use IZoomVideoSDKShareAction from the callback void onUserShareStatusChanged(IZoomVideoSDKShareHelper* pShareHelper, IZoomVideoSDKUser* pUser, IZoomVideoSDKShareAction* pShareAction) { if (!pShareAction) return; ZoomVideoSDKShareStatus status = pShareAction->getShareStatus(); if (status == ZoomVideoSDKShareStatus_Start || status == ZoomVideoSDKShareStatus_Resume) { // Canvas API (SDK-rendered) IZoomVideoSDKCanvas* shareCanvas = pShareAction->getShareCanvas(); if (shareCanvas) { shareCanvas->subscribeWithView(shareHwnd, ZoomVideoSDKVideoAspect_Original); } // OR Raw Data Pipe (self-rendered) // IZoomVideoSDKRawDataPipe* sharePipe = pShareAction->getSharePipe(); // sharePipe->subscribe(ZoomVideoSDKResolution_720P, myDelegate); } else if (status == ZoomVideoSDKShareStatus_Stop) { // Unsubscribe when share stops IZoomVideoSDKCanvas* shareCanvas = pShareAction->getShareCanvas(); if (shareCanvas) { shareCanvas->unSubscribeWithView(shareHwnd); } } } ``` **Why?** The `IZoomVideoSDKShareAction` represents a specific share stream and is only valid within the callback context. You cannot navigate to it via user objects. ### 4. Check nullptr Before Use ```cpp IZoomVideoSDKCanvas* canvas = user->GetVideoCanvas(); if (canvas) { canvas->subscribeWithView(hwnd, aspect, resolution); } ``` --- ## Practical Rules ### 1. Get Helpers After Initialize ```cpp // WRONG - SDK not initialized IZoomVideoSDKVideoHelper* helper = sdk->getVideoHelper(); // nullptr! sdk->initialize(params); // CORRECT sdk->initialize(params); IZoomVideoSDKVideoHelper* helper = sdk->getVideoHelper(); // Valid ``` ### 2. Get Session/Users After Join ```cpp // WRONG - not in session yet IZoomVideoSDKSession* session = sdk->getSessionInfo(); // nullptr! sdk->joinSession(context); // CORRECT - wait for onSessionJoin callback void onSessionJoin() { IZoomVideoSDKSession* session = sdk->getSessionInfo(); // Valid IZoomVideoSDKUser* myself = session->getMyself(); // Valid } ``` ### 3. One HWND Per Video Stream ```cpp // Each user needs their own window HWND selfWindow = CreateWindow(...); HWND user1Window = CreateWindow(...); HWND user2Window = CreateWindow(...); myself->GetVideoCanvas()->subscribeWithView(selfWindow, ...); user1->GetVideoCanvas()->subscribeWithView(user1Window, ...); user2->GetVideoCanvas()->subscribeWithView(user2Window, ...); ``` --- ## Deepest Paths (Maximum Depth = 5) | Path | Use Case | |------|----------| | `IZoomVideoSDK` → `getVideoHelper()` → `getCameraList()` → `IZoomVideoSDKCameraDevice` → `getDeviceId()` | Enumerate cameras | | `IZoomVideoSDK` → `getSessionInfo()` → `getMyself()` → `GetVideoPipe()` → `subscribe(delegate)` | Raw self video | | `IZoomVideoSDK` → `getSessionInfo()` → `getRemoteUsers()` → `user->GetVideoCanvas()` → `subscribeWithView()` | Remote video display | --- ## Related Documentation - [API Reference](../references/windows-reference.md) - Complete method signatures - [SKILL.md](../SKILL.md) - Main skill overview with code examples - [Video Rendering Guide](../SKILL.md#video-rendering---two-approaches) - Canvas vs Raw Data comparison --- **TL;DR**: Start at `IZoomVideoSDK`, navigate to helpers for YOUR streams, navigate to users for THEIR streams. Subscribe to video in `onUserVideoStatusChanged`, not `onUserJoin`.