Setting the file. One moment.
Subchapter 150.5
concepts/sdk-architecture-pattern.mdMarkdown9 KBView on GitHub
The Zoom Video SDK follows a perfectly consistent architecture. Every feature works the same way:
┌─────────────────────────────────────────────────────────────────┐
│ UNIVERSAL 3-STEP PATTERN │
├─────────────────────────────────────────────────────────────────┤
│ 1. GET SINGLETON → SDK, helpers, session, users │
│ 2. IMPLEMENT DELEGATE → Event callbacks (IZoomVideoSDKDelegate)│
│ 3. SUBSCRIBE & USE → Call methods, receive events │
└─────────────────────────────────────────────────────────────────┘Once you understand this pattern, you can implement ANY feature.
The SDK is a tree of singleton objects. You navigate to what you need:
// Root singleton
IZoomVideoSDK* sdk = CreateZoomVideoSDKObj();
// Level 1: Helpers (control YOUR streams)
IZoomVideoSDKVideoHelper* videoHelper = sdk->getVideoHelper();
IZoomVideoSDKAudioHelper* audioHelper = sdk->getAudioHelper();
IZoomVideoSDKShareHelper* shareHelper = sdk->getShareHelper();
IZoomVideoSDKChatHelper* chatHelper = sdk->getChatHelper();
// Level 2: Session
IZoomVideoSDKSession* session = sdk->getSessionInfo();
// Level 3: Users
IZoomVideoSDKUser* myself = session->getMyself();
IVideoSDKVector<IZoomVideoSDKUser*>* remoteUsers = session->getRemoteUsers();
// Level 4: Canvas/Pipe (per user)
IZoomVideoSDKCanvas* canvas = user->GetVideoCanvas();
IZoomVideoSDKRawDataPipe* pipe = user->GetVideoPipe();Key insight: You don’t construct these objects. You navigate to them.
The SDK uses observer pattern for events. Implement IZoomVideoSDKDelegate:
class MyDelegate : public IZoomVideoSDKDelegate {
public:
// Session lifecycle
void onSessionJoin() override {
std::cout << "Joined session!" << std::endl;
// Safe to start video, subscribe to users, etc.
}
void onSessionLeave() override {
std::cout << "Left session" << std::endl;
}
// User events
void onUserJoin(IZoomVideoSDKUserHelper* helper,
IVideoSDKVector<IZoomVideoSDKUser*>* userList) override {
// New users joined - but don't subscribe to video yet!
}
void onUserVideoStatusChanged(IZoomVideoSDKVideoHelper* helper,
IVideoSDKVector<IZoomVideoSDKUser*>* userList) override {
// NOW subscribe to video - it's ready
for (int i = 0; i < userList->GetCount(); i++) {
IZoomVideoSDKUser* user = userList->GetItem(i);
if (user->GetVideoPipe()->getVideoStatus().isOn) {
user->GetVideoCanvas()->subscribeWithView(hwnd, aspect, resolution);
}
}
}
// Chat events
void onChatNewMessageNotify(IZoomVideoSDKChatHelper* helper,
IZoomVideoSDKChatMessage* msg) override {
std::wcout << L"Chat: " << msg->getContent() << std::endl;
}
// Share events
void onUserShareStatusChanged(IZoomVideoSDKShareHelper* helper,
IZoomVideoSDKUser* user,
IZoomVideoSDKShareAction* shareAction) override {
// Subscribe to remote user's screen share
shareAction->subscribeWithView(shareHwnd, ZoomVideoSDKVideoAspect_Original);
}
// ... 80+ more callbacks (implement as empty if not needed)
void onError(ZoomVideoSDKErrors errorCode, int detailErrorCode) override {}
void onUserLeave(IZoomVideoSDKUserHelper*, IVideoSDKVector<IZoomVideoSDKUser*>*) override {}
// etc.
};Key insight: All 80+ methods must be implemented (even if empty).
Register your delegate and call methods:
// Register delegate BEFORE joining
sdk->addListener(new MyDelegate());
// Initialize
ZoomVideoSDKInitParams initParams;
initParams.domain = L"https://zoom.us";
initParams.videoRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;
sdk->initialize(initParams);
// Join session
ZoomVideoSDKSessionContext context;
context.sessionName = L"my-session";
context.userName = L"Bot";
context.token = L"your-jwt-token";
context.audioOption.connect = false; // Connect audio in onSessionJoin
sdk->joinSession(context);
// In onSessionJoin callback:
void onSessionJoin() override {
// Start audio
sdk->getAudioHelper()->startAudio();
// Start video
sdk->getVideoHelper()->startVideo();
// Subscribe to self video
IZoomVideoSDKUser* myself = sdk->getSessionInfo()->getMyself();
myself->GetVideoCanvas()->subscribeWithView(selfHwnd, aspect, resolution);
}// Get singleton
IZoomVideoSDKAudioHelper* audioHelper = sdk->getAudioHelper();
// Use
audioHelper->startAudio();
audioHelper->muteAudio(user);
audioHelper->unmuteAudio(user);
// Events arrive in delegate
void onUserAudioStatusChanged(...) override { }// Get singleton
IZoomVideoSDKVideoHelper* videoHelper = sdk->getVideoHelper();
// Use
videoHelper->startVideo();
videoHelper->stopVideo();
videoHelper->switchCamera(deviceId);
// Subscribe to user's video
user->GetVideoCanvas()->subscribeWithView(hwnd, aspect, resolution);
// Events arrive in delegate
void onUserVideoStatusChanged(...) override { }// Get singleton
IZoomVideoSDKChatHelper* chatHelper = sdk->getChatHelper();
// Use
chatHelper->sendChatToAll(L"Hello everyone!");
chatHelper->sendChatToUser(user, L"Private message");
// Events arrive in delegate
void onChatNewMessageNotify(...) override { }// Get singleton
IZoomVideoSDKShareHelper* shareHelper = sdk->getShareHelper();
// Use (start YOUR share)
shareHelper->startShareScreen(monitorId);
shareHelper->stopShare();
// Subscribe to REMOTE share (in callback)
void onUserShareStatusChanged(..., IZoomVideoSDKShareAction* shareAction) override {
shareAction->subscribeWithView(hwnd, aspect);
}// Get singleton
IZoomVideoSDKCmdChannel* cmdChannel = sdk->getCmdChannel();
// Use
cmdChannel->sendCommandToAll(L"custom-data");
cmdChannel->sendCommand(user, L"private-data");
// Events arrive in delegate
void onCommandReceived(IZoomVideoSDKUser* sender, const zchar_t* cmd) override { }| Aspect | Design Choice | Benefit |
|---|---|---|
| Singletons | One instance per feature | No object lifecycle management |
| Observer | Delegate callbacks | Decoupled, event-driven code |
| Navigation | Tree structure | Predictable access patterns |
| Consistency | Same pattern everywhere | Learn once, apply everywhere |
// WRONG - video not ready
void onUserJoin(...) {
user->GetVideoCanvas()->subscribeWithView(hwnd, ...); // Error!
}
// CORRECT - wait for video status
void onUserVideoStatusChanged(...) {
if (user->GetVideoPipe()->getVideoStatus().isOn) {
user->GetVideoCanvas()->subscribeWithView(hwnd, ...);
}
}// WRONG - callbacks never fire
sdk->joinSession(context);
while (true) { Sleep(100); } // No message pump!
// CORRECT - process Windows messages
while (!done) {
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(10);
}// WRONG - helpers control YOUR streams only
sdk->getVideoHelper()->startVideo(); // Starts YOUR camera
sdk->getVideoHelper()->stopVideo(); // Stops YOUR camera
// CORRECT - subscribe to remote users via their Canvas
remoteUser->GetVideoCanvas()->subscribeWithView(hwnd, ...);TL;DR: Get singleton → Implement delegate → Subscribe & use. This works for every feature.