MPP关键配置(讲解在注释中)

代码链接:https://download.csdn.net/download/qq_39839546/67314289
已设置成了0积分下载,欢迎大家下载分享。

为了方便配置功能,我们一般将Mpp配置的关键参数定义为结构体:

struct MPP_ENC_DATA  //编码所需要的数据
{// global flow control flaguint32_t frm_eos;uint32_t pkt_eos;uint32_t frame_count;uint64_t stream_size;// base flow contextMppCtx ctx;MppApi *mpi;MppEncPrepCfg prep_cfg;MppEncRcCfg rc_cfg;MppEncCodecCfg codec_cfg;// input / outputMppBuffer frm_buf;//为编码时数据存放开辟的内存MppEncSeiMode sei_mode;uint32_t width;//图像宽uint32_t height;//图像高
/*Stride:stride其实是图像处理中常用的概念,是关于数据对齐的一种概念。一行有 11 个像素(Width = 11), 对一个 32 位(每个像素 4 字节)的图像, Stride = 11 * 4 = 44.但还有个字节对齐的问题, 譬如:一行有 11 个像素(Width = 11), 对一个 24 位(每个像素 3 字节)的图像, Stride = 11 * 3 + 3 = 36.为什么不是 Stride = 33? 因为它是按 4 字节对齐的.根据上面道理, 我们可以手动计算 Stride 的值:1)Stride = 每像素占用的字节数(也就是像素位数/8) * Width;2)如果 Stride 不是 4 的倍数, 那么 Stride = Stride + (4 - Stride mod 4);
*/uint32_t hor_stride;uint32_t ver_stride;//输入图像格式MppFrameFormat fmt = MPP_FMT_YUV422_YUYV;//输出视频流格式MppCodingType type = MPP_VIDEO_CodingAVC;uint32_t num_frames;// resourcessize_t frame_size;/*在视频编码中,GOP的意思是画面组,指定了intra-和inter-帧的顺序。GOP是一个编码视频流中的一组连续的画面。每一个编码的视频流都由连续的GOP组成。压缩的视频流中GOP相对独立,解码器解码新的GOP时需要之前的帧来解码后面的帧,GOP的存在也可以实现在视频中更快地定位。*/int32_t gop = 60;//帧率int32_t fps = 30;//码率int32_t bps;//输出文件头FILE *fp_output;
};

然后是编码的基本配置流程:

#define MPP_ALIGN(x, a)         (((x)+(a)-1)&~((a)-1))memset(&mpp_enc_data, 0, sizeof(mpp_enc_data));mpp_enc_data.width = width;mpp_enc_data.height = height;//获取宽高的stride    mpp_enc_data.hor_stride = MPP_ALIGN(mpp_enc_data.width, 16);mpp_enc_data.ver_stride = MPP_ALIGN(mpp_enc_data.height, 16);mpp_enc_data.fmt = fmt;//MPP_FMT_BGR565;//MPP_FMT_YUV422_YUYV等;mpp_enc_data.type = type;mpp_enc_data.fps = fps;mpp_enc_data.gop = gop;//不同的图像格式有着不同的数据量大小,//比如MPP_FMT_YUV420SP_VU格式的图像数据量大小就是其图像像素量的3/2;if (mpp_enc_data.fmt <= MPP_FMT_YUV420SP_VU)mpp_enc_data.frame_size = mpp_enc_data.hor_stride * mpp_enc_data.ver_stride * 3/2;else if (mpp_enc_data.fmt <= MPP_FMT_YUV422_UYVY) {mpp_enc_data.hor_stride *= 2;mpp_enc_data.frame_size = mpp_enc_data.hor_stride * mpp_enc_data.ver_stride;}else {mpp_enc_data.frame_size = mpp_enc_data.hor_stride * mpp_enc_data.ver_stride * 4;}mpp_enc_data.fp_output = fopen(outPutFileName, "wb+");// 打开输出文件//开辟编码所需的内存ret = mpp_buffer_get(NULL, &mpp_enc_data.frm_buf, mpp_enc_data.frame_size);if (ret){printf("failed to get buffer for input frame ret %d\n", ret);goto MPP_INIT_OUT;}//创建 MPP context 和 MPP api 接口ret = mpp_create(&mpp_enc_data.ctx, &mpp_enc_data.mpi);if (ret){printf("mpp_create failed ret %d\n", ret);goto MPP_INIT_OUT;}/*初始化编码还是解码,以及编解码的格式MPP_CTX_DEC : 解码MPP_CTX_ENC : 编码MPP_VIDEO_CodingAVC : H.264MPP_VIDEO_CodingHEVC :  H.265MPP_VIDEO_CodingVP8 :  VP8MPP_VIDEO_CodingVP9 :  VP9MPP_VIDEO_CodingMJPEG : MJPEG*/ret = mpp_init(mpp_enc_data.ctx, MPP_CTX_ENC, mpp_enc_data.type);if (ret){printf("mpp_init failed ret %d\n", ret);goto MPP_INIT_OUT;}

