RK3568上如何使用MPP进行硬解码

目录

  • 前言
  • 正文
    • 一、FFmpeg 拉流处理
    • 二、RK3568 mpp硬解码
      • 1、简介
      • 2、普通mpp解码流程
      • 3、核心代码
    • END、总结的知识与问题
      • 1、一直出现`jitter buffer full` 这样的问题
      • 2、如何打印帧率?
      • 3、分析av_packet_alloc、av_init_packet、av_packet_unref、av_packet_free、av_frame_move_ref、av_packet_clone
  • 参考

前言

需求:我这边遇到的需求是需要在RK3568上进行拉流处理,然后使用MPP进行硬解码,但目前解码解的还是比较繁琐一点,不过,没关系把。先记录下,自己后面如果有对这个东西进行重写,再进行详细描述即可。
主要内容:
1、FFmpeg 拉流处理。
2、RK3568 MPP处理。

正文

一、FFmpeg 拉流处理

code

bool CVideoDecodeThd::_AnalysisFile(const QString& _sFilePath)
{
#ifdef ARMLOG_INFO << "play video file : " << _sFilePath.toStdString();// 打开文件流读取文件头解析出视频信息如轨道信息、时长等// m_pFormatCtx初始化为NULL,如果打开成功,它会被设置成非NULL的值,在不需要的时候可以通过avcodec_free_context释放。// 这个方法实际可以打开多种来源的数据,url可以是本地路径、rtmp地址等// 在不需要的时候通过avformat_close_input关闭文件流if (avformat_open_input(&m_pFormatCtx, _sFilePath.toLatin1().data(), nullptr, nullptr) != 0){LOG_ERROR << "can't open the file.";return false;}// 读取媒体文件的数据包以获取流信息if (avformat_find_stream_info(m_pFormatCtx, nullptr) < 0){LOG_ERROR << "can't find stream infomation.";return false;}//查找视频轨道int iVideoStream = av_find_best_stream(m_pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);if (iVideoStream < 0){LOG_ERROR << "can't find video stream";return false;}// 查找解码器AVCodecParameters *pCodecParame = m_pFormatCtx->streams[iVideoStream]->codecpar;if (!pCodecParame){LOG_ERROR << "can't find AVCodecParameters";return false;}m_pCodec = (AVCodec *)avcodec_find_decoder(pCodecParame->codec_id);if (!m_pCodec){LOG_ERROR << "Codec cant found, code id:" << pCodecParame->codec_id;return false;}m_pCodecCtx = avcodec_alloc_context3(m_pCodec);if (!m_pCodecCtx){LOG_ERROR << "can't alloc codec context";return false;}if (avcodec_parameters_to_context(m_pCodecCtx, (const AVCodecParameters *)pCodecParame) < 0){LOG_ERROR << "can't set codec params";avcodec_close(m_pCodecCtx);return false;}// 打开解码器if (avcodec_open2(m_pCodecCtx, m_pCodec, nullptr) < 0){LOG_ERROR << "can't open codec.";avcodec_close(m_pCodecCtx);return false;}LOG_INFO << "the file video format is : " << m_pCodec->name;float fFrameRate = 0.0;if (m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.den != 0 && m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.num != 0){fFrameRate = m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.num * 1.0 /m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.den;//每秒多少帧LOG_INFO << "Frame Rate : " << fFrameRate;}m_pFrame = av_frame_alloc();m_pFrameRGB = av_frame_alloc();m_pImageConvertCtx = sws_getContext(m_pCodecCtx->width, m_pCodecCtx->height,m_pCodecCtx->pix_fmt, m_pCodecCtx->width,m_pCodecCtx->height, AV_PIX_FMT_RGB32,SWS_BICUBIC, nullptr, nullptr, nullptr);int iNumBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, m_pCodecCtx->width, m_pCodecCtx->height, 1);uint8_t *pOutBuffer = (uint8_t*)av_malloc(iNumBytes * sizeof(uint8_t));av_image_fill_arrays(m_pFrameRGB->data, m_pFrameRGB->linesize, pOutBuffer, AV_PIX_FMT_RGB32, m_pCodecCtx->width, m_pCodecCtx->height, 1);int iYsize = m_pCodecCtx->width * m_pCodecCtx->height;AVPacket * pPacket = av_packet_alloc();  ///< 分配一个packet内存av_new_packet(pPacket, iYsize);     ///< 分配一个packet的数据av_dump_format(m_pFormatCtx, 0, _sFilePath.toLatin1().data(), 0);   ///< 输出视频信息qint64 iStartTime = 0;while (m_bRunning){if (0 == iStartTime){iStartTime = QDateTime::currentDateTime().toMSecsSinceEpoch();}if (av_read_frame(m_pFormatCtx, pPacket) < 0){emit SIGNAL_PlayEnd();break;      // 文件读取完成}while (!m_bPlay){QThread::msleep(20);if (!m_bRunning){break;}}if (pPacket->stream_index == iVideoStream){
#ifdef MPPint iRet = _DecodeAVPacket(pPacket);
#elifint iRet =avcodec_send_packet(m_pCodecCtx, pPacket);if (0 == iRet){while (avcodec_receive_frame(m_pCodecCtx, m_pFrame) >= 0){if (!m_bRunning || !m_bPlay){break;}sws_scale(m_pImageConvertCtx, (uint8_t const* const*)m_pFrame->data,m_pFrame->linesize, 0, m_pCodecCtx->height,m_pFrameRGB->data, m_pFrameRGB->linesize);QImage oImg ((uchar*) pOutBuffer, m_pCodecCtx->width, m_pCodecCtx->height, QImage::Format_RGB32);QImage oTempImg = oImg.copy();emit SIGNAL_NewFrame(oTempImg);int iInternal = QDateTime::currentDateTime().toMSecsSinceEpoch() - iStartTime;iStartTime = 0;int iSleepMs = 1000 / fFrameRate - iInternal;if (iSleepMs > 0){msleep(iSleepMs);}}}else{LOG_ERROR << "send packet fail : " << iRet;}
#endif}av_packet_unref(pPacket);//QThread::msleep(10);}QThread::msleep(500);   ///< 等待外界渲染完毕av_free(pOutBuffer);//回收数据包av_packet_free(&pPacket);//销毁帧av_frame_free(&m_pFrame);av_frame_free(&m_pFrameRGB);//销毁SwsContextsws_freeContext(m_pImageConvertCtx);//释放解码器avcodec_close(m_pCodecCtx);// 销毁AVFormatContextavformat_close_input(&m_pFormatCtx);LOG_INFO << "thread is finished";return true;
#elsereturn true;
#endif
}

