rkmpp库快速上手--rkmpp功能及使用详解(代码片段)

Geek.Fan Geek.Fan     2022-10-22     712

关键词:

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库快速上手--mpp解码入门(代码片段)

 一、RKMPP整个解码流程简单介绍一下:1.创建MPPcontext和MPPapi接口。(注意,和RGA一样,多个线程多个实例需要多个独立的的context)ret=mpp_create(&ctx,&mpi);if(MPP_OK!=ret)mpp_err("mpp_createfaile 查看详情

rkmpp库快速上手--mpp编码入门(代码片段)

   首先,了解MPP编码之前,先了解一下MPP的MPI接口。1、MPI接口结构   MPP设计的MPI接口,下面的图都来自于官方参考文档:           MppMem:C库malloc内存的封装;MppBuffer:dmabuf内存的封装;... 查看详情

rkmpp在ffmpeg上实现硬编解码(代码片段)

一、编译指令--enable-cross-compile--cross- prefix=/home/cks/linux_sdk/buildroot/output/rockchip_rk3399pro/host/bin/aarch64-buildroot-linux-gnu---sysroot=/home/cks/linux_sdk/buildroot/output/rockchip 查看详情

vue的安装及使用快速入门

...能。它提供了更加简洁、更易于理解的API,使得我们能够快速地上手并使用Vue.js。一、安装vue1、安装node.js,安装完node.js之后,npm也会自动安 查看详情

代码加约束masonry的使用(快速上手autolayout)及mmplaceholder标尺插件的使用

   前言    自己学习了一下Masonry这个代码加约束的第三方,发现确实很强大很方便,本来想把Masonry的介绍转载过来的,但是CSDN貌似没有转载功能,索性自己写一篇博客吧,顺便将自己的学习经验心得也分享出来,供大家参考,我自己... 查看详情

如何快速上手使用stm32库函数

一、背景如前文所述,利用标准库函数的好处在于,可以快速开发,不用去对着数据手册,小心翼翼的一位一位的配置那些繁复的寄存器,因为这些工作意法半导体已经找了一些顶级的工程师帮你做了,杰作既是其库函数。当然... 查看详情

前端layui框架快速上手详解(代码片段)

✍LayUI🔥前端框架LayUI详解地址🔥前端LayUI框架快速上手详解(一)https://blog.csdn.net/Augenstern_QXL/article/details/119748962🔥前端LayUI框架快速上手详解(二)https://blog.csdn.net/Augenstern_QXL/article/details/119 查看详情

rsync服务简介部署使用及原理详解(代码片段)

Rsync简介什么是rsync?Rsync是一款开源的、快速的、多功能的、可实现全量及增量的本地或远程数据同步备份的优秀工具。Rstync软件适用于unix/linux/windows等多种操作系统平台。Rsync英文全称为Remotesynchronization,即远程同步。从软件的... 查看详情

前端echarts可视化框架快速上手详解(代码片段)

✍目录脑图官方文档讲解较细致,但同时也较繁琐,本篇旨在记录常用样式的用法与注意点.参考视频:玩转ECharts可视化框架Pink老师前端ECharts框架学习1、ECharts1.1、ECharts下载1.1.1、Github下载下载并引入echarts.js文件(图... 查看详情

前端layui框架快速上手详解(代码片段)

✍目录总览🔥LayUI🔥前端框架LayUI详解地址🔥前端LayUI框架快速上手详解(一)https://blog.csdn.net/Augenstern_QXL/article/details/119748962🔥前端LayUI框架快速上手详解(二)https://blog.csdn.net/Augenstern_QXL/articl 查看详情

rkmppapi安装使用总结(代码片段)

...容api的原因,徒增几次无用的帧拷贝动作,并且使用的都是虚拟地址。在上一篇RGA的教学中,我们知道纯物 查看详情

前端echarts可视化框架快速上手详解(代码片段)

✍前端ECharts可视化框架完结🔥前端ECharts可视化框架完结地址🔥前端ECharts可视化框架快速上手详解(一)https://blog.csdn.net/Augenstern_QXL/article/details/119850486🔥前端ECharts可视化框架快速上手详解(二)https://blog.csdn.net/Augenste 查看详情

前端echarts可视化框架快速上手详解(代码片段)

✍目录脑图🔥前端ECharts可视化框架完结地址🔥前端ECharts可视化框架快速上手详解(一)https://blog.csdn.net/Augenstern_QXL/article/details/119850486🔥前端ECharts可视化框架快速上手详解(二)https://blog.csdn.net/Augenstern_QXL/artic 查看详情

自动化快速上手--python(10)--“类与继承”详解(代码片段)

 目录前言1、创建类1.1、了解类2、根据类创建实例2.1、访问属性2.2、调用方法2.3、创建多个实例3、使用类和实例3.1、给属性指定默认值3.2、修改属性值3.3、对属性值进行递增4、继承4.1、子类中定义属性以及方法4.2、将实例当属... 查看详情

轻松上手springsecurity,oauth,jwt(代码片段)

目录学习目标一.SpringSecurity1.SpringSecurity简介及快速入门<1>.SpringSecurity简介<2>.SpringSecurity快速入门2.SpringSecurity基本原理<1>.UserDetailsService详解<2>.PasswordEncoder密码解析器详解3.SpringSecurity自定义 查看详情

angularjs动画-nganimate--快速上手使用

AngularJS动画AngularJS提供了动画效果,可以配合CSS使用。AngularJS使用动画需要外部引入angular-animate.js库。1.把ngAnimate依赖注入写入当前的myApp模块中;<script>var app=angular.module(‘myApp‘,[‘ngAnimate‘]);</script>2.ngAnimate模型 查看详情

sqoop快速上手(代码片段)

ETL系列之Sqoop==楼兰==文章目录一、ETL简介1、关于ETL2、大数据与传统关系型数据库的关系3、Sqoop简介二、Sqoop下载三、Sqoop基础使用3.1前置软件3.2Sqoop安装3.3Sqoop基础使用1、查看Sqoop的帮助信息2、单独连接数据库3、导... 查看详情

arduino定时器&中断的使用和快速上手(代码片段)

Catalogue1.Intro2.什么是中断?3.中断快速上手4.什么是定时器?5.定时器快速上手5.1MsTimer25.2TimerOne6.注意事项7.总结8.References1.Intro定时器和中断都是单片机中的重要的功能,使用中断功能可以完成很多更加复杂的控制࿰... 查看详情