RKMedia
模块介绍
RKMedia 是对 RV1126/RV1109 内所有媒体资源调用进行了整合封装的一套 API 接口,它可以大大降低刚接触 RV1126/RV1109 芯片平台用户的开发成本,以少量代码就可以很快速、简单地调用 Soc 上所有媒体资源。同时 RKMedia 还提供了媒体资源以外的硬件资源联合调用 DEMO,如:RKAIQ、RKNN、RTSP 等等。RKMedia 的核心思想是把各个硬件资源独立成模块,模块开放出输入和输出端通过绑定的方式控制流从某个模块流出并且流入另外一个模块。这里会对每个模块进行相关介绍。

VI
-
VI为视频输入模块,可以理解为摄像头采集模块,需要注意的是VI需要RKAIQ的支持,在使用VI前需要提前初始化好RKAIQ,详细初始化步骤可以在 Example 中查看。 -
VI通道属性设置MPP_CHN_S stViChn; #定义模块设备通道结构体 stViChn.enModId = RK_ID_VI; #模块号为 RK_ID_VI stViChn.s32ChnId #设置 VI 通道号
VI_CHN_ATTR_S vi_chn_attr; #定义 VI 通道属性结构体指针 vi_chn_attr.u32BufCnt # VI 捕获视频缓冲区计数 vi_chn_attr.u32Width # video 宽度 vi_chn_attr.u32Height # video 高度 vi_chn_attr.enWorkMode # VI 通道工作模式 vi_chn_attr.pcVideoNode # video 节点路径 vi_chn_attr.enPixFmt # video 格式
-
设置
VI通道属性#函数定义: RK_MPI_VI_SetChnAttr(VI_PIPE ViPipe, VI_CHN ViChn, const VI_CHN_ATTR_S *pstChnAttr);
ViPipe 为 VI 管道号;ViChn 为 VI 通道号; pstChnAttr 为 VI 通道属性结构体指针。
#使用示例: RK_MPI_VI_SetChnAttr(vi_pipe, stViChn.s32ChnId, &vi_chn_attr);
-
启用
VI通道#函数定义: RK_S32 RK_MPI_VI_EnableChn(VI_PIPE ViPipe, VI_CHN ViChn);
ViPipe 为 VI 管道号; ViChn 为 VI 通道号。
#使用示例: RK_MPI_VI_EnableChn(vi_pipe, stViChn.s32ChnId);
VDEC
-
VDEC模块,即视频解码模块。本模块支持多路实时解码,且每路解码独立,支持H264/H265/MJPEG/JPEG解码。 -
解码通道属性设置
MPP_CHN_S stVdecChn; #定义模块设备通道结构体 stVdecChn.enModId = RK_ID_VDEC; #模块号为 RK_ID_VDEC stVdecChn.s32ChnId #设置解码通道号
VDEC_CHN_ATTR_S stVdecAttr; #定义解码通道属性结构体 stVdecAttr.enCodecType #解码格式 stVdecAttr.enMode #解码输入模式,支持帧/流 stVdecAttr.enDecodecMode #解码模式,支持硬件或软件解码
-
创建
VDEC解码通道#函数定义: RK_S32 RK_MPI_VDEC_CreateChn(VDEC_CHN VdChn, const VDEC_CHN_ATTR_S *pstAttr);
VdChn 为解码通道号; pstAttr 为解码通道属性指针。
#使用示例: RK_MPI_VDEC_CreateChn(stVdecChn.s32ChnId, &stVdecAttr);
RGA
-
RGA模块用于2D图像的裁剪、格式转换、缩放、旋转、图片叠加等。 -
使用
RGA处理可以大大降低 CPU 的负担,同时加快图片的处理速度。(参考:1080p 图片格式转换。RGA 耗时:7ms,Opencv 耗时:50ms) -
rkmedia中RGA通道仅支持裁剪、格式转换、缩放、旋转功能,图片叠加则需要单独调用librga.so库,参见《Rockchip_Developer_Guide_Linux_RGA_CN.pdf》 -
RGA通道属性设置MPP_CHN_S stRgaChn; #定义模块设备通道结构体 stRgaChn.enModId = RK_ID_RGA; #模块号为 RK_ID_RGA stRgaChn.s32ChnId #设置解码通道号
RGA_ATTR_S stRgaAttr; #定义 RGA 属性结构体 stRgaAttr.bEnBufPool #使能缓冲池 stRgaAttr.u16BufPoolCnt #缓冲池计数 stRgaAttr.u16Rotaion #旋转 取值范围 0,90,180,270 stRgaAttr.stImgIn.u32X # RGA 通道输入图片的X轴坐标 stRgaAttr.stImgIn.u32Y # RGA 通道输入图片的Y轴坐标 stRgaAttr.stImgIn.imgType # RGA 通道输入图片格式 stRgaAttr.stImgIn.u32Width #输入图片宽度 stRgaAttr.stImgIn.u32Height #输入图片高度 stRgaAttr.stImgIn.u32HorStride #输入图片虚宽 stRgaAttr.stImgIn.u32VirStride #输入图片虚高 stRgaAttr.stImgOut.u32X # RGA 通道输出图片的 X 轴坐标 stRgaAttr.stImgOut.u32Y # RGA 通道输出图片的 Y 轴坐标 stRgaAttr.stImgOut.imgType # RGA 通道输出图片格式 stRgaAttr.stImgOut.u32Width #输出图片宽度 stRgaAttr.stImgOut.u32Height #输出图片高度 stRgaAttr.stImgOut.u32HorStride #输出图片虚宽 stRgaAttr.stImgOut.u32VirStride #输出图片虚高
-
创建
RGA通道#函数定义: RK_S32 RK_MPI_RGA_CreateChn(RGA_CHN RgaChn, RGA_ATTR_S *pstRgaAttr);
RgaChn 为 RGA 通道号; pstAttr 为 RGA 通道属性指针。
#使用示例: RK_MPI_RGA_CreateChn(stRgaChn.s32ChnId, &stRgaAttr);
stride 宽度,通常与 buffer_width 相同。若 u32VirWidth 大于 buffer 宽度,则必须满足 16 对齐。 可使用 CELING_2_POWER 函数对变量进行 16 对齐设置。例如:
stRgaAttr.stImgOut.u32VirStride = CELING_2_POWER(u32Height,16);
VO
-
VO模块用于视频输出管理。VO模块是对DRM/KMS的封装,支持多 VOP以及多图层显示。MPP_CHN_S VoChn; #定义模块设备通道结构体 VO_CHN_ATTR_S stVoAttr = {0}; #定义视频输出属性结构体并初始化为 0 memset(&stVoAttr, 0, sizeof(stVoAttr));
stVoAttr.pcDevNode = "/dev/dri/card0"; #视频 VO 输出设备节点设置为"/dev/dri/card0" stVoAttr.emPlaneType #视频输出图层类型 stVoAttr.enImgType #输入图片格式 stVoAttr.u16Zpos #输出图层 Z 轴高度 stVoAttr.u32Width #视频输出宽度 stVoAttr.u32Height #视频输出高度 stVoAttr.stImgRect.s32X #视频输入图像区域的 X 轴坐标 stVoAttr.stImgRect.s32Y #视频输入图像区域的 Y 轴坐标 stVoAttr.stImgRect.u32Width #输入图像区域的宽度 stVoAttr.stImgRect.u32Height #输入图像区域的高度 stVoAttr.stDispRect.s32X #显示区域的起始 X 轴坐标 stVoAttr.stDispRect.s32Y #显示区域的起始 Y 轴坐标 stVoAttr.stDispRect.u32Width #显示区域的宽度 stVoAttr.stDispRect.u32Height #显示区域的高度
VoChn.enModId = RK_ID_VO; #模块号为 RK_ID_VO VoChn.s32DevId #模块设备号 VoChn.s32ChnId #模块通道号
-
创建
VO通道#函数定义: RK_S32 RK_MPI_VO_CreateChn(VO_CHN VoChn, const VO_CHN_ATTR_S *pstAttr);
VoChn 为 VO 通道号;pstAttr 为 VO 通道属性指针。
#使用示例: RK_MPI_VO_CreateChn(VoChn.s32ChnId, &stVoAttr);
VENC
-
VENC模块,即视频编码模块。本模块支持多路实时编码,且每路编码独立,编码协议和编码 profile 可以不同。支持视频编码同时,调度 Region 模块对编码图像内容进行叠加和遮挡。支持H264/H265/MJPEG/JPEG编码。MPP_CHN_S stVenChn; #定义模块设备通道结构体 stVenChn.enModId = RK_ID_VENC; #模块号设置为 RK_ID_VENC stVenChn.s32ChnId #编码通道号 memset(&venc_chn_attr, 0, sizeof(venc_chn_attr));
VENC_CHN_ATTR_S venc_chn_attr; #定义 VENC 通道属性结构体 venc_chn_attr.stVencAttr.enType #编码格式 venc_chn_attr.stVencAttr.imageType #编码图片格式 venc_chn_attr.stVencAttr.u32PicWidth #图片宽度 venc_chn_attr.stVencAttr.u32PicHeight #图片高度 venc_chn_attr.stVencAttr.u32VirWidth #图片虚宽 venc_chn_attr.stVencAttr.u32VirHeight #图片虚高 venc_chn_attr.stVencAttr.u32Profile #用于H264编码器的 Profile IDC 值
H264 或 H265 编码的参数参照以下配置
venc_chn_attr.stRcAttr.enRcMode #编码协议类型 根据具体使用编码格式配置
venc_chn_attr.stRcAttr.stH264Cbr.u32Gop = fps; # I 帧间隔,取值范围:[1, 65536]
venc_chn_attr.stRcAttr.stH264Cbr.u32BitRate =
u32Width * u32Height * fps / 14; #平均比特率,取值范围:[2000, 98000000],单位:bps
venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1; #目标帧率分子
venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = fps; #目标帧率分母
venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1; #数据源帧率分子
venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = fps; #数据源帧率分母
-
创建
VENC通道#函数定义: RK_S32 RK_MPI_VENC_CreateChn(VENC_CHN VeChn, VENC_CHN_ATTR_S *stVencChnAttr);
VeChn 为编码通道号; stVencChnAttr 编码通道属性指针。
#使用示例: RK_MPI_VENC_CreateChn(stVenChn.s32ChnId, &venc_chn_attr);
通道绑定
-
设置好通道属性之后就可以对通道进行绑定了。
-
数据源和数据接收者绑定接口如下:
RK_S32 RK_MPI_SYS_Bind(const MPP_CHN_S *pstSrcChn,const MPP_CHN_S *pstDestChn); #pstSrcChn 为源通道指针; pstDestChn 目的通道指针。
-
注:通道绑定需要注意源通道的输出与目的通道的输入的数据格式要对应。
通道数据发送与获取
-
常用向指定通道输入数据函数如下:
RK_S32 RK_MPI_SYS_SendMediaBuffer(MOD_ID_E enModID, RK_S32 s32ChnID, MEDIA_BUFFER buffer);
enModID 为模块号; s32ChnID 为通道号; buffer 为缓冲区。
-
常用向指定通道获取数据函数如下:
MEDIA_BUFFER RK_MPI_SYS_GetMediaBuffer(MOD_ID_E enModID, RK_S32 s32ChnID, RK_S32 s32MilliSec);
enModID 为模块号; s32ChnID 为通道号; s32MilliSec 为阻塞等待时间,当该值为 -1 的时候表示阻塞等待。
RTSP 拉流与推流
为了配合网络摄像头视频分析场景,我们提供了简单的 RTSP 拉流和推流接口。
-
RTSP拉流 使用firefly编写的库函数接口ffrtspGet可轻松实现拉取RTSP数据流。 需要配置结构体ffrtsp_get,其结构体配置介绍如下:struct FFRTSPGet ffrtsp_get; #定义 FFRTSPGet 类型结构体 ffrtsp_get.count = 0; ffrtsp_get.callback = NULL; ffrtsp_get.callback = FFRKMedia_Vdec_Send; #回调函数 ffrtsp_get.count #拉取的 RTSP 数据流的个数 ffrtsp_get.ffrtsp_get_info[i].url #拉取的 RTSP 数据流的链接
注:配置好结构体之后只需要创建一个线程任务即可实现拉取单个或多个 RTSP 数据流。
-
RTSP推流 使用firefly编写的库函数接口ffrtspPush可轻松实现推送RTSP数据流。 需要配置结构体ffrtsp_push,其结构体配置介绍如下:ffrtsp_push[i].idex #推送 RTSP 数据流的编号 ffrtsp_push[i].port #推送 RTSP 数据流的端口 ffrtsp_push[i].type = cfg.session_cfg[i].video_type; #推送 RTSP 数据流的视频类型
注:配置好结构体之后需要创建的线程个数要匹配推送的 RTSP 数据流的个数。
通道析构顺序
需要特别注意的是 rkmedia 对模块的析构顺序有特殊的要求:数据流管道中后级模块要先于前级模块销毁。比如:
VI --> RGA --> VENC
则建议析构顺序如下:
destroy VENC --> destroy RGA --> destroy VI
以 VI 为例, VI 是数据产生端。其生产的 buffer 在数据管道销毁时可能被后级占用,从而导致 VI 管理的资源也被占用。再次打开就会遇到 Device Busy 的错误。这个问题在频繁创建销毁数据通道时有概率发生。
- 详细
RKMEDIA使用请参考手册《Rockchip_Developer_Guide_Linux_RKMedia_CN.pdf》。
具体示例
为了方便用户快速掌握 RKMedia 的开发流程,Firefly 提供了大量的测试 DEMO。代码路径 SDK/external/rkmedia/example/、SDK/app/firefly_rkmedia_demo。
VI->GetFrame
-
该开发流程对应的 DEMO 示例为
rkmedia_vi_get_frame_test.c -
说明:设备输入保存至文件。演示
VI没有Bind时如何取视频流。 -
快速使用:
#从摄像头节点 rkispp_scale0 抓取 10 帧图片并保存为 1080p.nv12 文件 ./rkmedia_vi_get_frame_test -a /oem/etc/iqfiles/ -w 1920 -h 1080 -d rkispp_scale0 -o /tmp/1080p.nv12 -c 10
命令录取 10 帧图像数据,截取最后一帧来预览
使用 dd 跳过前 9 帧数据,得到最后一帧。3110400 = 1920 x 1080 x 3 / 2 一帧 NV12 数据大小
dd if=1080p.nv12 of=1080pl.nv12 bs=3110400 skip=9
使用 ffmpeg 把 NV12 图像转换为 PNG 格式。
fmpeg -y -f rawvideo -pix_fmt nv12 -ss 00:01 -r 1 -s 1920x1080 -i 1080pl.nv12 -frames:v 1 output.png
打开 output.png 预览。
-
DEMO 效果截图如下图所示:

