File: C:/Users/fred/.codex/.tmp/plugins/plugins/zoom/skills/video-sdk/linux/linux.md
---
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<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.
```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<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.
```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<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
```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<IZoomVideoSDKCameraDevice*>* 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<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
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.