RK3588 WebRTC 实时流卡顿优化:从视频桥接到 H.264 PTS 回退

**总结:**这次卡顿不是单纯的网络丢包或编码帧率不足,而是先由跨 pipeline 视频桥接和缩放增加了处理负担,后又暴露出 H.264 B 帧导致的非单调 PTS;最终通过 appsink/appsrc 直连、限制 videorate 补帧,并让不满足 PTS 条件的素材回退到 MPP 硬件编码,恢复了 WebRTC 的稳定呈现。

一、背景:RK3588 实时流为什么会卡顿

我们在RK3588上开发了一个音视频播放器,该播放器同时做两件事:一条路径把素材解码后送到 HDMI,另一条路径把视频和音频编码为 WebRTC 流,交给浏览器播放。两条路径共享解码输入,但对格式、时间戳、编码和调度的要求不同,因此"HDMI 看起来正常"并不等于"网页实时流正常"。

复制代码
视频文件 / 采集输入
        |
        v
MPP 硬件解码(把压缩视频变成 NV12 原始帧)
        |
        +--------------------+
        |                    |
        v                    v
   HDMI / kmssink       WebRTC 视频路径
                             |
                             +-- H.264 编码 -- RTP -- 浏览器

解码音频 -- HDMI/DEP 输出
        |
        +-- PCM -- Opus 编码 -- RTP -- 浏览器

本文中的 rk3588_gst_player 是运行在板端的 GStreamer 播放器进程;MPP 是 Rockchip 的硬件媒体处理框架,负责硬件解码和 H.264 编码。WebRTC 不是"把 HDMI 画面直接发出去",它还要求稳定的 RTP 时间线、浏览器可处理的编码格式和可预测的呈现时间戳。

(一)需要知道的几个术语

术语 本文中的含义
appsink/appsrc GStreamer 用于把一个 pipeline 的样本取出,再送入另一个 pipeline 的接口;本文用它替代跨 pipeline 的 inter sink。
RTP 承载 WebRTC 音视频的实时传输包;一帧 H.264 可能拆成多个 RTP 分片。
PTS / DTS PTS 是应该显示的时间,DTS 是解码顺序时间;B 帧可能让 PTS 暂时回退而 DTS 仍递增。
jitter buffer 浏览器为吸收网络和时间戳抖动而设置的缓冲区;时间线异常会让它变大并增加延迟。
B 帧 依赖前后参考帧进行显示重排的帧类型,压缩效率较高,但直通 WebRTC 时必须检查 PTS 是否仍然单调。
H.264 直通 不重新解码再编码,而是把素材中已压缩的 H.264 帧直接送入 WebRTC;它省掉编码成本,但也把源时间戳问题带进了实时流。

ZLMediaKit 是板端媒体服务器,编码后的 RTP 由实时流管理器共享给它,再由它分发给浏览器;因此本文重点分析的是"播放器产生什么样的帧和时间线",不是公网端口或浏览器页面本身。

本文记录对 RK3588 GStreamer Player 网页实时流卡顿问题的完整排查和优化过程,包括:

  • 最初现象和假设;
  • 为板端和浏览器补充的诊断指标;
  • 每一轮 pipeline 和配置调整;
  • H.264 直通引入 B 帧时间戳问题的原因;
  • 最终自动混合策略及验证数据;
  • 后续更换素材或继续优化时的回归方法。

本文只讨论"物理 HDMI 播放正常或相对流畅,但开启网页实时流后 HDMI 或网页出现卡顿"的问题。项目早期 4K60 双通道播放能力问题不在本文范围内。

(二)优化过程路线

这次排查不是一次性找到 PTS,而是沿着"先确认性能,再确认时间线"的路线逐步收敛:

复制代码
阶段 1:发现 WebRTC 开启后卡顿
          |
          v
阶段 2:发现跨 pipeline 桥接增加了额外负担
          |
          v
阶段 3:用 appsink/appsrc 替代 intervideosink/intervideosrc
          |
          v
阶段 4:尝试 H.264 直通,降低编码压力
          |
          v
阶段 5:发现 B 帧 PTS 重排导致 WebRTC 延迟和呈现卡顿
          |
          v