这个函数是关于拉流的函数,具体的拉流的比较详细的内容,在另一篇文章进行详细叙述。

二、RK3568 mpp硬解码

1、简介

瑞芯微提供的媒体处理软件平台(Media Process Platform,简称 MPP)是适用于瑞芯微芯片系列的
通用媒体处理软件平台。该平台对应用软件屏蔽了芯片相关的复杂底层处理,其目的是为了屏蔽不
同芯片的差异,为使用者提供统一的视频媒体处理接口(Media Process Interface,缩写 MPI)。MPP
提供的功能包括:
视频解码:
H.265 / H.264 / H.263 / VP9 / VP8 / MPEG-4 / MPEG-2 / MPEG-1 / VC1 / MJPEG
视频编码:
H.264 / VP8 / MJPEG
视频处理:
视频拷贝,缩放,色彩空间转换,场视频解交织(Deinterlace)

2、普通mpp解码流程

在这里插入图片描述

3、核心代码

上面mpp使用的地方在这里:
_DecodeAVPacket(pPacket);
基本就是拉流获取到AVPacket 包传入Mpp进行解码。
_InitMpp():

int CVideoDecodeThd::_InitMpp()
{LOG_INFO << "--> CVideoDecodeThd::_InitMpp Start";MPP_RET ret = mpp_create(&m_Ctx, &m_pMpi);if (MPP_OK != ret){LOG_INFO << ("mpp_create error\n");return -1;}/*** 4. 配置解器*      - 解码文件需要 split 模式*      - 设置非阻塞模式,0非阻塞(默认),-1阻塞,+val 超时(ms)*/RK_U32 need_split = -1;ret = m_pMpi->control(m_Ctx, MPP_DEC_SET_PARSER_SPLIT_MODE, (MppParam*)&need_split);if (MPP_OK != ret){LOG_INFO << ("mpi->control error MPP_DEC_SET_PARSER_SPLIT_MODE\n");return -1;}ret = mpp_init(m_Ctx, MPP_CTX_DEC, MPP_VIDEO_CodingAVC);  // 固定为H264if (MPP_OK != ret){LOG_INFO << ("mpp_init error\n");return -1;}//这个是为了后面测试使用的。真实使用不用。m_pOut_fp = fopen("/ics/test.yuv", "wb+");if (!m_pOut_fp){LOG_INFO << ("fopen error\n");return -1;}LOG_INFO << "--> CVideoDecodeThd::_InitMpp End";return 0;
}

