了解MPP的基本功能后,接下来具体分析编码的代码。首先把编码的代码提取出来,方便以后的使用。

完整的编码代码如下,相比较给出的示例代码,做了一些改动,输入的指令全部去除,将函数入口改为利用OpenCV打开USB摄像头,调用编码函数编码,编码后的数据保存在本地。

 #if defined(_WIN32)
#include "vld.h"
#endif #define MODULE_TAG "mpi_enc_test" #include <string.h>
#include <sys/time.h> #include "utils.h"
#include "rk_mpi.h"
#include "mpp_env.h"
#include "mpp_mem.h"
#include "mpp_log.h"
#include "mpp_time.h"
#include "mpp_common.h" #include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp> using namespace std;
using namespace cv; #define MAX_FILE_NAME_LENGTH 256
#define calTimeCost(begin,end)(end.tv_sec*1000.-begin.tv_sec*1000.+end.tv_usec/1000.-begin.tv_usec/1000.) typedef struct
{
MppCodingType type;
RK_U32 width;
RK_U32 height;
MppFrameFormat format; RK_U32 num_frames;
} MpiEncTestCmd; typedef struct
{
//global flow control flag
RK_U32 frm_eos;
RK_U32 pkt_eos;
RK_U32 frame_count;
RK_U64 stream_size; //input ang output file
FILE *fp_input;
FILE *fp_output; //input and output
MppBuffer frm_buf;
MppEncSeiMode sei_mode; //base flow context
MppCtx ctx;
MppApi *mpi;
MppEncPrepCfg prep_cfg;
MppEncRcCfg rc_cfg;
MppEncCodecCfg codec_cfg; //paramter for resource malloc
RK_U32 width;
RK_U32 height;
RK_U32 hor_stride; //horizontal stride
RK_U32 ver_stride; //vertical stride
MppFrameFormat fmt;
MppCodingType type;
RK_U32 num_frames; //resources
size_t frame_size;
//NOTE: packet buffer may overflow
size_t packet_size; //rate control runtime parameter
RK_S32 gop;
RK_S32 fps;
RK_S32 bps;
} MpiEncTestData; MpiEncTestData data;
MpiEncTestCmd cmd_ctx;
MpiEncTestData *ptr1; MPP_RET ret = MPP_OK; FILE *fp_out = fopen("result.h264", "w+b"); MPP_RET test_ctx_init(MpiEncTestData **data, MpiEncTestCmd *cmd)
{
MpiEncTestData *p = NULL;
MPP_RET ret = MPP_OK; if (!data || !cmd)
{
mpp_err_f("invalid input data %p cmd %p\n", data, cmd);
return MPP_ERR_NULL_PTR;
} p = mpp_calloc(MpiEncTestData, );
if (!p)
{
mpp_err_f("create MpiEncTestData failed\n");
ret = MPP_ERR_MALLOC;
goto RET;
} //get paramter from cmd
p->width = cmd->width;
p->height = cmd->height;
p->hor_stride = MPP_ALIGN(cmd->width, );
p->ver_stride = MPP_ALIGN(cmd->height, );
p->fmt = cmd->format;
p->type = cmd->type;
p->num_frames = cmd->num_frames; p->frame_size = p->hor_stride * p->ver_stride * / ;
p->packet_size = p->width * p->height; RET:
*data = p;
return ret;
} MPP_RET test_mpp_setup(MpiEncTestData *p2)
{
MPP_RET ret;
MppApi *mpi;
MppCtx ctx;
MppEncCodecCfg *codec_cfg;
MppEncPrepCfg *prep_cfg;
MppEncRcCfg *rc_cfg; if (NULL == p2)
{
return MPP_ERR_NULL_PTR;
} mpi = p2->mpi;
ctx = p2->ctx;
codec_cfg = &p2->codec_cfg;
prep_cfg = &p2->prep_cfg;
rc_cfg = &p2->rc_cfg; p2->fps = ;
p2->gop = ;
//p2->bps = p2->width * p2->height / 5 * p2->fps;
p2->bps = *; prep_cfg->change = MPP_ENC_PREP_CFG_CHANGE_INPUT | MPP_ENC_PREP_CFG_CHANGE_ROTATION | MPP_ENC_PREP_CFG_CHANGE_FORMAT;
prep_cfg->width = p2->width;
prep_cfg->height = p2->height;
prep_cfg->hor_stride = p2->hor_stride;
prep_cfg->ver_stride = p2->ver_stride;
prep_cfg->format = p2->fmt;
prep_cfg->rotation = MPP_ENC_ROT_0;
ret = mpi->control(ctx, MPP_ENC_SET_PREP_CFG, prep_cfg);
if (ret)
{
mpp_err("mpi control enc set prep cfg failed ret %d\n", ret);
goto RET;
} rc_cfg->change = MPP_ENC_RC_CFG_CHANGE_ALL;
rc_cfg->rc_mode = MPP_ENC_RC_MODE_VBR;
//rc_cfg->quality = MPP_ENC_RC_QUALITY_MEDIUM;
rc_cfg->quality = MPP_ENC_RC_QUALITY_CQP; if (rc_cfg->rc_mode == MPP_ENC_RC_MODE_CBR)
{
//constant bitrate has very small bps range of 1/16 bps
rc_cfg->bps_target = p2->bps;
rc_cfg->bps_max = p2->bps * / ;
rc_cfg->bps_min = p2->bps * / ;
}
else if (rc_cfg->rc_mode == MPP_ENC_RC_MODE_VBR)
{
if (rc_cfg->quality == MPP_ENC_RC_QUALITY_CQP)
{
//constant QP does not have bps
//rc_cfg->bps_target = -1;
//rc_cfg->bps_max = -1;
//rc_cfg->bps_min = -1; rc_cfg->bps_target = p2->bps;
rc_cfg->bps_max = p2->bps * / ;
rc_cfg->bps_min = p2->bps * / ;
}
else
{
//variable bitrate has large bps range
rc_cfg->bps_target = p2->bps;
rc_cfg->bps_max = p2->bps * / ;
rc_cfg->bps_min = p2->bps * / ;
}
} //fix input / output frame rate
rc_cfg->fps_in_flex = ;
rc_cfg->fps_in_num = p2->fps;
rc_cfg->fps_in_denorm = ;
rc_cfg->fps_out_flex = ;
rc_cfg->fps_out_num = p2->fps;
rc_cfg->fps_out_denorm = ; rc_cfg->gop = p2->gop;
rc_cfg->skip_cnt = ; mpp_log("mpi_enc_test bps %d fps %d gop %d\n", rc_cfg->bps_target, rc_cfg->fps_out_num, rc_cfg->gop);
ret = mpi->control(ctx, MPP_ENC_SET_RC_CFG, rc_cfg);
if (ret)
{
mpp_err("mpi control enc set rc cfg failed ret %d\n", ret);
goto RET;
} codec_cfg->coding = p2->type;
codec_cfg->h264.change = MPP_ENC_H264_CFG_CHANGE_PROFILE | MPP_ENC_H264_CFG_CHANGE_ENTROPY | MPP_ENC_H264_CFG_CHANGE_TRANS_8x8; //66 - Baseline profile
//77 - Main profile
//100 - High profile
codec_cfg->h264.profile = ; /*
* H.264 level_idc parameter
* 10 / 11 / 12 / 13 - qcif@15fps / cif@7.5fps / cif@15fps / cif@30fps
* 20 / 21 / 22 - cif@30fps / half-D1@@25fps / D1@12.5fps
* 30 / 31 / 32 - D1@25fps / 720p@30fps / 720p@60fps
* 40 / 41 / 42 - 1080p@30fps / 1080p@30fps / 1080p@60fps
* 50 / 51 / 52 - 4K@30fps
*/ codec_cfg->h264.level = ;
codec_cfg->h264.entropy_coding_mode = ;
codec_cfg->h264.cabac_init_idc = ;
//codec_cfg->h264.qp_min = 0;
//codec_cfg->h264.qp_max = 50; //codec_cfg->h264.transform8x8_mode = 0; ret = mpi->control(ctx, MPP_ENC_SET_CODEC_CFG, codec_cfg);
if (ret)
{
mpp_err("mpi control enc set codec cfg failed ret %d\n", ret);
goto RET;
} //optional
p2->sei_mode = MPP_ENC_SEI_MODE_ONE_FRAME;
ret = mpi->control(ctx, MPP_ENC_SET_SEI_CFG, &p2->sei_mode);
if (ret)
{
mpp_err("mpi control enc set sei cfg failed ret %d\n", ret);
goto RET;
} RET:
return ret;
} int pic_num = ;
MPP_RET read_yuv_buffer(RK_U8 *buf, Mat &yuvImg, RK_U32 width, RK_U32 height)
{
MPP_RET ret = MPP_OK;
RK_U32 read_size;
RK_U32 row = ;
RK_U8 *buf_y = buf;
RK_U8 *buf_u = buf_y + width * height;
RK_U8 *buf_v = buf_u + width * height / ; int yuv_size = width * height */; memcpy(buf, yuvImg.data, yuv_size); err:
return ret;
} MPP_RET test_mpp_run_yuv(Mat yuvImg, MppApi *mpi1, MppCtx &ctx1, unsigned char * &H264_buf, int &length)
{
MpiEncTestData *p3 = ptr1;
MPP_RET ret; MppFrame frame = NULL;
MppPacket packet = NULL;
void *buf = mpp_buffer_get_ptr(p3->frm_buf); ret = read_yuv_buffer(buf, yuvImg, p3->width, p3->height); ret = mpp_frame_init(&frame);
if (ret)
{
mpp_err_f("mpp_frame_init failed\n");
goto RET;
} mpp_frame_set_width(frame, p3->width);
mpp_frame_set_height(frame, p3->height);
mpp_frame_set_hor_stride(frame, p3->hor_stride);
mpp_frame_set_ver_stride(frame, p3->ver_stride);
mpp_frame_set_fmt(frame, p3->fmt);
mpp_frame_set_buffer(frame, p3->frm_buf);
mpp_frame_set_eos(frame, p3->frm_eos); ret = mpi1->encode_put_frame(ctx1, frame); if (ret)
{
mpp_err("mpp encode put frame failed\n");
goto RET;
} ret = mpi1->encode_get_packet(ctx1, &packet);
if (ret)
{
mpp_err("mpp encode get packet failed\n");
goto RET;
} if (packet)
{
//write packet to file here
void *ptr = mpp_packet_get_pos(packet);
size_t len = mpp_packet_get_length(packet); p3->pkt_eos = mpp_packet_get_eos(packet); H264_buf = new unsigned char[len]; memcpy(H264_buf, ptr, len);
length = len; mpp_packet_deinit(&packet);
p3->stream_size += len;
p3->frame_count++; if (p3->pkt_eos)
{
mpp_log("found last packet\n");
mpp_assert(p3->frm_eos);
}
}
RET:
return ret;
} MppApi *mpi;
MppCtx ctx; MpiEncTestData *test_mpp_run_yuv_init(MpiEncTestData *p, MpiEncTestCmd *cmd, int width , int height, unsigned char * &SPS_buf, int &SPS_length)
{
//MppApi *mpi;
//MppCtx ctx; cmd->width = width;
cmd->height = height;
cmd->type = MPP_VIDEO_CodingAVC;
cmd->format = MPP_FMT_YUV420P;
cmd->num_frames = ; ret = test_ctx_init(&p, cmd);
if (ret)
{
mpp_err_f("test data init failed ret %d\n", ret);
goto MPP_TEST_OUT;
}
mpp_log("p->frame_size = %d----------------\n", p->frame_size); ret = mpp_buffer_get(NULL, &p->frm_buf, p->frame_size);
if (ret)
{
mpp_err_f("failed to get buffer for input frame ret %d\n", ret);
goto MPP_TEST_OUT;
}
mpp_log("mpi_enc_test encoder test start w %d h %d type %d\n",p->width, p->height, p->type); //encoder demo
ret = mpp_create(&p->ctx, &p->mpi);
if (ret)
{
mpp_err("mpp_create failed ret %d\n", ret);
goto MPP_TEST_OUT;
} ret = mpp_init(p->ctx, MPP_CTX_ENC, p->type);
if (ret)
{
mpp_err("mpp_init failed ret %d\n", ret);
goto MPP_TEST_OUT;
} ret = test_mpp_setup(p);
if (ret)
{
mpp_err_f("test mpp setup failed ret %d\n", ret);
goto MPP_TEST_OUT;
} mpi = p->mpi;
ctx = p->ctx; //p->fp_output = fopen("output.h264", "w+b"); if (p->type == MPP_VIDEO_CodingAVC)
{
MppPacket packet = NULL; ret = mpi->control(ctx, MPP_ENC_GET_EXTRA_INFO, &packet);
if (ret)
{
mpp_err("mpi control enc get extra info failed\n");
} //get and write sps/pps for H.264
if (packet)
{
void *ptr = mpp_packet_get_pos(packet);
size_t len = mpp_packet_get_length(packet); SPS_buf = new unsigned char[len]; //fwrite(ptr, 1, len, fp_out);
memcpy(SPS_buf, ptr, len);
SPS_length = len; packet = NULL;
}
} return p;
MPP_TEST_OUT:
return p;
} MpiEncTestData *p = &data;
MpiEncTestCmd *cmd = &cmd_ctx; unsigned char *H264_buf = NULL;
int length = ;
unsigned char *SPS_buf = NULL;
int SPS_length = ; int frame_num = ; int MyYuvtoH264(int width, int height, Mat yuv_frame, unsigned char* (&encode_buf), int &encode_length)
{
if(frame_num == )
{
ptr1 = test_mpp_run_yuv_init(p, cmd, width, height, SPS_buf, SPS_length);
test_mpp_run_yuv(yuv_frame, mpi, ctx, H264_buf, length); encode_buf = new unsigned char[SPS_length + length];
memcpy(encode_buf, SPS_buf, SPS_length);
memcpy(encode_buf + SPS_length, H264_buf, length);
encode_length = length + SPS_length; frame_num++; delete H264_buf;
delete SPS_buf;
}
else
{
test_mpp_run_yuv(yuv_frame, mpi, ctx, H264_buf, length); encode_buf = new unsigned char[length]; memcpy(encode_buf, H264_buf, length);
encode_length = length; delete H264_buf;
} //fwrite(H264_buf, 1, length, fp_out);
return ;
}

