ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

RK3588多路硬解码与RGA 2D加速零拷贝实践

RK3588多路硬解码与RGA 2D加速零拷贝实践 做嵌入式Linux视频处理这几年手上的项目几乎绕不开“采集-解码-处理-显示/编码”这条链路。大多数时候我们都是在ARM SoC上跟CPU性能较劲尤其到了多路视频解码加实时处理这种场景CPU软解根本扛不住硬解成了唯一出路。瑞芯微的RK3588在这类需求里算是个热门平台自带强大的VPU视频编解码单元和RGA2D图形加速单元再加上MPPMedia Process Platform这套统一媒体处理框架理论上可以做到视频解码、格式转换、缩放、叠加全程不经过CPU拷贝。这篇就从头到尾聊一遍我在RK3588上做多路硬解码与2D加速的完整实践重点放在MPPRGA的零拷贝视频处理链路上。我最初接到这个需求的时候目标很明确8路1080p视频流同时硬解码解完之后每一路还要实时做YUV到RGB转换、缩放、OSD叠加最终送给AI推理模块。如果按照传统思路先软解再逐帧用CPU转格式RK3588那颗八核CPU基本就被榨干了AI推理就没资源跑。所以硬解码和2D加速是唯一合理的路线。这篇内容适合正在做RK3588、RK3568这一类瑞芯微平台音视频开发的工程师也适合准备把视频处理任务从CPU卸载到专用硬件上的人。我会把MPP硬解码的调用流程、RGA做2D加速时的格式约束和踩坑记录、以及零拷贝到底“零”在哪一环、怎么做到真零拷贝全部拆开来讲附上可直接参考的代码思路和排查方法。1. 项目背景与整体方案选型思考1.1 为什么选择MPPRGA这套组合瑞芯微平台上的MPP并不是一个新的软件库它从RK3288时代就存在一路迭代到RK3588已经很成熟了。MPP把VPU的能力封装成一套统一API底层自动分配硬件资源上层只需要关心码流输入和帧输出。而RGA是独立的2D硬件加速器负责对解码后的视频帧做各种内存拷贝、格式转换、缩放、旋转、镜像、混合叠加操作完全不占用CPU算力。这两者放在一起用最核心的原因只有一个让视频数据在解码器和RGA之间流动时全程不落入CPU可见的内存区域做数据搬运。解码器直接输出到一块DRM/ION物理连续内存RGA直接以FD文件描述符方式读取这块物理内存做处理处理完再输出给下一级CPU始终只操作元数据指针、时间戳、尺寸真正的像素数据一次都没被CPU碰过这就是“零拷贝”项目里最实在的价值。如果不用这套组合一般做法是VPU解码到一块内存然后mmap到用户态CPU拷贝一份做格式转换再拷贝给推理输入。这条路在单路低分辨率场景下勉强能接受但到了8路1080p就会立刻暴露问题——内存带宽占用高、CPU占用高、延迟抖动明显。RK3588的CPU确实不弱但也没富裕到能扛8路视频处理的份上所以方案敲定为“解码走MPP、格式转换和缩放走RGA、内存管理走DMA-BUF/ION”。1.2 整条链路的基本流程与模块划分按我在项目里的划分整条视频处理链路可以拆成五个模块拉流/解封装模块、MPP硬解码模块、RGA处理模块、Buffer池管理模块、输出/推理对接模块。拉流模块负责RTSP/GB28181/本地文件取流输出H.264/H.265裸流数据这一层CPU消耗基本可以忽略。硬解码模块负责把裸流送进MPP解出原始视频帧通常是NV12格式解码器工作完全在VPU里CPU只做控制。RGA处理模块拿到解码帧的DMA-BUF FD做格式转换和缩放最后扔给输出模块——可能是DRM/KMS直接显示也可能是AI推理输入还可能是硬编码器输入。整个链路里最关键的是Buffer池它是零拷贝能否成立的基础如果这块设计不好前面解码再快也会被内存拷贝卡住。我实际采用的Buffer管理思路是启动时就向系统申请一个大的ION/DMA-BUF内存池将内存块按帧大小对齐需求划分好解码器输出、RGA输入输出、显示/推理输入统一从这个池子里取内存用引用计数管理生命周期。这样从解码到处理到消费全程复用的都是同一批物理内存最多只是引用计数在加加减减。2. 硬件平台与系统环境准备2.1 RK3588平台关键外设和能力RK3588本身的数据手册大家都查得到我只说和视频处理强相关的部分。它的VPU支持非常广泛包括H.264/H.265/VP9/AV1的硬解码其中H.265和VP9支持到8K60fps级别H.264支持到4K120fps级别。这个解码能力意味着8路1080p对它来说远没到极限我实测8路1080p解码时VPU占用大概在30%上下大量余量留给RGA和AI。RGA方面RK3588有一颗RGA3和一颗RGA2RGA3是新一代核心性能和功能更强支持最大8K输入RGA2则负责一些基础任务。两套核心同时可用意味着理论上可以把“格式转换”和“合成叠加”分流到不同核心上做并行处理后续优化时可以考虑这一点。内存方面RK3588支持最多32GB LPDDR4/4X/5。需要提醒的是解码器虽然可以正常申请内存但要想性能稳定建议在内核启动参数里预留足够的CMA空间或者直接用DMA-BUF Heap来管理编解码内存。CMA预留太小会导致VPU在高分辨率或高帧率场景下内存申请失败这个坑我踩过后面细说。2.2 系统与依赖库版本选择我使用的是Rockchip官方提供的Linux SDK内核版本5.10系统是Debian11的rootfs。如果你拿到的是Android版本思路也差不多只是运行时服务和权限管理有差异。MPPlibrk_mpp建议直接用SDK里带的版本或者从Rockchip的GitHub仓库拉最新版自己编译。重点在于MPP版本要和你内核的VPU驱动版本匹配否则可能出现解码能力协商不一致的情况。RGA库librga同样用SDK自带的或者从rockchip-linux/rga拉代码编译编译时注意选择USE_RGA3相关开关。环境层面还要确认以下几点/dev/dri/renderD128节点是否存在用于DRM提交显示/dev/rga节点是否存在RGA设备节点kernel config里是否打开了CONFIG_DMABUF_HEAPS、CONFIG_ROCKCHIP_ION等相关选项。调试时也可以挂载debugfs查看RGA和VPU的工作状态比如/sys/kernel/debug/rkrga/load能看到RGA实时负载/sys/kernel/debug/mpp_service下有编解码器工作状态。3. MPP硬解码链路完整实操3.1 MPP解码器的基本结构与初始化使用MPP的核心思路是创建一个解码上下文绑定解码类型配置解码参数然后循环往解码器里塞压缩数据包Packet、从解码器里取解码帧Frame。MPP的常见解码类型包括H.264、H.265、VP9、AV1等我们在项目里用的主要是H.264和H.265拉流。初始化阶段需要特别注意两点首先创建context和初始化必须要绑定到一个解码线程上MPP内部虽然可以多线程调用但同一个context不建议跨线程同时操作多路解码应该用多个context每个context配一个独立线程其次MPP解码器对输入码流缓冲区有对齐要求码流数据一般在送入前要做mpp_packet_initBuffer大小要用MPP_ALIGN做对齐不要无脑malloc一个原始长度就扔进来。一个最简单的初始化流程是这样的MppCtx ctx; MppApi *mpi; mpp_create(ctx, mpi); MPP_RET ret mpp_init(ctx, MPP_CTX_DEC, MPP_VIDEO_CodingAVC); if (ret ! MPP_OK) { printf(mpp_init failed\n); return ret; } MppDecCfg cfg; mpp_dec_cfg_init(cfg); // 输出格式首选NV12零拷贝链路的默认选择 mpp_dec_cfg_set_u32(cfg, type, MPP_VIDEO_CodingAVC); mpp_dec_cfg_set_u32(cfg, width, 1920); mpp_dec_cfg_set_u32(cfg, height, 1080); mpp_dec_cfg_set_u32(cfg, format, MPP_FMT_NV12); mpi-control(ctx, MPP_DEC_SET_CFG, cfg);实际使用中MPP是支持动态解析分辨率变化的所以如果码流是可变分辨率可以不写死宽高让MPP自己通过SPS/PPS解析。但解码器输出格式建议固定在NV12或者NV12_10LE毕竟NV12是RGA和显示最友好的格式之一。3.2 解码主循环与帧输出管理解码主循环的逻辑其实不难难在控制好输入和输出的节奏。MPP的这种控制流模型可以理解为“生产者-消费者”模型——主循环不断把裸流数据包塞进去然后立刻检查有没有解好的帧取出来。但要注意MPP内部有解码延迟表现为解码器在拿到几个packet之后才输出第一帧这是正常现象不是卡住。解码主流程代码结构大致如下MppPacket packet; MppFrame frame; // 读取一包码流数据(从RTSP/文件等) size_t len read_stream(data_buf, data_size, pts); mpp_packet_init(packet, data_buf, len); mpp_packet_set_pts(packet, pts); mpp_packet_set_eos(packet, 0); // 送包 ret mpi-decode_put_packet(ctx, packet); if (ret MPP_OK) { // 取帧 while (mpi-decode_get_frame(ctx, frame) MPP_OK) { if (frame) { process_frame(frame); // RGA处理 mpp_frame_deinit(frame); } } }这里有一个很容易犯的错误码流包从文件或者网络上读出来后所占用的内存空间在整个decode_put_packet调用周期内必须保持有效不能提前释放。MPP内部并不拷贝你的码流数据它只是把内存地址记录下来交给VPU异步读取。你如果立刻释放了这块内存轻则花屏、解码报错重则直接崩溃。正确做法是维护一个码流Buffer回收队列等MPP确认这一包处理完之后再复用这块内存。可以通过mpp_packet_get_eos和回调机制或者简单地在足够长时间后再回收来实现。第二个容易踩的坑是EOS处理。当输入码流结束时一定要先发送一个带EOS标志的空包然后循环调decode_get_frame直到拿到带EOS标志的最后一个帧才能真正关闭解码器。否则VPU内部还有几帧缓存的画面没有刷出来直接断开会导致最后几帧丢失或者状态错乱。3.3 多路解码的线程模型与Buffer组管理多路解码时我一开始图省事想在一个线程里循环处理所有路结果发现一路码流出现网络抖动堵住了后面所有路都跟着遭殃。这里还是老老实实采用“一路一线程”的模型每路解码线程对应一个MPP context线程只循环处理当前路的packet和frame线程间通过队列传递解码输出的帧元数据。但这里立刻会碰到内存分配问题8路解码同时跑如果每帧都现申请现释放内存碎片和申请耗时都会成为瓶颈。所以我用MPP的MppBufferGroup来做统一管理。所有解码输出buffer都由这个buffer group分配解码器会直接从group里复用空闲buffer不会反复向内核申请内存。MppBufferGroup group; mpp_buffer_group_get_internal(group, MPP_BUFFER_TYPE_ION); mpp_buffer_group_limit_config(group, 1920 * 1080 * 3 / 2 * 16, 0); // 将group设置到解码器 mpi-control(ctx, MPP_DEC_SET_EXT_BUF_GROUP, group);上面这段代码里limit_config的第二个参数是组内buffer总量限制我按16帧解码缓冲来计算。对于1080p NV12来说一帧约3MB16帧就是48MB左右8路就是接近400MB内存预留。这些内存在启动时一次性申请好之后跑起来基本不再动态分配。MPP_DEC_SET_EXT_BUF_GROUP是这套管理方式的开关一旦设置成功解码器输出的frame就会从这块group里取内存配合后续的RGA处理可以做到“解码输出的FD直接给RGA全程不需要拷贝”。4. RGA 2D加速处理实操4.1 RGA的能力边界和API选择RGA最擅长的操作包括图片缩放、格式转换、旋转、镜像、裁剪、Alpha叠加、色彩空间转换等。做视频处理时用到最多的是“NV12转RGBBGRA/RGBA并缩放”这个组合操作RGA一次调用就能完成不需要两步走。Rockchip官方推荐使用im2d这套API它统一封装了RGA2/RGA3的差异函数接口非常简洁。实际代码里主要用到这几个函数imresize缩放、imcvtcolor格式转换、imrotate/imflip旋转镜像、improcess一站式处理可以同时做缩放、格式转换、裁剪等、imcheck参数检查。如果你需要极致控制也可以直接操作底层rga_ops接口但日常开发用im2d就够了。使用im2d之前必须明确一点RGA输入输出的内存必须是物理连续内存。所以上一章强调的ION/DMA-BUF内存池在RGA环节仍然是核心。你可以直接把MPP解出来的MppBuffer通过mpp_buffer_get_fd拿到对应的FD再传给RGA根本不经过CPU视角的虚拟地址。4.2 NV12转RGB并缩放的完整调用示例下面这段代码演示了最核心的场景把一帧1920x1080的NV12数据转成640x640的RGB888同时完成缩放供AI推理输入使用。我用的是im2d的improcess接口一步到位。#include im2d.h #include RgaApi.h // 输入buffer来自MPP解码输出 int in_fd mpp_buffer_get_fd(mpp_frame_buffer); // 输出buffer来自我们自己申请的RGA buffer pool int out_fd rga_buffer_pool_get_fd(); rga_buffer_t src wrapbuffer_fd_t(in_fd, 1920, 1080, RK_FORMAT_YCbCr_420_SP, 0, 0); rga_buffer_t dst wrapbuffer_fd_t(out_fd, 640, 640, RK_FORMAT_RGB_888, 0, 0); im_rect src_rect {0, 0, 1920, 1080}; im_rect dst_rect {0, 0, 640, 640}; int ret improcess(src, dst, src_rect, dst_rect, IM_SYNC); if (ret ! IM_STATUS_SUCCESS) { printf(improcess failed: %d\n, ret); }wrapbuffer_fd_t的第一个参数是输入FD后面依次是宽、高、像素格式。注意这里传入的宽高必须是RGA能接受的对齐值NV12输入宽通常要求16像素对齐高8像素对齐RGB888输出宽度建议4像素对齐。1920和640天然满足但如果你处理的是比如1282x722这种比较怪的分辨率就需要在做buffer分配时就按对齐后的width和height去分配否则RGA会返回参数错误。improcess最后一个参数IM_SYNC表示同步调用也就是说这个接口会等RGA硬件完成操作后才返回返回后输出buffer里的数据保证可用。如果改成IM_ASYNC则函数提交任务后立即返回需要用imsync或imWait去等结果性能更好但调试复杂度高我自己习惯先在同步模式下把流程调通再针对热点路径改成异步。4.3 常见格式转换的注意点和对齐陷阱RGA的格式支持非常全但对齐要求非常严格很多新手在这里花掉大量时间。NV12/NV21这类半平面格式输入宽度必须16像素对齐、高度8像素对齐RGB888/RGB565宽度必须4像素对齐。这里说的不是图像真实宽度而是RGA内部一次处理的最小粒度。如果你的图像宽度不满足要求两种解决办法一是分配buffer时就按对齐后宽度分配图像数据的stride大于实际显示宽度RGA通过设置wstride/hstride来正确解析图像二是先用RGA做一次copy到对齐大小的buffer再继续做后续操作。第一种方式效率高推荐优先使用。再强调一个很容易导致花屏的点NV12和NV21的U/V分量顺序是不同的。NV12是Y平面UV交错U在前NV21是Y平面VU交错V在前。如果你解码器输出的是NV12RGA输入配置却写成了NV21出来的画面颜色会是偏色的最常见的表现是红色和蓝色互换。排查这类问题最快的方法是先拿一张纯色测试图过一遍链路看RGA输出的RGB数值对不对。4.4 RGA处理多路视频时的调度与并行当8路视频同时需要做RGA处理时RGA设备的调度就很重要了。RK3588的RGA3和RGA2在/dev/rga节点下同一时刻只能有一个任务在硬件层面执行如果你8个线程同时往里面提交任务底层驱动会排队但多线程提交会带来上下文切换开销。更推荐的做法是写一个统一的RGA任务队列由一个或两个专门线程来提交任务。RGA3和RGA2在最新驱动里可以通过im2d的参数选择核心IM_RGA3和IM_RGA2可以分开指定。我在项目里把“格式转换类任务”全部划给RGA3把“小尺寸缩放/OSD叠加”划给RGA2两个核心可以并行工作实测吞吐提升了接近50%。队列调度时还有一点要注意RGA任务之间是非抢占的一个超大分辨率任务会把后面一堆小任务全部堵住。如果系统里同时有8K和1080p的任务混合最好根据任务大小做优先级分组把阻塞风险高的聚合任务放在最低优先级通道保证小任务的实时性。5. 零拷贝链路设计与Buffer管理实践5.1 零拷贝到底“零”在哪能带来多大收益很多文章喜欢提“零拷贝”但真正说清楚零在哪的很少。在MPPRGA这条链路上零拷贝是指从VPU解码输出开始到RGA处理完成再到下一级消费AI推理/显示/编码视频帧的像素数据始终停留在同一块物理内存上CPU不参与任何数据搬运。传统做法中CPU需要做数据搬运的地方包括VPU输出拷贝到用户态读FD、用户态格式转换走CPU计算、转换完再拷贝给推理输入写FD。三次全算下来1080p NV12一帧约3MB8路30fps就是每秒720MB的纯搬运量还要加上格式转换的CPU计算。这个量级会让CPU持续高负载同时消耗大量内存带宽。零拷贝之后CPU只负责传递FD和元数据解出来的帧在哪里、处理完的帧还在哪里CPU全程只是“指挥”不亲自“搬砖”。我在同一块板子上对比过非零拷贝方案下8路1080p30fps解码RGB转换CPU占用约75%AI推理基本跑不动换成MPPRGA零拷贝链路后同样8路CPU占用降到12%以下AI推理可以全速运行。这就是零拷贝最直接的收益。5.2 DMA-BUF FD在解码器与RGA之间的流转实现零拷贝的关键技术点在于FD的流转。MPP解码输出的MppBuffer背后是ION/dma-buf通过mpp_buffer_get_fd拿到FD后这个FD可以直接作为RGA的输入内存描述符。RGA驱动内部会根据FD找到对应的物理内存并完成DMA操作整个过程不经过用户态地址空间映射也不需要CPU去读这些像素。这里有个细节需要维护清楚FD的生命周期。如果你在RGA任务还没完成时就释放了MppBufferRGA硬件可能正在读这块内存轻则花屏重则崩溃。建议的做法是给MppBuffer加引用计数解码输出一帧时引用计数1RGA任务提交时1RGA任务完成回调时-1推理消费时1推理完成后-1引用计数归零时才真正归还buffer给MPP复用。// 伪代码引用计数维护 MppFrame frame get_frame_from_mpp(); buf_ref_inc(frame); // 交给RGA前加锁 int fd mpp_buffer_get_fd(mpp_frame_buffer); submit_rga_task(fd, ...); // RGA回调中 buf_ref_dec(frame); // RGA处理完解锁你可以用一个简易的atomic变量记录引用数不需要依赖复杂框架。需要注意的是mpp_frame_deinit调用时MPP本身会尝试回收buffer所以在引用计数不为零时不能轻易deinit要等所有消费者都释放完再统一归还。5.3 Buffer池的设计容量计算与复用策略Buffer池的容量需要精心计算太大浪费内存太小会导致解码器阻塞。我在设计8路1080p解码处理池时按下面这个公式估的量每路缓冲帧数 解码器内部缓冲(3~5帧) RGA处理队列缓冲(2~3帧) 推理输入缓冲(2帧) 显示/编码缓冲(2帧)总共大约10~12帧每路。按1080p NV12单帧3MB计算8路就是240MB~288MB。RK3588搭配8GB以上内存时这个预留完全可以接受。如果内存紧张可以对低优先级路的缓冲帧数做压缩比如显示/编码缓冲减到1帧但解码器内部缓冲不建议低于4帧否则高码率或I帧周期不规律时容易卡顿。Buffer复用策略上我采用了“空闲队列使用中计数”的模式。初始所有buffer挂在空闲队列需要时从空闲队列取处理完成后回到空闲队列。如果空闲队列为空就等——绝对不能临时申请内存临时申请大概率会触发缺页中断或CMA压缩导致帧周期抖动这在视频链路上是致命的。5.4 多路Buffer调度的同步与防撕裂多路视频并行时每一路的解码帧处理完成时间是不固定的可能出现某一路特别快、某一路特别慢的情况。Buffer调度层要做的是把每一路独立的处理节奏汇总成统一的心跳保证所有路的帧不会在输出端错乱。我这里的做法是每路解码线程把自己处理完的帧写入一个环形队列消费端推理线程或者显示线程按帧时间戳从各队列里取帧。最关键的一步是设置一个同步基准时间戳逻辑上每一路都输出“同一个时间点”附近的帧避免出现一路已经走到第100帧、另一路还在第80帧这种越拉越大的情况。做法是在启动各路线程后先让所有路由到一个公共起点比如第一帧到达后同时开跑之后每消费一帧都检查与基准的时间差如果某路落后太多就主动丢帧追赶进度保证整体流水的节奏一致。6. 性能实测数据与问题排查经验总结6.1 实测性能数据多路解码与2D加速占用我手上这套环境是RK35888GB LPDDR4XCPU为默认调频策略内存频率2133MHz。测试输入是8路1080p30fps的H.264码流总码率约24Mbps。解码由MPP硬解每路RGA做NV12转RGB888并缩放到640x640最终送给一个轻量级AI模型做检测。实测数据可以给你做个参照配置VPU占用CPU占用RGA占用帧率(每路)端到端延迟单路1080p解码RGAAI4%5%8%30fps约40ms8路1080p解码RGAAI28%11%42%30fps约50ms8路1080p解码CPU软件转RGB28%78%0%22fps约120msCPU占用从78%降到11%性能差距一目了然。RGA占用42%说明它还有余量后续如果加OSD叠加或者多路缩放也不会立刻触顶。端到端延迟从解码到AI输出约50ms对于监控类场景完全够用如果要进一步压延迟可以考虑把RGA从同步模式改成异步模式再对VPU侧做低延迟配置。6.2 排查实录解码输出花屏与RGA颜色异常做这套系统时我先后踩过两个特别典型的坑排查过程可以分享一下。第一个坑解码出来的画面偶尔花屏现象是画面顶部的几条线出现颜色错乱。一开始以为码流有问题查了很久才发现问题出在buffer复用上。我码流包内存复用过快MPP还没异步读完就被重新写入新数据导致VPU拿到的是半新半旧的数据。解决方式是给码流包加一个“已使用”标记确认MPP处理完当前packet后才把这块内存放回空闲队列。第二个坑RGA输出的RGB图像颜色偏色红色和蓝色互换。检查后发现输入格式我写成了RK_FORMAT_YCbCr_420_SP但实际上MPP输出的是NV12RK_FORMAT_YCbCr_420_SP默认解释顺序是YUV如果我的输入实际是NV21YVU就会偏色。最后通过纯红色测试图验证了数据送进去的U/V顺序改成匹配的实际格式就好了。这里建议大家在调试RGA颜色问题时可以直接喂一张代码生成的纯色NV12图看输出哪种颜色反了判断是U/V顺序问题还是色彩空间矩阵选择问题。6.3 性能分析工具与瓶颈定位方法定位性能瓶颈时单纯靠printf打点效率太低。我的习惯是先看硬件占用率再逐级排查。RK3588上可以直接读这几个节点# RGA负载 cat /sys/kernel/debug/rkrga/load # VPU工作状态 cat /sys/kernel/debug/mpp_service/summary # CPU各核负载 mpstat -P ALL 1RGA的load节点会显示每个核心的忙碌时间占比如果RGA3一直冲到90%以上说明该往RGA2分流或者压缩单帧处理时间。VPU的summary能看到当前活跃的编解码通道和帧率确认解码能力是否有富余。CPU负载如果异常偏高优先看是不是你在转换格式的代码里偷偷用了CPU计算——这种情况经常发生在你“只是把指针传了一下底层却隐式copy”的时候。6.4 避免过度优化的几点心得最后分享几段个人经验不一定是最优解但对避坑很有帮助。第一不要一开始就追求异步和极致性能。先用同步模式把整条链路跑通确保每一帧数据是正确的再逐步改成异步模式优化性能。我在初期一上来就用异步模式调RGA结果数据竞争导致各种偶发花屏排查成本非常高。后来改成同步模式确认逻辑正确再改回异步问题瞬间明朗。第二不要忽略内存对齐和stride问题。很多偶发问题不是因为代码逻辑错而是因为某个输入宽度不符合RGA对齐要求导致边界像素处理失败。写RGA处理函数时第一件事就是用imcheck检查参数确认源和目标的宽高、格式、stride全部合法再提交硬件。第三多路视频线程要克制使用锁。锁的竞争会造成线程阻塞阻塞会让buffer队列积压积压又会拉长延迟。我在代码里尽量用无锁队列SPSC环形队列配合原子变量做引用计数只有在极端情况下才用互斥锁保护全局状态。RK3588这套MPPRGA的组合做多路视频处理确实是一套非常顺手的方案。它最大的价值不是某个孤立的硬件加速能力而是把解码、处理、显示、推理的内存链路统一到DMA-BUF/FD这套体系里让整条视频流水线的数据流动成本降到最低。如果你正准备在自己的项目里做类似的多路视频处理希望这篇文章能帮你少走一段弯路。
返回列表