knowledge-work-plugins 之 Zoom Video SDK for Windows 官方示例应用完全指南:20 个 Sample 选型、代码模式与构建实战

原创2026-09-13 18:06:531,726 阅读
文章标签:AI 技能AI 插件人工智能MCP Clients

knowledge-work-plugins 之 Zoom Video SDK for Windows 官方示例应用完全指南:20 个 Sample 选型、代码模式与构建实战

本篇技术指南以 knowledge-work-plugins 仓库中 partner-built/zoom-plugin/skills/video-sdk/windows 技能库的官方示例参考文档为核心,系统讲解 Zoom Video SDK Windows 端(C++)官方示例工程的完整清单、推荐学习路径、关键代码模式与构建配置。读完本文,你将能够:根据业务需求(原始音视频采集、自定义音视频注入、云录制、信令、转写等)快速选定对应示例工程作为起点,理解贯穿所有示例的"单例获取 → 委托实现 → 订阅使用"通用架构,并掌握在 Visual Studio 中编译运行这些示例所需的全部前提与配置细节。


一、示例总览:20 个官方 Sample 一览

Zoom Video SDK Windows 官方示例仓库(videosdk-windows-rawdata-sample)提供了覆盖 SDK 主要能力的示例工程。下表汇总了全部示例的名称、功能定位与核心特性,是选型时的首要参考:

示例 描述 关键特性
VSDK_SkeletonDemo 最小化会话加入 最简单的入门起点
VSDK_getRawVideo 采集原始视频 YUV420 帧提取
VSDK_getRawAudio 采集原始音频 PCM 音频提取
VSDK_getRawShare 采集屏幕共享 共享内容捕获
VSDK_sendRawVideo 发送自定义视频 虚拟摄像头注入
VSDK_sendRawAudio 发送自定义音频 虚拟麦克风注入
VSDK_sendRawShare 发送自定义共享 自定义屏幕共享源
VSDK_CloudRecording 云录制 启停云端录制
VSDK_CommandChannel 自定义消息 收发自定义命令
VSDK_CallIn PSTN 呼入 电话拨入支持
VSDK_Callout PSTN 呼出 电话拨出支持
VSDK_ServiceQuality 网络统计 质量监控
VSDK_TranscriptionAndTranslation 实时转写 实时字幕
VSDK_MultiStreamVideo 多路视频流 多摄像头支持
VSDK_PreviewCameraAndMicrophone 设备预览 入会前设备测试
VSDK_Share2ndCameraAsMultiCam 第二摄像头 多摄像头共享
VSDK_Share2ndCameraAsShareScreenDemo 摄像头作为共享 摄像头内容共享
VSDK_ShareScreenPreprocessorDemo 共享预处理 自定义共享处理
VSDK_RTMSDemo 实时消息 RTMS 集成
VSDK_DuilibDemo2 完整 UI 演示 完整 GUI 应用

官方示例仓库与文档的链接信息可在技能库的 SKILL.md 与 windows.md 中查阅,本文聚焦于这些示例的本地化解读与代码模式。

从示例清单可以看出,官方示例覆盖了 SDK 的四大能力域:原始数据采集(Raw Data Capture)、原始数据注入(Raw Data Injection)、通信(Communication)与录制/流媒体/高级特性,下文第三节将按类别展开。


二、推荐学习路径:五个阶段吃透核心示例

官方建议的学习路径遵循"先跑通最小闭环,再逐项深入"的原则,与技能库中 concepts/sdk-architecture-pattern.md 所总结的"单例 → 委托 → 订阅"三段式架构一脉相承。

阶段 1:从 VSDK_SkeletonDemo 起步

这是代码量最小、最纯粹的会话加入示例,演示了以下 SDK 基础能力:

  • SDK 初始化(CreateZoomVideoSDKObj + initialize)
  • JWT 鉴权(token 字段)
  • 会话加入/离开(joinSession / leaveSession)
  • Windows 消息循环(回调驱动的关键前提)
  • 基础委托实现(IZoomVideoSDKDelegate)

需要掌握的核心模式:

// 1. 创建 SDK
IZoomVideoSDK* sdk = CreateZoomVideoSDKObj();

// 2. 初始化
ZoomVideoSDKInitParams params;
params.domain = L"https://zoom.us";
sdk->initialize(params);

// 3. 添加委托
sdk->addListener(myDelegate);

