--- name: video-sdk/linux description: "Zoom Video SDK for Linux - C++ integration for video sessions, raw audio/video capture, screen sharing, recording, and real-time communication" --- # Zoom Video SDK - Linux Development Expert guidance for developing with the Zoom Video SDK on Linux. This SDK enables headless video conferencing bots, raw media capture/injection, cloud recording, live streaming, and real-time transcription. **Official Documentation**: https://developers.zoom.us/docs/video-sdk/linux/ **API Reference**: https://marketplacefront.zoom.us/sdk/custom/linux/ **Sample Repository**: https://github.com/zoom/videosdk-linux-raw-recording-sample ## SDK Overview The Zoom Video SDK for Linux is a C++ library that provides: - **Session Management**: Join/leave video SDK sessions - **Raw Data Access**: Capture raw audio/video frames (YUV420, PCM) - **Raw Data Injection**: Send custom audio/video into sessions - **Screen Sharing**: Share screens or inject custom share sources - **Cloud Recording**: Record sessions to Zoom cloud - **Live Streaming**: Stream to RTMP endpoints (YouTube, etc.) - **Chat & Commands**: In-session messaging and command channels - **Live Transcription**: Real-time speech-to-text - **Subsessions**: Breakout room support - **Whiteboard**: Collaborative whiteboard features - **Annotations**: Screen share annotations ## Project Structure ``` project/ ├── CMakeLists.txt ├── config.json # Session credentials ├── src/ │ ├── main.cpp ├── include/ │ └── zoom_video_sdk/ # SDK headers │ ├── zoom_video_sdk_api.h │ ├── zoom_video_sdk_interface.h │ ├── zoom_video_sdk_delegate_interface.h │ ├── zoom_video_sdk_def.h │ └── helpers/ # Feature-specific interfaces │ ├── zoom_video_sdk_audio_helper_interface.h │ ├── zoom_video_sdk_audio_send_rawdata_interface.h # Virtual audio │ ├── zoom_video_sdk_video_helper_interface.h │ ├── zoom_video_sdk_video_source_helper_interface.h # Video injection │ └── zoom_video_sdk_user_helper_interface.h ├── lib/ │ └── zoom_video_sdk/ # SDK + Qt5 libraries │ ├── libvideosdk.so # Main SDK (from SDK tarball) │ ├── libcml.so # Required │ ├── libmpg123.so # Audio codec │ ├── cpthost # Host binary │ ├── libQt5Core.so.5 # Qt5 dependencies (from sdk samples/qt_libs) │ ├── libQt5Gui.so.5 │ ├── libQt5Network.so.5 │ ├── libQt5Qml.so.5 │ └── libQt5Quick.so.5 └── bin/ # Build output ``` **CRITICAL**: SDK requires Qt5 libraries. Copy from SDK samples (`qt_libs/Qt/lib/`) and create symlinks: ```bash cd lib/zoom_video_sdk ln -sf libQt5Core.so.5 libQt5Core.so ln -sf libQt5Gui.so.5 libQt5Gui.so # ... repeat for all Qt5 libs ``` ## Prerequisites ```bash # System dependencies sudo apt update sudo apt install -y build-essential gcc cmake sudo apt install -y libglib2.0-dev liblzma-dev libxcb-image0 libxcb-keysyms1 \ libxcb-xfixes0 libxcb-xkb1 libxcb-shape0 libxcb-shm0 libxcb-randr0 \ libxcb-xtest0 libgbm1 libxtst6 libgl1 libnss3 libasound2 libpulse0 # Qt5 is bundled with SDK - do NOT install system Qt5 # Copy Qt5 libs from SDK samples/qt_libs/Qt/lib/ to your lib directory # For headless Linux (no soundcard) sudo apt install -y pulseaudio # OR use virtual audio speaker/mic in code (recommended) # Create log directory mkdir -p ~/.zoom/logs ``` ## CMakeLists.txt Template ```cmake cmake_minimum_required(VERSION 3.14) project(ZoomVideoSDKBot VERSION 1.0.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(PkgConfig REQUIRED) pkg_check_modules(GLIB REQUIRED glib-2.0) # CRITICAL: Include both paths for nested SDK headers include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/include/zoom_video_sdk # For helpers/ relative includes ${GLIB_INCLUDE_DIRS} ) link_directories(${CMAKE_SOURCE_DIR}/lib/zoom_video_sdk) set(SOURCES src/main.cpp src/ZoomDelegate.cpp) add_executable(${PROJECT_NAME} ${SOURCES}) target_link_libraries(${PROJECT_NAME} videosdk ${GLIB_LIBRARIES} pthread Qt5Core Qt5Gui Qt5Network Qt5Qml Qt5Quick ) set_target_properties(${PROJECT_NAME} PROPERTIES BUILD_RPATH "${CMAKE_SOURCE_DIR}/lib/zoom_video_sdk" INSTALL_RPATH "${CMAKE_SOURCE_DIR}/lib/zoom_video_sdk" RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin ) ``` ## Configuration **config.json** (place in bin/ directory): ```json { "session_name": "your-session-name", "token": "your.jwt.token", "session_psw": "optional-password" } ``` **JWT Generation**: Use Zoom Video SDK credentials from marketplace.zoom.us ## Core Implementation Pattern ### 1. SDK Initialization ```cpp #include "zoom_video_sdk_api.h" #include "zoom_video_sdk_interface.h" #include "zoom_video_sdk_delegate_interface.h" USING_ZOOM_VIDEO_SDK_NAMESPACE IZoomVideoSDK* video_sdk_obj = CreateZoomVideoSDKObj(); ZoomVideoSDKInitParams init_params; init_params.domain = "https://zoom.us"; init_params.enableLog = true; init_params.logFilePrefix = "my_bot"; init_params.videoRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap; init_params.shareRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap; init_params.audioRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap; init_params.enableIndirectRawdata = false; ZoomVideoSDKErrors err = video_sdk_obj->initialize(init_params); ``` ### 2. Session Join ```cpp ZoomVideoSDKSessionContext session_context; session_context.sessionName = "session-name"; session_context.sessionPassword = "password"; session_context.userName = "Linux Bot"; session_context.token = "jwt-token"; session_context.sessionIdleTimeoutMins = 40; // 0 = never timeout session_context.autoLoadMutliStream = true; session_context.videoOption.localVideoOn = true; session_context.audioOption.connect = true; session_context.audioOption.mute = false; // For headless Linux - use virtual audio ZoomVideoSDKVirtualAudioSpeaker* vSpeaker = new ZoomVideoSDKVirtualAudioSpeaker(); session_context.virtualAudioSpeaker = vSpeaker; IZoomVideoSDKSession* session = video_sdk_obj->joinSession(session_context); ``` ### 3. Event Delegate ```cpp class MyDelegate : public IZoomVideoSDKDelegate { public: virtual void onSessionJoin() override { printf("Joined session!\n"); // Start audio subscription IZoomVideoSDKAudioHelper* audioHelper = video_sdk_obj->getAudioHelper(); if (audioHelper) { audioHelper->startAudio(); audioHelper->subscribe(); } } virtual void onSessionLeave() override { printf("Left session\n"); } virtual void onError(ZoomVideoSDKErrors errorCode, int detailErrorCode) override { printf("Error: %d, Detail: %d\n", errorCode, detailErrorCode); } virtual void onUserJoin(IZoomVideoSDKUserHelper* pUserHelper, IVideoSDKVector* userList) override { int count = userList->GetCount(); for (int i = 0; i < count; i++) { IZoomVideoSDKUser* user = userList->GetItem(i); // Subscribe to user's video user->GetVideoPipe()->subscribe(ZoomVideoSDKResolution_720P, videoDelegate); } } virtual void onMixedAudioRawDataReceived(AudioRawData* data_) override { // Handle mixed audio (all participants) char* buffer = data_->GetBuffer(); unsigned int len = data_->GetBufferLen(); unsigned int sampleRate = data_->GetSampleRate(); // Process PCM audio } virtual void onOneWayAudioRawDataReceived(AudioRawData* data_, IZoomVideoSDKUser* pUser) override { // Handle individual user audio } }; video_sdk_obj->addListener(new MyDelegate()); ``` ## IZoomVideoSDKDelegate - CRITICAL Notes **WARNING**: `IZoomVideoSDKDelegate` has many pure virtual methods. ALL must be implemented. **VERSION SENSITIVITY**: The delegate interface changes between SDK versions: - New callbacks are added, old ones may be removed - Method signatures may change - **ALWAYS check `zoom_video_sdk_delegate_interface.h` when upgrading SDK versions** ### Required Includes for Full Class Definitions **VERSION SENSITIVITY**: Forward declarations in `zoom_video_sdk_def.h` may change. Always verify which helper headers contain full class definitions. ```cpp // zoom_video_sdk_def.h only forward-declares classes! // Include helper headers for actual implementations: #include "zoom_video_sdk/helpers/zoom_video_sdk_user_helper_interface.h" // IZoomVideoSDKUser full definition #include "zoom_video_sdk/helpers/zoom_video_sdk_audio_send_rawdata_interface.h" // Virtual audio classes #include "zoom_video_sdk/helpers/zoom_video_sdk_video_source_helper_interface.h" // Video source/sender #include "zoom_video_sdk/zoom_video_sdk_chat_message_interface.h" // IZoomVideoSDKChatMessage ``` ### SDK 2.4.12 Method Signature Examples **VERSION SENSITIVITY**: Method signatures change between SDK versions. These are examples from 2.4.12 - **always verify against current SDK headers**. | Example Change | SDK 2.4.12 Signature | |----------------|----------------------| | `onSessionLeave()` | `onSessionLeave(ZoomVideoSDKSessionLeaveReason reason)` (both exist) | | `onUserShareStatusChanged(...)` | Takes `IZoomVideoSDKShareAction*` instead of `ZoomVideoSDKShareStatus, ZoomVideoSDKShareType` | | `message->getSenderUser()` | Now `message->getSendUser()` | | `info->getSpeaker()` | Now `info->getSpeakerName()` (returns `const zchar_t*` directly) | | `sender->send()` | Now `sender->Send()` (capital S) | | `user->getVideoStatus()` | Now `user->GetVideoPipe()->getVideoStatus()` | ### Required Empty Stub Callbacks **VERSION SENSITIVITY**: Callbacks are added/removed between SDK versions. Check `zoom_video_sdk_delegate_interface.h` for the complete current list. Must implement ALL these (use empty `{}` for unused ones): ```cpp virtual void onUserShareStatusChanged(IZoomVideoSDKShareHelper*, IZoomVideoSDKUser*, IZoomVideoSDKShareAction*) override {} virtual void onShareContentChanged(IZoomVideoSDKShareHelper*, IZoomVideoSDKUser*, IZoomVideoSDKShareAction*) override {} virtual void onFailedToStartShare(IZoomVideoSDKShareHelper*, IZoomVideoSDKUser*) override {} virtual void onShareSettingChanged(ZoomVideoSDKShareSetting) override {} virtual void onUserManagerChanged(IZoomVideoSDKUser*) override {} virtual void onAudioLevelChanged(unsigned int, bool, IZoomVideoSDKUser*) override {} virtual void onSpokenLanguageChanged(ILiveTranscriptionLanguage*) override {} virtual void onChatPrivilegeChanged(IZoomVideoSDKChatHelper*, ZoomVideoSDKChatPrivilegeType) override {} virtual void onSendFileStatus(IZoomVideoSDKSendFile*, const FileTransferStatus&) override {} virtual void onReceiveFileStatus(IZoomVideoSDKReceiveFile*, const FileTransferStatus&) override {} virtual void onShareNetworkStatusChanged(ZoomVideoSDKNetworkStatus, bool) override {} virtual void onUserNetworkStatusChanged(ZoomVideoSDKDataType, ZoomVideoSDKNetworkStatus, IZoomVideoSDKUser*) override {} virtual void onUserOverallNetworkStatusChanged(ZoomVideoSDKNetworkStatus, IZoomVideoSDKUser*) override {} virtual void onAnnotationHelperCleanUp(IZoomVideoSDKAnnotationHelper*) override {} virtual void onAnnotationPrivilegeChange(IZoomVideoSDKUser*, IZoomVideoSDKShareAction*) override {} virtual void onAnnotationHelperActived(void*) override {} virtual void onAnnotationToolTypeChanged(IZoomVideoSDKAnnotationHelper*, void*, ZoomVideoSDKAnnotationToolType) override {} virtual void onVideoAlphaChannelStatusChanged(bool) override {} virtual void onSpotlightVideoChanged(IZoomVideoSDKVideoHelper*, IVideoSDKVector*) override {} // ... and many more for live stream, subsession, broadcast, etc. ``` See `zoom_video_sdk_delegate_interface.h` for complete list (~90 methods). ### IZoomVideoSDKRawDataPipeDelegate Requirements **VERSION SENSITIVITY**: Interface methods may be added/removed between SDK versions. ```cpp class VideoDelegate : public IZoomVideoSDKRawDataPipeDelegate { virtual void onRawDataFrameReceived(YUVRawDataI420* data) override { /* handle */ } virtual void onRawDataStatusChanged(RawDataStatus status) override { /* handle */ } virtual void onShareCursorDataReceived(ZoomVideoSDKShareCursorData info) override {} // REQUIRED in 2.4.12 }; ``` ## Raw Data Capture ### Video Raw Data (YUV I420) ```cpp class VideoEncoder : public IZoomVideoSDKRawDataPipeDelegate { public: virtual void onRawDataFrameReceived(YUVRawDataI420* data) override { int width = data->GetStreamWidth(); int height = data->GetStreamHeight(); char* yBuffer = data->GetYBuffer(); char* uBuffer = data->GetUBuffer(); char* vBuffer = data->GetVBuffer(); char* alphaBuffer = data->GetAlphaBuffer(); // Optional unsigned long long timestamp = data->GetTimeStamp(); int rotation = data->GetRotation(); // 0, 90, 180, 270 bool isLimited = data->IsLimitedI420(); // Reference counting for async processing if (data->CanAddRef()) { data->AddRef(); // Process in background thread data->Release(); } } virtual void onRawDataStatusChanged(RawDataStatus status) override { // RawData_On or RawData_Off } }; // Subscribe to user's video user->GetVideoPipe()->subscribe(ZoomVideoSDKResolution_720P, new VideoEncoder()); ``` ### Audio Raw Data (PCM) ```cpp // Subscribe to audio in onSessionJoin or onUserAudioStatusChanged IZoomVideoSDKAudioHelper* audioHelper = video_sdk_obj->getAudioHelper(); audioHelper->subscribe(); // Receive in callbacks: virtual void onMixedAudioRawDataReceived(AudioRawData* data_) override { char* buffer = data_->GetBuffer(); // PCM 16-bit unsigned int len = data_->GetBufferLen(); unsigned int sampleRate = data_->GetSampleRate(); unsigned int channels = data_->GetChannelNum(); unsigned long long timestamp = data_->GetTimeStamp(); } // Convert PCM to MP3: // ffmpeg -f s16le -ar 32000 -i output.pcm output.mp3 ``` ## Raw Data Injection ### Virtual Audio Mic ```cpp class MyAudioMic : public IZoomVideoSDKVirtualAudioMic { public: virtual void onMicInitialize(IZoomVideoSDKAudioSender* sender) override { audio_sender_ = sender; } virtual void onMicStartSend() override { // Start sending audio frames } virtual void onMicStopSend() override {} virtual void onMicUninitialized() override { audio_sender_ = nullptr; } void SendAudio(char* data, unsigned int len, int sampleRate) { if (audio_sender_) { audio_sender_->Send(data, len, sampleRate); } } private: IZoomVideoSDKAudioSender* audio_sender_ = nullptr; }; // Set before joining session_context.virtualAudioMic = new MyAudioMic(); session_context.audioOption.connect = true; session_context.audioOption.mute = false; ``` ### Virtual Video Source ```cpp class MyVideoSource : public IZoomVideoSDKVideoSource { public: virtual void onInitialize(IZoomVideoSDKVideoSender* sender, IVideoSDKVector* caps, VideoSourceCapability& suggest) override { video_sender_ = sender; } virtual void onStartSend() override { // Start sending video frames } virtual void onStopSend() override {} virtual void onPropertyChange(...) override {} void SendFrame(char* y, char* u, char* v, int w, int h, int rotation) { if (video_sender_) { video_sender_->sendVideoFrame(y, u, v, w, h, 0, rotation); } } private: IZoomVideoSDKVideoSender* video_sender_ = nullptr; }; // Set before joining session_context.externalVideoSource = new MyVideoSource(); ``` ## Helper Interfaces ### Audio Helper ```cpp IZoomVideoSDKAudioHelper* audio = video_sdk_obj->getAudioHelper(); audio->startAudio(); audio->stopAudio(); audio->muteAudio(true); audio->subscribe(); // For raw data callbacks audio->unSubscribe(); ``` ### Video Helper ```cpp IZoomVideoSDKVideoHelper* video = video_sdk_obj->getVideoHelper(); video->startVideo(); video->stopVideo(); video->rotateMyVideo(90); IVideoSDKVector* cameras = video->getCameraList(); ``` ### Share Helper ```cpp IZoomVideoSDKShareHelper* share = video_sdk_obj->getShareHelper(); share->startShare(); share->startSharingExternalSource(shareSource); share->stopShare(); bool isSharing = share->isSharingOut(); ``` ### Chat Helper ```cpp IZoomVideoSDKChatHelper* chat = video_sdk_obj->getChatHelper(); chat->sendChatToAll("Hello!"); chat->sendChatToUser(user, "Private message"); ``` ### Recording Helper ```cpp IZoomVideoSDKRecordingHelper* rec = video_sdk_obj->getRecordingHelper(); if (rec->canStartRecording() == ZoomVideoSDKErrors_Success) { rec->startCloudRecording(); } ``` ### Live Stream Helper ```cpp IZoomVideoSDKLiveStreamHelper* ls = video_sdk_obj->getLiveStreamHelper(); if (ls->canStartLiveStream() == ZoomVideoSDKErrors_Success) { ls->startLiveStream("rtmp://...", "stream-key", "broadcast-url"); } ``` ### Live Transcription Helper **Note**: The session host can start/stop live transcription via the SDK object. Participants receive transcription messages via callbacks. ```cpp IZoomVideoSDKLiveTranscriptionHelper* ltt = video_sdk_obj->getLiveTranscriptionHelper(); // Check if transcription can be started (host privilege required) if (ltt->canStartLiveTranscription()) { ltt->startLiveTranscription(); } // Set spoken language (optional) IVideoSDKVector* languages = ltt->getAvailableSpokenLanguages(); if (languages && languages->GetCount() > 0) { ltt->setSpokenLanguage(languages->GetItem(0)->getLTTLanguageID()); } // Receive transcription in callback: virtual void onLiveTranscriptionMsgInfoReceived(ILiveTranscriptionMessageInfo* info) override { const char* speaker = info->getSpeakerName(); const char* content = info->getMessageContent(); printf("[%s]: %s\n", speaker, content); } // Monitor transcription status changes: virtual void onLiveTranscriptionStatus(ZoomVideoSDKLiveTranscriptionStatus status) override { // ZoomVideoSDKLiveTranscription_Status_Start or _Stop } ``` ## CMakeLists.txt Template ```cmake cmake_minimum_required(VERSION 3.10) project(ZoomVideoSDKBot) set(CMAKE_CXX_STANDARD 14) find_package(PkgConfig REQUIRED) pkg_check_modules(GLIB REQUIRED glib-2.0) include_directories( ${CMAKE_SOURCE_DIR}/src/include ${GLIB_INCLUDE_DIRS} ) link_directories(${CMAKE_SOURCE_DIR}/src/lib/zoom_video_sdk) add_executable(${PROJECT_NAME} src/main.cpp src/ZoomVideoSDKRawDataPipeDelegate.cpp src/ZoomVideoSDKVirtualAudioMic.cpp src/ZoomVideoSDKVirtualAudioSpeaker.cpp ) target_link_libraries(${PROJECT_NAME} videosdk ${GLIB_LIBRARIES} pthread z lzma ) ``` ## Build & Run ```bash cmake -B build cd build && make # Run from bin directory cd bin export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../src/lib/zoom_video_sdk ./ZoomVideoSDKBot ``` ## Headless Linux (Docker/WSL) ```bash # Install PulseAudio sudo apt install -y pulseaudio pulseaudio --start # Create virtual devices pactl load-module module-null-sink sink_name=virtual_speaker pactl load-module module-null-source source_name=virtual_mic # Configure mkdir -p ~/.config cat > ~/.config/zoomus.conf << EOF [General] system.audio.type=default EOF ``` Or use `IZoomVideoSDKVirtualAudioSpeaker` and `IZoomVideoSDKVirtualAudioMic` interfaces. ## Thread Safety - SDK callbacks are on SDK's internal threads - Don't perform heavy operations in callbacks - Use message queues for async processing - Don't call `cleanup()` inside callbacks ```cpp // Reference counting for async use if (data->CanAddRef()) { data->AddRef(); // Queue for processing data->Release(); // When done } ``` ## Custom UI Frameworks For applications requiring a graphical user interface, use these quickstart templates: ### Qt Framework (Recommended) **Repository**: https://github.com/tanchunsiong/videosdk-linux-qt-quickstart ```bash # Prerequisites sudo apt install -y qt6-base-dev qt6-tools-dev libasound2-dev cmake # Build cd videosdk-linux-qt-quickstart/src mkdir build && cd build export Qt6_DIR=/usr/lib/x86_64-linux-gnu/cmake/Qt6 cmake .. && make -j$(nproc) # Run ./run_qt_demo.sh ``` **Features**: - Qt6/Qt5 cross-platform GUI - Video rendering with QPainter + QImage - YUV-to-RGB conversion (ITU-R BT.601) - ALSA audio playback - Device selection (camera, mic, speaker) - Session management UI **Key Components**: - `QtMainWindow` - Main window with controls - `QtVideoWidget` - Video display widget - `QtVideoRenderer` - Video rendering logic - `QtPreviewVideoHandler` - Self video preview - `QtRemoteVideoHandler` - Remote video streams **Qt Platform Options**: ```bash # Desktop ./run_qt_demo.sh # Headless/Server QT_QPA_PLATFORM=offscreen ./run_qt_demo.sh # Wayland QT_QPA_PLATFORM=wayland ./run_qt_demo.sh # X11 with SSH forwarding ssh -X user@host export DISPLAY=:10.0 ./run_qt_demo.sh ``` ### GTK Framework **Repository**: https://github.com/tanchunsiong/videosdk-linux-gtk-quickstart ```bash # Prerequisites sudo apt install -y libgtkmm-3.0-dev libsdl2-dev libasound2-dev libcurl4-openssl-dev cmake # Build cd videosdk-linux-gtk-quickstart/src mkdir build && cd build cmake .. && make # Run cd ../bin && ./SkeletonDemo ``` **Features**: - GTKmm 3.0 native Linux GUI - SDL2 + Cairo video rendering - ALSA audio integration - Device hot-swapping - Separate self/remote video panels - Chat system **Key Components**: - `VideoRenderer` - SDL2-based rendering engine - `VideoDisplayBridge` - SDK to renderer connection - `PreviewVideoHandler` - Camera preview - `RemoteVideoRawDataHandler` - Remote participant video **Architecture**: ``` ┌─────────────────────────────────────────────────────────┐ │ GTK User Interface │ ├─────────────────────────────────────────────────────────┤ │ Session Controls │ Device Selection │ Video Controls │ ├─────────────────────────────────────────────────────────┤ │ Self Video │ Status Area │ Remote Video │ │ (Left Panel) │ │ (Right Panel) │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ Video Processing Layer │ │ VideoRenderer ←→ VideoDisplayBridge ←→ Device Manager │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ Zoom Video SDK │ └─────────────────────────────────────────────────────────┘ ``` ### Comparison | Feature | Qt | GTK | |---------|----|----| | Cross-platform | Yes (Windows, Mac, Linux) | Linux-focused | | Video Rendering | QPainter + QImage | SDL2 + Cairo | | Threading | Qt signals/slots | Glib main loop | | Modern C++ | Better support | Good support | | Performance | Slightly better | Good | | Learning Curve | Moderate | Moderate | **Choose Qt** for: Cross-platform apps, modern C++ features, better tooling **Choose GTK** for: Native Linux look, lightweight, GNOME integration ## Runtime Setup ### Required Symlinks ```bash cd lib/zoom_video_sdk # SDK library versioned symlink ln -sf libvideosdk.so libvideosdk.so.1 # Qt5 library symlinks (if using bundled Qt) ln -sf libQt5Core.so.5 libQt5Core.so ln -sf libQt5Gui.so.5 libQt5Gui.so ln -sf libQt5Network.so.5 libQt5Network.so ln -sf libQt5Qml.so.5 libQt5Qml.so ln -sf libQt5Quick.so.5 libQt5Quick.so ``` ### Output Directories Create directories for raw data capture (relative to binary location): ```bash mkdir -p bin/output/audio bin/output/video ``` ### Raw File Playback ```bash # Audio (PCM 16-bit, 32kHz, Mono) ffplay -f s16le -ar 32000 -ac 1 output/audio/mixed_audio.pcm # Video (YUV420P) - adjust resolution as needed ffplay -f rawvideo -pixel_format yuv420p -video_size 640x360 output/video/video.yuv # Convert audio to MP3 ffmpeg -f s16le -ar 32000 -ac 1 -i audio.pcm output.mp3 # Convert video to MP4 ffmpeg -f rawvideo -pixel_format yuv420p -video_size 640x360 -framerate 30 -i video.yuv -c:v libx264 output.mp4 ``` ## Error Codes | Code | Name | Description | |------|------|-------------| | 0 | Success | Operation succeeded | | 1001 | Auth_Error | Authentication failed | | 1002 | Auth_Empty_Token | No token provided | | 1003 | Auth_Wrong_Token | Invalid token | | 1004 | Auth_Expired_Token | Token expired | | 3001 | Session_Join_Failed | Failed to join | | 3008 | Session_Need_Password | Password required | | 3009 | Session_Password_Wrong | Wrong password | ## Reference Files See `linux-reference.md` for complete API documentation.