VI->RKNN->VENC->RTSP
-
该开发流程对应的 DEMO 示例为
rkmedia_vi_rknn_venc_rtsp_test.c -
说明:该示例解析 rtsp-nn.cfg 配置文件来配置
VI接口和VENC接口的输入端与输出端信息,将VI接口获取摄像头节点图片数据送入NPU进行AI模型推理,然后解析推理结果。根据推理结果进行像素点偏移画框,然后将处理好的数据使用接口RK_MPI_SYS_SendMediaBuffer发送到VENC编码接口进行编码,最后使用rtsp_tx_video接口将VENC编码好的数据流推流到RTSP网络。 -
快速使用:
路径需要有相关文件
./rkmedia_vi_rknn_venc_rtsp_test -a /oem/etc/iqfiles/ -c /oem/usr/share/rtsp-nn.cfg -b /oem/usr/share/rknn_model/box_priors.txt -l /oem/usr/share/rknn_model/coco_labels_list.txt -p /oem/usr/share/rknn_model/ssd_inception_v2_rv1109_rv1126.rknn
PC 端使用 VLC 播放器预览 RTSP 推流命令为
vlc rtsp://168.168.101.208:554/live/main_stream #注: 168.168.101.208 为开发板的 IP 地址,根据实际情况进行调整
-
DEMO 效果截图如下图所示:

RTSPGet->VDEC(Multi)->VO
-
该开发流程对应的 DEMO 示例为
rkmedia_rtspget_multi_test.cc -
说明:该示例使用
RTSP拉流接口ffrtspGet从多个网络摄像头获取数据,然后使用VDEC接口解码,接着使用RGA加速将原始数据构建成目标数据,最后使用RK_MPI_SYS_SendMediaBuffer接口将目标数据发送到VO接口以分屏输出显示。 -
快速使用:
RTSP 取流 2 个网络摄像头,每个摄像头取流 2 次,并输出显示到显示屏
./rkmedia_rtspget_multi_test rtsp://admin:firefly123@168.168.100.94:554/av_stream rtsp://admin:firefly123@168.168.100.94:554/av_stream rtsp://admin:firefly123@168.168.100.97:554/av_stream rtsp://admin:firefly123@168.168.100.97:554/av_stream
-
DEMO 效果截图如下图所示:

RTSPGet->VDEC->RKNN->VENC->RTSPPush
-
该开发流程对应的 DEMO 示例为
rkmedia_rtspget_vdec_rknn_venc_rtsp_test.cc -
说明:该示例可实现多个
RTSP拉流解码与多个RTSP编码推流的效果。其流程使用RTSP拉流接口ffrtspGet从多个网络摄像头获取数据,然后使用VDEC接口解码。将解码的图片数据送入NPU进行AI模型推理,然后解析推理结果,使用 OpenCV 将推理结果应用到图片上。VENC则对 OpenCV 处理后的图片进行编码,最后使用接口ffrtspPush将VENC编码好的数据流推流到RTSP网络。 -
快速使用:
#运行该示例需要有库文件 libffrtsp.so ./rkmedia_rtspget_vdec_rknn_venc_rtsp_test -c /usr/share/ffrtsp-nn.cfg -p /usr/share/rknn_model/ssd_inception_v2_rv1109_rv1126.rknn -l /usr/share/rknn_model/coco_labels_list.txt -b /usr/share/rknn_model/box_priors.txt
ffrtsp-nn.cfg 两个网络摄像头的配置分别为
video_type=6 video_fps=25 width=1920 height=1080 image_type=4 port=8554 video_url=rtsp://admin:firefly123@168.168.100.94:554/av_stream video_type=6 video_fps=25 width=1920 height=1080 image_type=4 port=8555 video_url=rtsp://admin:firefly123@168.168.100.97:554/av_stream
#则 PC 端使用 VLC 预览 RTSP 推流画面命令为 vlc rtsp://168.168.101.208:8554/H264_stream_0 vlc rtsp://168.168.101.208:8555/H264_stream_1 #注:168.168.101.208 为开发板的 IP ,根据实际情况进行调整
-
DEMO 效果截图如下图所示:
