3. WebRTC媒体栈概览

理解 WebRTC 的媒体栈,是做好 H.264 与 H.265 编码适配的前提。WebRTC 并不是一个单一协议,而是一条从设备硬件到远端屏幕的完整流水线:采集、前处理、编码、RTP 打包、网络传输、抖动缓冲、解码、渲染。编解码器只是这条流水线中的一环,但它与前后环节的耦合非常紧密——编码器输出什么格式的码流,直接决定 RTP 打包方式;协商阶段选定了哪种 codec,又决定了整条链路的能力边界。

一、媒体栈的六个阶段

从本地摄像头到远端播放器,媒体数据要经过六个关键阶段,每个阶段都有明确的输入输出和职责边界。

  • 采集(Capture):通过 getUserMedia 获取摄像头与麦克风数据,视频通常以 YUV 或 NV12 格式进入管线,音频为 PCM。采集帧率、分辨率、色彩空间在此确定。
  • 前处理(Pre-processing):包括缩放、裁剪、旋转、降噪、美颜等。WebRTC 内部会做分辨率自适应,根据带宽和 CPU 负载动态调整采集与编码分辨率。
  • 编码(Encode):视频经 VP8/VP9/H.264/H.265/AV1 编码为压缩码流,音频经 Opus 编码。编码器输出 NAL 单元或帧数据,并附带时间戳。
  • RTP 打包(Packetization):将编码后的帧按 MTU 切分为 RTP 包,视频使用 RFC 6184(H.264)或 RFC 7798(H.265)定义的打包模式,音频使用 RFC 7587(Opus)。
  • 传输(Transport):RTP 包经 SRTP 加密后通过 ICE/DTLS 建立的通道发送,配合 RTCP 反馈、NACK、PLI、FIR、TWCC 等机制做丢包恢复与拥塞控制。
  • 解码与渲染(Decode & Render):接收端做 jitter buffer 排序、丢包隐藏、解码,再交给渲染器上屏。音视频同步依赖 RTCP SR 中的 NTP 时间戳。
核心认知:编码器不是孤立模块。它的输出格式(Annex-B 还是 AVCC、是否带 SPS/PPS、是否支持分片)会直接影响 RTP 打包器的实现,而协商结果又决定了编码器是否被启用。
WebRTC 媒体栈:采集 → 编码 → RTP → 传输 → 解码 → 渲染 媒体采集 YUV / PCM 前处理 缩放 / 裁剪 / 旋转 视频编码器 H.264 / H.265 / VP8 / AV1 RTP 打包 RFC 6184 / 7798 SRTP 传输 ICE / DTLS / RTCP 网络传输 丢包 / 抖动 / 拥塞 抖动缓冲 排序 / 丢包隐藏 视频解码器 H.264 / H.265 / VP8 / AV1 音视频同步 RTCP SR / NTP 渲染上屏 VideoFrame / Canvas SDP 协商层:m=video 行 · rtpmap · fmtp · profile-level-id · offer/answer · setLocalDescription / setRemoteDescription 协商结果决定编码器与解码器的启用、参数配置与打包模式

二、编解码器协商机制

WebRTC 的编解码器协商基于 SDP Offer/Answer 模型。发起方在 SDP 的 m=video 行中列出自己支持的 payload type 与对应编码器,接收方从中选择交集并返回 Answer。整个过程由 RTCPeerConnection 的 setLocalDescription 与 setRemoteDescription 驱动。

以 H.264 为例,SDP 中会出现如下关键字段:

m=video 9 UDP/TLS/RTP/SAVPF 96 97 98
a=rtpmap:96 H264/90000
a=fmtp:96 profile-level-id=42e01f;packetization-mode=1;level-asymmetry-allowed=1
a=rtpmap:97 H265/90000
a=fmtp:97 profile-id=1;tier-flag=0;level-id=93
a=rtpmap:98 VP8/90000

其中 profile-level-id 是 H.264 协商的核心参数,42e01f 表示 Baseline Profile、Level 3.1、约束集为 1。packetization-mode=1 表示支持非交错模式,这是 WebRTC 中最常用的打包方式。H.265 则使用 profile-id、tier-flag、level-id 等参数描述能力。双方必须对这些参数达成一致,否则该 payload type 会被拒绝。

协商要素 H.264 H.265 影响
RTP 打包规范 RFC 6184 RFC 7798 决定 NAL 分片与聚合方式
关键参数 profile-level-id、packetization-mode profile-id、tier-flag、level-id 决定编码器配置与兼容性
浏览器支持 广泛支持 支持有限,需硬件与浏览器双端支持 影响能否启用 H.265
典型 payload type 96 / 97 98 / 99 动态分配,以 SDP 为准

三、典型场景:一次 H.264 视频通话的完整链路

假设 A 与 B 建立 1 对 1 视频通话,A 的浏览器支持 H.264 Baseline 与 VP8,B 只支持 H.264。协商过程如下:

  1. A 创建 Offer,m=video 行中列出 H.264(payload 96)与 VP8(payload 97)。
  2. B 收到 Offer 后,发现只支持 H.264,于是在 Answer 中只保留 payload 96,并回填自己支持的 profile-level-id。
  3. 双方 setLocalDescription / setRemoteDescription 完成后,编码器被配置为 H.264 Baseline,打包模式为 packetization-mode=1。
  4. A 端采集 1280×720@30fps,编码器输出 NAL 单元,RTP 打包器按 MTU 切分为多个 RTP 包,经 SRTP 发送。
  5. B 端 jitter buffer 排序后送入 H.264 解码器,解码为 YUV,再经渲染器上屏。
协商不是一次性的。当网络条件变化时,WebRTC 可能通过 renegotiation 重新协商编码器或分辨率。此外,setCodecPreferences 可以在创建 Offer 前调整编码器优先级,这对希望优先使用 H.265 的场景非常关键。

四、常见误区与复盘

  • 误区一:SDP 中列出了 H.265 就一定能用。实际上还要看浏览器是否真正实现了该编码器的编解码路径,以及硬件是否支持。很多浏览器在 SDP 中不暴露 H.265,即使底层硬件支持。
  • 误区二:profile-level-id 可以随意填写。如果填写的 level 高于实际编码能力,可能导致解码端无法处理;如果低于实际能力,又会浪费性能。必须与编码器实际配置一致。
  • 误区三:忽略 packetization-mode。mode=0 只支持单 NAL 打包,遇到大帧会失败;mode=1 支持 FU-A 分片,是 WebRTC 的推荐配置。
  • 误区四:认为协商只影响编码器。协商结果同时影响 RTP 打包器、解码器初始化和 jitter buffer 的策略,是整条链路的配置源头。
在适配 H.265 时,务必先确认三件事:浏览器是否在 SDP 中暴露 H.265、硬件编码器是否可用、对端是否支持 RFC 7798 打包。三者缺一,协商都会失败或回退到 H.264。