RKMPP库快速上手--RKMPP功能及使用详解
Posted Geek.Fan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RKMPP库快速上手--RKMPP功能及使用详解相关的知识,希望对你有一定的参考价值。
1、MPP简介
MPP(Media Process Platform )是Rockchip提供的一款硬件编解码媒体处理软件平台,适用于Rockchip芯片系列。它屏蔽了有关芯片的复杂底层处理,屏蔽了不同芯片的差异,为使用者提供了一组MPI统一接口。如果想达到最好的效果,必须要通过librockchip_mpp来直接编码实现编解码。
我们可以通过gstreamer和ffmpeg的mpp插件来使用mpp硬件加速,但是这两种应用程序都会因为兼容api的原因,徒增几次无用的帧拷贝动作,并且使用的都是虚拟地址。我们知道纯物理连续地址的硬件操作是非常快的,转到虚拟地址后效率就会降低。如果想榨干芯片的性能,开发最完美的代码,纯连续的物理buffer和mpp+rga是离不开的。
Mpp的API思路其实跟目前绝大多数的编解码库是一致的,都是queue/dequeue的队列操作方式,先设置好编解码状态,然后不停的queue/dequeue input/output buffer就可以实现编解码控制了。如果大家熟悉FFmpeg,那学习MPP会非常容易,MPP和FFmpeg的api非常相像。
Mpp库自带了编解码的sample,但是相关文档和注释较少,本文在原有sample的基础上添加了注释,并将部分功能封装成型。
2、MPP提供的功能
MPP提供有以下常用功能:
- 视频解码
- H.265 / H.264 / H.263 / VP9 / VP8 / MPEG-4 / MPEG-2 / MPEG-1 / VC1 / MJPEG
- 视频编码
- H.264 / VP8 / MJPEG
- 视频处理
- 视频拷贝,缩放,色彩空间转换,场视频解交织(Deinterlace)
3、MPP系统架构
4、MPP库的MPI接口介绍
5、MPI数据结构说明
MppMem: 为 C 库 malloc 内存的封装。
MppBuffer: 为硬件用的 dmabuf 内存的封装。
MppPacket: 为一维缓存封装,可以从 MppMem 和 MppBuffer 生成,表示码流数据。
MppFrame: 为二维帧数据封装,可以从 MppMem 和 MppBuffer 生成,表示图像数据。
MppMeta、MppTask:输入输出用任务的高级组合接口,可以指定输入输出方式;
使用 MppPacket 和 MppFrame 就可以简单有效的完成一般的视频编解码工作。
以视频解码为例,码流输入端把地址和大小赋值给 MppPacket,通过 put_packet 接口输入,在输出端通过 get_frame 接口得到输入图像 MppFrame,即可完成最简单的视频解码过程。
6、解码流程示例
7、RK-MPP库工具介绍与使用
MPP 提供了一些单元测试用的工具程序,这种程序可以对软硬件平台以及 MPP 库本身进行测试
mpp_info_test
用于读取和打印 MPP 库的版本信息,在反馈问题时,可以把打印出来信息附上。
mpp_buffer_test
用于测试内核的内存分配器是否正常。
mpp_mem_test
用于测试 C 库的内存分配器是否正常。
mpp_runtime_test
用于测试一些软硬件运行时环境是否正常。
mpp_platform_test
用于读取和测试芯片平台信息是否正常
8、交叉编译MPP库
unzip mpp-develop.zip && cd mpp-develop
cd build/linux/aarch64(根据平台选择) && vim arm.linux.cross.cmake(看下是否是对应平台的交叉编译工具)
./arm.linux.cross.cmake && make,编译之后,动态库在mpp目录下,测试工具在test目录下
将工具和动态库push到rk的平台上(一般rk的平台都已经自带MPP动态库了),以解码为例执行./mpi_dec_test -t 7 -i /sdcard/input.h264 -n 10
9、mpp测试程序说明及代码示例
mpi_enc_test:使用同步界面(轮询,出队和入队),对raw data进行编码以压缩视频。
mpi_dec_test:使用同步接口和异步接口(decode_put_packet和decode_get_frame),将视频压缩解码为yuv格式的raw data。
mpi_rc_test:编码使用详细的比特率控制配置。
mpi_rc2_test:使用详细的比特率控制配置进行编码,而cfg参数则来自mpi_rc.cfg。
mpi_test:mpi调用方法的简单描述,仅供参考
mpp_event_trigger:事件触发测试。
mpp_parse_cfg:mpp解析器cfg测试。
vpu_api_test:编码或解码使用旧版接口,以便与以前的vpu接口兼容
(1)、参考mpi_dec_test.c 写一个最简单的解码demo
#include <string.h>
#include "rk_mpi.h"
#include "mpp_mem.h"
#include "mpp_env.h"
#include "mpp_time.h"
#include "mpp_common.h"
#include "mpi_dec_utils.h"
#include "utils.h"
typedef struct
MppCtx ctx;
MppApi *mpi;
RK_U32 eos;
char *buf;
MppBufferGroup frm_grp;
MppPacket packet;
size_t packet_size;
MppFrame frame;
FILE *fp_input;
FILE *fp_output;
RK_S32 frame_count;
RK_S32 frame_num;
size_t max_usage;
MpiDecLoopData;
static int decode_simple(MpiDecLoopData *data)
RK_U32 pkt_done = 0;
RK_U32 err_info = 0;
MPP_RET ret = MPP_OK;
MppCtx ctx = data->ctx;
MppApi *mpi = data->mpi;
char *buf = data->buf;
MppPacket packet = data->packet;
MppFrame frame = NULL;
size_t read_size = fread(buf, 1, data->packet_size, data->fp_input);
if (read_size != data->packet_size || feof(data->fp_input))
printf("found last packet\\n");
data->eos = 1;
mpp_packet_write(packet, 0, buf, read_size);
mpp_packet_set_pos(packet, buf);
mpp_packet_set_length(packet, read_size);
if (data->eos)
mpp_packet_set_eos(packet);
do
if (!pkt_done)
ret = mpi->decode_put_packet(ctx, packet);
if (MPP_OK == ret)
pkt_done = 1;
do
RK_S32 get_frm = 0;
RK_U32 frm_eos = 0;
ret = mpi->decode_get_frame(ctx, &frame);
if (frame)
if (mpp_frame_get_info_change(frame))
RK_U32 width = mpp_frame_get_width(frame);
RK_U32 height = mpp_frame_get_height(frame);
RK_U32 hor_stride = mpp_frame_get_hor_stride(frame);
RK_U32 ver_stride = mpp_frame_get_ver_stride(frame);
RK_U32 buf_size = mpp_frame_get_buf_size(frame);
printf("decode_get_frame get info changed found\\n");
printf("decoder require buffer w:h [%d:%d] stride [%d:%d] buf_size %d", width, height, hor_stride, ver_stride, buf_size);
if (NULL == data->frm_grp)
ret = mpp_buffer_group_get_internal(&data->frm_grp, MPP_BUFFER_TYPE_ION);
if (ret)
printf("get mpp buffer group failed ret %d\\n", ret);
break;
ret = mpi->control(ctx, MPP_DEC_SET_EXT_BUF_GROUP, data->frm_grp);
if (ret)
printf("set buffer group failed ret %d\\n", ret);
break;
else
ret = mpp_buffer_group_clear(data->frm_grp);
if (ret)
printf("clear buffer group failed ret %d\\n", ret);
break;
ret = mpp_buffer_group_limit_config(data->frm_grp, buf_size, 24);
if (ret)
printf("limit buffer group failed ret %d\\n", ret);
break;
ret = mpi->control(ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
if (ret)
printf("info change ready failed ret %d\\n", ret);
break;
else
err_info = mpp_frame_get_errinfo(frame) | mpp_frame_get_discard(frame);
if (err_info)
printf("decoder_get_frame get err info:%d discard:%d.\\n", mpp_frame_get_errinfo(frame), mpp_frame_get_discard(frame));
data->frame_count++;
printf("decode_get_frame get frame %d\\n", data->frame_count);
if ( (!err_info) && (data->frame_count==data->frame_num))
dump_mpp_frame_to_file(frame, data->fp_output);
frm_eos = mpp_frame_get_eos(frame);
mpp_frame_deinit(&frame);
frame = NULL;
get_frm = 1;
if (data->frm_grp)
size_t usage = mpp_buffer_group_usage(data->frm_grp);
if (usage > data->max_usage)
data->max_usage = usage;
if (data->eos && pkt_done && !frm_eos)
msleep(10);
continue;
if (frm_eos)
printf("found last frame\\n");
break;
if (data->frame_num && data->frame_count >= data->frame_num)
data->eos = 1;
break;
if (get_frm)
continue;
break;
while (1);
if (data->frame_num && data->frame_count >= data->frame_num)
data->eos = 1;
printf("reach max frame number %d\\n", data->frame_count);
break;
if (pkt_done)
break;
while (1);
return ret;
int mpi_dec_test_decode(char **argv)
MPP_RET ret = MPP_OK;
size_t file_size = 0;
MppParam param = NULL;
RK_U32 need_split = 1;
MpiDecLoopData data;
memset(&data, 0, sizeof(data));
data.eos = 0;
data.packet_size = MPI_DEC_STREAM_SIZE;
data.frame_count = 0;
data.frame_num = 1;
data.fp_input = fopen(argv[1], "rb");
data.fp_output = fopen(argv[2], "w+");
if ( (NULL == data.fp_input) || (NULL == data.fp_output) )
printf("failed to open input/output file \\n");
goto MPP_TEST_OUT;
data.buf = mpp_malloc(char, data.packet_size);
ret = mpp_packet_init(&data.packet, data.buf, data.packet_size);
if(MPP_OK != ret)
printf("mpp_packet_init error\\n");
goto MPP_TEST_OUT;
ret = mpp_create(&data.ctx, &data.mpi);
if(MPP_OK != ret)
printf("mpp_create error\\n");
goto MPP_TEST_OUT;
ret = mpp_init(data.ctx, MPP_CTX_DEC, MPP_VIDEO_CodingAVC);
if (MPP_OK != ret)
printf("mpp_init failed\\n");
goto MPP_TEST_OUT;
param = &need_split;
ret = data.mpi->control(data.ctx, MPP_DEC_SET_PARSER_SPLIT_MODE, param);
if (MPP_OK != ret)
printf("mpi->control failed\\n");
goto MPP_TEST_OUT;
fseek(data.fp_input, 0L, SEEK_END);
file_size = ftell(data.fp_input);
rewind(data.fp_input);
printf("input file size %ld\\n", file_size);
while (!data.eos)
decode_simple(&data);
ret = data.mpi->reset(data.ctx);
if (MPP_OK != ret)
printf("mpi->reset failed\\n");
goto MPP_TEST_OUT;
MPP_TEST_OUT:
if (data.packet)
mpp_packet_deinit(&data.packet);
data.packet = NULL;
if (data.ctx)
mpp_destroy(data.ctx);
data.ctx = NULL;
if (data.buf)
mpp_free(data.buf);
data.buf = NULL;
if (data.frm_grp)
mpp_buffer_group_put(data.frm_grp);
data.frm_grp = NULL;
if (data.fp_output)
fclose(data.fp_output);
data.fp_output = NULL;
if (data.fp_input)
fclose(data.fp_input);
data.fp_input = NULL;
return ret;
int main(int argc, char **argv)
RK_S32 ret = 0;
if(argc != 3 )
printf("please input options\\n");
return -1;
ret = mpi_dec_test_decode(argv);
return ret;
10、测试MPP解码H264,解码输出格式为YUV420SP
./mpi_dec_test input.h264 out.yuv
以上是关于RKMPP库快速上手--RKMPP功能及使用详解的主要内容,如果未能解决你的问题,请参考以下文章