当前位置: 首页 > news >正文

RK3568基于mpp实现硬解码(二):FFmpeg + mpp实现ipc摄像头图像解码

在上一篇文章中已经成功编译mpp并导入到项目中,接下来基于mpi接口实现解码器

说明一下视频图像是通过ipc摄像头采集的。在项目中通过FFmpeg的RTSP拉流获取ipc摄像头的码流数据,FFmpeg拉流这部分不作叙述,只需要知道FFmpeg拉流后数据保存在AVPacket这个结构体当中。

一、MPI接口使用说明

官方文档中给出的图示已经介绍了MPI接口的使用过程 

关于解码过程可以参照test目录下的mpi_dec_test.c文件

二、AVPacket转为MppPacket

mpp的使用流程可以参考官方的mpi_dec_test.c。因为官方demo传入的是一个容器文件(比如MP4文件),我的项目要处理的是FFmpeg拉取的码流数据,数据保存在AVPacket结构体中。mpp要处理的是MppPacket结构体,所以要将AVPacket中的码流数据转到MppPacket中保存

三、分帧处理的问题

这个问题在官方文档中已有解释

四、相关代码实现

#mppvideodecode.h#ifndef MPPVIDEODECODE_H
#define MPPVIDEODECODE_H#define myDebug()       qDebug()<<"["<<this->metaObject()->className()<<"]"<<__FUNCTION__<<__LINE__#include <QObject>
#include <QDebug>
#include <unistd.h>#include "libavcodec/packet.h"
#include "rk_mpi.h"
#include "mpp_log.h"
#include "mpp_mem.h"
#include "mpi_dec_utils.h"
#include "utils.h"
#include "mpp_time.h"
#include "libavcodec/avcodec.h"typedef struct
{MppCtx ctx;MppApi *mpi;MppBufferGroup frm_grp;size_t max_usage;RK_S32 frame_count;  //解出帧的计数
} MpiDecLoopData;class MppVideoDecode : public QObject
{Q_OBJECT
public:MppVideoDecode();void initMpiDecLoopData();               //初始化mpp相关参数QVector<MppFrame> mppDecode(AVPacket* avpacket);  //解码QString getFrameFormat(MppFrame frame);           //获取图像像素格式   void deInit(); //释放相关资源
private:MppFrame m_frame = NULL;MppPacket m_packet = NULL;MpiDecLoopData m_data;QVector<MppFrame> m_vecMppFrame;  //存放解出的帧};#endif // MPPVIDEODECODE_H
#mppvideodecode.cpp#include "mppvideodecode.h"MppVideoDecode::MppVideoDecode()
{initMpiDecLoopData();
}void MppVideoDecode::deInit()
{if(m_packet){mpp_packet_deinit(&m_packet);m_packet = NULL;}if (m_data.frm_grp) {mpp_buffer_group_put(m_data.frm_grp);m_data.frm_grp = NULL;}for(MppFrame frame : m_vecMppFrame){if(frame){mpp_frame_deinit(&frame);frame = NULL;}}m_vecMppFrame.clear();//    if (m_data.ctx) {
//        mpp_destroy(m_data.ctx);
//        m_data.ctx = NULL;
//    }//    if(!m_vecMppFrame.isEmpty()){//        m_vecMppFrame.clear();//    }
}void MppVideoDecode::initMpiDecLoopData()
{MPP_RET ret         = MPP_OK;    //操作结果MppCtx ctx          = NULL;MppApi *mpi         = NULL;MpiCmd mpi_cmd      = MPP_CMD_BASE;  //控制模式MppParam param      = NULL;          //控制参数RK_U32 need_split   = 1;             //分帧标志位MppCodingType type  = MPP_VIDEO_CodingAVC;   //解码格式h.264memset(&m_data, 0, sizeof(m_data));ret = mpp_create(&ctx, &mpi);if (MPP_OK != ret) {mpp_err("mpp_create failed\n");if (m_data.ctx) {mpp_destroy(m_data.ctx);m_data.ctx = NULL;}}//内部分帧处理mpi_cmd = MPP_DEC_SET_PARSER_SPLIT_MODE;param = &need_split;ret = mpi->control(ctx, mpi_cmd, param);if (MPP_OK != ret) {mpp_err("mpi->control failed\n");if (m_data.ctx) {mpp_destroy(m_data.ctx);m_data.ctx = NULL;}}mpi_cmd = MPP_SET_INPUT_BLOCK;   //处理输入是否为阻塞模式(param为1表示输入缓冲区满时会阻塞)param = &need_split;ret = mpi->control(ctx, mpi_cmd, param);if (MPP_OK != ret) {mpp_err("mpi->control failed\n");if (m_data.ctx) {mpp_destroy(m_data.ctx);m_data.ctx = NULL;}}ret = mpp_init(ctx, MPP_CTX_DEC, type);if (MPP_OK != ret) {mpp_err("mpp_init failed\n");if (m_data.ctx) {mpp_destroy(m_data.ctx);m_data.ctx = NULL;}}m_data.ctx = ctx;m_data.mpi = mpi;m_data.frame_count = 0;
}QVector<MppFrame> MppVideoDecode::mppDecode(AVPacket* avpacket)
{RK_U32 pkt_done = 0;RK_U32 err_info = 0;MPP_RET ret = MPP_OK;MppCtx ctx  = m_data.ctx;MppApi *mpi = m_data.mpi;//RK_S64 t_s, t_e;mpp_packet_init(&m_packet, avpacket->data, avpacket->size);mpp_packet_set_pts(m_packet, avpacket->pts);                      //显示时间戳//    t_s = mpp_time();//第一层循环用于确认必须把packet送进解码器do {RK_S32 times = 5;ret = mpi->decode_put_packet(ctx, m_packet);if (MPP_OK == ret)pkt_done = 1;// then get all available frame and releasedo {RK_S32 get_frm = 0;   //用于判断一包packet中是否还有frameRK_U32 frm_eos = 0;   //表示图像结束的标志try_again:ret = mpi->decode_get_frame(ctx, &m_frame);if (MPP_ERR_TIMEOUT == ret) {if (times > 0) {times--;msleep(2);goto try_again;}mpp_err("decode_get_frame failed too much time\n");}if (MPP_OK != ret) {mpp_err("decode_get_frame failed ret %d\n", ret);break;}if (m_frame) {if (mpp_frame_get_info_change(m_frame)) {RK_U32 width = mpp_frame_get_width(m_frame);RK_U32 height = mpp_frame_get_height(m_frame);RK_U32 hor_stride = mpp_frame_get_hor_stride(m_frame);RK_U32 ver_stride = mpp_frame_get_ver_stride(m_frame);RK_U32 buf_size = mpp_frame_get_buf_size(m_frame);mpp_log("decode_get_frame get info changed found\n");mpp_log("decoder require buffer w:h [%d:%d] stride [%d:%d] buf_size %d",width, height, hor_stride, ver_stride, buf_size);ret = mpp_buffer_group_get_internal(&m_data.frm_grp, MPP_BUFFER_TYPE_ION);if (ret) {mpp_err("get mpp buffer group  failed ret %d\n", ret);break;}mpi->control(ctx, MPP_DEC_SET_EXT_BUF_GROUP, m_data.frm_grp);mpi->control(ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);} else {err_info = mpp_frame_get_errinfo(m_frame) | mpp_frame_get_discard(m_frame);if (err_info) {mpp_log("decoder_get_frame get err info:%d discard:%d.\n",mpp_frame_get_errinfo(m_frame), mpp_frame_get_discard(m_frame));}//                MppBuffer buffer = mpp_frame_get_buffer(m_frame);//                myDebug() << "buffer addr:" << buffer;}//                myDebug() << "the frame format is:" << getFrameFormat(frame); //获取图像格式(yuv or rgb)m_data.frame_count++;mpp_log("decode_get_frame get frame %d\n", m_data.frame_count);frm_eos = mpp_frame_get_eos(m_frame);  //图像结束标志get_frm = 1;m_vecMppFrame.append(m_frame);}// try get runtime frame memory usageif (m_data.frm_grp) {size_t usage = mpp_buffer_group_usage(m_data.frm_grp);if (usage > m_data.max_usage)m_data.max_usage = usage;}if (frm_eos) {mpp_log("found last frame\n");break;}if (get_frm)continue;break;} while (1);if(pkt_done)break;/** why sleep here:* mpi->decode_put_packet will failed when packet in internal queue is* full,waiting the package is consumed .Usually hardware decode one* frame which resolution is 1080p needs 2 ms,so here we sleep 3ms* * is enough.*/msleep(3);}while (1);//    t_e = mpp_time();//    myDebug() << "解一个packet所用时间为" << (t_e - t_s) / 1000 << "ms";for(const MppFrame frame : m_vecMppFrame){myDebug() << "addr111111:" << frame;}return m_vecMppFrame;
}QString MppVideoDecode::getFrameFormat(MppFrame frame)
{MppFrameFormat fmt = mpp_frame_get_fmt(frame);switch(fmt){case MPP_FMT_YUV420SP:return "MPP_FMT_YUV420SP";case MPP_FMT_YUV420SP_10BIT:return "MPP_FMT_YUV420SP_10BIT";case MPP_FMT_YUV422SP:return "MPP_FMT_YUV422SP";case MPP_FMT_YUV422SP_10BIT:return "MPP_FMT_YUV422SP_10BIT";case MPP_FMT_YUV420P:return "MPP_FMT_YUV420P";case MPP_FMT_YUV420SP_VU:return "MPP_FMT_YUV420SP_VU";case MPP_FMT_YUV422P:return "MPP_FMT_YUV422P";case MPP_FMT_YUV422SP_VU:return "MPP_FMT_YUV422SP_VU";case MPP_FMT_YUV422_YUYV:return "MPP_FMT_YUV422_YUYV";case MPP_FMT_YUV422_YVYU:return "MPP_FMT_YUV422_YVYU";case MPP_FMT_YUV422_UYVY:return "MPP_FMT_YUV422_UYVY";case MPP_FMT_YUV422_VYUY:return "MPP_FMT_YUV422_VYUY";case MPP_FMT_YUV400:return "MPP_FMT_YUV400";case MPP_FMT_YUV440SP:return "MPP_FMT_YUV440SP";case MPP_FMT_YUV411SP:return "MPP_FMT_YUV411SP";case MPP_FMT_YUV444SP:return "MPP_FMT_YUV444SP";case MPP_FMT_YUV444P:return "MPP_FMT_YUV444P";case MPP_FMT_YUV444SP_10BIT:return "MPP_FMT_YUV444SP_10BIT";case MPP_FMT_AYUV2BPP:return "MPP_FMT_AYUV2BPP";case MPP_FMT_AYUV1BPP:return "MPP_FMT_AYUV1BPP";case MPP_FMT_YUV_BUTT:return "MPP_FMT_YUV_BUTT";case MPP_FMT_RGB565:return "MPP_FMT_RGB565";case MPP_FMT_BGR565:return "MPP_FMT_BGR565";case MPP_FMT_RGB555:return "MPP_FMT_RGB555";case MPP_FMT_BGR555:return "MPP_FMT_BGR555";case MPP_FMT_RGB444:return "MPP_FMT_RGB444";case MPP_FMT_BGR444:return "MPP_FMT_BGR444";case MPP_FMT_RGB888:return "MPP_FMT_RGB888";case MPP_FMT_BGR888:return "MPP_FMT_BGR888";case MPP_FMT_RGB101010:return "MPP_FMT_RGB101010";case MPP_FMT_BGR101010:return "MPP_FMT_BGR101010";case MPP_FMT_ARGB8888:return "MPP_FMT_ARGB8888";case MPP_FMT_ABGR8888:return "MPP_FMT_ABGR8888";case MPP_FMT_BGRA8888:return "MPP_FMT_BGRA8888";case MPP_FMT_RGBA8888:return "MPP_FMT_RGBA8888";case MPP_FMT_ARGB4444:return "MPP_FMT_ARGB4444";case MPP_FMT_ARGB1555:return "MPP_FMT_ARGB1555";case MPP_FMT_RGB_BUTT:return "MPP_FMT_RGB_BUTT";case MPP_FMT_BUTT:return "MPP_FMT_BUTT";}
}

可以参考

https://github.com/MUZLATAN/ffmpeg_rtsp_mpp

五、decode_get_frame返回值为NULL

我在调用mpp库的mpp_create、mpp_init、mpp_packet_init、decode_put_packet甚至decode_get_frame返回的值都是0(正常),但是decode_get_frame解出来的frame为NULL

网上搜到的原因一般有两个
(1)在调用mpp_init时传入的解码类型与实际编码类型不相符(比如摄像头码流用的是H265编码,mpp_init传入的却是H264方法)
(2)FFmpeg拉流后的码流保存在AVPacket中,但是AVPacket中的码流不足以构成一帧数据所以解不出来

但是我的原因都不是以上这些。在FFmpeg拉流时,因为要不断从摄像头中拉数据,所以要用循环

while(1){...avformat_open_input();...
}

我在调用mpp时,把mpp_create()和mpp_init()也放在循环中了

while(1){...avformat_open_input();mpp(){mpp_create();mpp_init();decode_put_packet();decode_get_frame();}...
}

这就导致每拉一次流,就重新创建一个mpp对象,所以读取的frame为NULL。最后修改代码为以下,成功读取的frame

mpp(){mpp_create();mpp_init();
}
while(1){...avformat_open_input();...decode_put_packet();decode_get_frame();...
}

http://www.xdnf.cn/news/1202401.html

相关文章:

  • C++---初始化列表(initializer_list)
  • maven 打包报错 process terminated
  • 数据库原理
  • MCP资源管理深度实践:动态数据源集成方案
  • 终结集成乱局:模型上下文协议(MCP)如何重构AI工具生态?
  • 深入探索Linux:忙碌的车间“进程”间通信
  • 四、计算机组成原理——第6章:总线
  • 微信小程序——早餐小程序
  • LeetCode 85. 最大矩形
  • 「源力觉醒 创作者计划」_文心大模型4.5系列开源模型,意味着什么?对开发者、对行业生态有何影响?
  • SpringBoot 发送邮件
  • Datawhale AI夏令营--Task2:理解项目目标、从业务理解到技术实现!
  • 数值计算 | 图解基于龙格库塔法的微分方程计算与连续系统离散化(附Python实现)
  • MQTT之“SUBSCRIBE报文和SUBACK报文”
  • “太赫兹”
  • 【华为机试】210. 课程表 II
  • 自动化测试常用函数
  • XML Expat Parser:深入解析与高效应用
  • 【CDA干货】金融超市电商App经营数据分析案例
  • 写一个3D旋转的python程序
  • 字节跳动开源Coze,开启AI Agent开发新时代?
  • 【Linux篇章】穿越数据迷雾:HTTPS构筑网络安全的量子级护盾,重塑数字信任帝国!
  • 新能源行业B端极简设计:碳中和目标下的交互轻量化实践
  • 【数据架构09】人工智能及数据智能架构篇
  • 群晖Synology Drive:打造高效安全的私有云协作平台
  • 优测推出HarmonyOS全场景测试服务,解锁分布式场景应用卓越品质!
  • httpx 接口测试教程
  • HarmonyOS 6 云开发-用户头像上传云存储
  • 打通视频到AI的第一公里:轻量RTSP服务如何重塑边缘感知入口?
  • UniappDay04