MPP_encode

具体分析:

1、MPI接口结构:

在看代码前,整理下MPP设计的MPI接口,下面的图都来自于官方参考文档:

MppMem:C库malloc内存的封装;

MppBuffer:dmabuf内存的封装;

MppPacket:一维缓存封装,可以从MppMem、MppBuffer生成,用来表示码流数据;

MppFrame:二维帧数据封装,可以从MppMem、MppBuffer生成,用来表示图像数据;

MppMeta、MppTask:输入输出用任务的高级组合接口,可以指定输入输出方式;

使用MppPacket和MppFrame就可以完成一般的编解码工作。

以视频编码为例,图像输入端把图像数据和大小交给MppFrame,通过encode_put_frame输入,在输出端通过encode_get_packet得到编码后的码流MppPacket,就完成了一帧数据的编码。

2、MPI接口使用:

MPI是MPP提供给用户的接口,通过C结构里的函数指针方式提供给用户,用户可以通过MPP上下文结构MppCtx与MPI接口结构MppApi组合实现编解码器。

如上图所示,mpp_create,mpp_init,mpp_destory操纵MppCtx接口,红框内是实现编码与解码的过程。红框内的函数调用分为编解码流程接口put/get_packet/frame和相关的control和reset接口。

3、编码器接口

control编码配置命令

