Bodega

Skills › Productividad

video-sdk/windows

Zoom Video SDK for Windows - C++ integration for video sessions, raw audio/video capture, screen sharing, recording, and real-time communication

Descargar .zipVer en GitHub
Departamento
Productividad
Fuente
Oficial · anthropics/knowledge-work-plugins › zoom-plugin
Autor
Anthropic
Licencia
Apache-2.0
Idioma
Inglés
Riesgo
Bajo

Señales de riesgo

Cómo instalarla en Claude

  1. Descargue el .zip sin descomprimirlo.
  2. En Claude active Configuración › Capacidades › Crear y editar archivos.
  3. Vaya a Personalizar › Skills › Crear skill › Subir una skill y elija el archivo.

Instrucciones completas (SKILL.md)

Zoom Video SDK - Windows Development

Expert guidance for developing with the Zoom Video SDK on Windows. This SDK enables custom video applications, raw media capture/injection, cloud recording, live streaming, and real-time transcription on Windows platforms.

Official Documentation: https://developers.zoom.us/docs/video-sdk/windows/ API Reference: https://marketplacefront.zoom.us/sdk/custom/windows/ Sample Repository: https://github.com/zoom/videosdk-windows-rawdata-sample

Quick Links

New to Video SDK? Follow this path:

Reference:

Having issues?

Building a Custom UI?

SDK Overview

The Zoom Video SDK for Windows is a C++ library that provides:

Prerequisites

System Requirements

Visual Studio Workloads

Install these workloads via Visual Studio Installer:

Quick Start

C++ Application

#include <windows.h>
#include "zoom_video_sdk_api.h"
#include "zoom_video_sdk_interface.h"
#include "zoom_video_sdk_delegate_interface.h"

USING_ZOOM_VIDEO_SDK_NAMESPACE

// 1. Create SDK object
IZoomVideoSDK* video_sdk_obj = CreateZoomVideoSDKObj();

// 2. Initialize
ZoomVideoSDKInitParams init_params;
init_params.domain = L"https://zoom.us";
init_params.enableLog = true;
init_params.logFilePrefix = L"zoom_win_video";
init_params.videoRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;
init_params.shareRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;
init_params.audioRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;

ZoomVideoSDKErrors err = video_sdk_obj->initialize(init_params);

// 3. Add event listener
video_sdk_obj->addListener(myDelegate);

// 4. Join session (IMPORTANT: set audioOption.connect = false)
ZoomVideoSDKSessionContext session_context;
session_context.sessionName = L"my-session";
session_context.userName = L"Windows User";
session_context.token = L"your-jwt-token";
session_context.videoOption.localVideoOn = false;
session_context.audioOption.connect = false;  // Connect audio after join
session_context.audioOption.mute = true;

IZoomVideoSDKSession* session = video_sdk_obj->joinSession(session_context);

// 5. CRITICAL: Add Windows message pump for callbacks to work
bool running = true;
while (running) {
    // Process Windows messages (required for SDK callbacks)
    MSG msg;
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
    // Your application logic here
    Sleep(10);
}

C# Application

using ZoomVideoSDK;

var sdkManager = new ZoomSDKManager();
sdkManager.Initialize();
sdkManager.JoinSession("my-session", "jwt-token", "User Name", "");

Key Features

| Feature | Description | |---------|-------------| | Session Management | Join, leave, and manage video sessions | | Raw Video (YUV I420) | Capture and inject raw video frames | | Raw Audio (PCM) | Capture and inject raw audio data | | Screen Sharing | Share screens or custom content | | Cloud Recording | Record sessions to Zoom cloud | | Live Streaming | Stream to RTMP endpoints | | Chat | Send/receive chat messages | | Command Channel | Custom command messaging | | Live Transcription | Real-time speech-to-text | | C# Support | Full .NET Framework integration |

Sample Applications

Official Repository: https://github.com/zoom/videosdk-windows-rawdata-sample

