1. 项目缘起与整体设计思路
1.1 为什么盯上了 RK3528 这颗冷门 SoC
做直播推拉流方案的人都有一个共同的痛点:成本压不下来。市面上常见的方案要么是 x86 小主机配采集卡,一套下来大几百甚至上千;要么是高端 ARM 开发板,性能冗余但价格劝退。我最初的目标很明确——找一颗能硬编硬解、带 NPU、功耗低、价格便宜的 SoC,把整机 BOM 控制在极低水平,同时还要能同时跑六种协议、做 AI 推理、处理绿幕抠像。
翻了一圈芯片选型表之后,RK3528 进入了视野。这颗芯片定位是入门级多媒体处理,四核 Cortex-A53,主频 1.5GHz 左右,内置独立的视频编解码单元,支持 H.264/H.265 的硬件编解码,最关键的是它带了一个轻量级 NPU,算力虽然不高,但跑一些基础的图像推理任务完全够用。价格方面,芯片本身加上 1GB 内存的整套核心板方案,成本控制得相当夸张。
很多人会问,1GB 内存够吗?说实话,如果走纯软件路线,光是一个 FFmpeg 软编就能把内存吃干净。但这个项目的核心思路就是把所有能卸载到硬件单元的活儿全部卸载出去——编解码走 VPU,AI 推理走 NPU,CPU 只负责调度和数据搬运。这样一来,1GB 内存反而有了余量。
1.2 六协议并发的架构取舍
标题里说的“六协议”,指的是 RTMP、RTSP、SRT、HTTP-FLV、HLS、WebRTC 这六种常见的直播推拉流协议。要在同一块板子上同时支撑这些协议,架构设计上必须做减法。
我的做法是:底层统一用一套媒体管线,所有协议共享同一路解码后的帧数据,只是在输出封装层做分流。具体来说,输入流先经过协议解析模块,统一转成内部帧格式,然后送到 VPU 做硬解,解出来的 YUV 帧一路送去显示/预览,一路送去 AI 推理,一路送去绿幕处理,最后再根据配置分别封装成不同协议输出。
这样做的好处是避免了解码重复。如果你每个协议单独解一遍,1GB 内存根本扛不住。共享帧数据之后,内存占用主要取决于帧缓冲队列的长度,我实测下来把队列控制在 3 到 5 帧,内存峰值能压在 600MB 以内,留出足够余量给系统和 AI 模型。
注意:共享帧数据意味着你需要一套引用计数机制来管理帧的生命周期,否则容易出现帧被提前释放导致花屏的问题。这个坑我在早期版本踩过,后面单独讲。
1.3 硬编硬解与 AI 的协同逻辑
硬编硬解和 AI 推理看起来是两条独立的路径,但在实际管线里它们是有交集的。AI 推理需要的是解码后的原始帧,而硬解输出的帧直接在显存/内存里,不需要额外拷贝。关键是要让 NPU 和 VPU 的工作节奏匹配上。
我的方案是:VPU 解码输出帧之后,通过 DMA-BUF 的方式把帧的物理地址直接传给 NPU 做推理,避免一次内存拷贝。这个细节很关键,因为 1GB 内存的带宽有限,每一次多余的拷贝都会吃掉宝贵的带宽资源。实测下来,走 DMA-BUF 零拷贝路径,AI 推理的延迟能降低 30% 左右。
绿幕处理则是另一条路径。它需要对帧做逐像素的色度键控,这个操作如果纯用 CPU 做,1080p 30帧的负载就能把四核 A53 吃满。我的做法是把绿幕算法也卸载到 NPU 上,用一个小型的卷积网络做前景背景分割,效果比传统的色度键控更干净,尤其是处理半透明边缘和头发丝的时候。
2. 核心细节解析与实操要点
2.1 内存分配的精细化管理
1GB 内存听起来捉襟见肘,但如果你把每一块内存的用途都规划清楚,其实是够用的。我的分配策略是这样的:
| 用途 | 分配大小 | 说明 |
|---|---|---|
| 系统内核 + 根文件系统 | 约 200MB | 精简过的 Linux 系统,去掉不必要的服务 |
| VPU 帧缓冲 | 约 150MB | 按 1080p 三帧队列计算 |
| NPU 模型内存 | 约 100MB | 绿幕分割模型 + 其他推理模型 |
| 协议封装缓冲 | 约 80MB | 六协议共享的输出缓冲池 |
| 应用层 + 剩余 | 约 470MB | 留给动态分配和系统缓存 |
这个分配不是拍脑袋来的。VPU 帧缓冲的计算方式是:1920 × 1080 × 1.5(YUV420 每像素 1.5 字节)× 3 帧 ≈ 9.3MB,但实际驱动会预留更多对齐和元数据空间,所以按 150MB 预留比较稳妥。NPU 模型内存取决于你用的模型大小,我选的绿幕分割模型量化后大约 20MB,加上运行时开销按 100MB 预留。
实操心得:在系统启动参数里把 CMA(连续内存分配器)的大小设好,否则 VPU 和 NPU 在运行时申请连续内存容易失败。我一般设
cma=256M,这个值根据你的实际帧队列深度调整。
2.2 六协议的分流策略与优先级
六个协议同时跑,不可能每个都平均分配资源。我的做法是按优先级分层:
- 高优先级:RTMP 和 SRT。这两个是推流的主力协议,延迟要求高,分配最多的带宽和 CPU 时间片。
- 中优先级:RTSP 和 WebRTC。这两个主要用于拉流预览和互动,允许稍微高一点的延迟。
- 低优先级:HTTP-FLV 和 HLS。这两个是分发协议,对实时性要求不高,可以容忍更大的缓冲。
在代码层面,我用了一个简单的令牌桶算法来做带宽分配。每个协议有一个令牌桶,高优先级的桶容量大、补充速度快,低优先级的桶容量小、补充慢。当网络带宽紧张的时候,低优先级协议会自动降码率或者丢帧,保证高优先级协议的流畅度。
这个策略在实际测试中效果很明显。我模拟了一个上行带宽只有 4Mbps 的场景,六个协议同时推流,RTMP 和 SRT 基本没有卡顿,HTTP-FLV 和 HLS 出现了轻微的帧率下降,但整体观看体验可以接受。
2.3 硬编硬解的参数调优
RK3528 的 VPU 支持 H.264 和 H.265 的硬编硬解,但默认参数不一定适合直播场景。我调优的重点在三个地方:
第一是码率控制模式。默认的 CBR(固定码率)在画面静止时会浪费带宽,在画面剧烈运动时又不够用。我改成了 VBR(可变码率)配合最大码率限制,这样静态画面下码率能降到很低,运动画面下又能临时冲高。实测下来,同样的画质下,VBR 比 CBR 平均节省 20% 到 30% 的带宽。
第二是 GOP 长度。直播场景下 GOP 太长会导致首帧加载慢,太短又会影响压缩效率。我一般设 GOP 为帧率的两倍,比如 30fps 就设 60。这样首帧加载时间控制在 2 秒以内,压缩效率也还能接受。
第三是编码预设。RK3528 的 VPU 提供了几档编码预设,从速度优先到质量优先。直播场景我建议选中间档,纯速度优先画质损失明显,纯质量优先在 1GB 内存的板子上容易导致帧堆积。
# 典型的硬编参数配置示例 vpu_encoder_config: codec: h264 profile: high level: 4.1 bitrate_mode: vbr max_bitrate: 4000 # kbps target_bitrate: 2500 gop_size: 60 preset: balanced rc_mode: vbr2.4 AI 推理的模型选型与量化
NPU 的算力有限,模型选型必须克制。我试过几个开源的绿幕分割模型,最后选了一个轻量级的 U-Net 变体,参数量控制在 1M 以内。这个模型在 PC 上跑可能效果一般,但经过量化之后在 NPU 上跑,1080p 输入下能做到 15fps 左右的推理速度,配合帧队列缓冲,整体延迟可以接受。
量化是关键步骤。RK3528 的 NPU 支持 INT8 量化,我把 FP32 的模型转成 INT8 之后,推理速度提升了将近 3 倍,精度损失在可接受范围内。量化的具体流程是:先用校准数据集跑一遍,统计每一层的激活值分布,然后根据分布确定量化参数。校准数据集我用了大约 200 张包含各种背景和前景的图片,覆盖了常见的直播场景。
注意:量化后的模型一定要做精度验证,我遇到过量化后边缘出现明显锯齿的情况,后来调整了校准集的分布才解决。建议量化前后都跑一遍测试集,对比 IoU 指标,下降超过 5% 就要重新调整。
3. 实操过程与核心环节实现
3.1 系统环境的搭建与裁剪
拿到板子之后,第一步是烧录一个精简的 Linux 系统。我用的是一套基于 Buildroot 的定制系统,去掉了桌面环境、蓝牙、WiFi 驱动等不需要的组件,只保留网络、USB、VPU 和 NPU 的驱动。裁剪之后,系统启动时间从原来的 15 秒降到了 6 秒左右,内存占用也从 350MB 降到了 200MB 出头。
裁剪的原则是:只保留管线需要的驱动和库。具体来说,网络方面保留以太网和 USB 网卡驱动,视频方面保留 V4L2 和 VPU 驱动,AI 方面保留 NPU 驱动和运行时库。其他的一律去掉。文件系统用 SquashFS 压缩,进一步节省空间。
# 内核裁剪的关键配置项 CONFIG_VIDEO_ROCKCHIP_VPU=y CONFIG_ROCKCHIP_NPU=y CONFIG_DMABUF_HEAPS=y CONFIG_DMA_SHARED_BUFFER=y CONFIG_V4L2_MEM2MEM_DEV=y # 去掉不需要的 # CONFIG_BT=n # CONFIG_WIFI=n # CONFIG_SOUND=n3.2 媒体管线的搭建与调试
媒体管线是整个项目的核心。我用 GStreamer 作为框架,因为它对硬编硬解的支持比较成熟,而且插件生态丰富。管线的结构大致是这样的:
输入源(RTMP/RTSP/SRT)→ 解协议 → 解封装 → VPU 硬解 → 帧分发 → 分支一:显示预览;分支二:NPU 推理(绿幕/AI);分支三:VPU 硬编 → 协议封装 → 输出。
调试的时候最大的坑是帧同步。多个分支处理同一帧的速度不一样,如果不做同步,就会出现预览画面和推流画面不同步的情况。我的做法是在帧分发的地方加一个同步屏障,等所有分支都处理完当前帧之后再释放帧资源。这个屏障用信号量实现,超时时间设 100ms,超时就强制释放,避免死锁。
另一个坑是VPU 的并发访问。RK3528 只有一个 VPU 单元,解码和编码不能同时进行。我的解决方案是分时复用:解码和编码交替占用 VPU,通过一个互斥锁来协调。实测下来,1080p 30fps 的场景下,解码占 40% 的时间,编码占 50% 的时间,还有 10% 的余量,基本够用。
3.3 绿幕抠像的完整实现流程
绿幕抠像是这个项目里比较有亮点的部分。传统的色度键控方法在简单背景下效果还行,但遇到半透明物体、头发丝、运动模糊的时候就力不从心了。我用 NPU 做语义分割,把前景和背景分开,效果明显好很多。
具体流程是这样的:
- 采集帧:从 VPU 解码输出的帧队列里取一帧 YUV 数据。
- 预处理:把 YUV 转成 RGB,缩放到模型输入尺寸(我用的模型输入是 256×256),归一化。
- NPU 推理:把预处理后的数据送入 NPU,跑分割模型,输出一个前景概率图。
- 后处理:把概率图上采样回原始分辨率,做边缘羽化和平滑处理。
- 合成:用概率图作为 alpha 通道,把前景和新的背景合成。
整个流程在 NPU 上的耗时大约是 60ms 左右,加上前后处理,总延迟在 80ms 到 100ms 之间。对于直播场景来说,这个延迟是可以接受的。
实操心得:边缘羽化的时候,我试过用高斯模糊和导向滤波两种方法。高斯模糊速度快但边缘不够锐利,导向滤波效果好但计算量大。最后我选了一个折中方案:先用小半径高斯模糊做初步平滑,再用一个轻量级的边缘感知滤波器做细化。这样在 NPU 上额外增加的计算量很小,但边缘质量提升明显。
3.4 六协议输出的配置与测试
六个协议同时输出,配置上需要一些技巧。我的做法是给每个协议单独开一个输出线程,线程之间通过环形缓冲区共享编码后的数据。环形缓冲区的大小根据协议特性来定:RTMP 和 SRT 用小块(约 50 帧),HTTP-FLV 和 HLS 用大块(约 200 帧),RTSP 和 WebRTC 用中等块(约 100 帧)。
测试的时候我用了三台设备:一台推流端,一台拉流端,一台监控端。推流端模拟各种网络条件(丢包、延迟、抖动),拉流端用不同的播放器测试兼容性,监控端记录每个协议的延迟、卡顿率、码率波动等指标。
实测数据如下:
| 协议 | 平均延迟 | 卡顿率(弱网下) | 兼容性 |
|---|---|---|---|
| RTMP | 1.5s | 低 | 极好 |
| SRT | 0.8s | 极低 | 好 |
| RTSP | 1.2s | 低 | 好 |
| HTTP-FLV | 2.0s | 中 | 好 |
| HLS | 5.0s | 低 | 极好 |
| WebRTC | 0.5s | 中 | 一般 |
从数据可以看出,SRT 和 WebRTC 的延迟最低,但 WebRTC 的兼容性一般,部分播放器支持不够好。RTMP 和 HLS 的兼容性最好,但延迟偏高。实际部署的时候,可以根据场景选择性地开启协议,不需要六个全开。
4. 常见问题与排查技巧实录
4.1 内存不足导致的帧丢弃
这是最常见的问题。表现是画面偶尔卡顿,日志里出现“frame dropped”的警告。根本原因是帧队列堆积,内存不够用了。
排查思路:先看帧队列的长度,如果持续超过设定值,说明下游处理速度跟不上上游。这时候要定位是哪个环节慢——是 NPU 推理慢,还是编码慢,还是网络发送慢。我的经验是,大部分情况下是 NPU 推理慢,因为绿幕分割模型的计算量相对较大。
解决方法有三个:一是降低推理分辨率,比如从 256×256 降到 192×192;二是降低推理帧率,比如每两帧推理一次,中间帧用上一帧的结果;三是优化模型,去掉一些不必要的层。我一般先用第二个方法,因为改动最小,效果也最直接。
4.2 VPU 硬解花屏与绿屏
花屏和绿屏通常和帧的生命周期管理有关。如果帧被提前释放,VPU 还在读这块内存,就会输出花屏。如果帧的格式不对,比如 YUV 的排列方式不匹配,就会输出绿屏。
排查方法:在帧分发的地方加日志,记录每一帧的引用计数和状态。如果发现引用计数在 VPU 还没读完的时候就降到零了,那就是生命周期管理有问题。解决方法是加一个引用计数保护,确保 VPU 读完之前帧不会被释放。
绿屏的问题一般是格式不匹配。RK3528 的 VPU 输出可能是 NV12 格式,而你的显示或编码模块期望的是 I420 格式。这两种格式的 UV 排列方式不同,需要做转换。我一般是在管线里加一个格式转换插件,虽然多了一次拷贝,但能避免很多兼容性问题。
4.3 六协议并发时的网络拥塞
六个协议同时推流,网络带宽很容易成为瓶颈。表现是所有协议的延迟都升高,码率波动大,严重的时候会集体卡顿。
我的解决方案是引入一个自适应码率控制模块。这个模块实时监测网络状况(通过 RTT 和丢包率判断),当网络拥塞时,自动降低低优先级协议的码率,保证高优先级协议的流畅度。具体策略是:
- 网络良好(丢包率 < 1%):所有协议按设定码率运行。
- 网络一般(丢包率 1% - 5%):低优先级协议码率降低 30%。
- 网络较差(丢包率 > 5%):低优先级协议码率降低 60%,必要时暂停 HLS 输出。
这个策略在实际测试中效果很好,弱网下高优先级协议的卡顿率降低了 70% 以上。
4.4 NPU 推理结果不稳定的排查
NPU 推理结果不稳定,表现为绿幕抠像的边缘偶尔出现闪烁或者块状伪影。这个问题困扰了我很久,后来发现是输入数据归一化不一致导致的。
训练模型的时候用的是 0 到 1 的归一化,但推理的时候我忘了做同样的归一化,导致输入数据分布不匹配,推理结果自然不稳定。修正之后问题就消失了。
另一个可能的原因是NPU 的频率调节。RK3528 的 NPU 会根据负载动态调频,频率变化的时候推理结果可能会有微小差异。如果你的应用对稳定性要求极高,可以在系统里把 NPU 频率锁定在最高档,代价是功耗会稍微高一点。
常见问题速查表:
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 画面卡顿 | 帧队列堆积 | 查看队列长度 | 降低推理分辨率或帧率 |
| 花屏 | 帧提前释放 | 检查引用计数 | 加引用计数保护 |
| 绿屏 | 格式不匹配 | 检查像素格式 | 加格式转换插件 |
| 网络拥塞 | 带宽不足 | 监测 RTT 和丢包率 | 自适应码率控制 |
| 推理闪烁 | 归一化不一致 | 对比训练和推理预处理 | 统一归一化参数 |
| 启动失败 | CMA 不足 | 查看内核日志 | 增大 CMA 预留 |
4.5 长时间运行的稳定性优化
直播场景往往需要 7×24 小时运行,稳定性至关重要。我遇到过运行十几个小时之后内存泄漏的问题,表现是内存占用缓慢上升,最终 OOM。
排查内存泄漏用了 valgrind 和 mtrace 两个工具。最后定位到是协议封装模块里的一个缓冲区没有正确释放。修复之后,连续运行 72 小时内存占用稳定在 550MB 左右,没有明显增长。
除了内存泄漏,还要注意温度控制。RK3528 在满负载运行的时候发热不小,如果散热不好会触发降频,导致性能下降。我加了一个小风扇,配合散热片,核心温度控制在 60 度以内,基本不会降频。
另外建议加一个看门狗,定时检查各个模块的心跳。如果某个模块卡死,看门狗可以自动重启整个管线,保证服务的连续性。这个机制在实际部署中救过我好几次。
5. 性能边界与扩展方向
5.1 实测性能数据与瓶颈分析
经过几轮优化,最终的性能数据是这样的:1080p 30fps 输入,六协议同时输出,AI 绿幕处理开启,CPU 占用率约 45%,内存占用约 550MB,NPU 占用率约 70%,VPU 占用率约 90%。整体延迟控制在 2 秒以内(HLS 除外)。
瓶颈主要在 VPU 上。因为编解码共用同一个 VPU 单元,当输入分辨率提高到 1080p 60fps 的时候,VPU 占用率直接飙到 100%,开始丢帧。所以这个方案的性能边界就是 1080p 30fps,再高就不行了。
如果想支持更高的分辨率或帧率,有几个方向:一是换更强的 SoC,比如带独立编码器和解码器的型号;二是降低输出协议数量,比如只开 RTMP 和 SRT;三是降低 AI 推理的帧率,把 NPU 的负载让给 VPU。
5.2 低成本方案的适用场景与局限
这个方案最适合的场景是:中小规模的直播推流,对成本敏感,对延迟要求不是极端苛刻。比如电商直播、在线教育、活动直播等。这些场景通常不需要 4K,1080p 30fps 完全够用,而且对成本的敏感度很高。
局限也很明显:一是性能边界有限,不适合高分辨率高帧率的场景;二是 AI 能力有限,只能跑轻量级模型,复杂的 AI 处理(比如实时换脸、高级美颜)跑不动;三是内存紧张,不能同时跑太多额外的服务。
如果你的场景需要更强的 AI 能力或者更高的视频规格,建议考虑更高端的方案。但如果你的目标就是在极低成本下实现功能完整的直播推流,这个方案是经过实测验证的,可以直接参考。
5.3 后续可以尝试的优化方向
有几个方向我还在探索中。一是模型蒸馏,把更大的绿幕分割模型蒸馏成更小的模型,在保持精度的同时降低 NPU 负载。二是多板级联,用多块板子分担不同的协议输出,突破单板的性能边界。三是硬件加速的协议封装,把部分封装工作也卸载到硬件单元上,进一步降低 CPU 占用。
另外,我还在尝试把 WebRTC 的延迟进一步降低。目前 WebRTC 的延迟在 0.5 秒左右,主要花在抖动缓冲上。如果能优化抖动缓冲的策略,理论上可以降到 0.2 秒以内。这个对于互动直播场景会很有价值。
我个人在实际操作中的体会是,低成本方案的核心不在于堆硬件,而在于把每一份资源都用在刀刃上。RK3528 这颗芯片本身并不强,但通过合理的架构设计和精细的资源管理,它能做到的事情远超你的预期。关键是要理解每个硬件单元的能力边界,然后让它们协同工作,而不是互相抢资源。这个思路不仅适用于直播推流,也适用于其他资源受限的嵌入式场景。