编码开始之前要进行一定的参数配置,参数设置通过control接口。一般需要配置三类信息:

码率控制方式(MPPEncRcCfg),通过命令MPP_ENC_RC_CFG配置;

输入控制配置(MppEncPrepCfg),通过命令MPP_ENC_SET_PREP_CFG配置;

协议控制配置(MppEncCodecCfg),通过命令MPP_ENC_SET_CODEC_CFG配置;

基本流程:

三个配置中,码率控制配置和输入控制配置是必须的,协议控制配置时可选的高级选项。

encode_put_frame:

MPP_RET encode_put_frame(MppCtx ctx,MppFrame frame)

ctx:MPP编码器实例;

frame:带输入的图像数据;

encode_get_packet:

MPP_RET encode_get_packet(MppCtx ctx,MppPacket *packet)

ctx:MPP编码器实例;

packet:编码后的码流数据;

H264编码器的输出数据分为头信息(SPS/PPS/SEI)和图像数据(I/P Slice)两部分,头信息通过control接口的MPP_ENC_GET_EXTRA_INFO命令获取,图像数据通过encode_get_packet接口获取。获取头信息时,要保证所有的参数更新完毕,这样生成的头信息才是最新的。目前encode_get_packet函数获取的码流都含有00 00 00 01起始码,如果想去掉起始码,可以从起始码之后的地址进行拷贝。

