# High-Level Scenarios for Meeting SDK Linux This guide covers common bot architectures and patterns that remain stable across SDK versions, with flexible approaches to handle API changes. ## Scenario 1: Transcription Bot **Goal**: Join meetings to transcribe conversations in real-time. ### Architecture ``` ┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐ │ Meeting │───►│ Meeting SDK │───►│ Audio Stream │───►│ Transcription│ │ Starts │ │ Join + Auth │ │ (PCM 32kHz) │ │ Service │ └─────────────┘ └──────────────┘ └─────────────────┘ └──────────────┘ │ │ │ ▼ ▼ ▼ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐ │ StartRaw │ │ onMixedAudio │ │ Store │ │ Recording │ │ RawDataReceived │ │ Transcript │ └──────────────┘ └─────────────────┘ └──────────────┘ ``` ### Core Pattern (Version-Flexible) ```cpp // STEP 1: Initialize (stable across versions) InitParam init_params; init_params.strWebDomain = "https://zoom.us"; init_params.enableLogByDefault = true; init_params.rawdataOpts.audioRawDataMemoryMode = ZoomSDKRawDataMemoryModeHeap; InitSDK(init_params); // STEP 2: Authenticate (JWT token - stable) AuthContext auth_ctx; auth_ctx.jwt_token = getJWTToken(); // Generate from SDK credentials CreateAuthService(&auth_service); auth_service->SDKAuth(auth_ctx); // STEP 3: Join meeting (flexible - check SDK version for exact field names) JoinParam join_param; join_param.userType = SDK_UT_WITHOUT_LOGIN; auto& params = join_param.param.withoutloginuserJoin; params.meetingNumber = meeting_number; params.userName = "Transcription Bot"; params.psw = meeting_password; // Version-flexible: Check if these fields exist in your SDK version // params.app_privilege_token = obf_token; // v5.15+ // params.onBehalfToken = obf_token; // Some versions // params.userZAK = zak_token; // Alternative meeting_service->Join(join_param); // STEP 4: In onMeetingStatusChanged callback if (status == MEETING_STATUS_INMEETING) { // Get recording controller (pattern stable) auto* record_ctrl = meeting_service->GetMeetingRecordingController(); // Start raw recording (enables raw data access) if (record_ctrl->CanStartRawRecording() == SDKERR_SUCCESS) { record_ctrl->StartRawRecording(); } // Subscribe to audio auto* audio_helper = GetAudioRawdataHelper(); audio_helper->subscribe(new TranscriptionAudioDelegate()); } // STEP 5: Process audio class TranscriptionAudioDelegate : public IZoomSDKAudioRawDataDelegate { void onMixedAudioRawDataReceived(AudioRawData* data) override { // Send PCM data to transcription service sendToTranscriptionAPI(data->GetBuffer(), data->GetBufferLen()); } }; ``` ### Version Handling Strategy **Flexible field access**: ```cpp // Define a macro to check field existence at compile time #ifdef HAS_APP_PRIVILEGE_TOKEN params.app_privilege_token = token; #elif defined(HAS_ON_BEHALF_TOKEN) params.onBehalfToken = token; #else params.userZAK = token; #endif ``` **Runtime capability detection**: ```cpp SDKError canRecord = record_ctrl->CanStartRawRecording(); if (canRecord != SDKERR_SUCCESS) { // Fallback: Try local recording if (record_ctrl->CanStartRecording(true) == SDKERR_SUCCESS) { record_ctrl->StartRecording(time, path); } } ``` ### Key Resilience Patterns 1. **Always check CanXXX() before calling XXX()** 2. **Have fallback authentication methods** (OBF → ZAK → password-only) 3. **Detect capabilities**, don't assume 4. **Use error codes** to adapt behavior --- ## Scenario 2: Recording Bot (Video + Audio) **Goal**: Record meetings with synchronized audio and video. ### Architecture ``` ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐ │ Join Meeting │───►│ StartRawRecording│───►│ Subscribe │ │ │ │ │ │ Audio+Video │ └──────────────┘ └─────────────────┘ └──────────────┘ │ ┌──────────────────────────┴─────────────────┐ ▼ ▼ ┌──────────────┐ ┌──────────────┐ │ Write YUV420 │ │ Write PCM │ │ video.yuv │ │ audio.pcm │ └──────────────┘ └──────────────┘ │ │ └──────────────────────────────────────────┬─┘ ▼ ┌──────────────┐ │ FFmpeg Merge │ │ output.mp4 │ └──────────────┘ ``` ### Core Pattern ```cpp // After joining and starting raw recording... // Video subscription class VideoRecorderDelegate : public IZoomSDKRendererDelegate { std::ofstream videoFile; void onRawDataFrameReceived(YUVRawDataI420* data) override { int width = data->GetStreamWidth(); int height = data->GetStreamHeight(); // YUV420 format: Y + U + V planes (contiguous) videoFile.write(data->GetYBuffer(), width * height); videoFile.write(data->GetUBuffer(), (width/2) * (height/2)); videoFile.write(data->GetVBuffer(), (width/2) * (height/2)); } }; IZoomSDKRenderer* video_renderer; createRenderer(&video_renderer, new VideoRecorderDelegate()); video_renderer->setRawDataResolution(ZoomSDKResolution_720P); video_renderer->subscribe(user_id, RAW_DATA_TYPE_VIDEO); // Audio subscription class AudioRecorderDelegate : public IZoomSDKAudioRawDataDelegate { std::ofstream audioFile; void onMixedAudioRawDataReceived(AudioRawData* data) override { audioFile.write((char*)data->GetBuffer(), data->GetBufferLen()); } }; auto* audio_helper = GetAudioRawdataHelper(); audio_helper->subscribe(new AudioRecorderDelegate()); // Post-processing with FFmpeg system("ffmpeg -video_size 1280x720 -pixel_format yuv420p -f rawvideo -i video.yuv " "-f s16le -ar 32000 -ac 1 -i audio.pcm " "-c:v libx264 -c:a aac -shortest output.mp4"); ``` ### Handling Resolution Changes **Flexible resolution selection** (adapt to SDK version): ```cpp // Try highest resolution available, fall back gracefully ZoomSDKResolution resolutions[] = { ZoomSDKResolution_1080P, ZoomSDKResolution_720P, ZoomSDKResolution_360P }; for (auto res : resolutions) { SDKError err = video_renderer->setRawDataResolution(res); if (err == SDKERR_SUCCESS) { std::cout << "Using resolution: " << res << std::endl; break; } } ``` --- ## Scenario 3: AI Meeting Assistant **Goal**: Real-time meeting analysis with AI (sentiment, action items, summaries). ### Architecture ``` ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐ ┌──────────────┐ │ Audio Stream │───►│ Transcription │───►│ AI Analysis │───►│ Actions │ │ (Real-time) │ │ (AssemblyAI) │ │ (Anthropic) │ │ Identified │ └──────────────┘ └─────────────────┘ └──────────────┘ └──────────────┘ │ │ │ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │ Speaker │ │ Sentiment │ │ Generate │ │ Diarization │ │ Analysis │ │ Summary │ └─────────────────┘ └─────────────────┘ └──────────────┘ ``` ### Core Pattern ```cpp // Real-time streaming transcription class AIAssistantAudioDelegate : public IZoomSDKAudioRawDataDelegate { WebSocketClient transcription_ws; // AssemblyAI WebSocket AIClient ai_client; // Anthropic/OpenAI void onMixedAudioRawDataReceived(AudioRawData* data) override { // Stream audio to real-time transcription transcription_ws.send_audio(data->GetBuffer(), data->GetBufferLen()); } }; // Process transcription results void onTranscriptReceived(const std::string& text, const std::string& speaker) { // Accumulate context meeting_context += speaker + ": " + text + "\n"; // Every 30 seconds, analyze with AI if (should_analyze()) { std::string analysis = ai_client.analyze(meeting_context, { "extract_action_items", "detect_decisions", "identify_blockers" }); // Store results meeting_insights.push_back(analysis); } } // Post-meeting summary void onMeetingEnded() { std::string summary = ai_client.summarize(meeting_context, meeting_insights); save_to_database(meeting_id, summary); send_email_summary(participants, summary); } ``` ### Version Flexibility: Chat Integration Some SDK versions support in-meeting chat: ```cpp // Try to send AI insights to meeting chat auto* chat_ctrl = meeting_service->GetMeetingChatController(); if (chat_ctrl) { // Check if available if (chat_ctrl->CanSendChat()) { chat_ctrl->SendChatTo("AI detected action item: ..."); } } ``` --- ## Scenario 4: Monitoring & Quality Bot **Goal**: Monitor meeting quality metrics and alert on issues. ### Core Pattern ```cpp // Get service quality info auto* meeting_info = meeting_service->GetMeetingInfo(); // Polling loop (GLib timeout) gboolean check_quality(gpointer data) { // Audio stats auto* audio_stats = meeting_info->GetAudioStatistics(); if (audio_stats) { int jitter = audio_stats->jitter; int packet_loss = audio_stats->packet_loss_percent; if (packet_loss > 5) { alert("High packet loss: " + std::to_string(packet_loss) + "%"); } } // Video stats auto* video_stats = meeting_info->GetVideoStatistics(); if (video_stats) { int fps = video_stats->fps; int resolution_width = video_stats->width; if (fps < 15) { alert("Low FPS: " + std::to_string(fps)); } } return TRUE; // Continue polling } // Setup polling g_timeout_add_seconds(10, check_quality, nullptr); ``` --- ## Version Migration Guide ### When SDK Updates 1. **Read release notes** for breaking changes 2. **Check header files** for actual method signatures 3. **Test compilation** with `-Werror=deprecated` 4. **Update authentication** if new token types added 5. **Verify raw data flow** still works ### Common Breaking Changes | Change Type | Example | Mitigation | |-------------|---------|------------| | Struct field rename | `withoutloginuserJoin` → `withoutLoginUserJoin` | Use `#ifdef` or update | | Method signature change | `subscribe(user_id)` → `subscribe(user_id, type)` | Check return codes | | Enum value rename | `SDKERR_NORECORDINGINPROCESS` → `SDKERR_NO_RECORDING_IN_PROCESS` | Map old → new | | New required field | `app_privilege_token` becomes mandatory | Add to config | ### Testing Strategy ```cpp // Version detection at compile time #if ZOOM_SDK_VERSION >= 51500 // v5.15.0 #define USE_OBF_TOKEN #endif // Runtime capability testing bool test_raw_recording() { auto* ctrl = meeting_service->GetMeetingRecordingController(); return ctrl && ctrl->CanStartRawRecording() == SDKERR_SUCCESS; } ``` --- ## Docker Deployment Patterns ### Multi-Stage Build (Version-Flexible) ```dockerfile FROM ubuntu:22.04 AS builder # Install dependencies (stable) RUN apt-get update && apt-get install -y \ build-essential cmake \ libx11-xcb1 libxcb-xfixes0 libxcb-shape0 \ libglib2.0-dev libcurl4-openssl-dev # Copy SDK (any version) COPY zoom-meeting-sdk-linux_*.tar.gz /tmp/ RUN cd /tmp && tar xzf zoom-meeting-sdk-linux_*.tar.gz # Build app COPY . /app WORKDIR /app RUN cmake -B build && cd build && make # Runtime stage FROM ubuntu:22.04 COPY --from=builder /app/build/meetingBot /usr/local/bin/ COPY --from=builder /tmp/lib*.so /usr/local/lib/ # PulseAudio setup (required for raw audio) RUN apt-get update && apt-get install -y pulseaudio && \ mkdir -p ~/.config && \ echo "[General]\nsystem.audio.type=default" > ~/.config/zoomus.conf CMD ["meetingBot"] ``` --- ## Summary: Resilient Bot Checklist ✅ **Always check capabilities** before calling methods ✅ **Use heap memory mode** for raw data ✅ **Handle authentication fallbacks** (OBF → ZAK → password) ✅ **Detect SDK version** at compile/runtime ✅ **Test with actual SDK headers**, not documentation examples ✅ **Setup PulseAudio** for Docker/headless environments ✅ **Parse error codes** to adapt behavior ✅ **Keep authentication tokens fresh** (regenerate before expiry) ✅ **Log everything** for debugging version-specific issues