| Sample | Description | |--------|-------------| | VSDK_SkeletonDemo | Minimal session join - start here | | VSDK_getRawVideo | Capture YUV420 video frames | | VSDK_getRawAudio | Capture PCM audio | | VSDK_sendRawVideo | Inject custom video (virtual camera) | | VSDK_sendRawAudio | Inject custom audio (virtual mic) | | VSDK_CloudRecording | Cloud recording control | | VSDK_CommandChannel | Custom command messaging | | VSDK_TranscriptionAndTranslation | Live captions |

See complete guide: [Sample Applications Reference](references/samples.md)

Critical Gotchas and Best Practices

⚠️ CRITICAL: Windows Message Pump Required

The #1 issue that causes session joins to hang with no callbacks:

All Windows applications using the Zoom SDK MUST process Windows messages. The SDK uses Windows messages to deliver callbacks like onSessionJoin(), onError(), etc.

Problem: Without a message pump, joinSession() appears to succeed but callbacks never fire.

Solution: Add this to your main loop:

while (running) {
    // REQUIRED: Process Windows messages
    MSG msg;
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
    // Your application logic
    Sleep(10);
}

Applies to:

GUI applications using WinMain with standard message loop already have this.

Audio Connection Strategy

Best Practice: Set audioOption.connect = false when joining, then connect audio in the onSessionJoin() callback.

// During join
session_context.audioOption.connect = false;  // Don't connect yet
session_context.audioOption.mute = true;

// In onSessionJoin() callback
void onSessionJoin() override {
    IZoomVideoSDKAudioHelper* audioHelper = video_sdk_obj->getAudioHelper();
    if (audioHelper) {
        audioHelper->startAudio();  // Connect now
    }
}

Why: This pattern is used in all official Zoom samples. It separates session join from audio initialization for better reliability and error handling.

All Delegate Callbacks Must Be Implemented

The IZoomVideoSDKDelegate interface has 70+ pure virtual methods. ALL must be implemented, even if empty:

// Required even if you don't use them
void onProxyDetectComplete() override {}
void onUserWhiteboardShareStatusChanged(IZoomVideoSDKUser*, IZoomVideoSDKWhiteboardHelper*) override {}
// ... etc

Tip: Check the SDK version's zoom_video_sdk_delegate_interface.h for the complete list. The interface changes between SDK versions.

Memory Mode for Raw Data

Always use heap mode for raw data memory:

init_params.videoRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;
init_params.shareRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;
init_params.audioRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;

Stack mode can cause issues with large video frames.

Thread Safety

SDK callbacks execute on SDK threads, not your main thread:

Consult Official Samples First

When SDK behavior is unexpected, always check the official samples before troubleshooting:

Local samples:

Official samples show correct patterns for:

Video Rendering - Two Approaches

The Zoom SDK provides two different ways to render video. Choose based on your needs.

🎯 Canvas API (Recommended for Most Use Cases)

Best for: Standard applications, clean video quality, ease of implementation

The SDK renders video directly to your HWND. No YUV conversion needed.

// Subscribe to a user's video with Canvas API
IZoomVideoSDKCanvas* canvas = user->GetVideoCanvas();
if (canvas) {
    ZoomVideoSDKErrors ret = canvas->subscribeWithView(
        hwnd,                                    // Your window handle
        ZoomVideoSDKVideoAspect_PanAndScan,     // Fit to window, may crop
        ZoomVideoSDKResolution_Auto              // Let SDK choose best resolution
    );
    
    if (ret == ZoomVideoSDKErrors_Success) {
        // SDK is now rendering directly to your window!
    }
}

// Unsubscribe when done
canvas->unSubscribeWithView(hwnd);

Advantages:

Example from official .NET sample:

// Self video preview
IZoomVideoSDKCanvas* canvas = myself->GetVideoCanvas();
canvas->subscribeWithView(selfVideoHwnd, aspect, resolution);