阶段 6:根据 PTS/DTS 自动判断直通资格,不合格时回退硬件编码

后文每一轮优化都回答一个具体问题:瓶颈到底在板端处理量、桥接时间线、网络传输,还是源视频的显示时间戳。

二、最终结论

实时流卡顿不是由单一环节造成,而是先后包含三个问题:

  1. 固定输出为 1920x1080 时,将约 1280x718 的素材放大,增加了板端缩放、编码、网络和浏览器解码负担。
  2. 早期 intervideosink/intervideosrc + videorate 桥接会重复 surface 或产生 GAP buffer,开启实时流后可能同时影响 HDMI 和网页流的节奏。
  3. 后期 H.264 直通虽然消除了缩放和重新编码,但含 B 帧的点播素材存在非单调 PTS。该时间戳进入 WebRTC 后显著增大 jitter buffer,并导致 Chrome 丢帧和呈现卡顿。

最终方案由以下部分组成:

  • 使用 appsink/appsrc 直接桥接解码后的视频和 PCM 音频;
  • videorate 设置 200 ms 最大补帧间隔,避免长时间停播后生成大量 GAP 帧;
  • 支持 fixedsource 两种分辨率模式;
  • 使用共享 RTP,一路通道只处理/编码一次,多个浏览器复用结果;
  • source 模式使用自动混合视频策略;
  • H.264 先验证首个 GOP,只有 PTS 单调的素材才允许直通;
  • 检测到 PTS 回退且 DTS 单调时,将当前素材锁定到 MPP H.264 硬件编码;
  • 音频始终由 PCM 编码为 Opus,不执行音频直通。

最终测试中,浏览器丢帧由 57 降为 0,freeze 由 1 降为 0,实际呈现帧率由约 25.75 fps 恢复到约 30.04 fps。

三、初始现象与第一轮判断

最初观察到:

  • 只播放 HDMI 时相对流畅;
  • 开启实时流后,网页播放比 HDMI 卡;
  • 部分测试中,开启实时流后物理 HDMI 也比未开启时卡;
  • 板端硬件编码约 29.9 fps,最大输入帧间隔约 33 ms,说明最初不能只把问题归因于 MPP 编码不稳定。

当时使用的网页流接近 1920x1080、30 fps,而素材约为 1280x718、29.97 fps。初步风险包括:

  • videoscale 将非标准 720p 素材放大到 1080p;
  • 固定 30 fps 与素材原始帧率或 HDMI 显示刷新率不完全一致;
  • WebRTC 网络 jitter、浏览器 jitter buffer 和 VSync 合成可能丢帧;
  • 浏览器显示的"解码 fps"不等于用户真正看到的"呈现 fps"。

第一轮采用低风险配置,将网页流降到 720p30:

复制代码
[stream]
video_resolution_mode = fixed
video_width = 1280
video_height = 720
video_fps = 30
video_bitrate = 2500000

该调整降低了缩放、网络传输和浏览器解码压力,但只能缓解问题,不能解释所有偶发卡顿。

videoscale 负责改变画面尺寸,VSync 是显示器刷新与浏览器合成节奏。它们会影响最终"看到的帧率",但不能替代板端 RTP 和浏览器呈现指标。

四、先建立可观测性

排查过程中没有直接缩短 RTP 队列。原因是 H.264 一个图像可能拆成多个 RTP 分片,盲目缩短或丢弃队列中的单个 buffer 可能破坏一帧,反而增加花屏和卡顿。

(一)网页 CSV 指标

内置播放器增加了每 2 秒采样的 CSV 日志,主要字段包括:

字段 含义
decoded_fps Chrome 解码帧率
frames_dropped / dropped_delta 浏览器累计/本窗口丢帧
jitter_ms WebRTC 入站 RTP jitter
packets_received / packets_lost 视频 RTP 收包和丢包
freeze_count 浏览器检测到的累计冻结次数
presented_fps requestVideoFrameCallback 统计的实际呈现帧率
max_present_gap_ms 当前窗口最大呈现帧间隔
long_present_gap_count 当前窗口超过 50 ms 的呈现间隔次数
visibility_state 页面是 visible 还是 hidden
decoder_implementation 浏览器上报的解码器实现,部分 Chrome 版本为空
power_efficient_decoder 浏览器是否报告高能效解码器,部分版本为空
jitter_buffer_avg_delay_ms 浏览器累计 jitter buffer 平均延迟

