| name | video-sdk-windows |
| description | Zoom Video SDK for Windows - C++ integration for video sessions, raw audio/video capture, screen sharing, recording, and real-time communication |
| user-invocable | false |
| triggers | ["video sdk windows","windows video sdk","video sdk raw data windows","windows custom video","c++ video sdk"] |
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:
- SDK Architecture Pattern - Universal 3-step pattern for ANY feature
- Session Join Pattern - Complete working code to join a session
- Windows Message Loop - CRITICAL: Fix callbacks not firing
- Video Rendering - Display video with Canvas API
Reference:
Having issues?
Building a Custom UI?
SDK Overview
The Zoom Video SDK for Windows 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
- C# Integration: C++/CLI wrapper for .NET applications
Prerequisites
System Requirements
- OS: Windows 10 (1903 or later) or Windows 11
- Architecture: x64 (recommended), x86, or ARM64
- Visual Studio: 2019 or 2022 (Community, Professional, or Enterprise)
- Windows SDK: 10.0.19041.0 or later
- .NET Framework: 4.8 or later (for C# applications)
Visual Studio Workloads
Install these workloads via Visual Studio Installer:
-
Desktop development with C++
- MSVC v142 or v143 compiler
- Windows 10/11 SDK
- C++ CMake tools (optional)
-
.NET desktop development (for C# applications)
- .NET Framework 4.8 targeting pack
- C++/CLI support
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
IZoomVideoSDK* video_sdk_obj = CreateZoomVideoSDKObj();
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);
video_sdk_obj->addListener(myDelegate);
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;
session_context.audioOption.mute = true;
IZoomVideoSDKSession* session = video_sdk_obj->joinSession(session_context);
bool running = true;
while (running) {
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
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
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) {
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(10);
}
Applies to:
- Console applications (no automatic message pump)
- Custom main loops
- Applications that don't use standard WinMain/WndProc
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.
session_context.audioOption.connect = false;
session_context.audioOption.mute = true;
void onSessionJoin() override {
IZoomVideoSDKAudioHelper* audioHelper = video_sdk_obj->getAudioHelper();
if (audioHelper) {
audioHelper->startAudio();
}
}
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:
void onProxyDetectComplete() override {}
void onUserWhiteboardShareStatusChanged(IZoomVideoSDKUser*, IZoomVideoSDKWhiteboardHelper*) override {}
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:
- Don't perform heavy operations in callbacks
- Don't call
cleanup() from within callbacks
- Use thread-safe queues for passing data to UI thread
- Use mutexes when accessing shared state
Consult Official Samples First
When SDK behavior is unexpected, always check the official samples before troubleshooting:
Local samples:
C:\tempsdk\Zoom_VideoSDK_Windows_RawDataDemos\VSDK_SkeletonDemo\ (simplest)
C:\tempsdk\sdksamples\zoom-video-sdk-windows-2.4.12\Sample-Libs\x64\demo\
Official samples show correct patterns for:
- Message pump implementation ✓
- Audio connection strategy ✓
- Error handling ✓
- Memory management ✓
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.
IZoomVideoSDKCanvas* canvas = user->GetVideoCanvas();
if (canvas) {
ZoomVideoSDKErrors ret = canvas->subscribeWithView(
hwnd,
ZoomVideoSDKVideoAspect_PanAndScan,
ZoomVideoSDKResolution_Auto
);
if (ret == ZoomVideoSDKErrors_Success) {
}
}
canvas->unSubscribeWithView(hwnd);
Advantages:
- ✅ Best quality - SDK uses optimized, hardware-accelerated rendering
- ✅ No artifacts - Professional video quality
- ✅ Simple code - 3 lines to subscribe
- ✅ Better performance - No CPU-intensive YUV conversion
- ✅ Automatic scaling - SDK handles window resizing
- ✅ Aspect ratio - Built-in aspect ratio handling
Example from official .NET sample:
IZoomVideoSDKCanvas* canvas = myself->GetVideoCanvas();
canvas->subscribeWithView(selfVideoHwnd, aspect, resolution);
IZoomVideoSDKCanvas* remoteCanvas = remoteUser->GetVideoCanvas();
remoteCanvas->subscribeWithView(remoteVideoHwnd, aspect, resolution);
Video Aspect Options:
ZoomVideoSDKVideoAspect_Original - Letterbox/pillarbox, no cropping
ZoomVideoSDKVideoAspect_FullFilled - Fill window, may crop edges
ZoomVideoSDKVideoAspect_PanAndScan - Smart crop to fill window
ZoomVideoSDKVideoAspect_LetterBox - Show full video with black bars
Resolution Options:
ZoomVideoSDKResolution_90P
ZoomVideoSDKResolution_180P
ZoomVideoSDKResolution_360P - Good balance
ZoomVideoSDKResolution_720P - HD quality
ZoomVideoSDKResolution_1080P
ZoomVideoSDKResolution_Auto - Let SDK decide (recommended)
🔧 Raw Data Pipe (Advanced Use Cases)
Best for: Custom video processing, effects, recording, computer vision
You receive raw YUV420 frames and handle rendering yourself.
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();
ConvertYUVToRGB(yBuffer, uBuffer, vBuffer, width, height);
RenderToWindow(rgbBuffer, width, height);
}
void onRawDataStatusChanged(RawDataStatus status) override {
}
};
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];
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;
R = (R < 0) ? 0 : (R > 255) ? 255 : R;
G = (G < 0) ? 0 : (G > 255) ? 255 : G;
B = (B < 0) ? 0 : (B > 255) ? 255 : B;
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;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 24;
bmi.bmiHeader.biCompression = BI_RGB;
RECT rect;
GetClientRect(hwnd, &rect);
StretchDIBits(hdc,
0, 0, rect.right, rect.bottom,
0, 0, width, height,
rgbBuffer, &bmi,
DIB_RGB_COLORS, SRCCOPY);
ReleaseDC(hwnd, hdc);
}
Disadvantages:
- ⚠️ CPU intensive - YUV conversion can cause frame drops
- ⚠️ Artifacts - Manual rendering may show tearing/artifacts
- ⚠️ Complex - More code to maintain
- ⚠️ Performance - Slower than Canvas API
Use Raw Data When:
- Adding video filters/effects
- Recording to custom formats
- Computer vision processing
- Custom compositing
- Streaming to non-standard outputs
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();
videoHelper->startVideoCanvasPreview(selfVideoHwnd, aspect, resolution);
Remote Users (other participants):
Canvas API (recommended):
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:
onSessionJoin - Subscribe to self video
onUserJoin - Subscribe to new remote users
onUserVideoStatusChanged - Re-subscribe when video turns on/off
onUserLeave - Unsubscribe and cleanup
Complete Pattern:
class MainFrame : public IZoomVideoSDKDelegate {
private:
std::map<IZoomVideoSDKUser*, IZoomVideoSDKCanvas*> subscribedUsers_;
HWND videoWindow_;
public:
void onSessionJoin() override {
IZoomVideoSDKVideoHelper* videoHelper = sdk->getVideoHelper();
videoHelper->startVideo();
IZoomVideoSDKUser* myself = sdk->getSessionInfo()->getMyself();
SubscribeToUser(myself);
}
void onUserJoin(IZoomVideoSDKUserHelper*,
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) {
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) {
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 {
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:
- ✅ Subscribe in response to events (onUserJoin, onUserVideoStatusChanged)
- ✅ Always exclude current user from remote subscriptions
- ✅ Unsubscribe on onUserLeave
- ✅ Clean up all subscriptions on onSessionLeave
- ✅ Track subscriptions in a map for lifecycle management
⚠️ Screen Share Subscription (DIFFERENT from Video!)
CRITICAL: Screen share subscription uses IZoomVideoSDKShareAction from the callback, NOT user->GetShareCanvas()!
user->GetShareCanvas()->subscribeWithView(hwnd, ...);
void onUserShareStatusChanged(IZoomVideoSDKShareHelper* pShareHelper,
IZoomVideoSDKUser* pUser,
IZoomVideoSDKShareAction* pShareAction) {
if (!pShareAction) return;
ZoomVideoSDKShareStatus status = pShareAction->getShareStatus();
if (status == ZoomVideoSDKShareStatus_Start ||
status == ZoomVideoSDKShareStatus_Resume) {
IZoomVideoSDKCanvas* shareCanvas = pShareAction->getShareCanvas();
if (shareCanvas) {
shareCanvas->subscribeWithView(shareWindow_,
ZoomVideoSDKVideoAspect_Original);
}
}
else if (status == ZoomVideoSDKShareStatus_Stop) {
IZoomVideoSDKCanvas* shareCanvas = pShareAction->getShareCanvas();
if (shareCanvas) {
shareCanvas->unSubscribeWithView(shareWindow_);
}
}
}
Why is share different from video?
- Video: Each user has one video stream → use
user->GetVideoCanvas()
- Share: A user can have multiple share actions (multi-share) → use
IZoomVideoSDKShareAction* from callback
- The
IZoomVideoSDKShareAction object represents a specific share stream and contains the share status, type, and rendering interfaces
See also: Screen Share Subscription Example
Multi-User Video Layout
For multiple participants, you need one HWND per user:
HWND selfVideoWindow = CreateWindow(...);
HWND user1Window = CreateWindow(...);
HWND user2Window = CreateWindow(...);
myself->GetVideoCanvas()->subscribeWithView(selfVideoWindow, ...);
user1->GetVideoCanvas()->subscribeWithView(user1Window, ...);
user2->GetVideoCanvas()->subscribeWithView(user2Window, ...);
Layout Strategies:
- Grid layout (2x2, 3x3)
- Gallery view (scrollable)
- Active speaker (large) + thumbnails
- Picture-in-picture
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)
-
Callbacks not firing → Missing Windows message loop (99% of issues)
-
Video subscribe returns error 2 → Subscribing too early
-
Abstract class errors → Missing virtual method implementations
Key Insight
Once you learn the 3-step pattern, you can implement ANY feature:
- Get singleton → 2. Implement delegate → 3. Subscribe & use
See: SDK Architecture Pattern
Resources
Need help? Start with 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:
-
Overview → windows.md
-
Read the architecture pattern → concepts/sdk-architecture-pattern.md
- Universal formula: Singleton → Delegate → Subscribe
- Once you understand this, you can implement any feature
-
Fix build errors → troubleshooting/build-errors.md
- SDK header dependencies
- Required include order
-
Implement session join → examples/session-join-pattern.md
- Complete working JWT + session join code
-
Fix callback issues → troubleshooting/windows-message-loop.md
- CRITICAL: Why callbacks don't fire without Windows message loop
-
Implement video → examples/video-rendering.md
- Canvas API (SDK-rendered) vs Raw Data Pipe
-
Troubleshoot any issues → troubleshooting/common-issues.md
- Quick diagnostic checklist
- Error code tables
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
- SDK Architecture Pattern - Understand the pattern
- Session Join Pattern - Join sessions
- Video Rendering - Display video
- Windows Message Loop - Fix callback issues
I'm getting build errors
- Build Errors Guide - SDK header dependencies
- Delegate Methods - Abstract class errors
- Common Issues - Linker errors
I'm getting runtime errors
- Windows Message Loop - Callbacks not firing
- Common Issues - Error code tables
I want to view screen shares
- Screen Share Subscription - DIFFERENT from video!
- SDK Architecture Pattern - Event-driven pattern
- Video Rendering - Compare with video subscription
I want to capture raw video/audio
- Canvas vs Raw Data - Choose your approach
- Raw Video Capture - YUV420 frame capture
- Raw Audio Capture - PCM audio capture
- SDK Architecture Pattern - Subscription pattern
I want to send custom video/audio (virtual camera/mic)
- Send Raw Video - Inject custom video frames
- Send Raw Audio - Inject custom audio
- SDK Architecture Pattern - External source pattern
I want to record sessions
- Cloud Recording - Start/stop cloud recording
- API Reference - Recording helper methods
I want to use live transcription
- Transcription - Enable live captions
- Delegate Methods - Transcription callbacks
I want custom messaging between participants
- Command Channel - Send custom commands
- API Reference - Command channel methods
I want to build a Win32 native app
- Win32 Integration - Direct SDK + Canvas API
- Video Rendering - Canvas API patterns
- Production Guidelines - Best practices
I want to build a WinForms (.NET) app
- WinForms Integration - C++/CLI wrapper + Raw Data
- C++/CLI Patterns - gcroot, Finalizer, LockBits
- Production Guidelines - IDisposable, thread safety
I want to build a WPF (.NET) app
- WPF Integration - C++/CLI + BitmapSource
- Bitmap Conversion - Freeze(), Dispatcher
- Production Guidelines - Performance optimization
I want to use C# / .NET Framework (general)
- .NET Integration Overview - Complete C++/CLI wrapper guide
- Raw Video Capture - YUV→RGB conversion patterns
- Session Join Pattern - SDK initialization flow
I want to wrap ANY native C++ library for .NET
- C++/CLI Wrapper Patterns - Complete 8-pattern guide
- Pattern 1: Basic Structure - Project setup + class layout
- Pattern 3: gcroot Callbacks - Native→Managed events
- Pattern 4: IDisposable - Cleanup pattern
- Common Errors - Troubleshooting
I want to implement a specific feature
- SDK Architecture Pattern - START HERE!
- Singleton Hierarchy - Navigate to the feature
- API Reference - Method signatures
Most Critical Documents
1. SDK Architecture Pattern (MASTER DOCUMENT)
concepts/sdk-architecture-pattern.md
The universal 3-step pattern:
- Get singleton (SDK, helpers, session, users)
- Implement delegate (event callbacks)
- Subscribe and use
2. Windows Message Loop (MOST COMMON ISSUE)
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
5-level deep navigation showing how to reach every feature.
Key Learnings
Critical Discoveries:
-
Windows Message Loop is MANDATORY
-
Subscribe in onUserVideoStatusChanged, NOT onUserJoin
- Video may not be ready when user joins
- Wait for video status change callback
- See: Video Rendering
-
Two Rendering Paths
- Canvas API: SDK renders to your HWND (recommended)
- Raw Data Pipe: You receive YUV frames (advanced)
- See: Canvas vs Raw Data
-
Helpers Control YOUR Streams Only
videoHelper->startVideo() starts YOUR camera
- To see others, subscribe to their Canvas/Pipe
- See: Singleton Hierarchy
-
UI Framework Integration Differs by Platform
- Win32: Direct SDK, Canvas API (SDK renders to HWND) - best performance
- WinForms: C++/CLI wrapper, Raw Data Pipe, YUV→Bitmap, InvokeRequired
- WPF: Same wrapper + Bitmap→BitmapSource, Dispatcher, Freeze()
- See: UI Framework Integration
-
C++/CLI Wrapper Patterns (for ANY native library → .NET)
void* pointers - hide native types from managed headers
gcroot<T^> - prevent GC from collecting managed references in native code
- Finalizer + Destructor -
~Class() and !Class() for IDisposable cleanup
pin_ptr + Marshal::Copy - array/buffer conversion
LockBits - 100x faster than SetPixel for image manipulation
- Thread marshaling - InvokeRequired (WinForms) / Dispatcher (WPF)
- See: C++/CLI Wrapper Guide
-
Audio Connection Timing
- Set
audioOption.connect = false during join
- Call
startAudio() in onSessionJoin callback
- See: Production Guidelines
Quick Reference
"My code won't compile"
→ Build Errors Guide
"Callbacks never fire"
→ Windows Message Loop
"Video subscription returns error 2"
→ Video Rendering - Subscribe in onUserVideoStatusChanged
"Abstract class error"
→ Delegate Methods
"How do I implement [feature]?"
→ SDK Architecture Pattern
"How do I navigate to [controller]?"
→ Singleton Hierarchy
"What error code means what?"
→ Common Issues
Document Version
Based on Zoom Video SDK for Windows v2.x
Happy coding!
Remember: The SDK Architecture Pattern is your key to unlocking the entire SDK. Read it first!
Operations
- RUNBOOK.md - 5-minute preflight and debugging checklist.