后续的代码按照官方配置来即可,需要改动则按照官方注释按需修改。

图像编码过程

MppPacket : 存放编码数据,例如264、265数据
MppFrame : 存放解码的数据,例如YUV、RGB数据
MppTask : 一次编码或者解码的session

编码就是push MppFrame,输出MppPacket;
解码就是push MppPacket,输出MppFrame;

MPI包含两套接口做编解码:
一套是简易接口, 类似 decode_put_packet / decode_get_frame 这样put/get即可
一套是高级接口, 类似 poll / enqueue/ dequeue 这样的对input output队列进行操作

编码流程:

bool Encoder::process_image(uint8_t *p)
{MPP_RET ret = MPP_OK;MppFrame frame = NULL;MppPacket packet = NULL;//获取编码器buffer的头指针void *buf = mpp_buffer_get_ptr(mpp_enc_data.frm_buf);//将已有图像数据按16位对齐拷贝至编码器bufferread_yuv_image((uint8_t *)buf,  p,  mpp_enc_data.width, mpp_enc_data.height,mpp_enc_data.hor_stride, mpp_enc_data.ver_stride, mpp_enc_data.fmt);ret = mpp_frame_init(&frame);if (ret){printf("mpp_frame_init failed\n");return true;}mpp_frame_set_width(frame, mpp_enc_data.width);mpp_frame_set_height(frame, mpp_enc_data.height);mpp_frame_set_hor_stride(frame, mpp_enc_data.hor_stride);mpp_frame_set_ver_stride(frame, mpp_enc_data.ver_stride);mpp_frame_set_fmt(frame, mpp_enc_data.fmt);mpp_frame_set_buffer(frame, mpp_enc_data.frm_buf);mpp_frame_set_eos(frame, mpp_enc_data.frm_eos);//输入图像ret = mpp_enc_data.mpi->encode_put_frame(mpp_enc_data.ctx, frame);if (ret){printf("mpp encode put frame failed\n");return true;}//获得编码后的数据包//注意:有些时候编码尚未完成取包会得到空包,但是却不会报错//这时需要在此处建立一个循环等待编码完成再取包//若直接跳过,下次再调用mpp_enc_data.mpi->encode_put_frame(mpp_enc_data.ctx, frame)时//会因为上一帧数据没有取走而卡住。ret = mpp_enc_data.mpi->encode_get_packet(mpp_enc_data.ctx, &packet);if (ret){printf("mpp encode get packet failed\n");return true;}REGET:if (packet){// write packet to file herevoid *ptr   = mpp_packet_get_pos(packet);size_t len  = mpp_packet_get_length(packet);mpp_enc_data.pkt_eos = mpp_packet_get_eos(packet);if (mpp_enc_data.fp_output)fwrite(ptr, 1, len, mpp_enc_data.fp_output);mpp_packet_deinit(&packet);//printf("encoded frame %d size %d\n", mpp_enc_data.frame_count, len);mpp_enc_data.stream_size += len;mpp_enc_data.frame_count++;if (mpp_enc_data.pkt_eos){printf("found last packet\n");}}else goto REGET;//避免上述的编码阻塞情况if (mpp_enc_data.num_frames && mpp_enc_data.frame_count >= mpp_enc_data.num_frames){printf("encode max %d frames", mpp_enc_data.frame_count);return false;}if (mpp_enc_data.frm_eos && mpp_enc_data.pkt_eos)return false;return true;
}