CSV 最多保留约 12 小时,刷新页面后清空。分析时必须排除 visibility_state=hidden 的窗口,因为后台标签页会暂停或降低视频呈现频率。

(二)板端 5 秒统计

板端为共享实时流 pipeline 增加了每 5 秒汇总:

  • Encoder cadence:RTP 视频输出 fps 和最大帧间隔;
  • Audio RTP:Opus RTP 包数和字节数;
  • Audio bridge input:PCM 输入数量、字节数和时长;
  • Video modeh264-passthroughhardware-encode
  • Video stageappsrc_outvideorate_outvideoscale_outencoder_out 四阶段帧率和间隔;
  • Video rate/queuevideorate 输入、输出、丢弃、复制以及 queue 深度和溢出次数。

这些数据用于区分:

复制代码
板端输入或编码不稳
        ↓
RTP/network jitter 或丢包
        ↓
Chrome 解码不足
        ↓
浏览器合成/VSync 呈现不稳

仅看网页底部瞬时 fps 不足以判断问题,因为短窗口、抖动补偿和页面可见性都会使数值波动。

五、替换实时流桥接链路

早期重点检查的链路是:

复制代码
intervideosrc -> videorate -> videoscale -> mpph264enc

intervideosink/intervideosrc 按独立周期传递 surface,停流、恢复或上下游时钟不一致时可能重复旧 surface,并产生 GAP buffer。开启实时流后,该行为会让 videorate 继续补帧,增加额外调度和时间线扰动。

这里的 GAP buffer 可以理解为"时间线上有位置、但没有真实视频内容的占位帧";如果下游继续追赶时间线,短暂停流可能被放大成大量补帧。

最终改为:

复制代码
HDMI playbin 解码输出
        |
        +-> kmssink(物理 HDMI)
        |
        +-> appsink -> appsrc -> WebRTC 共享处理链

音频也改为 PCM appsink/appsrc 桥接,不再依赖 interaudiosink/interaudiosrc。每个 buffer 只转交一次,实时流开关通过常驻分支中的 valve 控制,不需要重建 HDMI 播放 pipeline。

这轮修改后,物理 HDMI 和网页流的主观流畅度都明显改善。

六、限制 videorate 长时间补帧

硬件编码回退链中的 videorate 使用:

复制代码
skip-to-first=true
max-duplication-time=200000000

即最大补帧间隔为 200 ms。正常的 29.97 fps 到 30 fps 调整远小于该值,不受影响;当素材停止很久后恢复时,不会为中断期间生成大量 GAP 帧追赶时间线。

对应实现位于 app/WebRtcStreamManager.cppStartEncoder()

"实时流持续开启、素材停止数小时后恢复"仍属于需要单独长时间验证的场景,不能仅凭短时测试认定完全覆盖。

七、fixed/source 两种分辨率模式

为避免要求节目单中的所有素材都使用同一分辨率,增加了两种配置模式。fixed 表示先把所有素材转换到统一输出规格;source 表示尽量保留素材的源分辨率,再由浏览器按页面尺寸显示。

(一)fixed

复制代码
video_resolution_mode = fixed
video_width = 1280
video_height = 720
video_fps = 30
video_bitrate = 2500000

行为:

  • 始终经过 videorate ! videoscale ! mpph264enc
  • 输出严格遵循配置宽高、帧率和码率;
  • 不启用源 H.264 直通;
  • 适合必须统一网页流规格的场景。

(二)source

复制代码
video_resolution_mode = source
video_fps = 30
video_bitrate = 2500000

行为:

  • video_widthvideo_height 被忽略;
  • 两个 HDMI 通道分别按各自素材分辨率工作;
  • 浏览器负责把视频按页面尺寸显示;
  • 无法直通时仍使用 MPP H.264 编码,但不强制缩放到固定宽高;
  • 4K 等高分辨率仍需确认浏览器解码能力和网络带宽。

