RTCPeerConnection 初始化必须配置 ICE 服务器,否则连接失败;媒体轨道须显式 addTrack;低延迟需硬件编码、禁B帧、限码率与同队列调度;键鼠事件应走 RTP 扩展头而非 DataChannel。

RTCPeerConnection 初始化必须配对 ICE 服务器
不配 iceServers,RTCPeerConnection 根本连不上——它不会自动猜 STUN 地址。哪怕只是本地测试,也得至少填一个公共 STUN,否则 iceConnectionState 会卡在 checking 或直接变 failed。
常见错误现象:onicecandidate 从不触发、onconnectionstatechange 停在 connecting、日志里反复出现 ICE failed, add a TURN server。
- 开发阶段用
{ urls: "stun:stun.l.google.com:19302" }足够,但别依赖它上线 - 生产环境必须部署自己的 STUN/TURN,Google 的服务不保证可用性,且无 TLS 支持(
stuns://才安全) - C++ 中配置需通过
webrtc::PeerConnectionInterface::RTCConfiguration构造,ice_servers是std::vector<:serveraddresses></:serveraddresses>类型,不是 JSON 字符串
MediaStream 和轨道必须手动 addTrack,不能 addStream
WebRTC C++ API 在 M110+ 版本已废弃 addStream(),调用它会静默失败或崩溃。所有媒体轨道(webrtc::VideoTrackInterface*、webrtc::AudioTrackInterface*)必须显式 addTrack() 到 RTCPeerConnection。
容易踩的坑:
立即学习“C++免费学习笔记(深入)”;
- 没调
pc->AddTrack(video_track, {kVideoTrackId}),远端收不到视频流,但连接状态看起来正常 - 音视频轨道共用同一个
stream_id,导致 Chrome 浏览器端无法识别轨道类型 - 本地预览用
VideoRenderer渲染,但忘了给 track 设置SetRenderer(),画面黑屏无报错
延迟优化关键在编码器参数和线程模型
默认编码器(如 VP8)在 C++ 环境下不启用硬件加速,软编延迟常超 100ms。低延迟不是靠“开开关”,而是组合调整:
- 强制使用硬件编码器:在
cricket::VideoEncoderConfig中设codec_type = kVideoCodecVP8后,再通过SetEncoderSettings()指定hardware_acceleration_enabled = true - 关闭帧间预测(B帧):
video_encoder_config.number_of_temporal_layers = 1,避免解码依赖链拉长延迟 - 限制最大分辨率与码率:
max_bitrate_bps = 1000000、max_framerate = 30,防止网络拥塞触发重传放大延迟 - 所有媒体操作(采集、编码、发送)必须跑在同一个
TaskQueueBase*上,跨队列调度会引入毫秒级抖动
DataChannel 不适合传键鼠事件,用 RTP 扩展头更稳
如果你在做远程控制类应用,别用 RTCDataChannel 传鼠标坐标或按键——它基于 SCTP,有重传机制,而键鼠事件是“过期即废”的。一次鼠标移动延迟 200ms,用户已经移走了,再送达反而造成错位。
正确做法是复用视频流通道,把控制指令塞进 RTP 包扩展头(RTP header extension):
- 注册自定义扩展 ID(如 4),格式为
uint8_t type; int16_t x; int16_t y; - 在
OnFrame()回调中,对每一帧视频调用frame->set_extension<toucheventextension>(event)</toucheventextension> - 远端在
OnFrame()解析扩展,立刻映射到屏幕坐标,不经过网络栈排队 - 这样端到端控制延迟可压到 30ms 内,且和视频流天然同步
真正难的不是写通连接,而是让音频时间戳对齐、让视频渲染帧率锁死、让键鼠事件不漂移——这些细节藏在 webrtc::Clock 实现、rtc::Thread 绑定、以及每一帧 VideoFrame 的 timestamp_us_ 计算逻辑里。漏掉任意一环,低延迟就只是纸面数字。


















