完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
MPP关键配置(讲解在注释中)
为了方便配置功能,我们一般将Mpp配置的关键参数定义为结构体: struct MPP_ENC_DATA //编码所需要的数据 { // global flow control flag uint32_t frm_eos; uint32_t pkt_eos; uint32_t frame_count; uint64_t stream_size; // base flow context MppCtx ctx; MppApi *mpi; MppEncPrepCfg prep_cfg; MppEncRcCfg rc_cfg; MppEncCodecCfg codec_cfg; // input / output MppBuffer 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; // resources size_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 %dn", 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 %dn", ret); goto MPP_INIT_OUT; } /*初始化编码还是解码,以及编解码的格式 MPP_CTX_DEC : 解码 MPP_CTX_ENC : 编码 MPP_VIDEO_CodingAVC : H.264 MPP_VIDEO_CodingHEVC : H.265 MPP_VIDEO_CodingVP8 : VP8 MPP_VIDEO_CodingVP9 : VP9 MPP_VIDEO_CodingMJPEG : MJPEG*/ ret = mpp_init(mpp_enc_data.ctx, MPP_CTX_ENC, mpp_enc_data.type); if (ret) { printf("mpp_init failed ret %dn", 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位对齐拷贝至编码器buffer read_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 failedn"); 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 failedn"); 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 failedn"); return true; } REGET: if (packet) { // write packet to file here void *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 %dn", 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 packetn"); } } 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_image switch (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 : { //在已有图像中,按行拷贝至buffer for (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; } |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
1988 浏览 1 评论
synopsys 的design ware:DW_fpv_div,浮点数除法器,默认32位下,想提升覆盖率(TMAX),如果用功能case去提升覆盖率呢?
2490 浏览 1 评论
RK3588 GStreamer调试四路鱼眼摄像头四宫格显示报错
5272 浏览 1 评论
【飞凌嵌入式OK3576-C开发板体验】RKNN神经网络-YOLO图像识别
254 浏览 0 评论
【飞凌嵌入式OK3576-C开发板体验】SSH远程登录网络配置及CAN通讯
1336 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-24 22:20 , Processed in 0.668489 second(s), Total 94, Slave 77 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号