物理 HDMI 和网页流是不同输出路径。HDMI 可由 DRM/KMS plane 适配显示器模式;网页流是否缩放由 [stream].video_resolution_mode 决定。

阶段小结: fixed 更容易控制输出规格,但会付出缩放成本;source 减少不必要的缩放,却要求后续的 H.264 直通资格判断更加严格。

八、第一版自动混合模式

H.264 直通看起来非常有吸引力,因为它可以跳过重新编码,减少 CPU 占用和端到端延迟;但代价是播放器必须承担源 H.264 时间戳质量的风险。为了进一步降低开启实时流后的开销,source 模式增加了两个视频分支:

复制代码
硬件编码分支:
NV12 appsrc -> videorate -> videoscale -> mpph264enc -> h264parse --+
                                                                  |
                                                                  +-> input-selector
                                                                  |   -> rtph264pay
H.264 直通分支:                                                  |   -> 共享视频 RTP
源 h264parse probe -> H.264 appsrc -> h264parse ------------------+

音频链保持:

复制代码
PCM appsrc -> audioconvert -> audioresample -> opusenc -> rtpopuspay

第一版判断 Baseline、Constrained Baseline、Main 和 High Profile 为浏览器可接受的 H.264,并在关键帧切换到直通。H.265、其他编码和不兼容 Profile 自动使用 MPP 编码。

测试曾确认直通工作:

复制代码
Video mode: h264-passthrough
passthroughInputFrames: 约 150 帧/5秒
appsrc_out: 0
videorate_out: 0
videoscale_out: 0
encoder_out: 0

这证明直通期间 mpph264enc 没有接收视频帧,网页实时流不再额外执行缩放和视频编码。HDMI 播放本身仍需要解码,音频仍需要 Opus 编码。

**阶段小结:**H.264 直通确实降低了板端处理量,但"能直通"只说明编码格式可接受,还没有证明 RTP 时间戳适合 WebRTC。
**转折提示:**到这里,问题已经从"性能不足"转变为"时间线不适合实时传输"。前面的桥接优化解决了额外处理负担,但不能保证点播 H.264 的 PTS 适合 WebRTC 直通。

九、直通后仍卡顿:发现 B 帧问题

第一版 H.264 直通后,板端输入和 RTP 输出仍接近 30 fps,但网页 CSV 显示:

  • Chrome 解码接近 30 fps;
  • 实际呈现显著低于 30 fps;
  • RTP 没有网络丢包;
  • jitter 和 jitter buffer 延迟明显升高;
  • 浏览器出现累计丢帧和 freeze。

问题不在"Chrome 是否支持 High Profile",而在"该压缩码流是否适合低延迟 WebRTC 直通"。

对测试素材执行:

复制代码
ffprobe -v error -select_streams v:0 \
  -show_entries stream=codec_name,profile,level,width,height,has_b_frames,r_frame_rate,avg_frame_rate,bit_rate \
  -of default=noprint_wrappers=1 \
  '/mnt/udisk/materials/复仇者联盟4-单音轨.mp4'

关键结果:

复制代码
codec_name=h264
profile=High
width=1280
height=718
has_b_frames=1
level=31
avg_frame_rate=30000/1001
bit_rate=1501271

再检查压缩包时间戳:

复制代码
ffprobe -v error -select_streams v:0 -read_intervals '%+#30' \
  -show_entries packet=pts_time,dts_time,flags -of csv=p=0 \
  '/mnt/udisk/materials/复仇者联盟4-单音轨.mp4'

前几帧呈现为:

复制代码
PTS: 33 -> 100 -> 67 -> 167 -> 133 -> 234 -> 200 ms
DTS:  0 ->  33 -> 67 -> 100 -> 133 -> 167 -> 200 ms

DTS 按解码顺序单调递增,但 PTS 因 B 帧显示重排持续前跳和回退。板端直通统计中的最大正向时间戳间隔因此反复达到 100 ms,而正常 29.97 fps 相邻显示帧间隔应约为 33.4 ms。