图像拷贝函数:

在mpp中,编码器输入图像是必须严格按照16位对齐的,所以如果我们自己的图像宽高所占的数据位不是16的整数。就需要对图像数据重新整理,按行将图像拷贝到我们在mpp初始化时申请的内存中,每拷贝一行后在当前行后增加空数据,使行的数据长度为16的整数。在拷贝完成所有行后还需要再末尾增加空行,使得行数满足16的整数,这就是mpp的16位对齐方法。

MPP_RET Encoder::read_yuv_image(uint8_t *buf, uint8_t *image, uint32_t width, uint32_t height,uint32_t hor_stride, uint32_t ver_stride, MppFrameFormat fmt)
{MPP_RET ret = MPP_OK;uint32_t read_size;uint32_t row = 0;//获得即将获得数据的buffer中,Y分量存储的首地址uint8_t *buf_y = buf;//U分量存储的首地址uint8_t *buf_u = buf_y + hor_stride * ver_stride; // NOTE: diff from gen_yuv_image//V分量存储的首地址uint8_t *buf_v = buf_u + hor_stride * ver_stride / 4; // NOTE: diff from gen_yuv_imageswitch (fmt) {case MPP_FMT_YUV420SP : {for (row = 0; row < height; row++) {memcpy(buf_y + row * hor_stride, image,width);image += width;}for (row = 0; row < height / 2; row++) {memcpy(buf_u + row * hor_stride, image, width);image += width;           }} break;以此图像格式为例,其他图像格式的情况类似。case MPP_FMT_YUV420P : {//在已有图像中,按行拷贝至bufferfor (row = 0; row < height; row++) {//buffer每次拷贝进width个数据,但是指针却要前进hor_stride个位置,这就是对齐的过程memcpy(buf_y + row * hor_stride, image, width);//每拷贝一行,就要按照让数据指针增加width,也就是到下一行的首地址。image += width;}for (row = 0; row < height / 2; row++) {memcpy(buf_u + row * hor_stride/2, image, width/2);image += width/2;}for (row = 0; row < height / 2; row++) {memcpy(buf_v + row * hor_stride/2, image, width/2);image += width/2;}} break;case MPP_FMT_ARGB8888 : {for (row = 0; row < height; row++) {memcpy(buf_y + row * hor_stride*4, image, width*4);image += width*4;}} break;case MPP_FMT_YUV422_YUYV :case MPP_FMT_YUV422_UYVY : {for (row = 0; row < height; row++) {memcpy(buf_y + row * hor_stride, image, width*2);image += width*2;}} break;default : {cout << "read image do not support fmt "<< endl;ret = MPP_ERR_VALUE;} break;}err:return ret;
}