MPP-编码示例的更多相关文章

  1. UTF-8、GB2312、GBK编码格式详解和编码示例

    UTF-8.GB2312.GBK编码格式详解 参考文章 UTF-8 使用1~4个字节对每个字符进行编码 128个ASCII字符字需要一个字节编码 带有附加符号的拉丁文.希腊文.西里尔字母.亚美尼亚语. ...

  2. 编码UTF-8

    ☯,首先,这并不是图片,这是一个unicode字符,Yin Yang,即阴阳符,码点为U+262F.如果你的浏览器无法显示,可以查看这个链接http://www.fileformat.info/inf ...

  3. URL 字符编码

    URL 编码会将字符转换为可通过因特网传输的格式. URL - 统一资源定位器 Web 浏览器通过 URL 从 web 服务器请求页面. URL 是网页的地址,比如http://www.cnblogs ...

  4. UTF-7编码

    目录 1 编码    1 2 编码代码(C++)    2 3 解码代码(C++)    4 4 测试代码(VC++)    7 1 编码 UTF-7编码的规则及特点为: 1)UTF16小于等于 0x ...

  5. C#调用Geocoding API进行地理编码与逆编码

    使用C#调用Geocoding API来将地址转为经纬度,或者将经纬度转变为具体的地址. Geocoding API的详细介绍参见:http://developer.baidu.com/map/web ...

  6. html-----018----HTML Web Server/HTML URL 字符编码

    HTML Web Server 如果希望向世界发布您的网站,那么您必须把它存放在 web 服务器上. 托管自己的网站 在自己的服务器上托管网站始终是一个选项.有几点需要考虑: 硬件支出 如果要运行“真 ...

  7. 【JAVA编码专题】深入分析 Java 中的中文编码问题

    http://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/ 几种常见的编码格式 为什么要编码 不知道大家有没有想过一个问题,那就是为什么 ...

  8. Alamofire源码解读系列(四)之参数编码(ParameterEncoding)

    本篇讲解参数编码的内容 前言 我们在开发中发的每一个请求都是通过URLRequest来进行封装的,可以通过一个URL生成URLRequest.那么如果我有一个参数字典,这个参数字典又是如何从客户端传递 ...

  9. JAVA NIO工作原理及代码示例

    简介:本文主要介绍了JAVA NIO中的Buffer, Channel, Selector的工作原理以及使用它们的若干注意事项,最后是利用它们实现服务器和客户端通信的代码实例. 欢迎探讨,如有错误敬请 ...

  10. js 对url进行编码和解码的三种方式

    一.escape 和 unescape escape 原理:对除 ASCII字母.数字.标点符号(@ * _ + - . /) 以外的字符进行编码 .编码的字符被替换成了十六进制的转义序列 不编码的字 ...

