# Zoom Meeting SDK (Linux)

Embed Zoom meeting capabilities into Linux applications for headless meeting bots and server-side integrations.

## Prerequisites

- Zoom app with Meeting SDK credentials (Client ID & Secret)
- Docker (recommended) or Linux environment (Ubuntu 22+, CentOS 8/9)
- C++ development toolchain (cmake, gcc, g++)
- GLib 2.0, libcurl, pthread

> **Need help with authentication?** See the **[zoom-oauth](../../oauth/SKILL.md)** skill for JWT token generation.

## Overview

The Linux SDK is a **C++ native SDK** designed for:
- **Headless meeting bots** - Join meetings without UI
- **Raw media access** - Capture/send audio/video streams
- **Server-side automation** - Scalable meeting integrations

## Quick Start

### 1. Download Linux SDK

Download from [Zoom Marketplace](https://marketplace.zoom.us/):
- Extract `zoom-meeting-sdk-linux_x86_64-{version}.tar`

### 2. Setup Project Structure

```
your-project/
  demo/
    include/h/          # SDK headers
    lib/zoom_meeting_sdk/
      libmeetingsdk.so
      libmeetingsdk.so.1  # symlink
      qt_libs/
      json/translations.json
    meeting_sdk_demo.cpp
    CMakeLists.txt
    config.txt
```

Copy SDK files:
```bash
cp -r h/* demo/include/h/
cp lib*.so demo/lib/zoom_meeting_sdk/
cp -r qt_libs demo/lib/zoom_meeting_sdk/
cp translation.json demo/lib/zoom_meeting_sdk/json/

# Create required symlink
cd demo/lib/zoom_meeting_sdk/
ln -s libmeetingsdk.so libmeetingsdk.so.1
```

### 3. Configure Credentials

Create `config.txt`:
```
meeting_number: "1234567890"
token: "YOUR_JWT_TOKEN"
meeting_password: "password123"
recording_token: ""
GetVideoRawData: "true"
GetAudioRawData: "true"
SendVideoRawData: "false"
SendAudioRawData: "false"
```

### 4. Build & Run

```bash
cd demo
cmake -B build
cd build
make
cd ../bin
./meetingSDKDemo
```

## Core Workflow

```
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  InitSDK    │───►│  AuthSDK    │───►│ JoinMeeting │───►│ Raw Data    │
│             │    │  (JWT)      │    │             │    │ Subscribe   │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘
                         │                   │
                         ▼                   ▼
                   OnAuthComplete      onInMeeting
                     callback           callback
```

## Code Examples

### 1. Initialize SDK

```cpp
#include "zoom_sdk.h"

USING_ZOOM_SDK_NAMESPACE

void InitMeetingSDK() {
    SDKError err(SDKERR_SUCCESS);
    InitParam initParam;

    initParam.strWebDomain = "https://zoom.us";
    initParam.strSupportUrl = "https://zoom.us";
    initParam.emLanguageID = LANGUAGE_English;
    initParam.enableLogByDefault = true;
    initParam.enableGenerateDump = true;

    err = InitSDK(initParam);
    if (err != SDKERR_SUCCESS) {
        std::cerr << "Init meetingSdk:error" << std::endl;
    }
}
```

### 2. Authenticate with JWT

```cpp
#include "auth_service_interface.h"

IAuthService* m_pAuthService;

void OnAuthenticationComplete() {
    JoinMeeting();  // Called on successful auth
}

void AuthMeetingSDK() {
    CreateAuthService(&m_pAuthService);
    
    m_pAuthService->SetEvent(
        new AuthServiceEventListener(&OnAuthenticationComplete)
    );
    
    AuthContext param;
    param.jwt_token = token.c_str();  // Your JWT token
    m_pAuthService->SDKAuth(param);
}
```

### 3. Join Meeting

```cpp
#include "meeting_service_interface.h"

IMeetingService* m_pMeetingService;
ISettingService* m_pSettingService;

void JoinMeeting() {
    CreateMeetingService(&m_pMeetingService);
    CreateSettingService(&m_pSettingService);
    
    // Set event listeners
    m_pMeetingService->SetEvent(
        new MeetingServiceEventListener(&onMeetingJoined, &onMeetingEnds, &onInMeeting)
    );
    
    // Prepare join parameters
    JoinParam joinParam;
    joinParam.userType = SDK_UT_WITHOUT_LOGIN;
    
    JoinParam4WithoutLogin& params = joinParam.param.withoutloginuserJoin;
    params.meetingNumber = std::stoull(meeting_number);
    params.userName = "BotUser";
    params.psw = meeting_password.c_str();
    params.isVideoOff = false;
    params.isAudioOff = false;
    
    m_pMeetingService->Join(joinParam);
}
```

### 4. Subscribe to Raw Video

```cpp
#include "rawdata/rawdata_renderer_interface.h"
#include "rawdata/zoom_rawdata_api.h"

class ZoomSDKRenderer : public IZoomSDKRendererDelegate {
public:
    void onRawDataFrameReceived(YUVRawDataI420* data) override {
        // YUV420 (I420) format - contiguous planar data (no strides)
        int width = data->GetStreamWidth();
        int height = data->GetStreamHeight();
        
        // Y plane: width * height bytes
        outputFile.write(data->GetYBuffer(), width * height);
        // U plane: (width/2) * (height/2) bytes
        outputFile.write(data->GetUBuffer(), (width / 2) * (height / 2));
        // V plane: (width/2) * (height/2) bytes
        outputFile.write(data->GetVBuffer(), (width / 2) * (height / 2));
    }
    
    void onRawDataStatusChanged(RawDataStatus status) override {}
    void onRendererBeDestroyed() override {}
};

// Subscribe after joining
IZoomSDKRenderer* videoHelper;
ZoomSDKRenderer* videoSource = new ZoomSDKRenderer();

createRenderer(&videoHelper, videoSource);
videoHelper->setRawDataResolution(ZoomSDKResolution_720P);
videoHelper->subscribe(userID, RAW_DATA_TYPE_VIDEO);
```

### 5. Subscribe to Raw Audio

```cpp
#include "rawdata/rawdata_audio_helper_interface.h"

class ZoomSDKAudioRawData : public IZoomSDKAudioRawDataDelegate {
public:
    void onMixedAudioRawDataReceived(AudioRawData* data) override {
        // Process PCM audio (mixed from all participants)
        pcmFile.write((char*)data->GetBuffer(), data->GetBufferLen());
    }
    
    void onOneWayAudioRawDataReceived(AudioRawData* data, uint32_t node_id) override {
        // Process audio from specific participant
    }
};

// Subscribe after joining
IZoomSDKAudioRawDataHelper* audioHelper = GetAudioRawdataHelper();
audioHelper->subscribe(new ZoomSDKAudioRawData());
```

### 6. GLib Main Loop

```cpp
#include <glib.h>

GMainLoop* loop;

gboolean timeout_callback(gpointer data) {
    return TRUE;  // Keep running
}

int main(int argc, char* argv[]) {
    InitMeetingSDK();
    AuthMeetingSDK();
    
    loop = g_main_loop_new(NULL, FALSE);
    g_timeout_add(1000, timeout_callback, loop);
    g_main_loop_run(loop);
    
    return 0;
}
```

## Available Examples

| Example | Description |
|---------|-------------|
| **SkeletonExample** | Minimal join meeting - start here |
| **GetRawVideoAndAudioExample** | Subscribe to raw audio/video streams |
| **GetRawVideoAndAudioAPIExample** | API-based raw data access |
| **SendRawVideoAndAudioExample** | Send custom video/audio as virtual camera/mic |
| **ChatExample** | In-meeting chat functionality |
| **BreakoutExample** | Breakout room management |
| **AllInOneExample** | Complete demo with all features |
| **SendRawVideoAndAudioWithRTMSExample** | Raw data with RTMS integration |

## Detailed References

### High-Level Guides
- **[concepts/high-level-scenarios.md](concepts/high-level-scenarios.md)** - Production bot architectures (transcription, recording, AI assistant)
- **[meeting-sdk-bot.md](meeting-sdk-bot.md)** - Resilient bot pattern with retry logic

### Platform Guide
- **[references/linux-reference.md](references/linux-reference.md)** - Dependencies, Docker setup, troubleshooting

### Authentication
- **[../references/authorization.md](../references/authorization.md)** - SDK JWT generation
- **[../references/bot-authentication.md](../references/bot-authentication.md)** - Bot token types (ZAK, OBF, JWT)

### Features
- **[../references/breakout-rooms.md](../references/breakout-rooms.md)** - Programmatic breakout room management
- **[../references/ai-companion.md](../references/ai-companion.md)** - AI Companion controls

## Sample Repositories

| Repository | Description |
|------------|-------------|
| [meetingsdk-headless-linux-sample](https://github.com/zoom/meetingsdk-headless-linux-sample) | Official headless bot with Docker |
| [meetingsdk-linux-raw-recording-sample](https://github.com/zoom/meetingsdk-linux-raw-recording-sample) | Raw audio/video access |

## Playing Raw Video/Audio Files

Raw YUV/PCM files have no headers - you must specify format explicitly.

### Play Raw YUV Video
```bash
ffplay -video_size 640x360 -pixel_format yuv420p -f rawvideo video.yuv
```

### Convert YUV to MP4
```bash
ffmpeg -video_size 640x360 -pixel_format yuv420p -f rawvideo -i video.yuv -c:v libx264 output.mp4
```

### Play Raw PCM Audio
```bash
ffplay -f s16le -ar 32000 -ac 1 audio.pcm
```

### Convert PCM to WAV
```bash
ffmpeg -f s16le -ar 32000 -ac 1 -i audio.pcm output.wav
```

### Combine Video + Audio
```bash
ffmpeg -video_size 640x360 -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
```

**Key flags:**
| Flag | Description |
|------|-------------|
| `-video_size WxH` | Frame dimensions (check output filename) |
| `-pixel_format yuv420p` | I420/YUV420 planar format |
| `-f rawvideo` | Raw video input (no container) |
| `-f s16le` | Signed 16-bit little-endian PCM |
| `-ar 32000` | Sample rate (Zoom uses 32kHz) |
| `-ac 1` | Mono (use `-ac 2` for stereo) |

## Compilation Tips

> **Note**: These are general patterns - specific methods/types may vary by SDK version.

### Event Listener Interfaces
SDK event listener interfaces (`IMeetingServiceEvent`, `IMeetingParticipantsCtrlEvent`, etc.) have **many pure virtual methods**. You must implement ALL of them, even with empty bodies, or you'll get "invalid new-expression of abstract class type" errors. Always check the SDK headers for the complete list.

### Include Order Issues
Some SDK headers don't include their own dependencies. If you encounter undefined type errors (like `AudioType` or `time_t`), try:
- Adding standard library includes (`<ctime>`, `<cstdint>`) before SDK headers
- Including related SDK headers in dependency order
- Checking which header defines the missing type

### Method Signature Mismatches
Reference samples may have outdated method names. Always verify against the actual SDK header files - they are the authoritative source.

## Authentication Requirements (2026 Update)

> **Important**: Beginning **March 2, 2026**, apps joining meetings outside their account must be authorized.

Use one of:
- **App Privilege Token (OBF)** - Recommended for bots (`app_privilege_token` in JoinParam)
- **ZAK Token** - Zoom Access Key (`userZAK` in JoinParam)
- **On Behalf Token** - For specific use cases (`onBehalfToken` in JoinParam)

## Resources

- **Official docs**: https://developers.zoom.us/docs/meeting-sdk/linux/
- **API Reference**: https://marketplacefront.zoom.us/sdk/meeting/linux/index.html
- **Developer forum**: https://devforum.zoom.us/
- **SDK download**: https://marketplace.zoom.us/