_DecodeAVPacket:

int CVideoDecodeThd::_DecodeAVPacket(AVPacket *_pPacket)
{LOG_INFO << "--> CVideoDecodeThd::_DecodeAVPacket Start";RK_U32 pkt_done = 0;MPP_RET ret = MPP_OK;MppPacket packet = NULL;MppFrame  frame  = NULL;LOG_INFO << "--->z CVideoDecodeThd::_DecodeAVPacket _pPacket size:"<<_pPacket->size;if (_pPacket->size <= 0){LOG_INFO << "--> CVideoDecodeThd::_DecodeAVPacket _pPacket->size is:"<<_pPacket->size;return -1;}ret = mpp_packet_init(&packet, _pPacket->data, _pPacket->size);if(ret < 0){LOG_INFO << "mpp_packet_init fail ret:"<<ret;return -1;}mpp_packet_set_pts(packet, _pPacket->pts);do{RK_S32 times = 5;// send the packet first if packet is not doneif (!pkt_done){LOG_INFO << "pkt remain:" << mpp_packet_get_length(packet);ret = m_pMpi->decode_put_packet(m_Ctx, packet);if (MPP_OK == ret){LOG_INFO << "pkt send success remain:" << mpp_packet_get_length(packet);pkt_done = 1;}}// then get all available frame and releasedo {
try_again:ret = m_pMpi->decode_get_frame(m_Ctx, &frame);if (MPP_ERR_TIMEOUT == ret){if (times > 0) {times--;msleep(2);goto try_again;}qDebug() << "decode_get_frame failed too much time:" <<ret;}if (frame == NULL){qDebug() << "get frame:"<<frame;return -1;}qDebug() << "get MPP_OK:" <<MPP_OK;qDebug() << "get ret:"<<ret;LOG_INFO << ("decode_get_frame success\n") <<"||"<<frame;dump_frame_to_file(m_Ctx, m_pMpi, frame, m_pOut_fp);if (mpp_frame_get_eos(frame)){LOG_INFO << ("mpp_frame_get_eos\n");mpp_frame_deinit(&frame);//                over = 1;continue;}mpp_frame_deinit(&frame);break;}while(1);msleep(3);break;}while(1);LOG_INFO << "--> CVideoDecodeThd::_DecodeAVPacket End";mpp_packet_deinit(&packet);return 1;
}

dump_frame_to_file:

void CVideoDecodeThd::dump_frame_to_file(MppCtx ctx, MppApi *mpi, MppFrame frame, FILE *out_fp)
{LOG_INFO << "decode_and_dump_to_file:" << frame;MPP_RET ret;if (mpp_frame_get_info_change(frame)) {LOG_INFO << ("mpp_frame_get_info_change\n");/*** 第一次解码会到这个分支,需要为解码器设置缓冲区.* 解码器缓冲区支持3种模式。参考【图像内存分配以及交互模式】Rockchip_Developer_Guide_MPP_CN.pdf* 这里使用纯内部模式。*/ret = mpi->control(ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);if (ret){LOG_INFO << "mpp_frame_get_info_change mpi->control error""MPP_DEC_SET_INFO_CHANGE_READY %d\n" << ret;}return;}RK_U32 err_info = mpp_frame_get_errinfo(frame);RK_U32 discard = mpp_frame_get_discard(frame);LOG_INFO << "err_info: discard:\n"<< err_info<<"||"<<discard;if (err_info){LOG_INFO << "--> CVideoDecodeThd::dump_frame_to_file err_info:"<<err_info;return;}// savedump_frame(frame, out_fp);return;
}

dump_frame:

void CVideoDecodeThd::dump_frame(MppFrame frame, FILE *out_fp)
{LOG_INFO << ("dump_frame_to_file\n");RK_U32 width    = 0;RK_U32 height   = 0;RK_U32 h_stride = 0;RK_U32 v_stride = 0;MppFrameFormat fmt  = MPP_FMT_YUV420SP;MppBuffer buffer    = NULL;RK_U8 *base = NULL;width    = mpp_frame_get_width(frame);height   = mpp_frame_get_height(frame);h_stride = mpp_frame_get_hor_stride(frame);v_stride = mpp_frame_get_ver_stride(frame);fmt      = mpp_frame_get_fmt(frame);buffer   = mpp_frame_get_buffer(frame);RK_U32 buf_size = mpp_frame_get_buf_size(frame);LOG_INFO << "w x h: %dx%d hor_stride:%d ver_stride:%d buf_size:%d\n"<<width<<"||"<< height<<"||"<<h_stride<<"||"<<v_stride<<"||"<< buf_size;if (NULL == buffer){LOG_INFO << ("buffer is null\n");return ;}base = (RK_U8 *)mpp_buffer_get_ptr(buffer);// MPP_FMT_YUV420SPif (fmt != MPP_FMT_YUV420SP){LOG_INFO << ("fmt %d not supported\n", fmt);return;}char *pOutput = NULL;int iLen = static_cast<int>(buf_size);LOG_INFO << "--->z iLen:"<<iLen;pOutput = (char*)base;if (iLen > 0 && pOutput){//用信号把QImage 发送出去emit SIGNAL_Image(QByteArray(pOutput, iLen), width, height);}
}

END、总结的知识与问题

1、一直出现jitter buffer full 这样的问题

原先开始跑,并不会出现这个问题,就是跑久了,直到抓了2000多个帧之后,慢慢就会报这个错,然后就不再拉流了,就很奇葩,这可是浪费了我很长的时间进行排查。
错误:

[rtsp @ 0x292ee690] jitter buffer full
[rtsp @ 0x292ee690] RTP: missed 84 packets
[rtsp @ 0x292ee690] jitter buffer full
[rtsp @ 0x292ee690] RTP: missed 8 packets

2、如何打印帧率?

    LOG_INFO << "the file video format is : " << m_pCodec->name;float fFrameRate = 0.0;if (m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.den != 0 && m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.num != 0){fFrameRate = m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.num * 1.0 /m_pFormatCtx->streams[iVideoStream]->avg_frame_rate.den;//每秒多少帧LOG_INFO << "Frame Rate : " << fFrameRate;}

3、分析av_packet_alloc、av_init_packet、av_packet_unref、av_packet_free、av_frame_move_ref、av_packet_clone

参考:从源码的层面理解ffmpeg这几个API

参考

1、从源码的层面理解ffmpeg这几个API
2、GitHub - MUZLATAN/ffmpeg_rtsp_mpp: ffmpeg 拉取rtsp h264流, 使用mpp解码, 目前在firefly 板子上跑通了
3、RK3568 MPP编码
4、Rockchip MPP(Media Process Platform)解码H264
5、mpi_dec_test 解码失败

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/333808.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Next City 数都上海应用创新大赛结果公布,子虔科技获奖

12月16日&#xff0c;以“应变求机 以数谋新”为主题的上海城市数字化转型体验周举办。作为上海城市数字化转型年终重磅活动&#xff0c;上海市人民政府副秘书长庄木弟&#xff0c;市经济和信息化工作党委书记程鹏&#xff0c;杨浦区委副书记、区长周海鹰&#xff0c;市经济和信…

数据结构实验2:队列的应用

目录 一、实验目的 二、实验原理 1.1 队列的基本操作 1.1.1 队列的定义 1.1.2 队列的初始化 1.1.3 入队操作 1.1.4 出队操作 1.1.5 检查队列是否为空 1.1.6 返回队列的长度 2.1队列的运用 三、实验内容 问题描述 代码 截图 分析 一、实验目的 1、理解并掌握队列…

C语言中常用的字符串函数(strlen、sizeof、sscanf、sprintf、strcpy)

C语言中常用的字符串函数 文章目录 C语言中常用的字符串函数1 strlen函数2 sizeof函数2.1 sizeof介绍2.2 sizeof用法 3 sscanf函数3.1 sscanf介绍3.2 sscanf用法3.3 sscanf高级用法 4 sprintf函数4.1 背景4.2 sprintf用法 5 strcpy函数5.1 strcpy介绍5.1 strcpy用法 1 strlen函…

快乐学Python,Python基础之代码复用?

上一篇文章中&#xff0c;我们了解了代码的分支结构&#xff08;if 家族语句&#xff09;和循环结构&#xff08;for 循环和 while 循环&#xff09;。通过了解这些结构&#xff0c;我们已经能够写出稍微复杂一些的代码。但当代码一多&#xff0c;就会遇到一些问题。 上一篇文…

韩语发音干货,零基础韩语学习,柯桥韩语知识点之发音规律

01.连音化 当收音遇到以元音为首音的音节时&#xff0c;收音要和该元音相连发音。 예: 독일[도길] 밥을 [바블] 우산이[우사니] 읽어요[일거요] 02.送气 ㄱ/ㄷ/ㅂ/ㅈ遇到ㅎ,送气化读成ㅋ/ㅌ/ㅍ/ㅊ 예: 어떻게[어떠케] 좋다[조타] 많지만[만치만] 백화점[배콰…

设计模式——工厂方法模式(Factory Method Pattern)

简单工厂模式 概述 说工厂方法模式之前&#xff0c;先说下简单工厂模式&#xff0c;简单工厂模式并不属于GoF 23个经典设计模式&#xff0c;但通常将它作为学习其他工厂模式的基础&#xff0c;它的设计思想很简单&#xff0c;其基本流程如下&#xff1a;首先将需要创建的各种不…

网络安全复习--简答整理

-----------------------------------------------------教材如上图------------------------------------------------------------ 1.对称加密和非对称加密各有什么特点&#xff1f;加密解密过程中有什么区别&#xff1f;优点P38【考】 对称加密的特点&#xff1a;在针对同一…

element plus 表格组件怎样在表格中显示图片

官方给的&#xff1a; <el-table-column label"Thumbnail" width"180"><template #default"scope"><div style"display: flex; align-items: center"><el-image :preview-src-list"srcList"/><…

信号的互相关计算及时延估计

1. 信号的互相关计算 互相关反映向量x和移位&#xff08;滞后&#xff09;向量y之间的相似性。 最直观的解释是&#xff1a;互相关的作用是为了找到信号在哪一时刻与另一信号最像&#xff08;另一信号为本身时就是自相关&#xff09;&#xff01; 滑动求互相关&#xff08;图…

全球化视野下的品牌出海:生态体系的构建与优化

随着全球化的不断深入&#xff0c;品牌出海已成为企业迈向国际市场的重要战略之一。在这个竞争激烈的时代&#xff0c;品牌要在国际市场上获得成功&#xff0c;不仅需要具备强大的产品力和市场洞察力&#xff0c;还需要构建和优化一个完善的生态体系。本文Nox聚星将和大家探讨在…

常见的Latex公式所用到的内容汇总

行内公式 f ( x ) a b f(x)ab f(x)ab 左右各加一个$&#xff0c;即为行内公式 $ f(x) ab $行间公式 $$ f(x) ab $$f ( x ) a b f(x)ab f(x)ab 手动编号 $$ f(x) a - b \tag{1.1} $$f ( x ) a − b (1.1) f(x)a-b \tag{1.1} f(x)a−b(1.1) 简单运算 -*/以及阿拉伯…

【已解决】Invalid bound statement (not found)

报错讯息 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.casey.mapper.SysRoleMapper.getUserRoleCode at org.apache.ibatis.binding.MapperMethod S q l C o m m a n d . < i n i t > ( M a p p e r M e t h o d . j a v a :…