// 4. 加入会话
ZoomVideoSDKSessionContext ctx;
ctx.sessionName = L"session";
ctx.token = L"jwt";
ctx.audioOption.connect = false;
sdk->joinSession(ctx);

// 5. 消息循环(关键!)
while (running) {
    MSG msg;
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    Sleep(10);
}

为什么消息循环"关键"? 技能库中的 troubleshooting/windows-message-loop.md 明确指出:Zoom Video SDK 依赖 Windows 消息机制派发回调。事件发生时,SDK 将消息投递到调用 joinSession() 所在线程的消息队列;若没有 PeekMessage/GetMessage 驱动的消息循环,onSessionJoin()、onError() 等回调永远不会触发——表现为"joinSession() 返回成功但无任何回调"。因此:

  • 消息循环必须与调用 SDK 方法的线程同一线程;
  • 回调内禁止阻塞(不要 sleep 或忙等);
  • GUI 应用(WinMain 标准消息循环)天然满足,控制台/自定义主循环必须显式补充。

阶段 2:视频采集 VSDK_getRawVideo

该示例演示如何捕获远端用户的原始 YUV420 视频帧,包含:

  • IZoomVideoSDKRawDataPipeDelegate 接口实现
  • onRawDataFrameReceived() 回调
  • YUV 缓冲区提取(Y、U、V 三平面)
  • 分辨率与旋转角处理

核心模式:

class VideoCapture : public IZoomVideoSDKRawDataPipeDelegate {
    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();
        // Process frames...
    }
};

// 订阅
IZoomVideoSDKRawDataPipe* pipe = user->GetVideoPipe();
pipe->subscribe(ZoomVideoSDKResolution_720P, videoCapture);

该示例对应技能库 examples/raw-video-capture.md 中的完整实现:RawVideoCapture 类在 onRawDataFrameReceived 中计算 ySize = width * height、uvSize = ySize / 4,随后将三平面依次写入 .yuv 文件,并支持分辨率变化检测与进度日志。原始 YUV 文件无文件头,回放/转码需显式指定格式(详见下文"原始视频处理的进阶要点")。

阶段 3:音频采集 VSDK_getRawAudio

该示例演示 PCM 原始音频采集,覆盖三种回调模式:混合音频(全体参会人)、逐用户音频分离与共享音频。核心格式为 32 kHz 采样率、16-bit 有符号、单声道/双声道小端 PCM。

核心模式:

void onMixedAudioRawDataReceived(AudioRawData* data) override {
    char* buffer = data->GetBuffer();
    int length = data->GetBufferLen();
    int sampleRate = data->GetSampleRate();  // 32000 Hz
    int channels = data->GetChannelNum();     // 1 or 2
    // Process PCM audio...
}

阶段 4:视频注入 VSDK_sendRawVideo

该示例将自定义视频作为虚拟摄像头发送,包含:

  • IZoomVideoSDKVideoSource 接口实现
  • 通过 sendVideoFrame() 发送帧
  • YUV 帧创建
  • 帧率控制

核心模式(流程): 自定义视频源(文件、摄像头或程序生成)→ 转换为 YUV420(I420)→ SDK 回调 onInitialize() 获取 IZoomVideoSDKVideoSender → 循环调用 sendVideoFrame() → 远端参会人看到自定义视频。完整实现见 examples/send-raw-video.md,其中 VideoSource 在 onStartSend() 中启动发送线程,按 1000 / fps 的帧间隔维护帧率,并通过 videoHelper->setExternalVideoSource(source) + videoHelper->startVideo() 注册启用——务必在设置外部源后调用 startVideo(),否则视频不会生效。

阶段 5:音频注入 VSDK_sendRawAudio

该示例将自定义音频作为虚拟麦克风发送,包含:

  • IZoomVideoSDKVirtualAudioMic 接口实现
  • PCM 音频发送
  • 采样率匹配

关键约束:SDK 要求精确的 32000 Hz 采样率(单声道、16-bit)。发送时应按 20ms 分块(32000 Hz 单声道下每块 640 采样 = 1280 字节),并使用 m_sender->send(buffer, BYTES_PER_CHUNK, SAMPLE_RATE) 发送,同时以 std::chrono::milliseconds(20) 精确控制节奏以保证音频不卡顿。完整实现见 examples/send-raw-audio.md,其 VirtualMic 同时支持从 .pcm 文件循环读取与程序生成 440 Hz 正弦波两种数据来源。


