一. RK3588的SDK源码下的external目录
在RK3588的SDK源码目录下,有一个external 目录,里面存放的是一些应用测试代码,其中external/rockit 目录存放的是Rockchip提供的媒体处理接口(Rockchip Media Process Interface,简称 RK MPI),可支持应用软件快速开发。包含硬件的编解码器,视频输出输入等软件开发。
二. 硬件解码程序 rk_mpi_vdec_test
external/rockit/mpi/example/mod/test_mpi_vdec.cpp 文件里面是一个RK编写的视频解码播放demo,源码test_mpi_vdec.cpp 编译后生成的可执行文件是 rk_mpi_vdec_test 。 RK3588有4个vp,分别是vp0~vp3。这个demo默认绑定显示控制器是vp0,使用HDMI进行视频播放。
比如播放一个1080P的H265视频文件可以如下:
./rk_mpi_vdec_test -i 00.h265 -C 12 -w 1920 -h 1080
-i 指定的是待解码的文件
-C 指定解码的格式 8:h264, 9:mjpeg, 12:h265
-w 是解码的视频宽
-h 是解码的视频高
视频播放的效果如下:
demo源码写的并不是很完善,比较多参数是直接在源码中写死的,比如分辨率固定是1920x1080,有些命令行参数并不起效,需要手动修改源码。
三. 修改播放视频为MIPI接口
由于我这边项目使用的是MIPI显示屏,使用的vop2中的vp2 进行视频输出,并且分辨率是2560x1536,需要修改程序中的部分代码:
修改代码 rockit/mpi/example/mod/test_mpi_vdec.cpp 中下面函数:
static RK_S32 create_vo(RK_U32 ChCnt) {
/* Enable VO */
VO_PUB_ATTR_S VoPubAttr;
VO_VIDEO_LAYER_ATTR_S stLayerAttr;
RK_S32 s32Ret = RK_SUCCESS;
VO_CHN_ATTR_S stChnAttr;
VO_LAYER VoLayer = 2; //更改为vp2
VO_DEV VoDev = 2; //更改为vp2
RK_MPI_VO_DisableLayer(VoLayer);
RK_MPI_VO_DisableLayer(4);
RK_MPI_VO_DisableLayer(5);
RK_MPI_VO_DisableLayer(6);
RK_MPI_VO_DisableLayer(7);
RK_MPI_VO_Disable(VoDev);
memset(&VoPubAttr, 0, sizeof(VO_PUB_ATTR_S));
memset(&stLayerAttr, 0, sizeof(VO_VIDEO_LAYER_ATTR_S));
stLayerAttr.enPixFormat = RK_FMT_RGB888;
stLayerAttr.stDispRect.s32X = 0;
stLayerAttr.stDispRect.s32Y = 0;
stLayerAttr.u32DispFrmRt = 30;
stLayerAttr.stDispRect.u32Width = 2560; //分辨率改成2560x1536
stLayerAttr.stDispRect.u32Height = 1536;
stLayerAttr.stImageSize.u32Width = 2560;
stLayerAttr.stImageSize.u32Height = 1536;
s32Ret = RK_MPI_VO_GetPubAttr(VoDev, &VoPubAttr);
if (s32Ret != RK_SUCCESS) {
return s32Ret;
}
VoPubAttr.enIntfType = VO_INTF_MIPI; /* 更改输出设备MIPI */
VoPubAttr.enIntfSync = VO_OUTPUT_DEFAULT;
..... 省略
RK_S32 unit_test_mpi_vdec(TEST_VDEC_CTX_S *ctx) {
MPP_CHN_S stSrcChn, stDestChn;
RK_S32 s32Ret = RK_FAILURE;
RK_U32 u32Ch = 0;
TEST_VDEC_CTX_S vdecCtx[VDEC_MAX_CHN_NUM];
pthread_t vdecThread[VDEC_MAX_CHN_NUM];
pthread_t getPicThread[VDEC_MAX_CHN_NUM];
for (u32Ch = 0; u32Ch < ctx->u32ChNum; u32Ch++) {
if (ctx->u32ChNum > 1) {
ctx->u32ChnIndex = u32Ch;
}
memcpy(&(vdecCtx[u32Ch]), ctx, sizeof(TEST_VDEC_CTX_S));
// Does not support JPEG stream framing, read the size of one picture at a time
// and send it to the decoder.
if (ctx->u32InputMode == VIDEO_MODE_STREAM || ctx->enCodecId == RK_VIDEO_ID_MJPEG ||
ctx->enCodecId == RK_VIDEO_ID_JPEG) {
mpi_create_stream_mode(&vdecCtx[u32Ch], u32Ch);
pthread_create(&vdecThread[u32Ch], 0, mpi_send_stream, reinterpret_cast<void *>(&vdecCtx[u32Ch]));
} else {
return -1;
}
//pthread_create(&getPicThread[u32Ch], 0, mpi_get_pic, reinterpret_cast<void *>(&vdecCtx[u32Ch]));
}
s32Ret = create_vo(ctx->u32ChNum);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("create vo ch failed");
return -1;
}
// bind vi to vo
for (int i = 0; i < ctx->u32ChNum; i++) {
stSrcChn.enModId = RK_ID_VDEC;
stSrcChn.s32DevId = 0;
stSrcChn.s32ChnId = i;
stDestChn.enModId = RK_ID_VO;
stDestChn.s32DevId = 2; //修改为vp2
stDestChn.s32ChnId = i;
s32Ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("vi band vo fail:%x", s32Ret);
return -1;
}
// enable vo
s32Ret = RK_MPI_VO_EnableChn(2, i);/* 启用vp2 */
if (s32Ret != RK_SUCCESS) {
RK_LOGE("Enalbe vo chn failed, s32Ret = %d\n", s32Ret);
return -1;
}
}
for (u32Ch = 0; u32Ch < ctx->u32ChNum; u32Ch++) {
pthread_join(vdecThread[u32Ch], RK_NULL);
pthread_join(getPicThread[u32Ch], RK_NULL);
vdecCtx[u32Ch].threadExit = RK_TRUE;
if (ctx->u32ChNum > 1) {
mpi_destory_vdec(&vdecCtx[u32Ch], u32Ch);
} else {
mpi_destory_vdec(&vdecCtx[u32Ch], ctx->u32ChnIndex);
}
}
return RK_SUCCESS;
}