需显式创建PeerConnectionFactoryInterface并传入网络、音频、视频三工厂,配置STUN服务器,手动采集摄像头帧并校准时间戳,逐个处理ICE候选者,实现VideoSinkInterface渲染。

直接用 C++ 原生 WebRTC API 实现 P2P 视频通话,不是加个 SDK 就完事——你得亲手管理 PeerConnection、MediaStream、VideoTrack、信令通道和 NAT 穿透逻辑。没有封装层兜底,出错时堆栈里全是 webrtc:: 命名空间的调用,调试门槛高,但可控性也最强。
怎么初始化 WebRTC 环境并创建 PeerConnection
WebRTC C++ 不提供单例或全局初始化入口,必须显式创建 webrtc::PeerConnectionFactoryInterface,它依赖三个核心工厂对象:网络、音频、视频。漏掉任一依赖(比如没传 rtc::Thread 给网络层),CreatePeerConnection 会静默返回空指针。
常见错误是复用主线程做网络 IO —— WebRTC 要求网络线程独立,否则 SetLocalDescription 卡死或回调不触发。
- 必须用
rtc::Thread::Create()创建专用网络线程,并在PeerConnectionFactoryDependencies中传入 -
cricket::FakeAudioDeviceModule可用于无音频场景,但视频必须配真实webrtc::VideoEncoderFactory和webrtc::VideoDecoderFactory(如webrtc::InternalEncoderFactory) -
PeerConnectionInterface::RTCConfiguration里至少填一个webrtc::PeerConnectionInterface::IceServer(哪怕只是{"stun:stun.l.google.com:19302"}),否则 ICE 连接永远卡在new
如何采集本地摄像头并绑定到 PeerConnection
C++ 版没有 getUserMedia 这种甜点 API,视频采集靠平台相关实现:Linux 用 webrtc::VideoCaptureModule(V4L2)、Windows 用 webrtc::DesktopCapturer 或第三方封装、macOS 需 AVFoundation 桥接。采集器启动后,要手动把帧送进 webrtc::VideoTrackSource 的 OnFrame 回调。
立即学习“C++免费学习笔记(深入)”;
容易被忽略的是线程安全:采集线程和 WebRTC 内部视频处理线程不同,必须用 rtc::scoped_refptr 包裹 webrtc::VideoTrackSource,且帧时间戳需用 rtc::TimeMicros() 校准,否则远端看到卡顿或花屏。
- 创建
webrtc::VideoTrackSource时传is_screencast = false,否则编码器可能跳过关键帧逻辑 - 调用
pc->AddTrack(video_track, {kStreamId})后,必须立刻调用pc->CreateOffer,否则 track 不会参与 SDP 生成 - SDP 中的
videom-line 必须含a=sendrecv,否则对方收不到视频流
为什么 setRemoteDescription 后 ICE 连接一直 failed
绝大多数失败不是因为代码写错,而是信令交换不完整或 ICE 候选者没发全。C++ API 要求你手动监听 OnIceCandidate,把每个 webrtc::IceCandidateInterface 序列化成字符串(用 candidate->ToString()),再通过信令通道发给对端;同样,收到对端候选者后,必须逐个调用 pc->AddIceCandidate,不能攒一批再加。
典型陷阱:OnIceCandidate 可能在 SetLocalDescription 前就触发(尤其 STUN 延迟低时),此时若信令通道未就绪,候选者就丢了。正确做法是缓存候选者列表,等信令通道 ready 后批量发送。
-
webrtc::PeerConnectionInterface::IceConnectionState为kIceConnectionFailed时,检查日志里是否有"Failed to connect to STUN server"或"No host candidates gathered" - 用
pc->GetStats查RTCIceCandidatePairStats的state字段,确认是否走到succeeded - 强制走 relay 时,
RTCConfiguration中type设为cricket::RELAY_PORT_TYPE,并确保 TURN 凭据有效(username/password过期会导致ice_connection_state卡在checking)
如何让远端视频渲染出来
WebRTC C++ 不提供内置播放器,你得自己实现 webrtc::VideoSinkInterface<:videoframe>,并在 OnFrame 里把 webrtc::VideoFrame 的 video_frame_buffer() 转成 OpenGL / Vulkan / Direct3D 可用纹理。缓冲区格式通常是 I420 或 NV12,frame.buffer()->ToI420() 会触发拷贝,性能敏感场景应直接读原始 rtc::scoped_refptr<:videoframebuffer>。
另一个坑是线程:渲染回调默认在 WebRTC 的 worker_thread 上触发,若你的渲染循环在主线程,必须用 PostTask 跨线程投递帧数据,否则 OpenGL 上下文错乱或崩溃。
- 注册 sink 用
video_track->AddSink(your_sink),不是pc->AddSink - 确保
your_sink生命周期长于video_track,否则AddSink后立即析构 sink 会 crash - 测试阶段可用
webrtc::test::FakeVideoRenderer替代自研渲染器,验证是否为渲染逻辑问题
最麻烦的从来不是编译通过,而是 ICE candidate 发送顺序错乱、视频帧时间戳跳变、或某条 AddIceCandidate 调用因线程竞争被丢弃——这些都不会抛异常,只表现为黑屏、单向通话、或连接秒断。建议从最小闭环(STUN + 本地回环)开始,逐项打开功能,用 pc->GetStats 和日志里的 webrtc::Logging 输出交叉验证每一步状态。











