从四路 4K 硬解成功,到完整推理的困局
写在前面
最近一直在做一个看起来很简单、实际上非常容易误判的事情:把四路 4K 视频送进 RK3588,完成硬件解码、目标检测、叠框、H.264 编码,再分别推回 SRS。
阶段中出现过两个看似矛盾的结果:
- 四路 4K H.264 硬解已经成功,而且可以实时跑到 30 FPS;
- 四路 4K 完整推理却不能稳定做到“每路高频检测 + 每路 30 FPS 输出”。
这两个结论并不矛盾。前者证明的是 RKVDEC/FFmpeg 硬解链路的能力,后者考验的是整台板卡上解码、内存、RGA、NPU、MPP 编码和网络输出的总资源预算。
本文记录这个过程,也解释为什么“4K 硬解成功”不能直接推导出“四路 4K 完整推理实时通过”。
一、之前的 4K 硬解确实成功了
1. 最初的测试目标很单纯
最初测试并没有把检测和编码放进来,测试链路是:
Windows 本地 4K 视频
↓
SRS RTMP
↓
板端 FFmpeg + h264_rkmpp
↓
null sink
板端使用的命令明确把 h264_rkmpp 放在输入端,输出只是 wrapped_avframe,不再做后续 MPP 编码。这样做的目的,是单独回答一个问题:
RK3588 上能不能同时把四路 3840×2160 H.264 RTMP 实时解码出来?
2. 中间还排查出一个网络问题
早期四路测试表现很差,输入 speed 只有 0.8~0.9 倍实时。最初怀疑过:
- RKVDEC 是否只启用了一颗核心;
- FFmpeg 是否选错了解码器;
- SRS 是否没有实时输出;
- 多路 RTMP 是否有丢包;
- 板端是否存在 TCP 接收队列积压。
最后确认的主要原因是 SRS 所在 Windows 主机的物理网卡只协商到了 100 Mbps。四路源视频大约 40 Mbps/路,四路聚合已经接近或超过物理链路上限。
恢复千兆链路后,点到点 TCP 测试达到约 787 Mbps,网络瓶颈被排除。
3. 四路纯硬解验收结果
在千兆链路和正确的 decoder 参数下,四路硬解测试结果如下:
- 输入:四路 3840×2160 H.264 RTMP;
- 解码器:
h264_rkmpp; - 输出:
wrapped_avframe,不做检测和编码; - 测试时间:约 60 秒;
- 四路最终 speed:约
1.02–1.05x; - 每路完成约 1770~1800 帧;
- 未出现软件解码回退;
- 未出现 MPP 解码错误。
所以这件事可以明确下结论:
四路 4K H.264 硬解本身已经通过实时门槛。
这不是模拟结果,也不是单路结果,而是 Windows 本地源 → SRS → 板端四路 h264_rkmpp 的实际拓扑验证。
二、为什么完整推理又变成了另一个问题
完整业务链路不是“解码结束就完事”,而是:
4K H.264
↓
h264_rkmpp 硬解
↓
NV12 / DMA-BUF
↓
RGA 颜色转换、缩放和 letterbox
↓
RKNN YOLOv8s INT8 推理
↓
CPU 输出获取、DFL 解码、NMS
↓
SharedDetections / 跟踪
↓
MPP 4K H.264 编码
↓
RTMP 推流 + MQTT
纯硬解测试只消耗了解码和网络输入资源;完整推理还额外增加了:
- 四路 4K NV12 buffer 的分配、同步和 DMA 访问;
- RGA 预处理;
- RKNN NPU 计算;
- 输出 tensor 获取和 CPU 后处理;
- 四路 MPP H.264 编码;
- 四路 RTMP 输出。
因此,纯硬解实时通过,只能证明“解码器有能力”,不能证明“整条流水线还有足够余量”。
三、完整推理阶段做了哪些修改
1. 让硬解帧进入 RGA、RKNN 和 MPP
以前的推理路径更多是围绕 CPU 帧和 V4L2 帧设计的,4K 硬解后得到的 MPP DMA-BUF 不能简单当成普通 raw_data 使用。
这次做了几项适配:
InferThread同时支持 CPU NV12/YUYV 和 MPP DMA-BUF;- RGA 处理使用实际 buffer 的 stride;
- CPU fallback 也按 stride 读取 NV12;
- 访问 MPP buffer 前进行只读同步;
TilingTask的 NV12 crop 同样适配 DMA-BUF 和 stride;- 保留 frame ID、PTS 和时间戳,保证检测结果能够和视频帧对齐。
单路板端运行时已经明确打印:
Resolution: 3840x2160
Decoder: h264_rkmpp
Format: nv12
并且后续出现 RKNN 推理、MPP 编码、RTMP 输出和 MQTT 连接日志。单路 4K 完整链路分别完成了 60 秒和 180 秒稳定性测试,输入、编码和输出约 30 FPS,编码/推流失败计数为 0。
2. 从多个 RKNN context 改成一个全局 NPU executor
最初的四路完整推理路径是每个 pipeline 各自创建 RKNN context,并都绑定三颗 NPU 核心。结果是四个 context 同时争用同一组 NPU 资源。
实测表现是:
- 单路 NPU 阶段约 27 ms;
- 四路并发时部分时段升到 50 ms;
- 压力进一步增加后,部分时段达到 85–88 ms;
- 视频输出随之出现明显卡顿。
因此新增了全局 Scheduler:
- 一个共享 RKNN context;
- 一个 all-core NPU executor;
- 一个 scheduler worker 串行执行推理事务;
- 每路一个 latest-frame mailbox;
- 旧帧被覆盖,不允许队列无限堆积;
- 统计 NPU 平均值、P95、busy ratio、queue wait 和每路实际 FPS。
这个修改解决的是 NPU context 争用,不是让 NPU 变成了更快的硬件。
3. 增加全局节流和固定相位
早期的 global_target_fps 只是配置校验,并没有真正限制全局发车间隔。现在增加了实际的全局节流和相位错峰:
四路 4 FPS:
channel 1: 0 ms
channel 2: 62 ms
channel 3: 125 ms
channel 4: 187 ms
调度器会按照绝对时间轴发车;如果某次执行已经错过下一个周期,就跳过过期周期,而不是连续追赶。这可以减少瞬时 RGA、NPU、内存和 MPP 资源争用。
需要特别说明:错峰不会增加每路检测频率。每路仍然是 4 FPS,就意味着检测框每 250 ms 才得到一次新测量。
四、单路、双路、四路分别是什么结果
单路:有余量
单路 4K 不主动跳帧时:
- 完整推理平均约 40.9 ms;
- NPU 阶段约 27 ms;
- 实际完整推理能力约 24 FPS;
- 视频编码和 RTMP 输出约 30 FPS。
另一个单路候选设置了 infer_every_n_frames=4,这代表每 4 个输入帧才推理一次,不是每秒 4 次。30 FPS 输入下理论约 7.5 FPS,180 秒实测约 6.8~7 FPS。
双路:每路 7 FPS 可以稳定
双路全局 Scheduler 测试使用每路目标 7 FPS:
- 实际约 6.9~7.0 FPS/路;
- 全局约 14 FPS;
- 两路视频输出约 30 FPS/路;
enc_drop=0;out_drop=0;write_fail=0。
这说明在当前板端资源下,双路每路 7 FPS 是可行的容量档位。
四路目标 7 FPS:预算超载
四路每路目标 7 FPS,总目标是 28 FPS。实测结果:
- 每路实际约 4.8~5.3 FPS;
- NPU 平均约 31.5~33.9 ms;
busy_ratio=0.98–0.99;- 结果视频有效窗口约 28.6~29.5 FPS,部分窗口约 27 FPS;
- 没有 RKNN 执行失败、编码失败或 RTMP 写失败。
这说明系统不是“跑不起来”,而是已经没有足够资源满足原来的预算。
四路目标 5 FPS:稍微缓解,但还不够
把每路目标降为 5 FPS、全局预算设为 21 FPS 后:
- 每路实际约 4.7~5.0 FPS;
busy_ratio=0.92–0.97;- 大部分视频窗口接近 30 FPS;
- 仍有部分窗口降到约 27 FPS。
这说明降低 NPU 调度预算有帮助,但四路 4K 解码、内存搬运和 MPP 编码的总压力仍然存在。
四路目标 4 FPS:视频稳了,但框不顺
加入全局 16 FPS 节流和固定相位后,四路 4 FPS 测试得到:
- 每路实际约 4.0 FPS;
busy_ratio降到约 0.72~0.76;- 四路视频输出约 29.8~30.2 FPS;
- 最后 10 秒 SRS 帧增量约 298/301/301/302;
- 所有编码和推流失败计数为 0。
从视频链路角度,这个结果是成功的;从检测框体验角度,它不合格。因为当前跟踪器主要做保持和 EMA 平滑,没有在检测间隔内持续预测框的位置,所以移动目标会表现为:
框停留一段时间 → 更新一次 → 再停留 → 再跳一次
用户已经明确不接受每路 4 FPS 的检测刷新率,因此这个配置只保留为容量基线,不进入最终配置。
五、当前真正卡住的地方
1. NPU 是单次推理的主要计算开销
单路 NPU 约 27 ms,是一次推理事务中最大的单项计算阶段。四路共享后,NPU 平均仍在 30~34 ms,P95 约 34~39 ms。
但四路问题不能只看 rknn_run(),因为完整 executor 还包括 RGA、cache sync、outputs_get 和后处理。
2. 四路 4K 媒体链路总量超过安全余量
稳定窗口中可以观察到:
- MPP 解码接收通常约 12~15 ms/帧;
- RGA/NV12 预处理约 3 ms/帧;
- MPP 4K 编码约 25~27 ms/帧;
- 多路 DMA buffer、内存带宽和硬件模块调度继续叠加。
这就是为什么 target=5 已经降低了 NPU 压力,部分输出窗口仍然只有约 27 FPS:瓶颈已经不是纯 NPU,而是完整 4K 媒体链路的总吞吐。
3. Queue wait 是后果,不是根因
Scheduler 中的 queue_wait_p95 在四路 4 FPS 时仍约 50~105 ms。这代表最新提交的帧需要等待全局 slot 和通道相位,不代表编码或推流丢帧。
latest mailbox 的策略是:宁可覆盖旧推理帧,也不让延迟无限增加。所以 mailbox_replaced 较高是预期行为;但它不会把 4 FPS 检测框变成 30 FPS 连续运动。
4. 网络问题已经不是当前主因
100 Mbps 物理链路的问题已经通过恢复千兆协商解决。四路纯 h264_rkmpp 硬解已经证明输入网络和硬解能力都可以达到实时。
当前更应该关注的是板端多路解码、NV12/DMA 内存、RGA、NPU 和 MPP 编码之间的资源竞争。
六、下一步怎么走
现在不能继续把“四路 4K 完整推理”简单理解成一个 FPS 数字。至少要同时满足三个条件:
- 每路检测刷新率达到可接受水平,不能停在 4 FPS;
- 四路视频输出稳定约 30 FPS;
- 不能依靠持续堆积队列来换取表面上的检测频率。
下一轮最合理的实验是重新测试带全局节流和固定相位的:
每路 target_infer_fps = 5
全局 global_target_fps = 20
相位 = 0 / 50 / 100 / 150 ms
之前的 target=5 测试是在全局节流和固定相位实现之前完成的,不能直接作为新调度器的最终结论。
如果 target=5 仍然让检测框出现明显跳变,那么继续把四路目标推到 6~7 FPS 很可能重新压垮媒体链路。更合理的方向是:让 NPU 以 5 FPS 左右负责检测校正,再在两次检测之间增加低成本的帧间运动跟踪或位置插值,使框可以按视频帧率连续移动。
总结
目前可以非常明确地说:
- 四路 4K
h264_rkmpp硬解已经成功; - 单路 4K 完整推理已经成功;
- 双路 4K 每路约 7 FPS 检测已经成功;
- 四路 4K 完整流水线已经完成功能闭环;
- 四路目标 7 FPS 资源超载;
- 四路目标 5 FPS 尚需在新调度器上重新公平验证;
- 四路目标 4 FPS 能保护视频输出,但检测框体验不被接受。
这不是“硬解成功后突然失败”,而是测试目标从“能不能解码”升级成了“整条 4K 业务链路能不能在资源预算内稳定工作”。前一个问题已经解决,后一个问题等待有经验的大佬解惑,

浙公网安备 33010602011771号