什么是rockit MPI:(下面是rockchip给出官方解释)
Rockchip提供的媒体处理接口(Rockchip Media Process Interface,简称 RK MPI),可支持应用软件快速开发。该平台整合了RK的硬件资源,对应用软件屏蔽了芯片相关的复杂的底层处理,并对应用软件直接提供接口完成相应功能。该平台支持应用软件快速开发以下功能:输入视频捕获、H.265/H.264/JPEG 编码、H.265/H.264/JPEG 解码、视频输出显示、视频图像前处理(包括裁剪、缩放、旋转)、智能、音频捕获及输出、音频编解码等功能。
这是其中一些模块介绍
多的内容就不说了,本菜鸡目前只熟悉其中部分模块,在此之前也并未接触过相关流媒体处理的相关内容,所以有些知识并不完善,请多指教了。废话不多说,接下来开始正文内容。
说一下项目背景
是需要在VO模块上叠加一层UI层,具体情况是有一个QT应用需要在最上层显示,即在切换屏幕画面后也要能够显示,因为之前这块信号源切换就是用的rockit所以在不更改之前的框架的基础上,解决方案是,将QT的UI实时显示rockit的UI层中,涉及的模块大致上有SYS,MB,VO,RGN等。
这里可以从rockchip官方给的一些demo入手可能理解起来会比较好一点。在rockchip给的SDK中,rockit相关代码的路径是在platform/external/rockit/。
目前(项目)现有的视频处理是外部视频流不进行处理直接给到显示设备,也就是VI -> VO,现在是需要加一层UI,也不需要去修改原先的视频流,只需要在原有的显示画面上叠加上我们的UI就行了,关于UI的显示模块有RGN,那就是RGN -> VO(虽然后续并没有使用RGN模块😅)。
那么我们先参考之前的demo代码以及结合rk提供的官方文档看一下大概的流程:
代码中main函数开始会进行参数的一些初始化工作,然后就进入相关模块的初始化。main函数中大部分东西不需要我们理解,可能会改到的地方就是一些参数的修改。
switch (ctx->enMode) {
case TEST_VI_MODE_VI_FRAME_ONLY:
if (!ctx->stViChnAttr.u32Depth) {
RK_LOGE("depth need > 0 when vi not bind any other module!");
ctx->stViChnAttr.u32Depth = ctx->stViChnAttr.stIspOpt.u32BufCount;
}
s32Ret = test_vi_get_release_frame_loop(ctx);
break;
case TEST_VI_MODE_BIND_VO:
s32Ret = test_vi_bind_vo_loop(ctx);
break;
case TEST_VI_MODE_VI_STREAM_ONLY:
if (!ctx->stViChnAttr.u32Depth) {
RK_LOGE("depth need > 0 when vi not bind any other module!");
ctx->stViChnAttr.u32Depth = ctx->stViChnAttr.stIspOpt.u32BufCount;
}
s32Ret = test_vi_get_release_stream_loop(ctx);
break;
default:
RK_LOGE("noC support such test mode:%d", ctx->enMode);
break;
}
进入到相关模块初始化函数,先进行的初始化的流程是VO,(其他模块的初始化流程可以去查看RK的官方文档有很详细的讲解,路径platform/external/rockit/mpi/doc),然后才是RGN,因为RGN模块是基于VO的区域管理。
首先失能图层和设备,不然修改和创建是不会生效的,然后获取原有视频输出设备的公共属性,在原有的基础上进行修改,然后重新设置回去,然后就可以使能设备了,然后再绑定图层到设备,绑定完设置图层的相关属性,然后设置图层的拼接模式,设置完成后就可以使能该图层了,最后就是对通道的设置,设置通道的相关属性然后使能就完成了整个VO的初始化流程。
RK_MPI_VO_DisableLayer(VoLayer);
RK_MPI_VO_Disable(VoDev);
s32Ret = RK_MPI_VO_GetPubAttr(VoDev, &VoPubAttr);
if (s32Ret != RK_SUCCESS) {
return s32Ret;
}
s32Ret = RK_MPI_VO_SetPubAttr(VoDev, &VoPubAttr);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("RK_MPI_VO_SetPubAttr failed,s32Ret:%#X\n", s32Ret);
return s32Ret;
}
s32Ret = RK_MPI_VO_Enable(VoDev);
if (s32Ret != RK_SUCCESS) {
return s32Ret;
}
s32Ret = RK_MPI_VO_BindLayer(VoLayer, VoDev, VO_LAYER_MODE_GRAPHIC);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("RK_MPI_VO_BindLayer failed,s32Ret:%#X\n", s32Ret);
return RK_FAILURE;
}
s32Ret = RK_MPI_VO_SetLayerAttr(VoLayer, &stLayerAttr);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("RK_MPI_VO_SetLayerAttr failed,s32Ret:%#X\n", s32Ret);
return RK_FAILURE;
}
RK_MPI_VO_SetLayerSpliceMode(VoLayer, VO_SPLICE_MODE_RGA);
s32Ret = RK_MPI_VO_EnableLayer(VoLayer);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("RK_MPI_VO_EnableLayer failed,s32Ret:%#X\n", s32Ret);
return RK_FAILURE;
}
s32Ret = RK_MPI_VO_SetChnAttr(VoLayer, u32Ch, &stChnAttr);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("set chn Attr failed,s32Ret:%#X\n", s32Ret);
return RK_FAILURE;
}
// enable vo graph
s32Ret = RK_MPI_VO_EnableChn(ctx->pstRgnCtx.vo_layer, ctx->pstRgnCtx.vo_chn);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("Enalbe vo chn failed, s32Ret = %x\n", s32Ret);
goto __FAILED;
}
以上就是VO模块的大概的初始化流程,说得有点抽象😂,不过里面有三个关键字就是设备,图层,通道
。这三者的关系与rockit内部视频输出链路是息息相关的,设备就是指的视频流的输出设备,一般就是你的显示器;图层就是你这个设备的图层,一个设备有三个图层视频层+图形层+鼠标层;通道是你这个视频流以什么方式到达对应设备的对应图层。
我们结合RK的文档详解一下相关函数
RK_MPI_VO_GetPubAttr
在失能设备后,获取设备属性便于后面进行修改。
RK_MPI_VO_SetPubAttr
获取设备的公共属性后,我们一般修改的是:
VoPubAttr.enIntfType = VO_INTF_HDMI;
VoPubAttr.enIntfSync = VO_OUTPUT_1080P60;
这两个参数指定了设备接口类型和设备的显示配置。修改完后就可以调用下面的函数设置。
RK_MPI_VO_Enable
然后关于设备修改完成后就可以对其进行使能。
RK_MPI_VO_BindLayer
然后就是对图层的操作了,在失能图层后首先就是要先绑定图层到设备上。
在这里选择的图层号有以下几种,具体区别可以参考官方文档
#define RK356X_VOP_LAYER_CLUSTER_0 0
#define RK356X_VOP_LAYER_CLUSTER_1 2
#define RK356X_VOP_LAYER_ESMART_0 4
#define RK356X_VOP_LAYER_ESMART_1 5
#define RK356X_VOP_LAYER_SMART_0 6
#define RK356X_VOP_LAYER_SMART_1 7
图层类型的话有下面几种,鼠标层,图形层,视频层,虚拟层,一般是用前三种,而且一个设备只能最多支持鼠标层,图形层,视频层各一个。
typedef enum rkVO_LAYER_MODE_E {
VO_LAYER_MODE_CURSOR = 0,
VO_LAYER_MODE_GRAPHIC,
VO_LAYER_MODE_VIDEO,
VO_LAYER_MODE_VIRTUAL,
VO_LAYER_MODE_BUTT
} VO_LAYER_MODE_E;
RK_MPI_VO_SetLayerAttr
绑定对应图层到对应设备上后,可以配置图层属性。
该函数通过第二个参数 VO_VIDEO_LAYER_ATTR_S 来配置相关属性,相关参数的配置如下
stLayerAttr.enCompressMode = ctx->enCompressMode;
stLayerAttr.enPixFormat = static_cast<PIXEL_FORMAT_E>(ctx->s32VoPixFormat);
stLayerAttr.stDispRect.s32X = ctx->stVoDispRect.s32X;
stLayerAttr.stDispRect.s32Y = ctx->stVoDispRect.s32Y;
stLayerAttr.u32DispFrmRt = ctx->u32VoDispFrmRt;
stLayerAttr.stDispRect.u32Width = ctx->stVoDispRect.u32Width;
stLayerAttr.stDispRect.u32Height = ctx->stVoDispRect.u32Height;
stLayerAttr.stImageSize.u32Width = ctx->stVoImageSize.u32Width;
stLayerAttr.stImageSize.u32Height = ctx->stVoImageSize.u32Height;
stLayerAttr.bBypassFrame = RK_FALSE;
RK_MPI_VO_SetLayerSpliceMode
其中bBypassFrame属性需要为false,视频流才会在后面rgn进行拼接,不然视频会直接送到图层去,后续需要为其图层设置拼接的方式,如下。
也可以不调用该函数,因为默认图层使用GPU合成方式。
我这里只有当bBypassFrame属性需要为true时,设置GPU合成的方式才有效,否则就会报错create fance falied的错误,可能是不支持吧😥。
RK_MPI_VO_EnableLayer
这样我们图层就设置完成了,最后给图层使能即可。
RK_MPI_VO_SetChnAttr
然后就是设置通道属性了。
第三个参数设置通道属性,具体如下:
RK_MPI_VO_EnableChn
最后使能通道就可以了。
我感觉这个流程就好像你去到某个地方,你想去哪一栋楼(dev),然后想去哪一层(layer),具体怎么去坐电梯还是走楼梯(chn),一个只能自己理解的奇怪比喻😂。