// Remote user video
IZoomVideoSDKCanvas* remoteCanvas = remoteUser->GetVideoCanvas();
remoteCanvas->subscribeWithView(remoteVideoHwnd, aspect, resolution);

Video Aspect Options:

Resolution Options:

🔧 Raw Data Pipe (Advanced Use Cases)

Best for: Custom video processing, effects, recording, computer vision

You receive raw YUV420 frames and handle rendering yourself.

// 1. Create a delegate to receive frames
class VideoRenderer : public IZoomVideoSDKRawDataPipeDelegate {
public:
    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();
        
        // Convert YUV420 to RGB and render
        ConvertYUVToRGB(yBuffer, uBuffer, vBuffer, width, height);
        RenderToWindow(rgbBuffer, width, height);
    }
    
    void onRawDataStatusChanged(RawDataStatus status) override {
        // Handle video on/off
    }
};

// 2. Subscribe to raw data
IZoomVideoSDKRawDataPipe* pipe = user->GetVideoPipe();
VideoRenderer* renderer = new VideoRenderer();
pipe->subscribe(ZoomVideoSDKResolution_720P, renderer);

YUV420 to RGB Conversion (ITU-R BT.601):

void ConvertYUV420ToRGB(char* yBuffer, char* uBuffer, char* vBuffer, 
                        int width, int height) {
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            int yIndex = y * width + x;
            int uvIndex = (y / 2) * (width / 2) + (x / 2);
            
            int Y = (unsigned char)yBuffer[yIndex];
            int U = (unsigned char)uBuffer[uvIndex];
            int V = (unsigned char)vBuffer[uvIndex];
            
            // YUV to RGB conversion
            int C = Y - 16;
            int D = U - 128;
            int E = V - 128;
            
            int R = (298 * C + 409 * E + 128) >> 8;
            int G = (298 * C - 100 * D - 208 * E + 128) >> 8;
            int B = (298 * C + 516 * D + 128) >> 8;
            
            // Clamp to [0, 255]
            R = (R < 0) ? 0 : (R > 255) ? 255 : R;
            G = (G < 0) ? 0 : (G > 255) ? 255 : G;
            B = (B < 0) ? 0 : (B > 255) ? 255 : B;
            
            // Store RGB (BGR format for Windows)
            rgbBuffer[yIndex * 3 + 0] = (unsigned char)B;
            rgbBuffer[yIndex * 3 + 1] = (unsigned char)G;
            rgbBuffer[yIndex * 3 + 2] = (unsigned char)R;
        }
    }
}

Render with GDI:

void RenderToWindow(unsigned char* rgbBuffer, int width, int height) {
    HDC hdc = GetDC(hwnd);
    
    BITMAPINFO bmi = {};
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = width;
    bmi.bmiHeader.biHeight = -height;  // Negative for top-down
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 24;     // 24-bit RGB
    bmi.bmiHeader.biCompression = BI_RGB;
    
    RECT rect;
    GetClientRect(hwnd, &rect);
    
    StretchDIBits(hdc,
        0, 0, rect.right, rect.bottom,  // Destination
        0, 0, width, height,              // Source
        rgbBuffer, &bmi,
        DIB_RGB_COLORS, SRCCOPY);
    
    ReleaseDC(hwnd, hdc);
}

Disadvantages:

Use Raw Data When:

Self Video vs Remote Users

Self Video (your own camera):

Option A: Canvas API

IZoomVideoSDKSession* session = sdk->getSessionInfo();
IZoomVideoSDKUser* myself = session->getMyself();
IZoomVideoSDKCanvas* canvas = myself->GetVideoCanvas();
canvas->subscribeWithView(selfVideoHwnd, aspect, resolution);

Option B: Video Preview (for self only)

IZoomVideoSDKVideoHelper* videoHelper = sdk->getVideoHelper();
videoHelper->startVideo();  // Start transmission

// For preview rendering
videoHelper->startVideoCanvasPreview(selfVideoHwnd, aspect, resolution);

Remote Users (other participants):

Canvas API (recommended):

// In onUserJoin callback
void onUserJoin(IZoomVideoSDKUserHelper*, IVideoSDKVector<IZoomVideoSDKUser*>* userList) {
    for (int i = 0; i < userList->GetCount(); i++) {
        IZoomVideoSDKUser* user = userList->GetItem(i);
        IZoomVideoSDKCanvas* canvas = user->GetVideoCanvas();
        canvas->subscribeWithView(userVideoHwnd, aspect, resolution);
    }
}

Event-Driven Subscription Pattern

⚠️ CRITICAL: Video subscription must be event-driven and manual.

Key Events:

Complete Pattern:

class MainFrame : public IZoomVideoSDKDelegate {
private:
    std::map<IZoomVideoSDKUser*, IZoomVideoSDKCanvas*> subscribedUsers_;
    HWND videoWindow_;
    
public:
    void onSessionJoin() override {
        // Start your own video
        IZoomVideoSDKVideoHelper* videoHelper = sdk->getVideoHelper();
        videoHelper->startVideo();
        
        // Subscribe to self video
        IZoomVideoSDKUser* myself = sdk->getSessionInfo()->getMyself();
        SubscribeToUser(myself);
    }
    
    void onUserJoin(IZoomVideoSDKUserHelper*, 
                    IVideoSDKVector<IZoomVideoSDKUser*>* userList) override {
        // Get current user to exclude self
        IZoomVideoSDKUser* myself = sdk->getSessionInfo()->getMyself();
        
        for (int i = 0; i < userList->GetCount(); i++) {
            IZoomVideoSDKUser* user = userList->GetItem(i);
            
            // IMPORTANT: Only subscribe to REMOTE users!
            if (user != myself) {
                SubscribeToUser(user);
            }
        }
    }
    
    void onUserVideoStatusChanged(IZoomVideoSDKVideoHelper*, 
                                  IVideoSDKVector<IZoomVideoSDKUser*>* userList) override {
        IZoomVideoSDKUser* myself = sdk->getSessionInfo()->getMyself();
        
        for (int i = 0; i < userList->GetCount(); i++) {
            IZoomVideoSDKUser* user = userList->GetItem(i);
            if (user != myself) {
                // Re-subscribe when video status changes
                SubscribeToUser(user);
            }
        }
    }
    
    void onUserLeave(IZoomVideoSDKUserHelper*, 
                    IVideoSDKVector<IZoomVideoSDKUser*>* userList) override {
        for (int i = 0; i < userList->GetCount(); i++) {
            IZoomVideoSDKUser* user = userList->GetItem(i);
            UnsubscribeFromUser(user);
        }
    }
    
    void onSessionLeave() override {
        // Cleanup all subscriptions
        for (auto& pair : subscribedUsers_) {
            IZoomVideoSDKCanvas* canvas = pair.second;
            if (canvas) {
                canvas->unSubscribeWithView(videoWindow_);
            }
        }
        subscribedUsers_.clear();
    }
    
private:
    void SubscribeToUser(IZoomVideoSDKUser* user) {
        if (!user || subscribedUsers_.find(user) != subscribedUsers_.end())
            return;
            
        IZoomVideoSDKCanvas* canvas = user->GetVideoCanvas();
        if (canvas) {
            ZoomVideoSDKErrors ret = canvas->subscribeWithView(
                videoWindow_,
                ZoomVideoSDKVideoAspect_PanAndScan,
                ZoomVideoSDKResolution_Auto
            );
            
            if (ret == ZoomVideoSDKErrors_Success) {
                subscribedUsers_[user] = canvas;
            }
        }
    }
    
