当前位置:网站首页>RKMPP库快速上手--(一)RKMPP功能及使用详解
RKMPP库快速上手--(一)RKMPP功能及使用详解
2022-08-02 13:49:00 【Geek.Fan】
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
边栏推荐
- Detailed explanation of ORACLE expdp/impdp
- 智能指针-使用、避坑和实现
- 关于Google词向量模型(googlenews-vectors-negative300.bin)的导入问题
- 【ONE·Data || 排序入门】
- Flashback Technology of Oracle Database
- 嵌入式系统驱动初级【2】——字符设备驱动基础上_基础框架
- RISC-V instruction format and 6 basic integer instructions
- SQL函数 TRUNCATE
- 你真的懂单例模式么
- MySQL - ERROR 1045 (28000): Access denied for user ‘root’@‘localhost’ (using password: YES)
猜你喜欢
Mysql index details (with pictures and texts)
WPF效果第一百九十三篇之登录实现
基于 WeihanLi.Npoi 实现excel导入时纯汉字的日期转换
自媒体创作怎样提高原创度,打造爆款作品?
乐心湖‘s Blog——MySQL入门到精通 —— 囊括 MySQL 入门 以及 SQL 语句优化 —— 索引原理 —— 性能分析 —— 存储引擎特点以及选择 —— 面试题
tinymce 如何实现动态国际化
苏州大学:从 PostgreSQL 到 TDengine
网络安全第二次作业
Win11怎么修改关机界面颜色?Win11修改关机界面颜色的方法
Seata Distributed Transaction
随机推荐
Enterprise Network Planning Based on Huawei eNSP
关于C#使用DateTime数据的细节
好用的php空间,推荐国内三个优质的免费PHP空间[通俗易懂]
Based on the flask mall administrator functions
二进制中1的个数
[C language] Analysis of function recursion (1)
rhce第三天作业
鲁大师7月新机性能/流畅榜:骁龙8+正面对决天玑9000+,性能跑分突破123万!
永远退出机器学习界!
你真的懂单例模式么
“二舅”火了,自媒体短视频“爆火”的基本要素,你知道吗?
Embedded system driver primary [2] - based on character device driver _ basic framework
面试官:可以谈谈乐观锁和悲观锁吗
Mysql视图
stack && queue
Redis all
Large and comprehensive pom file example
网络安全第三次作业
Selenium本地打开远程浏览器
RHCE第一天作业