随机推荐

  1. d3实现家族树

      1.  jQuery和CSS3支持移动手机的DOM元素移动和缩放插件:panzoom   2.拖动:jqueryUI-Draggable.touchpunch   3.图表:echart.heig ...

  2. SQL优化--inner、left join替换in、not in、except

    新系统上线,用户基数16万,各种查询timeout.打开砂锅问到底,直接看sql语句吧,都是泪呀,一大堆in\not in\except.这里总结一下,怎么替换掉in\not in\except. 1 ...

  3. sqlserver2014无法打开报Cannot find one or more components_修复方案

    前言:我跟网上大家的原因基本一样,就是好久没用sqlserver了,中间也对VS进行过卸载升级等,突然有一天发现,打开Sqlserver时打不开了,出了一个弹框:Cannot find one or ...

  4. 2019腾讯WXG移动客户端暑期实习面经

    微信这个比较迷,二面完官网流程灰了,但是过了一周多突然来三面,下午面完三面晚上HR面,第三天offer call, 莫名其妙过了 之前以为已经挂了,所以没有写面经,现在距一面已经快一个月了,只能还记得 ...

  5. Go基础(3)

    demo1: package main import "fmt" func print() { for i := 1; i < 10; i++ { for j := 1; j ...

  6. Hadoop系列006-HDFS概念及命令行操作

    本人微信公众号,欢迎扫码关注! HDFS概念及命令行操作 一.HDFS概念 1.1 概念 HDFS,它是一个文件系统,用于存储文件,通过目录树来定位文件:其次,它是分布式的,由很多服务器联合起来实现其 ...

  7. body标签中l的相关标签

    字体标签: h1~h6.font. u.b,.strong. em. sup. sub 排版标签: div,.span.br.hr.center.pre 图片标签: img 超链接: a 列表标签: ...

  8. 总结C语言字符检测函数:isalnum、isalpha...

    前言:最近一直在刷leetcode的题,用到isalnum函数,用man手册查找了一下,总共有13个相关函数如下: #include <ctype.h> int isalnum(int c ...

  9. 阿里 Java 手册系列教程:为啥强制子类、父类变量名不同?

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 父子类变量名相同会咋样? 为啥强制子类.父类变量名不同? ...

  10. 【设计模式+原型理解】第一章:使用Javascript来巧妙实现经典的设计模式

    刚开始学习设计模式之前,我是没想说要学习设计模式的,我只是因为想学习JS中的原型prototype知识,一开始我想JS中为什么要存在原型这个东西?于是慢慢通过原型而接触到设计模式,后来发现我这个过程是 ...