Chrome 虽然能够解码这些帧,但 WebRTC jitter buffer 需要吸收非单调显示时间戳,最终表现为缓冲延迟增大、呈现帧率下降和间歇丢帧。更换开源网页播放器不能消除源 RTP 时间戳问题。

这并不意味着 WebRTC 协议禁止 B 帧;这里的结论只针对本案例的低延迟直通链路:当源 PTS 频繁重排时,重新编码成单调时间线比直接复用点播码流更稳妥。

**定位结论:**这一轮数据把问题从"网络是否丢包"进一步缩小到"源 H.264 的显示时间线是否适合实时直通"。

十、最终自动混合策略

最终策略不再只检查 H.264 Profile,而是加入运行时 PTS 资格判断。

(一)新素材开始

  1. 默认选择硬件编码分支;
  2. 记录压缩 H.264 的 PTS 和 DTS;
  3. 在硬件编码继续输出的同时观察首个完整 GOP;
  4. 首个 GOP 内 PTS 始终单调时,才在下一个关键帧切换直通。

(二) B 帧判定

同时满足以下条件时判定存在呈现重排:

复制代码
当前 PTS < 上一个 PTS
DTS 没有回退
当前 buffer 没有 DISCONT
caps 没有切换

这样可把 B 帧重排与 seek、素材切换或时间线重置区分开。

一旦检测到 B 帧重排:

  • 当前素材锁定为 hardware-encode
  • 后续即使遇到关键帧也不再尝试直通;
  • 只有 NotifyVideoSourceReset() 收到真实素材切换事件后才清除锁定并重新判断。

板端会打印:

复制代码
H.264 passthrough disabled for current source: PTS rollback ...
with monotonic DTS (B-frame reordering detected)

Video mode: hardware-encode
(H.264 PTS reordering blocked passthrough)

资格判断和状态切换位于 app/WebRtcStreamManager.cppPushEncodedVideoSample();压缩 H.264 probe 位于 app/CGStreamerPlayer.cpp;通道转发位于 app/ChannelPlayer.cpp

十一、最终数据对比

对比日志:

  • B 帧 H.264 直通:rk3588-webrtc-channel-1-20260807133047103.csv
  • 增加 PTS 回退保护后:rk3588-webrtc-channel-1-20260807140256820.csv

统计均排除连接初始阶段;最终日志还排除了 visibility_state=hidden 的三个后台标签页窗口。

指标 B 帧 H.264 直通 PTS 检测后 MPP 回退
Chrome 解码 fps 29.60 30.03
页面可见时实际呈现 fps 25.75 30.04
jitter 55.9 ms 1.4 ms
jitter buffer 平均延迟 151.9 ms 12.4 ms
最大呈现帧间隔 200.1 ms 66.7 ms
每 2 秒长呈现间隔次数 14.9 1.8
浏览器累计丢帧 57 0
freeze 1 0
视频 RTP 丢包 0 0
音频 RTP 丢包 0 0

板端最终稳定统计:

复制代码
Video mode: hardware-encode (H.264 PTS reordering blocked passthrough)
Encoder cadence: 约 30 fps
maxFrameGapMs: 33
encoder_out: 约 150 帧/5秒
queueOverruns: 0
Audio RTP: 约 250 包/5秒

这组数据说明:

  • MPP 编码输出时间戳恢复为单调的约 33 ms 间隔;
  • jitter buffer 平均延迟降低约 92%;
  • 浏览器解码和实际呈现都恢复到约 30 fps;
  • 主观改善与板端、RTP 和浏览器三层数据一致。

十二、快照下的推荐配置

素材分辨率不统一、希望避免不必要缩放时:

复制代码
[stream]
max_viewers_per_channel = 4
video_resolution_mode = source
video_width = 1280
video_height = 720
video_fps = 30
video_bitrate = 2500000
hdmi1_av_delay_ms = 0
hdmi2_av_delay_ms = 0

source 模式下,video_widthvideo_height 不参与网页流处理;保留这两个值是为了以后切回 fixed 时有明确配置。

如果业务要求所有网页流严格统一为 1280x720、30 fps,则使用:

复制代码
video_resolution_mode = fixed
video_width = 1280
video_height = 720
video_fps = 30
video_bitrate = 2500000