RK3399Pro-硬件编解码器MPP库快速上手--(三)MPP编码关键代码讲解相关推荐

  1. RKMPP库快速上手--(三)MPP解码入门

    一.RKMPP整个解码流程简单介绍一下: 1. 创建 MPP context 和 MPP api 接口. (注意,和RGA一样,多个线程多个实例需要多个独立的的context) ret = mpp_c ...

  2. RKMPP库快速上手--(四)MPP编码入门

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

  3. RKMPP库快速上手--(二)MPP关键配置

    一.RKMPP编码配置总结: 为了方便配置功能,我们一般将Mpp配置的关键参数定义为结构体: struct MPP_ENC_DATA //编码所需要的数据 { // global flow contr ...

  4. Eigen矩阵运算库快速上手

    目录 1. 配置 2. 初始化 2.1 Array类 2.2 Vector类 2.3 Matrix类 2.4 Vector赋值 2.5 高级初始化 3. 矩阵计算 3.1 矩阵基本计算 3.2 线性求 ...

  5. element-plus组件库快速上手教程

    前言 一般我们开发单页面应用(关于单页面应用是什么在本专栏的另一篇文章中有介绍)时,特别是针对和我一样打算走后端开发的同学,对于前端只需要有了解的程度就行了,但是平常做项目练手时经常会要自己写前端的东 ...

  6. 密码学C语言函数库——Miracl库快速上手中文指南(VC)

    一.简介 密码学学习.研究人员往往着重于理论研究,难以与实践直接挂钩,今天介绍一下国外著名密码学C语言函数库--Miracl库的使用方法. Miracl库的官方网站是http://www.shamus ...

  7. python爬虫requests-Python爬虫---requests库快速上手

    一.requests库简介 requests是Python的一个HTTP相关的库 requests安装: pip install requests 二.GET请求 import requests #首 ...

  8. c语言添加miracl库,密码学C语言函数库——Miracl库快速上手中文指南(VC)

    一.简介 密码学学习.研究人员往往着重于理论研究,难以与实践直接挂钩,今天介绍一下国外著名密码学C语言函数库--Miracl库的使用方法. 该库针对公钥密码学和椭圆曲线密码学的实现,写了很多函数,在这 ...

  9. Golang单元测试快速上手(三) 高级技巧

    注:本文由linstring原创,博客地址:https://blog.csdn.net/lin_strong 转载请注明出处 第一部分链接: https://blog.csdn.net/lin_str ...

最新文章

  1. Datawhale组队学习周报(第017周)
  2. linux 命令终端显示-bash-4.2#解决方法
  3. 零拷贝概念 -- linux内核
  4. Lucene-3.0.0配置
  5. Windows下memcached.exe的安装与配置
  6. .animate css,animate-css
  7. HDOJ---3790 最短路径问题[Dijkstra算法||SPFA]
  8. 数据访问优化性能(转载)
  9. 墙裂推荐!2020Android阿里腾讯百度字节美团网易爱奇艺校招面试汇总
  10. 设为首页 和 收藏本页
  11. 2021-09-1311. 盛最多水的容器 数组 双指针
  12. ionic3-ng4学习见闻--(多环境方案)
  13. 活着只是你幸运--512汶川大地震募捐倡议
  14. JAVA 相关书籍推荐(全)
  15. 一款局域网的聊天工具
  16. 少儿编程家长一定要看看这些值得参加的青少年专业权威赛考
  17. 重庆大学计算机学院郭松涛,C.-C.Jay Kuo教授访问重大计算机学院
  18. 到底该如何书写概要设计说明书
  19. 一支雪糕卖66?钟薛高,你就偷着乐吧
  20. 深度学习究竟怎么入门?两位Google大神掀起剑气之争

热门文章

  1. Python制作简易OCR文字识别系统
  2. 让Unity游戏后台运行
  3. leetcode91、 解码方法
  4. 今日头条中生活领域范围详解,怎样提高垂直度?
  5. 谁在今日头条赚到大钱了?
  6. 【LISP】Pascal Costanza:极端片面的Lisp介绍
  7. java SSM 框架 微信自定义菜单 快递接口 SpringMVC mybatis redis
  8. 在华为云专属月,找到开启互联网第二增长曲线的一把钥匙
  9. 可视门铃、wifi连体衣:苹果的智能家居野心
  10. 华为手机如何升级鸿蒙系统_华为手机可以更新鸿蒙系统了吗?都支持哪些手机?...