三、示例分类:按功能域选型

除按学习路径推进外,还可以按功能域快速定位示例:

原始数据采集(Raw Data Capture)

示例 输入 输出
VSDK_getRawVideo 远端视频 YUV420 帧
VSDK_getRawAudio 会话音频 PCM 采样
VSDK_getRawShare 屏幕共享 YUV420 帧

原始数据注入(Raw Data Injection)

示例 输入 输出
VSDK_sendRawVideo YUV420 帧 虚拟摄像头
VSDK_sendRawAudio PCM 采样 虚拟麦克风
VSDK_sendRawShare YUV420 帧 屏幕共享

通信(Communication)

示例 特性
VSDK_CommandChannel 自定义命令消息(限 60 条/秒)
VSDK_CallIn PSTN 电话拨入
VSDK_Callout PSTN 电话拨出

命令通道的 60 条/秒速率限制在 references/windows-reference.md 中亦有对应记载:IZoomVideoSDKCmdChannel 提供 sendCommand(user, command) 单发与 sendCommandToAll(command) 广播两种方法,收到命令时触发委托的 onCommandReceived(sender, cmd) 回调。

录制与流媒体(Recording & Streaming)

示例 特性
VSDK_CloudRecording Zoom 云端录制
VSDK_RTMSDemo 实时消息服务(RTMS)

高级特性(Advanced Features)

示例 特性
VSDK_ServiceQuality 网络质量统计
VSDK_TranscriptionAndTranslation 实时字幕
VSDK_MultiStreamVideo 多路视频流
VSDK_PreviewCameraAndMicrophone 入会前设备预览
VSDK_ShareScreenPreprocessorDemo 自定义共享处理

四、跨示例通用模式:所有 Sample 共同遵循的代码骨架

无论实现哪个功能,所有官方示例都遵循同一套初始化与事件驱动骨架。这与 concepts/sdk-architecture-pattern.md 中的"万能三段式"完全一致:① 获取单例 → ② 实现委托 → ③ 订阅并使用。

1. 初始化模式

// 初始化 SDK
IZoomVideoSDK* sdk = CreateZoomVideoSDKObj();
ZoomVideoSDKInitParams params;
params.domain = L"https://zoom.us";
params.videoRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;
sdk->initialize(params);

内存模式注意事项:技能库 SKILL.md 特别强调,原始数据的内存模式应始终使用堆模式(ZoomVideoSDKRawDataMemoryModeHeap),并同时为视频、共享、音频三路分别设置:

params.videoRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;
params.shareRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;
params.audioRawDataMemoryMode = ZoomVideoSDKRawDataMemoryModeHeap;

堆模式(而非栈模式)可避免大尺寸视频帧导致的内存问题;此外还可通过 params.enableLog = true 与 params.logFilePrefix 开启日志以辅助排查。

2. 委托注册

必须始终在加入会话之前注册委托:

sdk->addListener(new MyDelegate());
sdk->joinSession(context);

若在 joinSession() 之后才调用 addListener(),将错过早期事件(examples/session-join-pattern.md 中明确标注此为错误用法)。同时注意:IZoomVideoSDKDelegate 拥有 70~80+ 个纯虚方法,全部必须实现(哪怕为空实现),否则会产生抽象类编译错误。

3. 音频连接策略

官方推荐"先入会、后连音频"的两段式:入会时设置 audioOption.connect = false,在 onSessionJoin() 回调中再调用 startAudio():

context.audioOption.connect = false;  // 入会配置

void onSessionJoin() override {
    sdk->getAudioHelper()->startAudio();  // 在此连接
}

该策略分离了会话加入与音频初始化,可提高可靠性并便于错误处理。同理,自己的摄像头应在 onSessionJoin 中通过 videoHelper->startVideo() 开启。

4. 消息循环

所有示例都包含消息循环,这是回调触发的必要前提(前文已详述):

while (!g_exit) {
    MSG msg;
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    Sleep(10);
}

五、构建与配置

前置条件

  1. Visual Studio 2019 或 2022(安装"使用 C++ 的桌面开发"工作负载,含 MSVC v142/v143 编译器与 Windows 10/11 SDK)
  2. Windows SDK 10.0.19041.0 及以上
  3. Zoom Video SDK(从 Zoom Marketplace 下载)

构建步骤

  1. 打开解决方案文件(.sln)
  2. 将平台设置为 x64
  3. 将配置设置为 Release
  4. 生成解决方案(Ctrl+Shift+B)
  5. 将 SDK DLL 拷贝到输出目录

技能库 SKILL.md 补充了运行时环境的基线:Windows 10(1903 或更高)或 Windows 11,支持 x64(推荐)、x86 与 ARM64 架构;若使用 C#/.NET 应用,还需 .NET Framework 4.8 及以上与 C++/CLI 支持。

配置文件

每个示例通过 config.json 提供运行参数:

{
  "jwt": "your-jwt-token",
  "session_name": "test-session",
  "password": "",
  "user_name": "Bot"
}

在 examples/session-join-pattern.md 的完整实现中,LoadConfig() 会读取该 JSON(使用 jsoncpp),将 jwt、session_name、password、user_name(默认 "Bot")映射为全局宽字符串,再填入 ZoomVideoSDKSessionContext 完成入会。若文件无法打开或 SDK 初始化失败,程序会立即退出并返回错误码。


六、进阶深化:示例背后的 SDK 实现细节

理解示例代码后,可进一步结合技能库参考文档深入底层实现。

6.1 五层 API 层级与"导航式"访问

references/windows-reference.md 将 SDK 建模为五层深的单例对象树,所有功能都通过"导航"而非"构造"获取:

  • Level 1 入口:CreateZoomVideoSDKObj() → IZoomVideoSDK*,提供 initialize、joinSession、addListener、getVideoHelper、getAudioHelper、getShareHelper、getChatHelper、getCmdChannel、getRecordingHelper 等入口;
  • Level 2 核心 Helper 与会话:IZoomVideoSDKSession(getMyself、getRemoteUsers)、IZoomVideoSDKVideoHelper(仅控制自己的摄像头)、IZoomVideoSDKAudioHelper、IZoomVideoSDKShareHelper;
  • Level 3 用户与渲染对象:IZoomVideoSDKUser(GetVideoCanvas、GetVideoPipe、getVideoStatus)、IZoomVideoSDKCanvas(SDK 渲染)、IZoomVideoSDKRawDataPipe(原始 YUV)、IZoomVideoSDKShareAction(共享控制);
  • Level 4 设备/聊天/回调:IZoomVideoSDKCameraDevice、IZoomVideoSDKChatHelper、IZoomVideoSDKRawDataPipeDelegate;
  • Level 5 原始数据与工具:YUVRawDataI420、AudioRawData、IZoomVideoSDKUserHelper、IZoomVideoSDKCmdChannel 等。

关键区分:VideoHelper/AudioHelper 只控制自己的音视频流(startVideo() 开启自己的摄像头);观看远端参会人的视频,必须通过其 user->GetVideoCanvas()->subscribeWithView(hwnd, aspect, resolution) 或 user->GetVideoPipe()->subscribe(resolution, delegate) 订阅。屏幕共享则更特殊:远端共享必须使用 onUserShareStatusChanged 回调中的 IZoomVideoSDKShareAction(而非 user->GetShareCanvas()),因为一个用户可能同时存在多个共享流。

6.2 两种渲染/处理路线:Canvas API 与 Raw Data Pipe

路线 接口 适用场景
Canvas API IZoomVideoSDKCanvas::subscribeWithView(HWND) 标准应用、画质最佳、实现最简单
Raw Data Pipe IZoomVideoSDKRawDataPipe::subscribe(delegate) 自定义处理、特效、录制、计算机视觉

Canvas API 由 SDK 直接渲染到窗口句柄,无需 YUV 转换,支持 ZoomVideoSDKVideoAspect_Original / FullFilled / PanAndScan / LetterBox 等纵横比与 ZoomVideoSDKResolution_90P / 180P / 360P / 720P / 1080P / Auto 等分辨率选项;Raw Data Pipe 则向委托回调交付 YUVRawDataI420 帧,交由应用自行转换渲染(GDI/DirectX/OpenGL 均可)。

6.3 原始视频处理的进阶要点(对应 VSDK_getRawVideo)

examples/raw-video-capture.md 给出了 YUV420(I420 平面)的内存布局:Y 平面为全分辨率(width × height 字节),U/V 平面各为四分之一分辨率((width/2) × (height/2) 字节),单帧总大小为 width × height × 1.5 字节:

分辨率 Y 缓冲区 U 缓冲区 V 缓冲区 总计
720p(1280×720) 921,600 230,400 230,400 1,382,400
1080p(1920×1080) 2,073,600 518,400 518,400 3,110,400
360p(640×360) 230,400 57,600 57,600 345,600

YUVRawDataI420 的主要方法包括 GetYBuffer/GetUBuffer/GetVBuffer(三平面指针)、GetStreamWidth/GetStreamHeight(帧尺寸)、GetRotation(0/90/180/270)、GetTimeStamp(时间戳)以及 CanAddRef/AddRef/Release(引用计数,用于跨回调异步处理)。采集到的 .yuv 文件可用 FFplay 回放或 FFmpeg 转码:

ffplay -video_size 1280x720 -pixel_format yuv420p -f rawvideo video.yuv
ffmpeg -video_size 1280x720 -pixel_format yuv420p -framerate 30 -f rawvideo -i video.yuv -c:v libx264 output.mp4

性能优化建议:预分配 RGB 缓冲区(仅尺寸变化时重分配);回调内使用 AddRef() 将帧投递到独立处理线程,处理完 Release();AI 处理订阅低分辨率(ZoomVideoSDKResolution_360P),显示则用高分辨率 Canvas。

6.4 原始音频采集格式(对应 VSDK_getRawAudio)

examples/raw-audio-capture.md 明确音频格式为:PCM 未压缩、32000 Hz、16-bit 有符号、单声道或双声道、小端序,每次回调约 640~1280 字节(20~40ms 音频)。三种回调分别对应:

  • onMixedAudioRawDataReceived:全体参会人混合音频,适合整场录制;
  • onOneWayAudioRawDataReceived:逐用户音频(需特殊配置),适合按发言人转写;
  • onSharedAudioRawDataReceived:屏幕共享内容音频,与参会人音频分离。

AudioRawData 提供 GetBuffer()(PCM 缓冲区)、GetBufferLen()(字节数)、GetSampleRate()(通常 32000)与 GetChannelNum()(1=单声道,2=双声道)。采集到的 .pcm 可用 FFplay 回放或转为 WAV:

ffplay -f s16le -ar 32000 -ac 1 audio.pcm
ffmpeg -f s16le -ar 32000 -ac 1 -i audio.pcm output.wav

常见坑位:音频回调不触发(未在 onSessionJoin 连接音频)、FFmpeg 中错误使用 44100 Hz、静音(无人发言或全员静音,需检查 onUserAudioStatusChanged)。

6.5 订阅时机与错误码(调试示例的关键依据)

references/windows-reference.md 强调两条时序铁律:

  1. 不要在 onUserJoin 中订阅视频——此时视频可能尚未就绪,会触发 Error 2(Internal_Error)。正确做法是在 onUserVideoStatusChanged 中检查 pipe->getVideoStatus().isOn 为真后再订阅,并跳过自己(if (user == myself) continue;);
  2. 订阅过快会触发 ZoomVideoSDKSubscribeFailReason_TooFrequentCall(=6),处理方式是在相邻订阅调用之间插入 Sleep(200)。

常用错误码速查:0 Success、1 Wrong_Usage(错误状态调用)、2 Internal_Error(视频未就绪/过早订阅)、7 Invalid_Parameter(NULL 指针、非法 HWND)、8 Call_Too_Frequently(调用过于频繁)。

6.6 线程安全与生命周期

SDK 回调运行在 SDK 线程而非主线程:不要在回调中执行重操作、不要在回调中调用 cleanup()、跨线程传递数据使用线程安全队列(如 YUVRawDataI420 的引用计数配合生产者-消费者队列)并加锁保护共享状态。退会时统一在 onUserLeave / onSessionLeave 中解除订阅并清理,最后按 leaveSession(false) → cleanup() → DestroyZoomVideoSDKObj() 的顺序释放。


七、相关文档导航

本文对应的示例参考文档位于 references/samples.md,技能库中与之配套的深度资料还包括:


行动建议:以 VSDK_SkeletonDemo 打通"初始化 → 委托 → 入会 → 消息循环"的最小闭环,再根据业务诉求选择 VSDK_getRawVideo 或 VSDK_getRawAudio 切入原始数据采集;若需要向会话注入内容,则对应研读 VSDK_sendRawVideo(虚拟摄像头)与 VSDK_sendRawAudio(虚拟麦克风)。任何示例运行异常,优先检查消息循环是否存在、委托是否在入会前注册、订阅是否发生在视频就绪之后。

登录后查看全文
knowledge-work-plugins