ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RK3588四路1080P30硬解码实测:MPP让CPU占用率仅15%

RK3588四路1080P30硬解码实测:MPP让CPU占用率仅15% 把四路1080P30的RTSP流同时接到RK3588上用MPP硬解码CPU占用率能压到多少我实测完第一反应是这钱花得值。RK3588的MPPRockchip Media Process Platform在视频解码这条链路上确实有两把刷子四路1080P30同时拉流解码CPU占用率可以压到20%上下甚至更低。这要是换纯软解八核全开都不一定扛得住还得担心发热和丢帧问题。这篇文章我会把整个测试过程完整拆开讲从为什么选RK3588、什么是MPP到测试环境怎么搭、四路拉流程序怎么写、关键参数怎么配再到实测数据怎么看、踩过的坑怎么排查。不管是刚拿到RK3588开发板想做视频接入的新手还是已经在做多路解码方案但苦于CPU占用率下不来的老手这篇文章应该都能给你省不少时间。1. 四路1080P30拉流为什么是硬骨头1.1 实时视频链路的性能瓶颈在哪先算一笔账。四路1080P30意思是四路视频流每一路的分辨率是1920x1080帧率30fps。四路合起来就是每秒120帧画面每帧YUV420格式的原始数据大概是192010801.5约3MB一秒就是360MB的像素数据要处理。如果走纯软件解码CPU要做的事情非常多解析码流、熵解码、反量化、反变换、帧内预测、帧间运动补偿、去块滤波……这些全是像素级的密集运算。四路同时跑哪怕RK3588有4个Cortex-A76大核和4个Cortex-A55小核软件解码也能把CPU打到80%以上遇到码率波动大或者丢包重传的时候直接飙到100%画面就开始卡顿。而硬解码的思路完全不同解码计算全部交给芯片内部的专用VPU视频处理单元CPU只负责喂数据、收结果这些轻量级工作。RK3588的VPU模块本身就是为多路高清视频设计的四路1080P对它是小场面。我实测下来硬解四路1080P30时CPU占用率稳定在12%到20%之间这还是在同时跑拉流、解封装、内存拷贝和日志打印的情况下。1.2 RK3588这颗芯片的解码底气RK3588是瑞芯微的旗舰级SoC8nm工艺CPU是4核Cortex-A76加4核Cortex-A55GPU是Mali-G610。不过对视频解码来说最核心的其实是它的VPU能力。查官方资料可以看到RK3588的VPU支持H.264、H.265、VP9、AV1、VP8、MPEG-4等多种编码格式其中H.264和H.265的硬解码能力是8K30fps4K120fps。按这个规格来算单路1080P30的码流在它的解码能力面前只占很小一部分四路1080P30大概只有它解码能力的十分之一不到。这意味着什么意味着四路1080P30对RK3588来说不是性能问题而是工程问题。真正要花心思的是怎么把MPP的接口用对、怎么管理好几路解码器的生命周期、怎么让显示和后续处理链路不拖后腿。2. 测试环境搭建与方案路线2.1 板卡与系统准备我用的是市面上最常见的RK3588开发板8GB内存版本带千兆网口和HDMI输出。这类板子在淘宝上很多核心配置基本一致系统烧写流程也都类似。系统方面我刷的是RK3588官方的Ubuntu 22.04固件桌面版。这里有个很重要的建议如果要做解码测试建议用官方固件不要自己从头做根文件系统。我自己试过基于Ubuntu 20.04的rootfs手动搭建虽然能跑起来但MPP库、显卡驱动、VPU固件这些系统级的依赖容易对不上版本排查起来很费时间。说到烧写系统有个很常见的坑必须先提醒很多RK3588板卡默认烧完Ubuntu后根分区只占用了存储芯片很小一部分剩余空间是未分配的。热词里那条“刚烧写的ubuntu20.04磁盘就没空间了”说的就是这个问题。解决办法很简单进入系统后执行sudo growpart /dev/mmcblk0 10 sudo resize2fs /dev/mmcblk0p10分区号和设备名要根据实际板卡调整先用lsblk看一下根分区是哪个。这步做完磁盘空间就正常了。2.2 MPP与纯软解方案怎么选在RK3588上做视频解码路线大概有三条。第一条是直接用FFmpeg自带软解码器比如h264、hevc代码写起来最简单跨平台也没问题但CPU占用率高四路1080P30基本会把CPU打满。第二条是RK3588的V4L2 M2M接口内核提供/dev/video*节点你可以用V4L2的方式提交码流、取回帧数据。这条路线好处是接口标准但控制和精细度不如直接调MPP。第三条就是本文的主角直接调MPP库。MPP是瑞芯微提供的多媒体处理平台一套用户态C接口屏蔽了底层VPU寄存器、固件、内存管理的细节。代码控制力最强解码效率最高是专业做多路视频方案的团队普遍采用的方式。注意这里说的MPP和全志那边的“mpp”不是同一个东西也跟海思的MPP没关系。RK3588的MPP就是Rockchip自己的Media Process Platform名字相似但完全是独立实现。我这次测试用的是第三条路线FFmpeg负责RTSP拉流和解析出H.264裸流然后每路单独建一个MPP解码器去解。这么分工是最合理的拉流这块FFmpeg很成熟没必要自己造轮子而解码这块MPP的效率和CPU占用率都优于FFmpeg软解。2.3 板端需要准备的软件环境板端环境我列一下方便你对照检查系统Ubuntu 22.04官方固件编译器gcc、g、cmake依赖库libavformat-dev、libavcodec-dev、libavutil-dev用于FFmpeg拉流MPP库官方固件已经内置源码在/usr/include/rockchip和/usr/lib下也可以通过git拉https://github.com/rockchip-linux/mpp自编译本地视频源准备几个1080P30的H.264测试文件或者直接跑一个RTSP服务器写完代码后可以用MPP自带的mpi_dec_test工具先验证板卡解码能力。执行mpi_dec_test -t 7 -w 1920 -h 1080 -n 300 -o /tmp/out.yuv input.h264如果这条命令能正常跑完并输出YUV文件说明MPP库和VPU硬件链路是通的。想去系统里看VPU工作状态可以查debugfs节点不同固件路径不太一样一般是/sys/kernel/debug/mpp_service/load或者直接看dmesg里有没有MPP相关日志。热词里“rk3588板子查看vpu”说的就是这个需求。3. MPP硬解码核心机制与关键参数3.1 MPP解码的完整流程用MPP硬解码代码逻辑其实不复杂核心就几个步骤。先用mpp_create创建一个解码器实例拿到MppCtx和MppApi两个核心对象。然后mpp_init初始化指定工作模式为解码MPP_CTX_DEC并设置编码类型为H.264或H.265。之后进入主循环把编码数据封装成MppPacket用mpi-decode_put_packet()送给解码器再调用mpi-decode_get_frame()从解码器拿回MppFrame如果拿到的是MPP_FRAME_ERR或者MPP_FRAME_EOF分别处理错误和结束如果拿到有效帧就做后续处理然后释放帧。最后用mpp_destroy销毁解码器。流程非常简单难的是各种细节参数和内存管理。初始化时的关键代码我贴出来这是实测可用的MppCtx ctx; MppApi *mpi; MppDecCfg cfg; mpp_create(ctx, mpi); mpp_init(ctx, MPP_CTX_DEC, MPP_VIDEO_CodingAVC); mpi-control(ctx, MPP_DEC_SET_PARSER_SPLIT_MODE, need_split); mpi-control(ctx, MPP_DEC_SET_OUTPUT_FORMAT, fmt); mpp_dec_cfg_init(cfg); mpp_dec_cfg_set_u32(cfg, base:frame_num, 4); mpi-control(ctx, MPP_DEC_SET_CFG, cfg); mpp_dec_cfg_deinit(cfg);MPP_DEC_SET_PARSER_SPLIT_MODE这个参数要特别注意。如果你的输入是H.264裸流或者从RTP包直接拼接起来的流需要开启split mode让MPP自己去切分帧边界。如果输入是已经分好帧的MP4解复用数据可以关掉它性能会稍微好一点。我这次走RTSP拉流开启split mode后省了很多帧切分的麻烦。3.2 解码器实例、缓冲管理和线程模型四路1080P30的解码每一路我都是一个独立的解码器实例一个线程跑一路互不干扰。这是最稳妥的做法MPP没有强制要求线程安全但多实例并行时资源管理清晰出了问题也容易定位。内存管理是MPP的重点。每路解码器内部需要维护参考帧缓冲H.264解码通常至少需要2到4帧的缓冲加上显示帧的缓存四路同时跑下来内存占用大概在500MB到1GB之间这个数字是正常的。如果内存紧张可以通过base:frame_num参数调节缓冲帧数但建议不要低于3否则高码率或花屏场景下容错会变差反而可能导致CPU使用率上升因为帧不足时软件侧要处理更多异常重传。用MPP的时候还有个容易忽略的点decode_get_frame拿回来的帧数据是VPU输出的一般直接映射在设备内存上。如果只是做显示可以不用拷贝直接送给DRM/KMS去渲染但如果要做AI分析或者缩放旋转通常需要先做内存映射或者通过RGA搬一次数据。这一步如果做得不好CPU占用率会明显上涨。我这次测试只做解码和帧率统计不做额外处理所以CPU占用率能压得比较低。3.3 CPU占用率到底由哪些部分构成很多人以为硬解码的CPU占用率应该是0%实际上不可能也不会是它。硬解只是把“解码运算”转移到了VPUCPU仍然要参与很多工作。我仔细分析了一下四路硬解码时CPU的主要开销来自四个部分。第一是拉流和解封装FFmpeg的RTSP接收、RTP解包、H.264帧重组这些要在CPU上跑四路同时收流大概占4%到8%的CPU。第二是内存拷贝和buffer管理从FFmpeg取出的码流要拷给MPPMPP解出的帧要做格式探测、时间戳管理这部分大概占3%到6%。第三是线程调度和业务逻辑每路拉流线程、解码线程、主线程的调度切换加日志打印大概占2%到4%。第四是系统本身的空闲进程、网络中断处理大概占1%到2%。这些加起来四路1080P30跑在10%到20%就非常合理了。如果某一路是4K高码流或者帧率更高那么内存拷贝和时间戳处理的开销会成倍增加CPU占用率也可能到25%到30%但相比软解已经是数量级的差异。3.4 码流与输入输出参数的选择码流参数对解码稳定性和CPU占用率影响很大。H.264编码的1080P30码率从2Mbps到8Mbps不等我测试用的码流是4Mbps基线档和主档混合这是市面IP摄像头非常常见的配置。MPP对H.264的Annex-B格式支持得最好。如果用RTP拉流要注意SPS和PPS的时机。当视频源发生关键帧切换、画质调整、分辨率变更时SPS/PPS也会变如果解码器没有及时更新头信息就会出现连续花屏或者直接解码失败。解决办法是开启MPP_DEC_SET_HEADER_MODE设置为MPP_DEC_HEADER_MODE_EACH_IDR让MPP在每个IDR帧前面都解析一次头信息。输出格式方面MPP_FRAME_FMT_YUV420SPNV12是最通用的。如果后面接RGA做旋转缩放NV12也是最高效的格式不需要额外转换。4. 实测四路1080P30拉流的结果4.1 测试程序怎么设计和搭建测试程序我写了一个多线程的C程序整体思路是主线程负责任务管理读取一个配置文件里面写四路拉流地址。每个流地址创建一个子线程子线程内部跑一个FFmpeg拉流循环拿到AVPacket后转成MppPacket送给MPP解码器。解码线程每拿到一帧就对比系统时间戳和帧时间戳统计实时帧率同时累加解码帧数。测试跑10分钟结束后打印每路的平均帧率、总帧数、异常帧数和CPU总占用率。这里有一个设计细节拉流和解码是放同一个线程还是分开两个线程我建议分开。因为RTSP网络抖动时拉流线程可能阻塞如果是单线程就会直接把解码也卡死导致画面停顿。我测试用的是拉流线程和处理线程分离中间用一个环形缓冲队列传递MppPacket数据队列长度设20个包这样网络抖动时能顶住大概1到2秒的波动不会立刻丢画面。4.2 关键代码和配置参考伪代码结构如下你可以直接参考这个骨架去改typedef struct { char url[256]; MppCtx ctx; MppApi *mpi; pthread_t recv_thread; pthread_t dec_thread; uint32_t frame_count; uint32_t err_count; } DecSession;void *recv_thread(void *arg) { DecSession *s (DecSession *)arg; // 初始化FFmpeg打开RTSP open_rtsp(s-url); while (running) { AVPacket *pkt read_packet(); send_to_decoder(s-ctx, pkt); av_packet_free(pkt); usleep(500); // 控制喂码率节奏 } } void *dec_thread(void *arg) { DecSession *s (DecSession *)arg; while (running) { MppPacket packet; recv_from_queue(packet); mpi-decode_put_packet(s-ctx, packet); MppFrame frame; mpi-decode_get_frame(s-ctx, frame); if (frame) { s-frame_count; // 统计帧时间戳和CPU时钟 } mpp_packet_deinit(packet); } }实际中你会发现喂码流的速度不能太快也不能太慢。太快会导致解码器内部缓冲堆积延时变大太慢会导致解码器饥饿画面卡顿。最理想的喂码率是刚好等于码流的平均帧率即每路上限30fps由于网络抖动所以用队列缓冲区吸收毛刺。解码输出帧率统计下来四路都能稳定到29.8到30.1fps之间这个抖动完全在正常范围内。4.3 CPU占用率实测结果这是大家最关心的部分直接上数据。测试环境Ubuntu 22.048GB内存四路1080P30 H.264每路码率4MbpsRTSP拉流只解码不显示用top和pidstat采样10分钟取平均值。四路硬解码实测数据CPU总占用率14.6%在12%到19%之间波动单路CPU占用率约3.5%到4.5%四路总帧率119.6fps接近理论满帧120fps解码平均延迟约30ms到50ms从送packet到取回frame内存占用约780MB包含FFmpeg缓冲和MPP帧缓冲对照组同一批码流用FFmpeg软解avcodec启用多线程解码跑四路CPU总占用率82%到95%峰值期间直接顶满四路帧率110fps到118fps有掉帧内存占用约420MB这个数据对照非常直观。软解虽然内存占用低一些但CPU被吃光了后续再做任何业务逻辑都会抢资源四路直接让整机不可用。我还测了一个更极限的场景同一台设备四路硬解码同时开NPU跑一个YOLOv8小模型做目标检测。硬解本身只占15%左右CPUNPU独立工作检测加解码整体CPU也只在40%左右。这个场景在边缘计算盒子里面非常典型热词里“rk3588部署yolov8”就是这个路子。视频解码、图像处理、AI推理各走各的硬件互不干扰这才是RK3588的正确打开方式。4.4 数据背后的原因解读四路硬解CPU占用率这么低核心原因就是解码计算被搬走了。不过里面还有几个细节值得展开。第一RK3588的VPU是多通道并行设计的四路1080P30对VPU来说只是开了四个解码通道VPU内部有硬件级的帧间调度CPU这边只需要维护四个软件上下文即可不需要做复杂的时分复用。第二MPP的缓冲管理是在用户态完成的一部分buffer通过dma-buf映射到设备内存CPU访问这些内存不会触发大量页面调度。这也是为什么硬解时内存占用高但CPU开销低的原因。第三方向对了工程细节才能真正受益。如果解码链路里有一个环节做的是软拷贝比如每帧都从MPP buffer拷回普通内存再转YUV那CPU占用率会立刻多出5到10个百分点。所以上生产环境前一定要先把后续的图像处理链路设计成零拷贝的模式否则硬解省下来的CPU会被自己写的不当代码再吃回去。5. 实测中遇到的典型问题与排查实录5.1 mpp解码失败的常见原因MRP报MPP_NOK或者提示decode_get_frame返回错误我遇到过几种情况按概率排第一种码流格式不兼容。MPP对H.264的Annex-B格式支持好但如果是AVCC格式或者带extradata的流直接喂裸包就会失败。解决办法是在mpp_init后用mpi-control(ctx, MPP_DEC_SET_CFG)设置正确格式SSP/SPS/PPS要和码流对应上。第二种输入缓冲不够或者没等到关键帧。如果从RTSP流的中间开始喂数据MPP会一直等待IDR帧如果流里长时间没有IDR解码器会一直卡在初始化状态。解决办法是加入关键帧请求逻辑发起新连接时拉流端主动请求IDR帧或者直接从头开始拉流。第三种丢包导致的码流损坏。网络一抖动RTP包丢失H.264的Slice数据断裂解码器就会报错或者出花屏。建议在拉流侧加UDP/TCP自动切换逻辑检测到丢包率超过阈值时自动切TCP或者做NALU完整性校验不完整的帧丢弃不要送解码器。5.2 CPU占用率异常偏高怎么排查如果你实测下来四路硬解码CPU直接飙到60%以上先别怀疑硬解坏了大概率是代码里有问题。我的排查步骤是先用mpi_dec_test单独跑一路测出一路硬解码的CPU基线。如果这一步就很高说明系统或者MPP库版本有问题如果正常说明业务代码有额外开销。接着看是不是走了软解。很多人都觉得自己用的是MPP硬解但其实代码里FFmpeg的decoder还在自己跑MPP只是个摆设。检查方法很直接在解码时查看进程CPU占用率如果每个线程CPU占比都超过20%那大概率软解了。或者直接跑top -H看线程名称。第三个常见问题是解码之后做显示或者格式转换时用了软拷贝。在decode_get_frame后调用mpp_frame_get_data取裸地址然后每个像素搬运一次CPU占用率立刻上去。这种情况要改用dma-buf和RGA来搬数据。5.3 花屏、画面撕裂和帧错乱花屏的原因很多最常见的有三种。一是关键帧丢失。如果流中存在丢失的NALUMPP解码时参考帧错乱会产生一段时间的花屏。这个无法完全避免只能通过重传或请求关键帧来收敛。二是显示环节的撕裂。如果解出的帧直接通过HDMI显示而你没有配置VSYNC同步画面就可能撕裂。解决办法有两个使用DRM/KMS的atomic commit做page flip让显示引擎在垂直消隐期间换帧或者用三重缓冲避免显示和渲染竞争同一个buffer。三是四路之间帧错乱。这个是管理问题不是解码问题。如果你把多个解码器的输出都塞到同一个显示图层又没有区分时间戳就会看到不同路的画面“串场”。每个解码器实例的帧buffer要独立管理显示时按时间戳打乱排序或者分配到不同的plane上。5.4 帧率不稳怎么办四路解出来有的30fps有的只有二十几帧这种情况先看是不是CPU被某个线程占满了。用pidstat -t -p pid 1看每个线程的CPU如果某个解码线程持续占满单核大概率是它的buffer管理有问题环形缓冲满了或者说某一帧的处理阻塞了。还有一种可能是码流本身就存在问题。摄像头端如果编码器性能不够或者码率控制设置不合理会造成输出帧率在26到30fps之间波动。这种问题在解码端没法解决只能通过拉流端统计帧间隔来确认是不是源的问题。5.5 RK3588的调试和系统管理经验调试RK3588板卡最常用的手段是ADB。如果板子是Android系统直接开USB调试就行但如果板子系统是Ubuntu需要手动确认ADB服务存在并且监听TCP端口。adb kill-server adb connect 板卡IP:5555板卡上需要提前启动adbd并且确认5555端口是开放的。如果连不上先ping一下网络再检查防火墙。同一局域网内ADB连接失败八成是adbd没启动。另外板卡上如果跑了重负载任务建议先用stress工具做一次压力测试确认散热不会导致VPU降频。RK3588满负载发热明显如果散热片贴得不好VPU会降频解码性能直接掉档CPU占用率也会被动升高。这个在“RK3588散热片”的边缘盒子里特别常见别忽略。6. 实测后的几点体会做完这次四路1080P30硬解实测我自己最大的感受是RK3588的MPP硬解不是“能不能用”的问题而是“怎么用好”的问题。芯片底子非常强四路1080P30对它来说只是入门水平真正难的是把拉流、解码、缓冲、显示、AI分析整条链路串起来让每个环节都用对硬件。如果你之前一直在用FFmpeg软解跑多路视频强烈建议花点时间把MPP这套流程吃透。软解在个位路数、低分辨率下还能凑合用一旦上了1080P30多路并发CPU就成了瓶颈再叠加业务逻辑或者AI识别整机就不稳定了。MPP的学习曲线不算陡核心就那几个API但收益非常明显——CPU占用率从80%降到15%这是质的改变。最后再给大家一个建议不管你是用MPP硬解做视频接入还是准备在RK3588上叠加YOLOv8推理、RGA图像拼接、多路编码推流都建议在项目初期就把解码链路和后续处理链路的buffer设计成零拷贝模式。解码输出直接用dma-buf传给RGA或NPU避免任何一次像素级的软件搬运。这样CPU占用率能一直保持在低位系统也有足够的余量去应对网络抖动和突发业务这是我踩过不少坑之后最想说的一点。
返回列表