    void UnsubscribeFromUser(IZoomVideoSDKUser* user) {
        auto it = subscribedUsers_.find(user);
        if (it != subscribedUsers_.end()) {
            IZoomVideoSDKCanvas* canvas = it->second;
            if (canvas) {
                canvas->unSubscribeWithView(videoWindow_);
            }
            subscribedUsers_.erase(it);
        }
    }
};

Key Points:

⚠️ Screen Share Subscription (DIFFERENT from Video!)

CRITICAL: Screen share subscription uses IZoomVideoSDKShareAction from the callback, NOT user->GetShareCanvas()!

// WRONG - This won't work for remote screen shares!
user->GetShareCanvas()->subscribeWithView(hwnd, ...);

// CORRECT - Use IZoomVideoSDKShareAction from onUserShareStatusChanged callback
void onUserShareStatusChanged(IZoomVideoSDKShareHelper* pShareHelper,
                               IZoomVideoSDKUser* pUser,
                               IZoomVideoSDKShareAction* pShareAction) {
    if (!pShareAction) return;
    
    ZoomVideoSDKShareStatus status = pShareAction->getShareStatus();
    
    if (status == ZoomVideoSDKShareStatus_Start || 
        status == ZoomVideoSDKShareStatus_Resume) {
        // Subscribe to the share using Canvas API
        IZoomVideoSDKCanvas* shareCanvas = pShareAction->getShareCanvas();
        if (shareCanvas) {
            shareCanvas->subscribeWithView(shareWindow_, 
                ZoomVideoSDKVideoAspect_Original);
        }
    }
    else if (status == ZoomVideoSDKShareStatus_Stop) {
        // Unsubscribe when share stops
        IZoomVideoSDKCanvas* shareCanvas = pShareAction->getShareCanvas();
        if (shareCanvas) {
            shareCanvas->unSubscribeWithView(shareWindow_);
        }
    }
}

Why is share different from video?

See also: [Screen Share Subscription Example](examples/screen-share-subscription.md)

Multi-User Video Layout

For multiple participants, you need one HWND per user:

// Create separate windows/panels for each user
HWND selfVideoWindow = CreateWindow(...);   // Your video
HWND user1Window = CreateWindow(...);       // User 1's video
HWND user2Window = CreateWindow(...);       // User 2's video

// Subscribe each user to their own window
myself->GetVideoCanvas()->subscribeWithView(selfVideoWindow, ...);
user1->GetVideoCanvas()->subscribeWithView(user1Window, ...);
user2->GetVideoCanvas()->subscribeWithView(user2Window, ...);

Layout Strategies:

Common Video Issues

| Issue | Cause | Solution | |-------|-------|----------| | Video not showing | Not calling startVideo() | Call videoHelper->startVideo() in onSessionJoin | | Artifacts/tearing | Using Raw Data Pipe | Switch to Canvas API | | Poor performance | YUV conversion on UI thread | Use Canvas API or move conversion to worker thread | | Video freezes | Not processing Windows messages | Add message pump to main loop | | Can't see self | Subscribing to wrong user | Use session->getMyself() for self video | | Seeing self in remote list | Not excluding self | Check if (user != myself) before subscribing |

Complete Documentation Library

This skill includes comprehensive guides organized by category:

Core Concepts (Start Here!)

Complete Examples

UI Framework Integration

C++/CLI Wrapper Patterns (Wrapping ANY Native Library)

Troubleshooting

References

Most Critical Issues (From Real Debugging)

Key Insight

Once you learn the 3-step pattern, you can implement ANY feature:

See: [SDK Architecture Pattern](concepts/sdk-architecture-pattern.md)

Resources

---

Need help? Start with [SKILL.md](SKILL.md) for complete navigation.

Merged from video-sdk/windows/SKILL.md

Zoom Video SDK Windows - Complete Documentation Index

Quick Start Path

If you're new to the SDK, follow this order:

---

Documentation Structure

