Видеоприложения Zoom для Windows на C++
Руководство разработчика: как создавать на Windows (C++ и C#) видеоприложения с Zoom Video SDK, отрисовкой видео, сырыми данными и записью.
- Что делает
- Руководство разработчика: как создавать на Windows (C++ и C#) видеоприложения с Zoom Video SDK, отрисовкой видео, сырыми данными и записью.
- Когда брать
- Когда нужно создать настольное приложение Windows с собственной видеосессией, захватом или подачей аудио и видео, облачной записью.
- Когда не брать
- Если нужна обычная встреча Zoom или приложение не под Windows.
- Пример запроса
- Помоги написать на C++ приложение для Windows, которое входит в сессию Zoom Video SDK и показывает видео участников.
- Нужно подключить
- терминал
Входит в плагин zoom-plugin. В Cowork и Claude Code можно поставить плагин целиком.
Как включить
- Скачайте архив и распакуйте его.
- Положите папку
video-sdk-windowsв~/.claude/skills/. - Откройте Claude Code и опишите задачу своими словами: Claude подхватит скилл по описанию.
Текст
---
name: video-sdk/windows
description: "Zoom Video SDK для Windows - интеграция на C++ для видеосессий, захвата сырых аудио и видео, демонстрации экрана, записи и общения в реальном времени"
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
Экспертное руководство по разработке с Zoom Video SDK на Windows. Этот SDK позволяет создавать собственные видеоприложения, захватывать и подавать «сырые» медиаданные, вести облачную запись, прямые трансляции и живую транскрипцию на платформах Windows.
Официальная документация: https://developers.zoom.us/docs/video-sdk/windows/ Справочник API: https://marketplacefront.zoom.us/sdk/custom/windows/ Репозиторий с примерами: https://github.com/zoom/videosdk-windows-rawdata-sample
Быстрые ссылки
Впервые работаешь с Video SDK? Иди по этому пути:
- [Архитектурный шаблон SDK](concepts/sdk-architecture-pattern.md) - Универсальный трёхшаговый шаблон для ЛЮБОЙ функции
- [Шаблон входа в сессию](examples/session-join-pattern.md) - Полный рабочий код входа в сессию
- [Цикл сообщений Windows](troubleshooting/windows-message-loop.md) - ВАЖНО: как исправить обратные вызовы, которые не срабатывают
- [Отрисовка видео](examples/video-rendering.md) - Показ видео через Canvas API
Справочные материалы:
- [Иерархия одиночек (singleton)](concepts/singleton-hierarchy.md) - Пятиуровневая карта навигации по SDK
- [Справочник API](references/windows-reference.md) - Методы, коды ошибок, правила по времени вызовов
- [Методы делегата](references/delegate-methods.md) - Все 80+ методов обратного вызова
- [Примеры приложений](references/samples.md) - Руководство по официальным примерам
- [windows.md](windows.md) - Дополнительный обзорный документ (в виде указателя)
- [SKILL.md](SKILL.md) - Полная навигация по документации
Что-то не работает?
- Обратные вызовы не срабатывают → [Цикл сообщений Windows](troubleshooting/windows-message-loop.md)
- Ошибки сборки → [Руководство по ошибкам сборки](troubleshooting/build-errors.md)
- Не удаётся подписаться на видео → [Отрисовка видео](examples/video-rendering.md) (подписывайся в
onUserVideoStatusChanged) - Быстрая диагностика → [Частые проблемы](troubleshooting/common-issues.md)
Создаёшь собственный интерфейс?
- [Canvas или сырые данные](concepts/canvas-vs-raw-data.md) - Выбери подход к отрисовке
- [Захват сырого видео](examples/raw-video-capture.md) - Обработка кадров YUV420
Обзор SDK
Zoom Video SDK для Windows - это библиотека на C++, которая предоставляет:
- Управление сессиями: вход в сессии Video SDK и выход из них
- Доступ к сырым данным: захват сырых кадров аудио и видео (YUV420, PCM)
- Подача сырых данных: отправка собственного аудио и видео в сессии
- Демонстрация экрана: показ экрана или подача собственных источников
- Облачная запись: запись сессий в облако Zoom
- Прямые трансляции: трансляция на RTMP-адреса (YouTube и др.)
- Чат и команды: обмен сообщениями и командные каналы во время сессии
- Живая транскрипция: распознавание речи в реальном времени
- Подсессии: поддержка комнат для групповой работы
- Доска: возможности совместной доски
- Аннотации: пометки поверх демонстрации экрана
- Интеграция с C#: обёртка C++/CLI для приложений .NET
Предварительные требования
Системные требования
- ОС: Windows 10 (1903 или новее) или Windows 11
- Архитектура: x64 (рекомендуется), x86 или ARM64
- Visual Studio: 2019 или 2022 (Community, Professional или Enterprise)
- Windows SDK: 10.0.19041.0 или новее
- .NET Framework: 4.8 или новее (для приложений на C#)
Рабочие нагрузки Visual Studio
Установи эти рабочие нагрузки через Visual Studio Installer:
- Разработка классических приложений на C++
- Компилятор MSVC v142 или v143
- Windows 10/11 SDK
- Инструменты C++ CMake (по желанию)
- Разработка классических приложений .NET (для приложений на C#)
- Пакет таргетинга .NET Framework 4.8
- Поддержка C++/CLI
Быстрый старт
Приложение на C++
#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#
using ZoomVideoSDK;
var sdkManager = new ZoomSDKManager();
sdkManager.Initialize();
sdkManager.JoinSession("my-session", "jwt-token", "User Name", "");
Ключевые возможности
| Возможность | Описание |
|---|---|
| Управление сессией | Вход, выход и управление видеосессиями |
| Сырое видео (YUV I420) | Захват и подача сырых кадров видео |
| Сырой звук (PCM) | Захват и подача сырых аудиоданных |
| Демонстрация экрана | Показ экрана или собственного содержимого |
| Облачная запись | Запись сессий в облако Zoom |
| Прямые трансляции | Трансляция на RTMP-адреса |
| Чат | Отправка и приём сообщений чата |
| Командный канал | Пересылка собственных команд |
| Живая транскрипция | Распознавание речи в реальном времени |
| Поддержка C# | Полная интеграция с .NET Framework |
Примеры приложений
Официальный репозиторий: https://github.com/zoom/videosdk-windows-rawdata-sample
| Пример | Описание |
|---|---|
| VSDK_SkeletonDemo | Минимальный вход в сессию - начни отсюда |
| VSDK_getRawVideo | Захват кадров видео YUV420 |
| VSDK_getRawAudio | Захват звука PCM |
| VSDK_sendRawVideo | Подача собственного видео (виртуальная камера) |
| VSDK_sendRawAudio | Подача собственного звука (виртуальный микрофон) |
| VSDK_CloudRecording | Управление облачной записью |
| VSDK_CommandChannel | Пересылка собственных команд |
| VSDK_TranscriptionAndTranslation | Живые субтитры |
Полное руководство: [Справочник по примерам приложений](references/samples.md)
Критические подводные камни и лучшие практики
⚠️ ВАЖНО: нужен цикл обработки сообщений Windows
Проблема №1, из-за которой вход в сессию зависает без обратных вызовов:
Все приложения Windows, использующие Zoom SDK, ОБЯЗАНЫ обрабатывать сообщения Windows. SDK доставляет обратные вызовы вроде onSessionJoin(), onError() и другие через сообщения Windows.
Проблема: без цикла сообщений joinSession() будто бы срабатывает успешно, но обратные вызовы не приходят.
Решение: добавь это в главный цикл:
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);
}
Касается:
- Консольных приложений (в них нет автоматического цикла сообщений)
- Собственных главных циклов
- Приложений, не использующих стандартные WinMain/WndProc
В графических приложениях со стандартным циклом сообщений WinMain он уже есть.
Стратегия подключения звука
Лучшая практика: при входе задай audioOption.connect = false, а звук подключай в обратном вызове onSessionJoin().
// 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
}
}
Почему: этот шаблон используется во всех официальных примерах Zoom. Он отделяет вход в сессию от инициализации звука, что повышает надёжность и упрощает обработку ошибок.
Нужно реализовать ВСЕ обратные вызовы делегата
В интерфейсе IZoomVideoSDKDelegate более 70 чисто виртуальных методов. ВСЕ должны быть реализованы, пусть даже с пустым телом:
// Required even if you don't use them
void onProxyDetectComplete() override {}
void onUserWhiteboardShareStatusChanged(IZoomVideoSDKUser*, IZoomVideoSDKWhiteboardHelper*) override {}
// ... etc
Совет: полный список смотри в файле zoom_video_sdk_delegate_interface.h своей версии SDK. Интерфейс меняется от версии к версии.
Режим памяти для сырых данных
Для памяти сырых данных всегда используй режим heap:
init_params.videoRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;
init_params.shareRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;
init_params.audioRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;
Режим стека может вызывать проблемы с большими кадрами видео.
Потокобезопасность
Обратные вызовы SDK выполняются в потоках SDK, а не в твоём главном потоке:
- Не выполняй в обратных вызовах тяжёлые операции
- Не вызывай
cleanup()изнутри обратных вызовов - Передавай данные в поток интерфейса через потокобезопасные очереди
- Используй мьютексы при доступе к общему состоянию
Сначала смотри официальные примеры
Если SDK ведёт себя неожиданно, сначала сверься с официальными примерами, а потом ищи неполадки:
Локальные примеры:
C:\tempsdk\Zoom_VideoSDK_Windows_RawDataDemos\VSDK_SkeletonDemo\(самый простой)C:\tempsdk\sdksamples\zoom-video-sdk-windows-2.4.12\Sample-Libs\x64\demo\
Официальные примеры показывают правильные шаблоны для:
- Реализации цикла сообщений ✓
- Стратегии подключения звука ✓
- Обработки ошибок ✓
- Управления памятью ✓
Отрисовка видео - два подхода
Zoom SDK предлагает два разных способа отрисовки видео. Выбирай по своим задачам.
🎯 Canvas API (рекомендуется для большинства случаев)
Лучше всего подходит для: обычных приложений, чистого качества видео, простоты реализации
SDK отрисовывает видео прямо в твоё окно (HWND). Преобразование YUV не нужно.
// 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);
Преимущества:
- ✅ Лучшее качество - SDK использует оптимизированную отрисовку с аппаратным ускорением
- ✅ Без артефактов - профессиональное качество видео
- ✅ Простой код - подписка в 3 строки
- ✅ Выше производительность - нет ресурсоёмкого преобразования YUV на процессоре
- ✅ Автоматическое масштабирование - SDK сам обрабатывает изменение размера окна
- ✅ Соотношение сторон - встроенная обработка соотношения сторон
Пример из официального примера на .NET:
// Self video preview
IZoomVideoSDKCanvas* canvas = myself->GetVideoCanvas();
canvas->subscribeWithView(selfVideoHwnd, aspect, resolution);
// Remote user video
IZoomVideoSDKCanvas* remoteCanvas = remoteUser->GetVideoCanvas();
remoteCanvas->subscribeWithView(remoteVideoHwnd, aspect, resolution);
Варианты соотношения сторон видео:
ZoomVideoSDKVideoAspect_Original- поля сверху/снизу или по бокам, без обрезкиZoomVideoSDKVideoAspect_FullFilled- заполнение окна, края могут обрезатьсяZoomVideoSDKVideoAspect_PanAndScan- умная обрезка для заполнения окнаZoomVideoSDKVideoAspect_LetterBox- показ всего видео с чёрными полосами
Варианты разрешения:
ZoomVideoSDKResolution_90PZoomVideoSDKResolution_180PZoomVideoSDKResolution_360P- хороший балансZoomVideoSDKResolution_720P- качество HDZoomVideoSDKResolution_1080PZoomVideoSDKResolution_Auto- пусть решает SDK (рекомендуется)
🔧 Канал сырых данных (для сложных случаев)
Лучше всего подходит для: собственной обработки видео, эффектов, записи, компьютерного зрения
Ты получаешь сырые кадры YUV420 и сам отвечаешь за отрисовку.
// 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 в RGB (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;
}
}
}
Отрисовка через 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);
}
Недостатки:
- ⚠️ Нагрузка на процессор - преобразование YUV может приводить к потере кадров
- ⚠️ Артефакты - при ручной отрисовке возможны разрывы изображения и артефакты
- ⚠️ Сложность - больше кода для сопровождения
- ⚠️ Производительность - медленнее, чем Canvas API
Используй сырые данные, когда нужно:
- Добавлять фильтры и эффекты к видео
- Записывать в собственные форматы
- Выполнять обработку для компьютерного зрения
- Делать собственный композитинг
- Транслировать на нестандартные выходы
Своё видео и видео других участников
Своё видео (твоя собственная камера):
Вариант А: Canvas API
IZoomVideoSDKSession* session = sdk->getSessionInfo();
IZoomVideoSDKUser* myself = session->getMyself();
IZoomVideoSDKCanvas* canvas = myself->GetVideoCanvas();
canvas->subscribeWithView(selfVideoHwnd, aspect, resolution);
Вариант Б: предпросмотр видео (только для себя)
IZoomVideoSDKVideoHelper* videoHelper = sdk->getVideoHelper();
videoHelper->startVideo(); // Start transmission
// For preview rendering
videoHelper->startVideoCanvasPreview(selfVideoHwnd, aspect, resolution);
Другие участники:
Canvas API (рекомендуется):
// 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);
}
}
Шаблон подписки на событиях
⚠️ ВАЖНО: подписка на видео должна быть событийной и ручной.
Ключевые события:
- **
onSessionJoin** - подписка на своё видео - **
onUserJoin** - подписка на новых удалённых участников - **
onUserVideoStatusChanged** - повторная подписка, когда видео включается или выключается - **
onUserLeave** - отписка и очистка
Полный шаблон:
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);
}
}
};
Главное:
- ✅ Подписывайся в ответ на события (onUserJoin, onUserVideoStatusChanged)
- ✅ Всегда исключай текущего пользователя из подписок на удалённых участников
- ✅ Отписывайся в onUserLeave
- ✅ Очищай все подписки в onSessionLeave
- ✅ Отслеживай подписки в словаре (map) для управления жизненным циклом
⚠️ Подписка на демонстрацию экрана (ОТЛИЧАЕТСЯ от видео!)
ВАЖНО: подписка на демонстрацию экрана использует IZoomVideoSDKShareAction из обратного вызова, а НЕ 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_);
}
}
}
Почему демонстрация экрана отличается от видео?
- Видео: у каждого пользователя один видеопоток → используй
user->GetVideoCanvas() - Демонстрация экрана: у пользователя может быть несколько действий показа (мультидемонстрация) → используй
IZoomVideoSDKShareAction*из обратного вызова - Объект
IZoomVideoSDKShareActionпредставляет конкретный поток демонстрации и содержит статус показа, тип и интерфейсы отрисовки
См. также: [Пример подписки на демонстрацию экрана](examples/screen-share-subscription.md)
Раскладка видео нескольких участников
Для нескольких участников нужно по одному HWND на каждого пользователя:
// 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, ...);
Стратегии раскладки:
- Сетка (2x2, 3x3)
- Галерея (с прокруткой)
- Активный говорящий (крупно) и миниатюры
- Картинка в картинке
Частые проблемы с видео
| Проблема | Причина | Решение |
|---|---|---|
| Видео не отображается | Не вызван startVideo() | Вызови videoHelper->startVideo() в onSessionJoin |
| Артефакты и разрывы изображения | Используется канал сырых данных | Перейди на Canvas API |
| Низкая производительность | Преобразование YUV в потоке интерфейса | Используй Canvas API или вынеси преобразование в рабочий поток |
| Видео зависает | Не обрабатываются сообщения Windows | Добавь цикл сообщений в главный цикл |
| Не видно себя | Подписка не на того пользователя | Для своего видео используй session->getMyself() |
| Себя видно в списке удалённых участников | Себя не исключили | Перед подпиской проверь if (user != myself) |
Полная библиотека документации
В этот скилл входят подробные руководства по категориям:
Основные понятия (начни отсюда!)
- [Архитектурный шаблон SDK](concepts/sdk-architecture-pattern.md) - Универсальный трёхшаговый шаблон для ЛЮБОЙ функции
- [Иерархия одиночек](concepts/singleton-hierarchy.md) - Пятиуровневый путеводитель
- [Canvas или сырые данные](concepts/canvas-vs-raw-data.md) - Выбери подход к отрисовке
Полные примеры
- [Шаблон входа в сессию](examples/session-join-pattern.md) - Аутентификация JWT и вход в сессию с полным кодом
- [Отрисовка видео](examples/video-rendering.md) - Показ видео через Canvas API
- [Подписка на демонстрацию экрана](examples/screen-share-subscription.md) - Просмотр демонстрации экрана других участников (ОТЛИЧАЕТСЯ от видео!)
- [Захват сырого видео](examples/raw-video-capture.md) - Захват кадров YUV420
- [Захват сырого звука](examples/raw-audio-capture.md) - Захват звука PCM
- [Отправка сырого видео](examples/send-raw-video.md) - Виртуальная камера (подача собственного видео)
- [Отправка сырого звука](examples/send-raw-audio.md) - Виртуальный микрофон (подача собственного звука)
- [Облачная запись](examples/cloud-recording.md) - Управление облачной записью
- [Командный канал](examples/command-channel.md) - Пересылка собственных команд
- [Транскрипция](examples/transcription.md) - Живая транскрипция и субтитры
Интеграция с UI-фреймворками
- [Win32 Native](examples/dotnet-winforms/README.md#option-1-win32-native-c---direct-sdk) - Прямое использование SDK с Canvas API (лучшая производительность)
- [WinForms (.NET)](examples/dotnet-winforms/README.md#option-2-winforms-c--ccli-wrapper) - Обёртка C++/CLI и канал сырых данных
- [WPF (.NET)](examples/dotnet-winforms/README.md#option-3-wpf-c--ccli-wrapper) - Обёртка C++/CLI и преобразование в BitmapSource
- [Рекомендации по качеству для промышленной эксплуатации](examples/dotnet-winforms/README.md#production-quality-review) - Контрольный список и типичные проблемы
Шаблоны обёртки C++/CLI (для обёртывания ЛЮБОЙ нативной библиотеки)
- [Полное руководство](examples/dotnet-winforms/README.md#ccli-wrapper-patterns-for-net-integration) - 8 шаблонов взаимодействия нативного кода с .NET
- [Шаблон 1: базовая структура](examples/dotnet-winforms/README.md#pattern-1-basic-wrapper-structure) - Настройка проекта, устройство класса
- **[Шаблон 2: указатели void*](examples/dotnet-winforms/README.md#pattern-2-opaque-void-pointers)** - Скрытие нативных типов
- [Шаблон 3: обратные вызовы gcroot](examples/dotnet-winforms/README.md#pattern-3-gcrootT-for-nativemanaged-callbacks) - События из нативного кода в управляемый
- [Шаблон 4: IDisposable](examples/dotnet-winforms/README.md#pattern-4-destructor--finalizer-idisposable) - Шаблон очистки
- [Шаблон 5: строки](examples/dotnet-winforms/README.md#pattern-5-string-conversion) - String^ ↔ wstring/string
- [Шаблон 6: массивы](examples/dotnet-winforms/README.md#pattern-6-arraybuffer-conversion) - pin_ptr, Marshal::Copy
- [Шаблон 7: потоки](examples/dotnet-winforms/README.md#pattern-7-thread-marshaling-native-thread--ui-thread) - Передача в поток интерфейса
- [Шаблон 8: LockBits](examples/dotnet-winforms/README.md#pattern-8-lockbits-for-fast-image-manipulation) - Быстрое преобразование изображений
- [Частые ошибки](examples/dotnet-winforms/README.md#common-wrapper-errors) - Устранение неполадок
Устранение неполадок
- [Цикл сообщений Windows](troubleshooting/windows-message-loop.md) - ВАЖНО: почему не срабатывают обратные вызовы
- [Ошибки сборки](troubleshooting/build-errors.md) - Исправление зависимостей заголовков SDK
- [Частые проблемы](troubleshooting/common-issues.md) - Быстрая диагностика и коды ошибок
Справочные материалы
- [Справочник API](references/windows-reference.md) - Пятиуровневая иерархия API, методы, коды ошибок
- [Методы делегата](references/delegate-methods.md) - Все 80+ методов обратного вызова
- [SKILL.md](SKILL.md) - Полный путеводитель по навигации
Самые критичные проблемы (из реальной отладки)
- Обратные вызовы не срабатывают → нет цикла сообщений Windows (99% проблем)
- См.: [Руководство по циклу сообщений Windows](troubleshooting/windows-message-loop.md)
- Подписка на видео возвращает ошибку 2 → подписка слишком рано
- См.: [Отрисовка видео](examples/video-rendering.md) - подписывайся в
onUserVideoStatusChanged
- Ошибки абстрактного класса → не реализованы виртуальные методы
- См.: [Методы делегата](references/delegate-methods.md)
Главная мысль
Усвоив трёхшаговый шаблон, ты сможешь реализовать ЛЮБУЮ функцию:
- Получить одиночку → 2. Реализовать делегат → 3. Подписаться и использовать
См.: [Архитектурный шаблон SDK](concepts/sdk-architecture-pattern.md)
Ресурсы
- Официальная документация: https://developers.zoom.us/docs/video-sdk/windows/
- Справочник API: https://marketplacefront.zoom.us/sdk/custom/windows/
- Форум разработчиков: https://devforum.zoom.us/
- Примеры на GitHub: https://github.com/zoom/videosdk-windows-rawdata-sample
- Рабочий пример:
C:\tempsdk\zoom-video-sdk-windows-sample\(полная реализация)
Нужна помощь? Начни с [SKILL.md](SKILL.md): там полная навигация.
Объединено из video-sdk/windows/SKILL.md
Zoom Video SDK для Windows - полный указатель документации
Путь быстрого старта
Если ты впервые работаешь с SDK, иди в таком порядке:
- Обзор → [windows.md](windows.md)
- Прочитай архитектурный шаблон → [concepts/sdk-architecture-pattern.md](concepts/sdk-architecture-pattern.md)
- Универсальная формула: одиночка (Singleton) → делегат (Delegate) → подписка (Subscribe)
- Поняв её, ты сможешь реализовать любую функцию
- Исправь ошибки сборки → [troubleshooting/build-errors.md](troubleshooting/build-errors.md)
- Зависимости заголовков SDK
- Обязательный порядок подключения (include)
- Реализуй вход в сессию → [examples/session-join-pattern.md](examples/session-join-pattern.md)
- Полный рабочий код с JWT и входом в сессию
- Исправь проблемы с обратными вызовами → [troubleshooting/windows-message-loop.md](troubleshooting/windows-message-loop.md)
- ВАЖНО: почему без цикла сообщений Windows обратные вызовы не срабатывают
- Реализуй видео → [examples/video-rendering.md](examples/video-rendering.md)
- Canvas API (отрисовку делает SDK) или канал сырых данных
- Разбери любые проблемы → [troubleshooting/common-issues.md](troubleshooting/common-issues.md)
- Контрольный список быстрой диагностики
- Таблицы кодов ошибок
Структура документации
video-sdk/windows/
├── SKILL.md # Общий обзор скилла
├── SKILL.md # Этот файл - навигация по документации
├── windows.md # Дополнительный обзорный документ (в виде указателя)
│
├── concepts/ # Основные архитектурные шаблоны
│ ├── sdk-architecture-pattern.md # Универсальная формула для ЛЮБОЙ функции
│ ├── singleton-hierarchy.md # Пятиуровневый путеводитель
│ └── canvas-vs-raw-data.md # Отрисовка силами SDK или своими силами
│
├── examples/ # Полные рабочие примеры кода
│ ├── session-join-pattern.md # Аутентификация JWT и вход в сессию
│ ├── video-rendering.md # Показ видео через Canvas API
│ ├── screen-share-subscription.md # Просмотр демонстрации экрана других участников
│ ├── raw-video-capture.md # Захват сырых кадров YUV420
│ ├── raw-audio-capture.md # Захват звука PCM
│ ├── send-raw-video.md # Виртуальная камера (подача видео)
│ ├── send-raw-audio.md # Виртуальный микрофон (подача звука)
│ ├── cloud-recording.md # Управление облачной записью
│ ├── command-channel.md # Пересылка собственных команд
│ ├── transcription.md # Живая транскрипция и субтитры
│ └── dotnet-winforms/ # Интеграция с UI-фреймворками
│ └── README.md # Шаблоны Win32, WinForms, WPF
│ # Шаблоны обёртки C++/CLI
│ # Рекомендации по качеству для промышленной эксплуатации
│
├── troubleshooting/ # Руководства по решению проблем
│ ├── windows-message-loop.md # ВАЖНО - почему не срабатывают обратные вызовы
│ ├── build-errors.md # Исправление зависимостей заголовков
│ └── common-issues.md # Порядок быстрой диагностики
│
└── references/ # Справочная документация
├── windows-reference.md # Иерархия API, методы, коды ошибок
├── delegate-methods.md # Все 80+ методов обратного вызова
└── samples.md # Руководство по официальным примерам
По сценариям использования
Я хочу создать видеоприложение
- [Архитектурный шаблон SDK](concepts/sdk-architecture-pattern.md) - Пойми шаблон
- [Шаблон входа в сессию](examples/session-join-pattern.md) - Входи в сессии
- [Отрисовка видео](examples/video-rendering.md) - Показывай видео
- [Цикл сообщений Windows](troubleshooting/windows-message-loop.md) - Исправь проблемы с обратными вызовами
У меня ошибки сборки
- [Руководство по ошибкам сборки](troubleshooting/build-errors.md) - Зависимости заголовков SDK
- [Методы делегата](references/delegate-methods.md) - Ошибки абстрактного класса
- [Частые проблемы](troubleshooting/common-issues.md) - Ошибки компоновщика
У меня ошибки при выполнении
- [Цикл сообщений Windows](troubleshooting/windows-message-loop.md) - Обратные вызовы не срабатывают
- [Частые проблемы](troubleshooting/common-issues.md) - Таблицы кодов ошибок
Я хочу просматривать демонстрацию экрана
- [Подписка на демонстрацию экрана](examples/screen-share-subscription.md) - ОТЛИЧАЕТСЯ от видео!
- [Архитектурный шаблон SDK](concepts/sdk-architecture-pattern.md) - Событийный шаблон
- [Отрисовка видео](examples/video-rendering.md) - Сравни с подпиской на видео
Я хочу захватывать сырое видео и звук
- [Canvas или сырые данные](concepts/canvas-vs-raw-data.md) - Выбери подход
- [Захват сырого видео](examples/raw-video-capture.md) - Захват кадров YUV420
- [Захват сырого звука](examples/raw-audio-capture.md) - Захват звука PCM
- [Архитектурный шаблон SDK](concepts/sdk-architecture-pattern.md) - Шаблон подписки
Я хочу отправлять собственное видео и звук (виртуальные камера и микрофон)
- [Отправка сырого видео](examples/send-raw-video.md) - Подача собственных кадров видео
- [Отправка сырого звука](examples/send-raw-audio.md) - Подача собственного звука
- [Архитектурный шаблон SDK](concepts/sdk-architecture-pattern.md) - Шаблон внешнего источника
Я хочу записывать сессии
- [Облачная запись](examples/cloud-recording.md) - Запуск и остановка облачной записи
- [Справочник API](references/windows-reference.md) - Методы помощника записи
Я хочу использовать живую транскрипцию
- [Транскрипция](examples/transcription.md) - Включение живых субтитров
- [Методы делегата](references/delegate-methods.md) - Обратные вызовы транскрипции
Мне нужен обмен собственными сообщениями между участниками
- [Командный канал](examples/command-channel.md) - Отправка собственных команд
- [Справочник API](references/windows-reference.md) - Методы командного канала
Я хочу создать нативное приложение Win32
- [Интеграция с Win32](examples/dotnet-winforms/README.md#option-1-win32-native-c---direct-sdk) - Прямое использование SDK и Canvas API
- [Отрисовка видео](examples/video-rendering.md) - Шаблоны Canvas API
- [Рекомендации для промышленной эксплуатации](examples/dotnet-winforms/README.md#production-quality-review) - Лучшие практики
Я хочу создать приложение WinForms (.NET)
- [Интеграция с WinForms](examples/dotnet-winforms/README.md#option-2-winforms-c--ccli-wrapper) - Обёртка C++/CLI и сырые данные
- [Шаблоны C++/CLI](examples/dotnet-winforms/README.md#ccli-wrapper-patterns-for-net-integration) - gcroot, финализатор, LockBits
- [Рекомендации для промышленной эксплуатации](examples/dotnet-winforms/README.md#production-quality-review) - IDisposable, потокобезопасность
Я хочу создать приложение WPF (.NET)
- [Интеграция с WPF](examples/dotnet-winforms/README.md#option-3-wpf-c--ccli-wrapper) - C++/CLI и BitmapSource
- [Преобразование Bitmap](examples/dotnet-winforms/README.md#2-bitmap--bitmapsource-conversion) - Freeze(), Dispatcher
- [Рекомендации для промышленной эксплуатации](examples/dotnet-winforms/README.md#production-quality-review) - Оптимизация производительности
Я хочу использовать C# / .NET Framework (в общем)
- [Обзор интеграции с .NET](examples/dotnet-winforms/README.md) - Полное руководство по обёртке C++/CLI
- [Захват сырого видео](examples/raw-video-capture.md) - Шаблоны преобразования YUV→RGB
- [Шаблон входа в сессию](examples/session-join-pattern.md) - Порядок инициализации SDK
Я хочу обернуть ЛЮБУЮ нативную библиотеку C++ для .NET
- [Шаблоны обёртки C++/CLI](examples/dotnet-winforms/README.md#ccli-wrapper-patterns-for-net-integration) - Полное руководство из 8 шаблонов
- [Шаблон 1: базовая структура](examples/dotnet-winforms/README.md#pattern-1-basic-wrapper-structure) - Настройка проекта и устройство класса
- [Шаблон 3: обратные вызовы gcroot](examples/dotnet-winforms/README.md#pattern-3-gcrootT-for-nativemanaged-callbacks) - События из нативного кода в управляемый
- [Шаблон 4: IDisposable](examples/dotnet-winforms/README.md#pattern-4-destructor--finalizer-idisposable) - Шаблон очистки
- [Частые ошибки](examples/dotnet-winforms/README.md#common-wrapper-errors) - Устранение неполадок
Я хочу реализовать конкретную функцию
- [Архитектурный шаблон SDK](concepts/sdk-architecture-pattern.md) - НАЧНИ ОТСЮДА!
- [Иерархия одиночек](concepts/singleton-hierarchy.md) - Найди путь к функции
- [Справочник API](references/windows-reference.md) - Сигнатуры методов
Самые важные документы
1. Архитектурный шаблон SDK (ГЛАВНЫЙ ДОКУМЕНТ)
[concepts/sdk-architecture-pattern.md](concepts/sdk-architecture-pattern.md)
Универсальный трёхшаговый шаблон:
- Получить одиночку (SDK, помощники, сессия, пользователи)
- Реализовать делегат (обработчики событий)
- Подписаться и использовать
2. Цикл сообщений Windows (САМАЯ ЧАСТАЯ ПРОБЛЕМА)
[troubleshooting/windows-message-loop.md](troubleshooting/windows-message-loop.md)
99% проблем «обратные вызовы не срабатывают» вызваны отсутствием цикла сообщений Windows.
3. Иерархия одиночек (КАРТА НАВИГАЦИИ)
[concepts/singleton-hierarchy.md](concepts/singleton-hierarchy.md)
Пятиуровневая навигация, показывающая, как добраться до любой функции.
Главные выводы
Критические находки:
- Цикл сообщений Windows ОБЯЗАТЕЛЕН
- SDK использует насос сообщений Windows для обратных вызовов
- Без него обратные вызовы ставятся в очередь, но не срабатывают
- См.: [Руководство по циклу сообщений Windows](troubleshooting/windows-message-loop.md)
- Подписывайся в onUserVideoStatusChanged, а НЕ в onUserJoin
- Видео может быть не готово в момент входа пользователя
- Дождись обратного вызова об изменении статуса видео
- См.: [Отрисовка видео](examples/video-rendering.md)
- Два пути отрисовки
- Canvas API: SDK отрисовывает в твоё окно HWND (рекомендуется)
- Канал сырых данных: ты получаешь кадры YUV (для сложных случаев)
- См.: [Canvas или сырые данные](concepts/canvas-vs-raw-data.md)
- Помощники управляют только ТВОИМИ потоками
videoHelper->startVideo()запускает ТВОЮ камеру- Чтобы видеть остальных, подпишись на их Canvas или канал
- См.: [Иерархия одиночек](concepts/singleton-hierarchy.md)
- Интеграция с UI-фреймворками зависит от платформы
- Win32: прямой SDK, Canvas API (SDK отрисовывает в HWND) - лучшая производительность
- WinForms: обёртка C++/CLI, канал сырых данных, YUV→Bitmap, InvokeRequired
- WPF: та же обёртка плюс Bitmap→BitmapSource, Dispatcher, Freeze()
- См.: [Интеграция с UI-фреймворками](examples/dotnet-winforms/README.md)
- Шаблоны обёртки C++/CLI (для ЛЮБОЙ нативной библиотеки → .NET)
- Указатели
void*- скрывают нативные типы от управляемых заголовков gcroot<T^>- не даёт сборщику мусора уничтожить управляемые ссылки в нативном коде- Финализатор и деструктор -
~Class()и!Class()для очистки по IDisposable pin_ptr+Marshal::Copy- преобразование массивов и буферовLockBits- в 100 раз быстрее SetPixel при обработке изображений- Передача между потоками - InvokeRequired (WinForms) / Dispatcher (WPF)
- См.: [Руководство по обёртке C++/CLI](examples/dotnet-winforms/README.md#ccli-wrapper-patterns-for-net-integration)
- Момент подключения звука
- Во время входа задай
audioOption.connect = false - Вызови
startAudio()в обратном вызовеonSessionJoin - См.: [Рекомендации для промышленной эксплуатации](examples/dotnet-winforms/README.md#production-quality-review)
Краткий справочник
«Мой код не компилируется»
→ [Руководство по ошибкам сборки](troubleshooting/build-errors.md)
«Обратные вызовы никогда не срабатывают»
→ [Цикл сообщений Windows](troubleshooting/windows-message-loop.md)
«Подписка на видео возвращает ошибку 2»
→ [Отрисовка видео](examples/video-rendering.md) - подписывайся в onUserVideoStatusChanged
«Ошибка абстрактного класса»
→ [Методы делегата](references/delegate-methods.md)
«Как реализовать [функцию]?»
→ [Архитектурный шаблон SDK](concepts/sdk-architecture-pattern.md)
«Как добраться до [контроллера]?»
→ [Иерархия одиночек](concepts/singleton-hierarchy.md)
«Что означает этот код ошибки?»
→ [Частые проблемы](troubleshooting/common-issues.md)
Версия документа
Основано на Zoom Video SDK для Windows v2.x
Приятного программирования!
Помни: [архитектурный шаблон SDK](concepts/sdk-architecture-pattern.md) - это ключ ко всему SDK. Прочитай его первым!
Операции
- [RUNBOOK.md](RUNBOOK.md) - пятиминутная предварительная проверка и контрольный список отладки.
Перевод: iiuniversitet. Оригинал: https://github.com/anthropics/knowledge-work-plugins/tree/main/partner-built/zoom-plugin/skills/video-sdk/windows, лицензия MIT. Изменения: перевод на русский язык.
Оригинал на английском
---
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:**
1. **[SDK Architecture Pattern](concepts/sdk-architecture-pattern.md)** - Universal 3-step pattern for ANY feature
2. **[Session Join Pattern](examples/session-join-pattern.md)** - Complete working code to join a session
3. **[Windows Message Loop](troubleshooting/windows-message-loop.md)** - **CRITICAL**: Fix callbacks not firing
4. **[Video Rendering](examples/video-rendering.md)** - Display video with Canvas API
**Reference:**
- **[Singleton Hierarchy](concepts/singleton-hierarchy.md)** - 5-level SDK navigation map
- **[API Reference](references/windows-reference.md)** - Methods, error codes, timing rules
- **[Delegate Methods](references/delegate-methods.md)** - All 80+ callback methods
- **[Sample Applications](references/samples.md)** - Official samples guide
- **[windows.md](windows.md)** - Secondary overview doc (pointer-style)
- **[SKILL.md](SKILL.md)** - Complete documentation navigation
**Having issues?**
- Callbacks not firing → [Windows Message Loop](troubleshooting/windows-message-loop.md)
- Build errors → [Build Errors Guide](troubleshooting/build-errors.md)
- Video subscribe fails → [Video Rendering](examples/video-rendering.md) (subscribe in `onUserVideoStatusChanged`)
- Quick diagnostics → [Common Issues](troubleshooting/common-issues.md)
**Building a Custom UI?**
- [Canvas vs Raw Data](concepts/canvas-vs-raw-data.md) - Choose your rendering approach
- [Raw Video Capture](examples/raw-video-capture.md) - YUV420 frame processing
## 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:
1. **Desktop development with C++**
- MSVC v142 or v143 compiler
- Windows 10/11 SDK
- C++ CMake tools (optional)
2. **.NET desktop development** (for C# applications)
- .NET Framework 4.8 targeting pack
- C++/CLI support
## Quick Start
### C++ Application
```cpp
#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
```csharp
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:
```cpp
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**:
- 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.
```cpp
// 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:
```cpp
// 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:
```cpp
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**.
```cpp
// 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**:
- ✅ **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**:
```cpp
// 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**:
- `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.
```cpp
// 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):
```cpp
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**:
```cpp
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**:
- ⚠️ **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**
```cpp
IZoomVideoSDKSession* session = sdk->getSessionInfo();
IZoomVideoSDKUser* myself = session->getMyself();
IZoomVideoSDKCanvas* canvas = myself->GetVideoCanvas();
canvas->subscribeWithView(selfVideoHwnd, aspect, resolution);
```
**Option B: Video Preview** (for self only)
```cpp
IZoomVideoSDKVideoHelper* videoHelper = sdk->getVideoHelper();
videoHelper->startVideo(); // Start transmission
// For preview rendering
videoHelper->startVideoCanvasPreview(selfVideoHwnd, aspect, resolution);
```
**Remote Users** (other participants):
**Canvas API** (recommended):
```cpp
// 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**:
1. **`onSessionJoin`** - Subscribe to self video
2. **`onUserJoin`** - Subscribe to new remote users
3. **`onUserVideoStatusChanged`** - Re-subscribe when video turns on/off
4. **`onUserLeave`** - Unsubscribe and cleanup
**Complete Pattern**:
```cpp
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**:
- ✅ 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()`!
```cpp
// 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?**
- **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](examples/screen-share-subscription.md)
### Multi-User Video Layout
For multiple participants, you need **one HWND per user**:
```cpp
// 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**:
- 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!)
- **[SDK Architecture Pattern](concepts/sdk-architecture-pattern.md)** - Universal 3-step pattern for ANY feature
- **[Singleton Hierarchy](concepts/singleton-hierarchy.md)** - 5-level navigation guide
- **[Canvas vs Raw Data](concepts/canvas-vs-raw-data.md)** - Choose your rendering approach
### Complete Examples
- **[Session Join Pattern](examples/session-join-pattern.md)** - JWT auth + session join with full code
- **[Video Rendering](examples/video-rendering.md)** - Canvas API video display
- **[Screen Share Subscription](examples/screen-share-subscription.md)** - View remote screen shares (DIFFERENT from video!)
- **[Raw Video Capture](examples/raw-video-capture.md)** - YUV420 frame capture
- **[Raw Audio Capture](examples/raw-audio-capture.md)** - PCM audio capture
- **[Send Raw Video](examples/send-raw-video.md)** - Virtual camera (inject custom video)
- **[Send Raw Audio](examples/send-raw-audio.md)** - Virtual mic (inject custom audio)
- **[Cloud Recording](examples/cloud-recording.md)** - Cloud recording control
- **[Command Channel](examples/command-channel.md)** - Custom command messaging
- **[Transcription](examples/transcription.md)** - Live transcription/captions
### UI Framework Integration
- **[Win32 Native](examples/dotnet-winforms/README.md#option-1-win32-native-c---direct-sdk)** - Direct SDK usage with Canvas API (best performance)
- **[WinForms (.NET)](examples/dotnet-winforms/README.md#option-2-winforms-c--ccli-wrapper)** - C++/CLI wrapper + Raw Data Pipe
- **[WPF (.NET)](examples/dotnet-winforms/README.md#option-3-wpf-c--ccli-wrapper)** - C++/CLI wrapper + BitmapSource conversion
- **[Production Quality Guidelines](examples/dotnet-winforms/README.md#production-quality-review)** - Checklist and common issues
### C++/CLI Wrapper Patterns (Wrapping ANY Native Library)
- **[Complete Guide](examples/dotnet-winforms/README.md#ccli-wrapper-patterns-for-net-integration)** - 8 patterns for native→.NET interop
- **[Pattern 1: Basic Structure](examples/dotnet-winforms/README.md#pattern-1-basic-wrapper-structure)** - Project setup, class layout
- **[Pattern 2: void* Pointers](examples/dotnet-winforms/README.md#pattern-2-opaque-void-pointers)** - Hide native types
- **[Pattern 3: gcroot Callbacks](examples/dotnet-winforms/README.md#pattern-3-gcrootT-for-nativemanaged-callbacks)** - Native→Managed events
- **[Pattern 4: IDisposable](examples/dotnet-winforms/README.md#pattern-4-destructor--finalizer-idisposable)** - Cleanup pattern
- **[Pattern 5: Strings](examples/dotnet-winforms/README.md#pattern-5-string-conversion)** - String^ ↔ wstring/string
- **[Pattern 6: Arrays](examples/dotnet-winforms/README.md#pattern-6-arraybuffer-conversion)** - pin_ptr, Marshal::Copy
- **[Pattern 7: Threading](examples/dotnet-winforms/README.md#pattern-7-thread-marshaling-native-thread--ui-thread)** - UI thread dispatch
- **[Pattern 8: LockBits](examples/dotnet-winforms/README.md#pattern-8-lockbits-for-fast-image-manipulation)** - Fast image conversion
- **[Common Errors](examples/dotnet-winforms/README.md#common-wrapper-errors)** - Troubleshooting
### Troubleshooting
- **[Windows Message Loop](troubleshooting/windows-message-loop.md)** - **CRITICAL**: Why callbacks don't fire
- **[Build Errors](troubleshooting/build-errors.md)** - SDK header dependency fixes
- **[Common Issues](troubleshooting/common-issues.md)** - Quick diagnostics & error codes
### References
- **[API Reference](references/windows-reference.md)** - 5-level API hierarchy, methods, error codes
- **[Delegate Methods](references/delegate-methods.md)** - All 80+ callback methods
- **[SKILL.md](SKILL.md)** - Complete navigation guide
### Most Critical Issues (From Real Debugging)
1. **Callbacks not firing** → Missing Windows message loop (99% of issues)
- See: [Windows Message Loop Guide](troubleshooting/windows-message-loop.md)
2. **Video subscribe returns error 2** → Subscribing too early
- See: [Video Rendering](examples/video-rendering.md) - Subscribe in `onUserVideoStatusChanged`
3. **Abstract class errors** → Missing virtual method implementations
- See: [Delegate Methods](references/delegate-methods.md)
### Key Insight
**Once you learn the 3-step pattern, you can implement ANY feature:**
1. Get singleton → 2. Implement delegate → 3. Subscribe & use
See: [SDK Architecture Pattern](concepts/sdk-architecture-pattern.md)
## Resources
- **Official Docs**: https://developers.zoom.us/docs/video-sdk/windows/
- **API Reference**: https://marketplacefront.zoom.us/sdk/custom/windows/
- **Dev Forum**: https://devforum.zoom.us/
- **GitHub Samples**: https://github.com/zoom/videosdk-windows-rawdata-sample
- **Working Sample**: `C:\tempsdk\zoom-video-sdk-windows-sample\` (complete implementation)
---
**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:**
0. **Overview** → [windows.md](windows.md)
1. **Read the architecture pattern** → [concepts/sdk-architecture-pattern.md](concepts/sdk-architecture-pattern.md)
- Universal formula: Singleton → Delegate → Subscribe
- Once you understand this, you can implement any feature
2. **Fix build errors** → [troubleshooting/build-errors.md](troubleshooting/build-errors.md)
- SDK header dependencies
- Required include order
3. **Implement session join** → [examples/session-join-pattern.md](examples/session-join-pattern.md)
- Complete working JWT + session join code
4. **Fix callback issues** → [troubleshooting/windows-message-loop.md](troubleshooting/windows-message-loop.md)
- **CRITICAL**: Why callbacks don't fire without Windows message loop
5. **Implement video** → [examples/video-rendering.md](examples/video-rendering.md)
- Canvas API (SDK-rendered) vs Raw Data Pipe
6. **Troubleshoot any issues** → [troubleshooting/common-issues.md](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
1. [SDK Architecture Pattern](concepts/sdk-architecture-pattern.md) - Understand the pattern
2. [Session Join Pattern](examples/session-join-pattern.md) - Join sessions
3. [Video Rendering](examples/video-rendering.md) - Display video
4. [Windows Message Loop](troubleshooting/windows-message-loop.md) - Fix callback issues
### I'm getting build errors
1. [Build Errors Guide](troubleshooting/build-errors.md) - SDK header dependencies
2. [Delegate Methods](references/delegate-methods.md) - Abstract class errors
3. [Common Issues](troubleshooting/common-issues.md) - Linker errors
### I'm getting runtime errors
1. [Windows Message Loop](troubleshooting/windows-message-loop.md) - Callbacks not firing
2. [Common Issues](troubleshooting/common-issues.md) - Error code tables
### I want to view screen shares
1. [Screen Share Subscription](examples/screen-share-subscription.md) - **DIFFERENT from video!**
2. [SDK Architecture Pattern](concepts/sdk-architecture-pattern.md) - Event-driven pattern
3. [Video Rendering](examples/video-rendering.md) - Compare with video subscription
### I want to capture raw video/audio
1. [Canvas vs Raw Data](concepts/canvas-vs-raw-data.md) - Choose your approach
2. [Raw Video Capture](examples/raw-video-capture.md) - YUV420 frame capture
3. [Raw Audio Capture](examples/raw-audio-capture.md) - PCM audio capture
4. [SDK Architecture Pattern](concepts/sdk-architecture-pattern.md) - Subscription pattern
### I want to send custom video/audio (virtual camera/mic)
1. [Send Raw Video](examples/send-raw-video.md) - Inject custom video frames
2. [Send Raw Audio](examples/send-raw-audio.md) - Inject custom audio
3. [SDK Architecture Pattern](concepts/sdk-architecture-pattern.md) - External source pattern
### I want to record sessions
1. [Cloud Recording](examples/cloud-recording.md) - Start/stop cloud recording
2. [API Reference](references/windows-reference.md) - Recording helper methods
### I want to use live transcription
1. [Transcription](examples/transcription.md) - Enable live captions
2. [Delegate Methods](references/delegate-methods.md) - Transcription callbacks
### I want custom messaging between participants
1. [Command Channel](examples/command-channel.md) - Send custom commands
2. [API Reference](references/windows-reference.md) - Command channel methods
### I want to build a Win32 native app
1. [Win32 Integration](examples/dotnet-winforms/README.md#option-1-win32-native-c---direct-sdk) - Direct SDK + Canvas API
2. [Video Rendering](examples/video-rendering.md) - Canvas API patterns
3. [Production Guidelines](examples/dotnet-winforms/README.md#production-quality-review) - Best practices
### I want to build a WinForms (.NET) app
1. [WinForms Integration](examples/dotnet-winforms/README.md#option-2-winforms-c--ccli-wrapper) - C++/CLI wrapper + Raw Data
2. [C++/CLI Patterns](examples/dotnet-winforms/README.md#ccli-wrapper-patterns-for-net-integration) - gcroot, Finalizer, LockBits
3. [Production Guidelines](examples/dotnet-winforms/README.md#production-quality-review) - IDisposable, thread safety
### I want to build a WPF (.NET) app
1. [WPF Integration](examples/dotnet-winforms/README.md#option-3-wpf-c--ccli-wrapper) - C++/CLI + BitmapSource
2. [Bitmap Conversion](examples/dotnet-winforms/README.md#2-bitmap--bitmapsource-conversion) - Freeze(), Dispatcher
3. [Production Guidelines](examples/dotnet-winforms/README.md#production-quality-review) - Performance optimization
### I want to use C# / .NET Framework (general)
1. [.NET Integration Overview](examples/dotnet-winforms/README.md) - **Complete C++/CLI wrapper guide**
2. [Raw Video Capture](examples/raw-video-capture.md) - YUV→RGB conversion patterns
3. [Session Join Pattern](examples/session-join-pattern.md) - SDK initialization flow
### I want to wrap ANY native C++ library for .NET
1. [C++/CLI Wrapper Patterns](examples/dotnet-winforms/README.md#ccli-wrapper-patterns-for-net-integration) - **Complete 8-pattern guide**
2. [Pattern 1: Basic Structure](examples/dotnet-winforms/README.md#pattern-1-basic-wrapper-structure) - Project setup + class layout
3. [Pattern 3: gcroot Callbacks](examples/dotnet-winforms/README.md#pattern-3-gcrootT-for-nativemanaged-callbacks) - Native→Managed events
4. [Pattern 4: IDisposable](examples/dotnet-winforms/README.md#pattern-4-destructor--finalizer-idisposable) - Cleanup pattern
5. [Common Errors](examples/dotnet-winforms/README.md#common-wrapper-errors) - Troubleshooting
### I want to implement a specific feature
1. [SDK Architecture Pattern](concepts/sdk-architecture-pattern.md) - **START HERE!**
2. [Singleton Hierarchy](concepts/singleton-hierarchy.md) - Navigate to the feature
3. [API Reference](references/windows-reference.md) - Method signatures
---
## Most Critical Documents
### 1. SDK Architecture Pattern (MASTER DOCUMENT)
**[concepts/sdk-architecture-pattern.md](concepts/sdk-architecture-pattern.md)**
The universal 3-step pattern:
1. Get singleton (SDK, helpers, session, users)
2. Implement delegate (event callbacks)
3. Subscribe and use
### 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:
1. **Windows Message Loop is MANDATORY**
- SDK uses Windows message pump for callbacks
- Without it, callbacks are queued but never fire
- See: [Windows Message Loop Guide](troubleshooting/windows-message-loop.md)
2. **Subscribe in onUserVideoStatusChanged, NOT onUserJoin**
- Video may not be ready when user joins
- Wait for video status change callback
- See: [Video Rendering](examples/video-rendering.md)
3. **Two Rendering Paths**
- Canvas API: SDK renders to your HWND (recommended)
- Raw Data Pipe: You receive YUV frames (advanced)
- See: [Canvas vs Raw Data](concepts/canvas-vs-raw-data.md)
4. **Helpers Control YOUR Streams Only**
- `videoHelper->startVideo()` starts YOUR camera
- To see others, subscribe to their Canvas/Pipe
- See: [Singleton Hierarchy](concepts/singleton-hierarchy.md)
5. **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](examples/dotnet-winforms/README.md)
6. **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](examples/dotnet-winforms/README.md#ccli-wrapper-patterns-for-net-integration)
7. **Audio Connection Timing**
- Set `audioOption.connect = false` during join
- Call `startAudio()` in `onSessionJoin` callback
- See: [Production Guidelines](examples/dotnet-winforms/README.md#production-quality-review)
---
## 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
- [RUNBOOK.md](RUNBOOK.md) - 5-minute preflight and debugging checklist.
Источник: anthropics/knowledge-work-plugins / zoom-plugin / video-sdk/windows ↗. Ссылка проверена 2026-10-10.