十三、回归测试方法

(一)构建和部署

在 Ubuntu 开发机执行:

复制代码
bash scripts/build.sh
bash scripts/upload.sh

板端启动:

复制代码
/root/Test/gst-test/use-gst-test.sh \
  /root/Test/gst-test/app/rk3588_gst_player

(二)含 B 帧 H.264

预期日志:

复制代码
H.264 passthrough disabled ... PTS rollback
Video mode: hardware-encode (H.264 PTS reordering blocked passthrough)
encoder_out: 约目标 fps
maxFrameGapMs: 接近单帧间隔

不应在后续关键帧重新切回直通。

(三)无 B 帧且 PTS 单调的 H.264

预期日志:

复制代码
H.264 passthrough qualification started
Video mode switched to H.264 passthrough
Video stage encoder_out: frames=0

需要使用已确认 has_b_frames=0 的素材完成板端回归。

(四)H.265 或其他编码

预期始终为:

复制代码
Video mode: hardware-encode
encoder_out: 约目标 fps

(五)浏览器 CSV

测试时保持页面可见并至少采集 2 分钟。重点检查:

  • decoded_fpspresented_fps 是否接近目标帧率;
  • frames_dropped 是否持续增长;
  • freeze_count 是否增长;
  • jitter_ms 是否长期偏高;
  • jitter_buffer_avg_delay_ms 是否持续升高;
  • packets_lost 是否增长;
  • max_present_gap_ms 是否反复达到 100 ms 以上。

(六)仍需覆盖的场景

  • 实时流持续开启、素材停止数小时后恢复;
  • H.264 无 B 帧素材完成首 GOP 验证并切换直通;
  • H.264/H.265/不同分辨率素材连续切换;
  • 两个 HDMI 同时使用不同分辨率和不同编码;
  • 多浏览器同时连接同一通道;
  • 浏览器开启声音后的长时间音视频连续性;
  • fixedsource 重启切换;
  • 页面后台再回到前台后的统计恢复。

十四、经验总结

  1. 编码 fps 稳定不代表网页实际呈现稳定,必须同时采集浏览器呈现数据。
  2. packets_lost=0 不代表没有 RTP 时序问题;非单调 PTS 同样会放大 jitter buffer。
  3. 浏览器支持某个 H.264 Profile,不等于该码流适合低延迟 WebRTC 直通。
  4. 点播 H.264 常使用 B 帧提高压缩效率,而在低延迟 WebRTC 场景中,无 B 帧、单调 PTS 的编码输出通常更容易获得稳定表现。
  5. RTP 队列不是第一个应该调整的参数。先定位丢帧发生在板端、网络、解码还是呈现阶段。
  6. 自动直通必须有可回退条件,并且回退结果需要按素材锁定,避免模式反复切换。
  7. 页面不可见时的 presented_fps=0 是浏览器正常节流,分析 CSV 时必须结合 visibility_state
  8. 优化最终必须由主观观察和板端、RTP、浏览器三层数据共同验证。
相关推荐
深念Y22 分钟前
视频平台架构决策:从存储到转码的选型逻辑
架构·音视频
show4332 小时前
2026小程序视频转文字多语种识别引擎选型:22方言25外语
小程序·音视频
2601_955662461 天前
AI 配音工具 7 款实测:短视频、影视解说、小说推文音质横向对比
人工智能·音视频·语音识别·视频
阿童木写作2 天前
跨境电商图片翻译工具,批量翻译视频字幕一键抠图
人工智能·python·音视频
深圳市宝华视联2 天前
医院内网部署手术示教系统注意事项
嵌入式硬件·实时互动·音视频·实时音视频·视频编解码·嵌入式实时数据库
小柯南敲键盘2 天前
跨境电商图片翻译工具,批量处理视频字幕与抠图
人工智能·python·音视频
weixin_433417672 天前
阿里视频大模型wan2.7‑t2v,可以生成视频
人工智能·python·音视频
阿童木写作2 天前
跨境电商图片翻译工具,批量翻译视频字幕还免费
python·音视频
stuartevil2 天前
AI图生视频常见坑:参考图风格不一致怎么解决
人工智能·深度学习·音视频