video-sdk/windows/
├── SKILL.md                           # Main skill overview
├── SKILL.md                           # This file - navigation guide
├── windows.md                          # Secondary overview doc (pointer-style)
│
├── concepts/                          # Core architectural patterns
│   ├── sdk-architecture-pattern.md   # Universal formula for ANY feature
│   ├── singleton-hierarchy.md        # 5-level navigation guide
│   └── canvas-vs-raw-data.md         # SDK-rendered vs self-rendered choice
│
├── examples/                          # Complete working code
│   ├── session-join-pattern.md       # JWT auth + session join
│   ├── video-rendering.md            # Canvas API video display
│   ├── screen-share-subscription.md  # View remote screen shares
│   ├── raw-video-capture.md          # YUV420 raw frame capture
│   ├── raw-audio-capture.md          # PCM audio capture
│   ├── send-raw-video.md             # Virtual camera (inject video)
│   ├── send-raw-audio.md             # Virtual mic (inject audio)
│   ├── cloud-recording.md            # Cloud recording control
│   ├── command-channel.md            # Custom command messaging
│   ├── transcription.md              # Live transcription/captions
│   └── dotnet-winforms/              # UI Framework integration
│       └── README.md                 # Win32, WinForms, WPF patterns
│                                     # C++/CLI wrapper patterns
│                                     # Production quality guidelines
│
├── troubleshooting/                   # Problem solving guides
│   ├── windows-message-loop.md       # CRITICAL - Why callbacks fail
│   ├── build-errors.md               # Header dependency fixes
│   └── common-issues.md              # Quick diagnostic workflow
│
└── references/                        # Reference documentation
    ├── windows-reference.md           # API hierarchy, methods, error codes
    ├── delegate-methods.md            # All 80+ callback methods
    └── samples.md                     # Official samples guide

---

By Use Case

I want to build a video app

I'm getting build errors

I'm getting runtime errors

I want to view screen shares

I want to capture raw video/audio

I want to send custom video/audio (virtual camera/mic)

I want to record sessions

I want to use live transcription

I want custom messaging between participants

I want to build a Win32 native app

I want to build a WinForms (.NET) app

I want to build a WPF (.NET) app

I want to use C# / .NET Framework (general)

I want to wrap ANY native C++ library for .NET

I want to implement a specific feature

---

Most Critical Documents

1. SDK Architecture Pattern (MASTER DOCUMENT)

[concepts/sdk-architecture-pattern.md](concepts/sdk-architecture-pattern.md)

The universal 3-step pattern:

2. Windows Message Loop (MOST COMMON ISSUE)

[troubleshooting/windows-message-loop.md](troubleshooting/windows-message-loop.md)

99% of "callbacks not firing" issues are caused by missing Windows message loop.

3. Singleton Hierarchy (NAVIGATION MAP)

[concepts/singleton-hierarchy.md](concepts/singleton-hierarchy.md)

5-level deep navigation showing how to reach every feature.

---

Key Learnings

Critical Discoveries:

---

Quick Reference

"My code won't compile"

→ [Build Errors Guide](troubleshooting/build-errors.md)

"Callbacks never fire"

→ [Windows Message Loop](troubleshooting/windows-message-loop.md)

"Video subscription returns error 2"

→ [Video Rendering](examples/video-rendering.md) - Subscribe in onUserVideoStatusChanged

"Abstract class error"

→ [Delegate Methods](references/delegate-methods.md)

"How do I implement [feature]?"

→ [SDK Architecture Pattern](concepts/sdk-architecture-pattern.md)

"How do I navigate to [controller]?"

→ [Singleton Hierarchy](concepts/singleton-hierarchy.md)

"What error code means what?"

→ [Common Issues](troubleshooting/common-issues.md)

---

Document Version

Based on Zoom Video SDK for Windows v2.x

---

Happy coding!

Remember: The [SDK Architecture Pattern](concepts/sdk-architecture-pattern.md) is your key to unlocking the entire SDK. Read it first!

Operations