前面两篇已经介绍了VI和VO的使用,本章节来介绍rkmedia且也是瑞芯微平台重点部分:编解码。
目录
rkmedia编解码部分是对mpp接口进行封装提供给用户使用。
mpp是瑞芯微提供的媒体处理软件平台(Media Process Platform, 简称 MPP)是适用于瑞芯微芯片系列的通用媒体处理软件平台。只要是rk平台的的编解码都是用的mpp,区别在于编解码能力不同以及不同芯片支持的格式等不同。编解码都是硬件编解码,编解码资源是分开的。
MPP 提供的功能包括:
视频解码:
H.265 / H.264 / H.263 / VP9 / VP8 / MPEG-4 / MPEG-2 / MPEG-1 / VC1 / MJPEG
视频编码:
H.264 / VP8 / MJPEG
rv1126编解码能力如下:
rv1109编解码能力如下:
以上编解码能力是总的编解码能力,如需多路编解码,可以自行换算。
VENC 模块,即视频编码模块。此模块支持多路实时编码,且每路编码独立,编码协议和编码 profile 可以不同。支持视频编码同时,调度 Region 模块对编码图像内容进行叠加和遮挡。支持
H264/H265/MJPEG/JPEG编码,支持的码控类型为CBR和VBR。
以H264 CBR为例:
- venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;
- venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
- venc_chn_attr.stRcAttr.stH264Cbr.u32Gop = 30;
- venc_chn_attr.stRcAttr.stH264Cbr.u32BitRate = u32Width * u32Height;
- // frame rate: in 30/1, out 30/1.
- venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;
- venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 30;
- venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;
- venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 30;
-
- venc_chn_attr.stVencAttr.imageType = IMAGE_TYPE_NV12;
- venc_chn_attr.stVencAttr.u32PicWidth = u32Width;
- venc_chn_attr.stVencAttr.u32PicHeight = u32Height;
- venc_chn_attr.stVencAttr.u32VirWidth = u32Width;
- venc_chn_attr.stVencAttr.u32VirHeight = u32Height;
- venc_chn_attr.stVencAttr.u32Profile = 77;
- ret = RK_MPI_VENC_CreateChn(0, &venc_chn_attr);
- if (ret) {
- printf("ERROR: create VENC[0] error! ret=%d\n", ret);
- return 0;
- }
设置jpeg/mjpeg编码时,可以设置zoom宽高,实际上是经过rga进行缩放后再送去编码:
- VENC_CHN_ATTR_S venc_chn_attr;
- memset(&venc_chn_attr, 0, sizeof(venc_chn_attr));
- venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_JPEG;
- venc_chn_attr.stVencAttr.imageType = enPixFmt;
- venc_chn_attr.stVencAttr.u32PicWidth = u32SrcWidth;
- venc_chn_attr.stVencAttr.u32PicHeight = u32SrcHeight;
- venc_chn_attr.stVencAttr.u32VirWidth = u32SrcWidth;
- venc_chn_attr.stVencAttr.u32VirHeight = u32SrcHeight;
-
- /*********************************************************************/
- venc_chn_attr.stVencAttr.stAttrJpege.u32ZoomWidth = u32DstWidth;
- venc_chn_attr.stVencAttr.stAttrJpege.u32ZoomHeight = u32DstHeight;
- venc_chn_attr.stVencAttr.stAttrJpege.u32ZoomVirWidth = u32DstWidth;
- venc_chn_attr.stVencAttr.stAttrJpege.u32ZoomVirHeight = u32DstHeight;
- /*********************************************************************/
-
- venc_chn_attr.stVencAttr.enRotation = enRotation;
- venc_chn_attr.stVencAttr.stAttrJpege.bSupportDCF = RK_TRUE;
- venc_chn_attr.stVencAttr.stAttrJpege.stMPFCfg.u8LargeThumbNailNum = 2;
- venc_chn_attr.stVencAttr.stAttrJpege.stMPFCfg.astLargeThumbNailSize[0]
- .u32Width = 164;
- venc_chn_attr.stVencAttr.stAttrJpege.stMPFCfg.astLargeThumbNailSize[0]
- .u32Height = 128;
- venc_chn_attr.stVencAttr.stAttrJpege.stMPFCfg.astLargeThumbNailSize[1]
- .u32Width = 128;
- venc_chn_attr.stVencAttr.stAttrJpege.stMPFCfg.astLargeThumbNailSize[1]
- .u32Height = 164;
- ret = RK_MPI_VENC_CreateChn(0, &venc_chn_attr);
- if (ret) {
- printf("Create Venc failed! ret=%d\n", ret);
- return -1;
- }
1、编码是没有缩放功能的,输入的分辨率要和配置的分辨率一样,否则执行过程会报错。如需要动态切换分分辨率,需要disable venc、解绑、重新配置vi分辨率、重新配置venc分辨率、绑定。
2、帧率控制参数是比例关系。比如默认配置的是1 30 1 30,实际比例是1:1。所以代表输入多少帧率就编码多少帧率。很多人会错误的认为是实际帧率的关系,觉得帧率控制有问题。
3、编码h26x视频编码协议都是基于YUV420的,所以如果是yuv422输入编码出来的数据会有色彩偏差,这一点一般用hdmi作为输入的时候容易遇到。因为mpp内部会把输入的非yuv420格式(yuv422、argb8888)转换成yuv420,造成色彩丢失。
4、编码jpeg时会多样一些,因为是图像。图像和视频还是有区别的。
默认情况下编码的qp值已经设置成
qpMin:8, qpMax:48, qpMinI:8, qpMaxI:48
如果对码率控制的比较严格的情况下需要自行修改qp值,至于qp值具体配置成什么,那要根据自己的实际情况自己慢慢去调整。
- RK_MPI_VENC_GetRcParam(0,&stRcParam);
- printf("pstRcParam->s32FirstFrameStartQp=%d\n",stRcParam.s32FirstFrameStartQp);
- stRcParam.stParamH264.u32StepQp = 4;
- stRcParam.stParamH264.u32MaxQp = 50;
- stRcParam.stParamH264.u32MinQp = 23;
- stRcParam.stParamH264.u32MaxIQp = 48;
- stRcParam.stParamH264.u32MinIQp = 18;
- RK_MPI_VENC_SetRcParam(0, &stRcParam);
对于编码高级参数在rkmedia的手册中描述的不多,这边截取海思手册中的描述,总体使用都差不多。
在学习/使用编码高级参数还是需要多多去动手,多修改对应参数将保存的编码数据对比,这样才可以深入了解各个参数设置的意思。
rkmedia的venc中添加位图,使用的是RK_MPI_VENC_RGN_SetBitMap 接口去实现。位图只支持ARGB8888的格式,且最多支持8个区域优先级从REGION_ID_0~REGION_ID_7递增。
- #define TEST_ARGB32_YELLOW 0xFFFFFF00
- #define TEST_ARGB32_RED 0xFFFF0000
- #define TEST_ARGB32_GREEN 0xFF00FF00
- #define TEST_ARGB32_BLUE 0xFF0000FF
- #define TEST_ARGB32_TRANS 0x00000000
- #define TEST_ARGB32_WHITE 0xF0FFFFFF
-
- static void set_argb8888_buffer(RK_U32 *buf, RK_U32 size, RK_U32 color) {
- for (RK_U32 i = 0; buf && (i < size); i++)
- *(buf + i) = color;
- }
-
-
- RK_MPI_VENC_RGN_Init(0, NULL);
-
- BITMAP_S BitMap;
- BitMap.enPixelFormat = PIXEL_FORMAT_ARGB_8888;
- BitMap.u32Width = 160;
- BitMap.u32Height = 160;
- BitMap.pData = malloc(BitMap.u32Width * 4 * BitMap.u32Height);
- RK_U8 *ColorData = (RK_U8 *)BitMap.pData;
- RK_U16 ColorBlockSize = BitMap.u32Height * BitMap.u32Width;
- set_argb8888_buffer((RK_U32 *)ColorData, ColorBlockSize / 4,
- TEST_ARGB32_WHITE);
- set_argb8888_buffer((RK_U32 *)(ColorData + ColorBlockSize),
- ColorBlockSize / 4, TEST_ARGB32_TRANS);
- set_argb8888_buffer((RK_U32 *)(ColorData + 2 * ColorBlockSize),
- ColorBlockSize / 4, TEST_ARGB32_RED);
- set_argb8888_buffer((RK_U32 *)(ColorData + 3 * ColorBlockSize),
- ColorBlockSize / 4, TEST_ARGB32_BLUE);
-
- // Case 1: Canvas and bitmap are equal in size
- OSD_REGION_INFO_S RngInfo;
- RngInfo.enRegionId = REGION_ID_0;
- RngInfo.u32PosX = 160;
- RngInfo.u32PosY = 160;
- RngInfo.u32Width = 160;
- RngInfo.u32Height = 160;
- RngInfo.u8Enable = 1;
- RngInfo.u8Inverse = 0;
- RK_MPI_VENC_RGN_SetBitMap(0, &RngInfo, &BitMap);
注意点:
1、编码的位图只支持256色,所以如果是叠加彩色图片会出现色彩有偏差的问题,可以换成RGA的位图叠加函数去实现。
2、每次更新数据的时候,需要重新调用RK_MPI_VENC_RGN_SetBitMap接口去刷一遍数据
3、想要显示文字osd,还是需要把文字的类型转成argb的数据,送进去显示。给个链接可以参考:海思3516a实现OSD叠加水印_烫手的热山药的博客-CSDN博客
VDEC 模块,即视频解码模块。此模块支持多路实时解码,且每路解码独立,支持
H264/H265/MJPEG/JPEG解码。
- VDEC_CHN_ATTR_S stVdecAttr;
- memset(&stVdecAttr, 0, sizeof(stVdecAttr));
- stVdecAttr.enCodecType = enCodecType;
- stVdecAttr.enMode = VIDEO_MODE_FRAME;
-
- if (stVdecAttr.enCodecType == RK_CODEC_TYPE_MJPEG)
- stVdecAttr.enMode = VIDEO_MODE_FRAME;
- else
- stVdecAttr.enMode = VIDEO_MODE_STREAM;
- stVdecAttr.enDecodecMode = VIDEO_DECODEC_HADRWARE;
-
- ret = RK_MPI_VDEC_CreateChn(0, &stVdecAttr);
- if (ret)
- {
- printf("Create Vdec[0] failed! ret=%d\n", ret);
- return -1;
- }
解码的初始化比较简单。解码的方式分为两种:帧模式和流模式。
帧模式需要在送数据前已经分好帧,流模式则是在内部分帧
注意:
1、jpeg和mjpeg解码只支持帧模式,没有流模式。
2、在解码中送数据的时间延时要自己控制,送的太快或者太慢都会导致解码器报错。
3、解码时需要收到第一个I帧才会解码
编码:
查看编码信息 | cat /proc/mpp_service/session_summary |
查看硬件编解码时间 | echo 0x100 > /sys/module/rk_vcodec/parameters/mpp_dev_debug |
查看h26x编码频率 | cat /sys/kernel/debug/clk/clk_summary | grep enc |
查看mjpeg编码频率 | cat /sys/kernel/debug/clk/clk_summary | grep vepu |
设置26x编码频率 | echo 600000000 > /proc/mpp_service/rkvenc/clk_core(594000000是最高频率) |
解码:
查看jpeg解码频率 | cat /sys/kernel/debug/clk/clk_summary | grep jpeg |
1、jpeg的旋转不支持缩放旋转,只能单独旋转
2、jpeg编码不支持rgb格式,支持argb格式
3、不管是输入格式是i还是p 编码出来的都是p(P代表逐行扫描,i代表隔行扫描)
4、编码帧率达不到预期
原因:可能达到编码能力的瓶颈。
解决:以下步骤挨个测试
(1)抬高编码频率
(2)DDR定频 修改 rkbin/RKBOOT/RV1126MINIALL.ini 所有924改成1056
(3)cpu定频
echo userspace > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
echo 1512000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed
(4)提高cpu优先级
io -4 0xfe830008 0x202
(5)关闭温控
echo user_space > /sys/class/thermal/thermal_zone0/policy
echo 0 > /sys/class/thermal/thermal_zone0/cdev0/cur_state
echo 0 > /sys/class/thermal/thermal_zone0/cdev0/cur_state
1、在获取vdec的yuv数据时,将获取的数据直接写入到文件中,打开后会出现一部分绿色的情况
解决:主要是写文件的方式有问题
- int width,height,h_stride,v_stride;
- RK_U8 *base_y;
- RK_U8 *base_c;
- while (!quit) {
- mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VDEC, 0, 500);
- if (!mb) {
- printf("RK_MPI_SYS_GetMediaBuffer get null buffer in 500ms...\n");
- return NULL;
- }
-
- sprintf(picname, "/userdata/rv1126_dec_%d", ccount++);
- vdec_file_save = fopen(picname, "w");
-
- base_y = RK_MPI_MB_GetPtr(mb);
- base_c = RK_MPI_MB_GetPtr(mb) + h_stride * v_stride;
- for (i = 0; i < height; i++, base_y += h_stride)
- {
- fwrite(base_y, 1, width, vdec_file_save);
- }
- for (i = 0; i < height / 2; i++, base_c += h_stride)
- {
- fwrite(base_c, 1, width, vdec_file_save);
- }
-
- fclose(vdec_file_save);
- memset(picname, 0 ,64);
- RK_MPI_MB_ReleaseBuffer(mb);
- }
2、在使用vdec解码jpeg时,会导致缓存逐渐增大。
解决:需要配置 /proc/mpp_service/vdpu/session_buffers 这个值,默认是40,可以将其改小
3、解码时出现
[ 669.352997] mpp_rkvdec2 fdf80200.rkvdec: resetting...
[ 669.353460] mpp_rkvdec2 fdf80200.rkvdec: reset done 打印
原因:mpp吧数据丢给硬件解码后设定了一个200ms的等待时间,如果超时硬件认为有异常,所以复位了。
解决:增加延时时间( kernel/drivers/video/rockchip/mpp)
暂时关于VENC/VENC部分写这么多,有新的建议后续加上来