轻量级跨平台实时音视频传输库,为 CrossDesk 提供媒体编解码、P2P / TURN 连接、网络自适应和数据传输能力。应用负责采集、播放、渲染及业务协议,MiniRTC 负责传输链路。
English · 公开 API · CrossDesk · 信令服务端
核心能力 · 平台与编解码 · 构建 · 接入流程 · 媒体与内存 · 连接配置 · 排查与验证
- 媒体传输: H.264 / AV1 视频、Opus 音频、多媒体流和面向指定 Peer 的发送接口。
- 连接与穿透: WSS 信令、ICE / STUN、TURN UDP / TCP、动态 TURN 凭据,以及可用网关上的 UPnP 映射。
- 网络自适应: SRTP、RTP / RTCP、NACK 重传、带宽估计、拥塞控制,以及帧率 / 分辨率降级策略。
- 数据传输: 可靠与非可靠数据流;键鼠、剪贴板、文件等数据的序列化与业务处理由应用实现。
- 浏览器互通: 通过 libdatachannel 路径与 CrossDesk Web Client 交互。需要匹配的信令与媒体协商,不是任意 WebRTC 服务的即插即用客户端。
构建产物是 C++17 静态库,公开函数采用 extern "C" 链接。当前头文件含 C++ 类型与语法,不能直接作为纯 C 头文件使用。
| 平台 | H.264 硬件路径 | H.264 软件路径 | AV1 编码 / 解码 | 可选原生解码输出 |
|---|---|---|---|---|
| Windows x64 | NVIDIA NVENC / NVDEC,需启用 CUDA | OpenH264 | SVT-AV1 / dav1d | 软件解码 CPU NV12;NVDEC CUDA NV12 |
| Linux x86-64 | NVIDIA NVENC / NVDEC,需启用 CUDA | OpenH264 | SVT-AV1 / dav1d | 软件解码 CPU NV12;NVDEC CUDA NV12 |
| Linux arm64 | 当前工厂使用软件路径 | OpenH264 | SVT-AV1 / dav1d | 软件解码 CPU NV12 |
| macOS | VideoToolbox | OpenH264 | SVT-AV1 / dav1d | VideoToolbox 的 CVPixelBufferRef |
| iOS | VideoToolbox | OpenH264 | SVT-AV1 / dav1d | VideoToolbox 的 CVPixelBufferRef |
- 硬件路径同时受构建配置、运行时
hardware_acceleration和设备能力影响。H.264 硬件编码初始化失败时,初始化工厂会尝试回退到 OpenH264。 - 当前 AV1 工厂使用 SVT-AV1 编码 / dav1d 解码。libaom 也参与默认构建,但不是工厂选用的 AV1 实现;Apple 平台没有 VideoToolbox AV1 路径。
- 设置
native_video_output=true请求原生帧。Apple 软件解码仍返回 CPU 数据;Windows / Linux 软件解码可返回 CPU NV12 描述符。原生输出不等于所有路径都零拷贝。
需要 Git、Xmake 和 C++17 工具链:Windows 使用 MSVC 与 Windows SDK,Linux 使用 GCC / Clang,macOS 使用 Xcode 工具链;iOS 构建需要完整 Xcode 与 iPhoneOS SDK。
依赖由 xmake.lua 与 thirdparty 的包配方解析,包括编解码库、libnice、libdatachannel、SRTP、KCP 和 UPnP 依赖栈。配方会引入 CMake、Meson、Ninja、NASM 等构建工具;首次构建需要访问依赖源,耗时与缓存状态有关。
git clone https://github.com/kunkundi/minirtc.git
cd minirtc
xmake f -m release --USE_CUDA=false -y
xmake b minirtc将 release 改为 debug 可构建调试版本;使用 -p / -a 显式指定平台和架构。Windows 当前设置静态 CRT(MT),最终应用应保持运行库配置一致。
CUDA: 以下是 Linux x86-64 示例,需先安装匹配的 CUDA SDK 与 NVIDIA 驱动,并将路径替换为本机实际路径:
xmake f -p linux -a x86_64 -m release \
--USE_CUDA=true --CUDA_DIR=/usr/local/cuda -y
xmake b minirtcWindows 可使用 --USE_CUDA=true 配合 CUDA_PATH 或 --CUDA_DIR 指定 SDK。编入 CUDA 后仍需在 Params 中设置 hardware_acceleration=true;Apple 的 VideoToolbox 不需要 CUDA。
当前 CrossDesk iOS 集成使用 iOS 16.0+、arm64 真机。在 macOS 终端的 MiniRTC 根目录执行:
xmake f -c -p iphoneos -a arm64 -m release \
--target_minver=16.0 --USE_CUDA=false -y
xmake b minirtc此命令生成 iOS 静态库,不会生成 App 或 XCFramework,也不代表模拟器构建已验证。切回桌面构建时应重新配置目标平台。Xcode 工程的依赖合并、链接和 SDK 环境处理可参考 CrossDesk 的 iOS 构建脚本。
| 选项 | 默认值 | 说明 |
|---|---|---|
USE_CUDA |
false |
在支持的平台编入 NVIDIA 编解码后端 |
CUDA_DIR |
空,自动检测 | 优先显式配置,再检查 CUDA_PATH / CUDA_HOME 及常见安装路径 |
MINIRTC_INCLUDE_VIRTUAL_ICE_INTERFACES |
false |
是否允许从 VPN / TUN 等虚拟接口收集 ICE 候选 |
xmake show -t minirtc
xmake install -o build/install minirtc第一条命令显示当前目标与依赖信息;第二条将库和公开头文件安装到 build/install。库为 libminirtc.a 或 Windows 上的 minirtc.lib。仅复制该静态库并不足以链接完整应用,还需要对应架构和配置的依赖库、系统库及 Apple frameworks。完整依赖以 Xmake 解析结果为准。
常用诊断命令:xmake f --menu 查看选项,xmake b -vy minirtc 输出详细构建日志,xmake b -r minirtc 重建目标。
以 src/api/minirtc.h 为接口依据。当前流程是:
- 使用
Params params{}清零,填写地址、媒体选项和回调。Params没有应用级默认配置,零初始化也不会自动启用 TURN 或 SRTP。 CreatePeer(¶ms)创建实例;检查返回值。注册AddVideoStream、AddAudioStream和AddDataStream,在会话协商前固定流名称。- 调用
Init(peer)启动异步信令连接。返回 0 不表示登录成功;以on_signal_status收到SignalConnected为准。 - 发起方使用
JoinConnection(peer, "远端ID@密码")加入 CrossDesk 会话。user_id是本地登录身份,不能用远端目标 ID 替代;具体身份注册与密码规则由信令服务决定。 on_connection_status收到Connected后,根据已注册的流名称发送媒体或数据。API 返回值不代表远端已收到内容。- 退出会话调用
LeaveConnection;停止应用侧采集和发送任务,再用if (peer) DestroyPeer(&peer);销毁实例。销毁后指针置空。
以下函数构造直接配置模式的参数,不发起网络连接。替换示例 IP;回调由应用实现,在 CreatePeer 前补充需要的媒体和数据接收回调。
#include <cstdio>
#include "minirtc.h"
Params MakeParams(const char* login_identity,
OnSignalStatus on_signal,
OnConnectionStatus on_connection,
OnNetStatusReport on_stats,
void* context) {
Params params{};
params.use_cfg_file = false;
std::snprintf(params.signal_server_ip, sizeof(params.signal_server_ip),
"%s", "203.0.113.10");
params.signal_server_port = 9099;
params.hardware_acceleration = true;
params.native_video_output = false;
params.av1_encoding = false;
params.turn_mode = TurnAutoUdpTcp;
params.enable_srtp = true;
params.video_content_type = VideoContentType::ScreenContent;
params.video_quality = QualityHigh;
params.video_frame_rate = 60;
params.video_degradation_preference =
VideoDegradationPreference::MaintainResolution;
params.on_signal_status = on_signal;
params.on_connection_status = on_connection;
params.on_net_status_report = on_stats;
params.user_id = login_identity;
params.user_data = context;
return params;
}signal_server_ip 填主机名或 IP,不带 wss://、端口或路径;库会拼接 WSS 地址。STUN / TURN 主机字段也不带 URL 前缀。示例 9099 / 3478 来自 CrossDesk Server 的 Compose 默认值,不是 Params 自带的默认端口。
- 必须提供有效的
on_signal_status、on_connection_status和on_net_status_report。 当前实现有直接调用这些回调的路径;不使用时也应提供同签名空函数。 - 视频使用
on_receive_video_frame,音频使用on_receive_audio_buffer,应用数据使用on_receive_data_buffer。旧字段on_receive_video_buffer不接入当前视频帧分发路径。 on_signal_message接收未被库内部信令处理器消费的消息;它不是全部原始信令的监听器。- 回调可来自内部工作线程。将 UI 操作投递到应用线程,及时复制或保留需要的数据,并避免在回调内同步销毁 Peer。
user_data在 Peer 完全销毁前必须有效;user_id指向的字符串至少保持到Init完成读取。 - 标识和缓冲区按回调给出的长度读取,不依赖额外的
\0终止符。网络报告的指针也是回调期间借用的。
| 数据 | 当前约定 |
|---|---|
| CPU 视频输入 | MiniRtcVideoFrame:连续 NV12,正偶数宽高,至少 width × height × 3 / 2 字节 |
| 原生视频 | MiniRtcNativeVideoFrame:按 type 使用 CPU NV12、CUDA NV12 或 CVPixelBuffer 载荷 |
| 音频输入 | MiniRtcAudioFrame:48 kHz、单声道、16-bit PCM,每帧 10 ms,即 480 个采样 / 960 字节 |
| 时间戳 | 采集时间使用单调时钟微秒;可用 GetSystemTimeMicros(peer),不要混用 Unix 毫秒 |
| 普通数据 | AddDataStream(..., false) 配合 SendDataFrame |
| 可靠数据 | AddDataStream(..., true) 配合 SendReliableDataFrame |
所有发送接口都有相应的 *ToPeer 形式用于指定远端。RequestVideoKeyFrame / RequestAllVideoKeyFrames 用于请求本地发送编码器产生关键帧,不是通用的远端解码恢复命令。
启用原生输出后,MiniRtcVideoFrame::data 可以为空,即使 size 仍为逻辑 NV12 大小。接收端应先检查 native_frame;需要连续 CPU 数据时调用描述符的 copy_to_nv12 并检查返回值,不能直接解引用 CUDA 地址。
跨回调保留原生帧: 描述符指针只在当前调用 / 回调中有效。先复制描述符并调用 retain(owner),最后调用 release(owner);仅保存指针或复制 MiniRtcVideoFrame 不会延长所有者生命周期。CPU 缓冲区也应在异步使用前复制。输入描述符须正确设置 struct_size、尺寸、载荷、所有者与回调,具体校验见 native_video_frame.h。
video_frame_rate 当前支持 30 / 60,其他值归一化为 60;降级策略可选 MaintainFrameRate、MaintainResolution、Balanced。应用仍负责按合适的频率提交采集帧。
turn_mode |
行为 |
|---|---|
TurnDisabled |
仅直连 / STUN 候选 |
TurnAutoUdpTcp |
直连候选,加 TURN UDP / TCP 回退 |
TurnForceUdp |
仅 TURN UDP 中继 |
TurnForceTcp |
仅 TURN TCP 中继 |
- 与当前 CrossDesk Server 配合时,登录和协商消息会携带临时 TURN 凭据。库收到时校验
host、port、username、password、expires_at,更新后续连接配置;无需把COTURN_AUTH_SECRET写入应用。 - 示例参数将静态 TURN 地址与用户名 / 密码留空,使用服务端下发值。需要中继时,服务端必须正确配置 Coturn、公布可达地址并签发有效凭据;
TurnAutoUdpTcp本身不会部署中继服务。 - UPnP 依赖始终编入;原生 ICE 路径在非强制中继模式尝试网关映射。能否成功取决于网络与网关支持。
- 匹配的 libnice 补丁允许受支持的原生对端在使用中继后继续尝试升级到 P2P;强制中继模式不执行这种升级。不要将其理解为所有对端都能无条件切回直连。
- 默认忽略若干虚拟接口前缀,包括 Docker、TUN / TAP、WireGuard、Tailscale、ZeroTier。需要通过这些接口收集候选时,以
--MINIRTC_INCLUDE_VIRTUAL_ICE_INTERFACES=true重新配置并构建。列表与补丁见 libnice 配方。
信令通过 WSS 连接,证书验证使用系统信任能力。自签服务端需要在运行应用的设备上信任根证书;排查证书有效期与主机名 / IP,证书错误通过 SignalTlsCertError 报告。服务部署和端口说明见 CrossDesk Server。
use_cfg_file=true 时,从 cfg_path 指向的 INI 读取服务器与媒体配置,直接填写的对应 Params 字段不会作为逐项回退值。cfg_path 是固定字符数组,应复制路径内容;回调、user_id、user_data 仍由 Params 提供。
config/config.ini 是历史配置示例,包含旧地址和静态 TURN 凭据,不能直接作为当前部署配置使用。INI 缺省值与 Params{} 的零值也不同;支持的字段与解析规则以 PeerConnection::Init 为准。
| 现象 | 检查方向 |
|---|---|
Init 返回成功但未登录 |
等待信令状态回调,检查 WSS 地址、服务可达性及证书 |
| 登录时崩溃 | 检查三个必需回调是否有效,以及上下文生命周期 |
| 可登录但无法建立会话 | 检查双方服务、远端 ID / 密码、TURN 凭据、UDP / TCP 与中继端口 |
| 启用原生输出后黑屏 | 不要只读取 data;检查帧类型、stride、所有者生命周期及渲染路径 |
| 音频帧被拒绝 | 确认是 48 kHz 单声道 16-bit PCM,且每次提交恰好 10 ms |
| CUDA 编译或链接失败 | 检查平台 / 架构、SDK 路径、驱动、依赖与最终应用的链接设置 |
| UPnP / libnice 符号或扩展缺失 | 检查是否使用当前包配方与补丁,避免混用旧缓存或另一套 GLib / GUPnP |
当前 xmake.lua 构建静态库及对象目标,没有为 tests/ 注册可直接运行的测试目标。尤其 旧 PeerConnection 示例 仍使用已移除的接口,不应作为当前 API 教程或直接宣称能够运行。
验证集成时,至少构建目标平台并完成两端登录、直连 / 强制 TURN、媒体收发、数据收发与退出重连。公开头文件的编译检查不能替代这些网络和媒体测试。实际应用可参考 CrossDesk 的 桌面集成 和 iOS 桥接。
提交问题时附上源码版本、目标平台 / 架构、Xmake 配置、编解码与 TURN 模式,以及脱敏日志。
MiniRTC 原创代码使用 LGPL-3.0-only。第三方代码、依赖和分发注意事项见 THIRD_PARTY_NOTICES.md。