# Session Join Pattern Complete working code for initializing the SDK and joining a session. ## Overview ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ InitSDK │───►│ AddListener│───►│ JoinSession │───►│ onSessionJoin│ │ │ │ (delegate) │ │ (JWT) │ │ callback │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ ``` --- ## Complete Working Code ### main.cpp ```cpp #include #include #include #include #include #include #include // SDK headers #include "zoom_video_sdk_api.h" #include "zoom_video_sdk_interface.h" #include "zoom_video_sdk_delegate_interface.h" // JSON parsing (optional - for config file) #include USING_ZOOM_VIDEO_SDK_NAMESPACE // Global state IZoomVideoSDK* g_sdk = nullptr; bool g_inSession = false; bool g_exit = false; // Configuration std::wstring g_jwt; std::wstring g_sessionName; std::wstring g_sessionPassword; std::wstring g_userName; //───────────────────────────────────────────────────────────────────────────── // DELEGATE IMPLEMENTATION //───────────────────────────────────────────────────────────────────────────── class MyDelegate : public IZoomVideoSDKDelegate { public: // === SESSION LIFECYCLE === void onSessionJoin() override { std::cout << "[EVENT] Session joined successfully!" << std::endl; g_inSession = true; // Connect audio IZoomVideoSDKAudioHelper* audioHelper = g_sdk->getAudioHelper(); if (audioHelper) { audioHelper->startAudio(); std::cout << "[ACTION] Audio connected" << std::endl; } // Start video IZoomVideoSDKVideoHelper* videoHelper = g_sdk->getVideoHelper(); if (videoHelper) { videoHelper->startVideo(); std::cout << "[ACTION] Video started" << std::endl; } } void onSessionLeave() override { std::cout << "[EVENT] Session left" << std::endl; g_inSession = false; g_exit = true; } void onError(ZoomVideoSDKErrors errorCode, int detailErrorCode) override { std::cout << "[ERROR] Code: " << errorCode << ", Detail: " << detailErrorCode << std::endl; } // === USER EVENTS === void onUserJoin(IZoomVideoSDKUserHelper* helper, IVideoSDKVector* userList) override { for (int i = 0; i < userList->GetCount(); i++) { IZoomVideoSDKUser* user = userList->GetItem(i); std::wcout << L"[EVENT] User joined: " << user->getUserName() << std::endl; } } void onUserLeave(IZoomVideoSDKUserHelper* helper, IVideoSDKVector* userList) override { for (int i = 0; i < userList->GetCount(); i++) { IZoomVideoSDKUser* user = userList->GetItem(i); std::wcout << L"[EVENT] User left: " << user->getUserName() << std::endl; } } void onUserVideoStatusChanged(IZoomVideoSDKVideoHelper* helper, IVideoSDKVector* userList) override { for (int i = 0; i < userList->GetCount(); i++) { IZoomVideoSDKUser* user = userList->GetItem(i); bool isOn = user->GetVideoPipe()->getVideoStatus().isOn; std::wcout << L"[EVENT] Video " << (isOn ? L"ON" : L"OFF") << L": " << user->getUserName() << std::endl; } } void onUserAudioStatusChanged(IZoomVideoSDKAudioHelper* helper, IVideoSDKVector* userList) override { for (int i = 0; i < userList->GetCount(); i++) { IZoomVideoSDKUser* user = userList->GetItem(i); bool isMuted = user->getAudioStatus().isMuted; std::wcout << L"[EVENT] Audio " << (isMuted ? L"muted" : L"unmuted") << L": " << user->getUserName() << std::endl; } } // === CHAT === void onChatNewMessageNotify(IZoomVideoSDKChatHelper* helper, IZoomVideoSDKChatMessage* msg) override { std::wcout << L"[CHAT] " << msg->getSendUser()->getUserName() << L": " << msg->getContent() << std::endl; } // === REQUIRED EMPTY IMPLEMENTATIONS === // (All pure virtual methods must be implemented) void onSessionLeave(ZoomVideoSDKSessionLeaveReason reason) override {} void onSessionNeedPassword(IZoomVideoSDKPasswordHandler* handler) override {} void onSessionPasswordWrong(IZoomVideoSDKPasswordHandler* handler) override {} void onUserHostChanged(IZoomVideoSDKUserHelper* helper, IZoomVideoSDKUser* user) override {} void onUserManagerChanged(IZoomVideoSDKUser* user) override {} void onUserNameChanged(IZoomVideoSDKUser* user) override {} void onUserActiveAudioChanged(IZoomVideoSDKAudioHelper* helper, IVideoSDKVector* list) override {} void onMixedAudioRawDataReceived(AudioRawData* data) override {} void onOneWayAudioRawDataReceived(AudioRawData* data, IZoomVideoSDKUser* user) override {} void onSharedAudioRawDataReceived(AudioRawData* data) override {} void onUserShareStatusChanged(IZoomVideoSDKShareHelper* helper, IZoomVideoSDKUser* user, IZoomVideoSDKShareAction* action) override {} void onShareContentChanged(IZoomVideoSDKShareHelper* helper, IZoomVideoSDKUser* user, IZoomVideoSDKShareAction* action) override {} void onFailedToStartShare(IZoomVideoSDKShareHelper* helper, IZoomVideoSDKUser* user) override {} void onShareContentSizeChanged(IZoomVideoSDKShareHelper* helper, IZoomVideoSDKUser* user, IZoomVideoSDKShareAction* action) override {} void onLiveStreamStatusChanged(IZoomVideoSDKLiveStreamHelper* helper, ZoomVideoSDKLiveStreamStatus status) override {} void onChatMsgDeleteNotification(IZoomVideoSDKChatHelper* helper, const zchar_t* msgID, ZoomVideoSDKChatMessageDeleteType type) override {} void onChatPrivilegeChanged(IZoomVideoSDKChatHelper* helper, ZoomVideoSDKChatPrivilegeType privilege) override {} void onLiveTranscriptionStatus(ZoomVideoSDKLiveTranscriptionStatus status) override {} void onLiveTranscriptionMsgInfoReceived(ILiveTranscriptionMessageInfo* info) override {} void onOriginalLanguageMsgReceived(ILiveTranscriptionMessageInfo* info) override {} void onLiveTranscriptionMsgError(ILiveTranscriptionLanguage* spoken, ILiveTranscriptionLanguage* transcript) override {} void onProxyDetectComplete() override {} void onProxySettingNotification(IZoomVideoSDKProxySettingHandler* handler) override {} void onSSLCertVerifiedFailNotification(IZoomVideoSDKSSLCertificateInfo* info) override {} void onUserVideoNetworkStatusChanged(ZoomVideoSDKNetworkStatus status, IZoomVideoSDKUser* user) override {} void onCallCRCDeviceStatusChanged(ZoomVideoSDKCRCCallStatus status) override {} void onVideoCanvasSubscribeFail(ZoomVideoSDKSubscribeFailReason reason, IZoomVideoSDKUser* user, void* handle) override {} void onShareCanvasSubscribeFail(IZoomVideoSDKUser* user, void* handle, IZoomVideoSDKShareAction* action) override {} void onAnnotationHelperCleanUp(IZoomVideoSDKAnnotationHelper* helper) override {} void onAnnotationPrivilegeChange(IZoomVideoSDKUser* user, IZoomVideoSDKShareAction* action) override {} void onAnnotationHelperActived(void* handle) override {} void onSendFileStatus(IZoomVideoSDKSendFile* file, const FileTransferStatus& status) override {} void onReceiveFileStatus(IZoomVideoSDKReceiveFile* file, const FileTransferStatus& status) override {} void onInviteByPhoneStatus(PhoneStatus status, PhoneFailedReason reason) override {} void onCalloutJoinSuccess(IZoomVideoSDKUser* user, const zchar_t* phoneNumber) override {} void onCameraControlRequestResult(IZoomVideoSDKUser* user, bool approved) override {} void onCameraControlRequestReceived(IZoomVideoSDKUser* user, ZoomVideoSDKCameraControlRequestType type, IZoomVideoSDKCameraControlRequestHandler* handler) override {} void onCommandReceived(IZoomVideoSDKUser* sender, const zchar_t* cmd) override {} void onCommandChannelConnectResult(bool success) override {} void onCloudRecordingStatus(RecordingStatus status, IZoomVideoSDKRecordingConsentHandler* handler) override {} void onHostAskUnmute() override {} void onUserRecordingConsent(IZoomVideoSDKUser* user) override {} void onMultiCameraStreamStatusChanged(ZoomVideoSDKMultiCameraStreamStatus status, IZoomVideoSDKUser* user, IZoomVideoSDKRawDataPipe* pipe) override {} void onMicSpeakerVolumeChanged(unsigned int micVol, unsigned int speakerVol) override {} void onAudioDeviceStatusChanged(ZoomVideoSDKAudioDeviceType type, ZoomVideoSDKAudioDeviceStatus status) override {} void onTestMicStatusChanged(ZoomVideoSDK_TESTMIC_STATUS status) override {} void onSelectedAudioDeviceChanged() override {} void onCameraListChanged() override {} void onSpotlightVideoChanged(IZoomVideoSDKVideoHelper* helper, IVideoSDKVector* list) override {} void onVideoAlphaChannelStatusChanged(bool isOn) override {} void onRemoteControlStatus(IZoomVideoSDKUser* user, IZoomVideoSDKShareAction* action, ZoomVideoSDKRemoteControlStatus status) override {} void onRemoteControlRequestReceived(IZoomVideoSDKUser* user, IZoomVideoSDKShareAction* action, IZoomVideoSDKRemoteControlRequestHandler* handler) override {} void onRemoteControlServiceInstallResult(bool success) override {} void onBindIncomingLiveStreamResponse(bool success, const zchar_t* streamKeyID) override {} void onUnbindIncomingLiveStreamResponse(bool success, const zchar_t* streamKeyID) override {} void onIncomingLiveStreamStatusResponse(bool success, IVideoSDKVector* list) override {} void onStartIncomingLiveStreamResponse(bool success, const zchar_t* streamKeyID) override {} void onStopIncomingLiveStreamResponse(bool success, const zchar_t* streamKeyID) override {} void onUserWhiteboardShareStatusChanged(IZoomVideoSDKUser* user, IZoomVideoSDKWhiteboardHelper* helper) override {} }; //───────────────────────────────────────────────────────────────────────────── // CONFIGURATION //───────────────────────────────────────────────────────────────────────────── bool LoadConfig(const std::string& filename) { std::ifstream file(filename); if (!file.is_open()) { std::cerr << "Cannot open " << filename << std::endl; return false; } Json::Value config; file >> config; std::string jwt = config["jwt"].asString(); std::string sessionName = config["session_name"].asString(); std::string password = config.get("password", "").asString(); std::string userName = config.get("user_name", "Bot").asString(); g_jwt = std::wstring(jwt.begin(), jwt.end()); g_sessionName = std::wstring(sessionName.begin(), sessionName.end()); g_sessionPassword = std::wstring(password.begin(), password.end()); g_userName = std::wstring(userName.begin(), userName.end()); return true; } //───────────────────────────────────────────────────────────────────────────── // SDK OPERATIONS //───────────────────────────────────────────────────────────────────────────── bool InitializeSDK() { g_sdk = CreateZoomVideoSDKObj(); if (!g_sdk) { std::cerr << "Failed to create SDK object" << std::endl; return false; } ZoomVideoSDKInitParams params; params.domain = L"https://zoom.us"; params.enableLog = true; params.logFilePrefix = L"zoom_video_sdk"; params.videoRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap; params.shareRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap; params.audioRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap; ZoomVideoSDKErrors err = g_sdk->initialize(params); if (err != ZoomVideoSDKErrors_Success) { std::cerr << "SDK initialize failed: " << err << std::endl; return false; } std::cout << "SDK initialized successfully" << std::endl; return true; } bool JoinSession() { // Register delegate BEFORE joining g_sdk->addListener(new MyDelegate()); ZoomVideoSDKSessionContext context; context.sessionName = g_sessionName.c_str(); context.userName = g_userName.c_str(); context.token = g_jwt.c_str(); context.sessionPassword = g_sessionPassword.c_str(); // IMPORTANT: Connect audio in onSessionJoin callback context.audioOption.connect = false; context.audioOption.mute = true; context.videoOption.localVideoOn = false; IZoomVideoSDKSession* session = g_sdk->joinSession(context); if (!session) { std::cerr << "joinSession returned null" << std::endl; return false; } std::cout << "Join session initiated..." << std::endl; return true; } void Cleanup() { if (g_sdk) { if (g_inSession) { g_sdk->leaveSession(false); } g_sdk->cleanup(); DestroyZoomVideoSDKObj(); g_sdk = nullptr; } } //───────────────────────────────────────────────────────────────────────────── // MAIN //───────────────────────────────────────────────────────────────────────────── int main() { // Initialize COM (required for some SDK features) CoInitialize(NULL); // Load configuration if (!LoadConfig("config.json")) { return 1; } // Initialize SDK if (!InitializeSDK()) { return 1; } // Join session if (!JoinSession()) { Cleanup(); return 1; } // ═══════════════════════════════════════════════════════════════════════ // CRITICAL: Windows message loop // Without this, callbacks will NEVER fire! // ═══════════════════════════════════════════════════════════════════════ std::cout << "Running message loop (Ctrl+C to exit)..." << std::endl; MSG msg; while (!g_exit) { // Process all pending Windows messages while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { g_exit = true; break; } TranslateMessage(&msg); DispatchMessage(&msg); } // Small sleep to avoid busy-waiting std::this_thread::sleep_for(std::chrono::milliseconds(10)); } // Cleanup Cleanup(); CoUninitialize(); std::cout << "Exited cleanly" << std::endl; return 0; } ``` ### config.json ```json { "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "session_name": "my-session", "password": "", "user_name": "Bot" } ``` --- ## Key Points ### 1. Register Delegate BEFORE Joining ```cpp // CORRECT g_sdk->addListener(new MyDelegate()); g_sdk->joinSession(context); // WRONG - callbacks will be missed g_sdk->joinSession(context); g_sdk->addListener(new MyDelegate()); // Too late! ``` ### 2. Set audioOption.connect = false ```cpp context.audioOption.connect = false; // Connect in onSessionJoin context.audioOption.mute = true; ``` Then connect audio in the callback: ```cpp void onSessionJoin() override { g_sdk->getAudioHelper()->startAudio(); } ``` ### 3. Windows Message Loop is MANDATORY ```cpp // This is NOT optional! while (!g_exit) { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } Sleep(10); } ``` ### 4. Implement ALL Delegate Methods The `IZoomVideoSDKDelegate` interface has 80+ pure virtual methods. **All must be implemented**, even if empty. --- ## Related Documentation - [Windows Message Loop](../troubleshooting/windows-message-loop.md) - Why message loop is critical - [Delegate Methods](../references/delegate-methods.md) - All 80+ callback methods - [Video Rendering](video-rendering.md) - Subscribe to video after join - [SDK Architecture Pattern](../concepts/sdk-architecture-pattern.md) - Universal pattern --- **TL;DR**: Initialize → Add delegate → Join with `audioOption.connect = false` → Run message loop → Connect audio in `onSessionJoin`.