Files
2026-07-13 12:20:06 +08:00

26 KiB

name, description
name description
video-sdk/linux 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:

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

# 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_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):

{
    "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

#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

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

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<IZoomVideoSDKUser*>* 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.

// 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):

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<IZoomVideoSDKUser*>*) 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.

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)

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)

// 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

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

class MyVideoSource : public IZoomVideoSDKVideoSource {
public:
    virtual void onInitialize(IZoomVideoSDKVideoSender* sender,
                             IVideoSDKVector<VideoSourceCapability>* 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

IZoomVideoSDKAudioHelper* audio = video_sdk_obj->getAudioHelper();
audio->startAudio();
audio->stopAudio();
audio->muteAudio(true);
audio->subscribe();    // For raw data callbacks
audio->unSubscribe();

Video Helper

IZoomVideoSDKVideoHelper* video = video_sdk_obj->getVideoHelper();
video->startVideo();
video->stopVideo();
video->rotateMyVideo(90);
IVideoSDKVector<IZoomVideoSDKCameraDevice*>* cameras = video->getCameraList();

Share Helper

IZoomVideoSDKShareHelper* share = video_sdk_obj->getShareHelper();
share->startShare();
share->startSharingExternalSource(shareSource);
share->stopShare();
bool isSharing = share->isSharingOut();

Chat Helper

IZoomVideoSDKChatHelper* chat = video_sdk_obj->getChatHelper();
chat->sendChatToAll("Hello!");
chat->sendChatToUser(user, "Private message");

Recording Helper

IZoomVideoSDKRecordingHelper* rec = video_sdk_obj->getRecordingHelper();
if (rec->canStartRecording() == ZoomVideoSDKErrors_Success) {
    rec->startCloudRecording();
}

Live Stream Helper

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.

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<ILiveTranscriptionLanguage*>* 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_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

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)

# 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
// 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:

Repository: https://github.com/tanchunsiong/videosdk-linux-qt-quickstart

# 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:

# 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

# 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

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):

mkdir -p bin/output/audio bin/output/video

Raw File Playback

# 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.