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.mzph.cn/news/608188.shtml

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

相关文章

机器人技能学习-robosuite-0-入门介绍

文章目录 前言模块介绍实战案例1&#xff1a;从 demo 中创建自己的 env案例2&#xff1a;更换属于自己的物体 前言 资料太少、资料太少、资料太少&#xff0c;重要的事说三边&#xff0c;想根据自己实际场景自定义下机器人&#xff0c;结果发现无路可走&#xff0c;鉴于缺少参…

判断vector、string是否存在某个元素

1、string字符串中是否存在某个字符&#xff08;char&#xff09; string中find()返回值是字母在母串中的位置&#xff08;下标索引&#xff09;&#xff0c;如果没有找到&#xff0c;那么会返回一个特别的标记npos。&#xff08;返回值可以看成是一个int型的数&#xff09; …

软件测试概念及分类整理汇总

前言 测试小伙伴在谈论软件测试分类&#xff0c;五花八门的分类&#xff0c;眼花缭乱。因为将各个维度划分的内容都整到一块了&#xff0c;在加上各自不同的见解与补充&#xff0c;各种冲突...... Findyou我经过多年测试总结基本定为4类测试(最多5类&#xff0c;自动化或者兼容…

2401d,d导入C的问题

原文 D中是否可用仅C头文件库? 在C语言中,我需要这样做: #define STB_DS_IMPLEMENTATION #include "stb_ds.h"在包含h文件前,必须在单个C文件中定义. 在D中试过: enum STB_DS_IMPLEMENTATION 1; import stb_ds;但它不管用.有建议吗?也许使用中间C文件会工作 ,但…

phpstudy面板Table ‘mysql.proc‘ doesn‘t exist解决办法

原因分析&#xff1a;误删了mysql数据库 解决办法如下&#xff1a; 1、停止服务 2、先把mysql文件夹下的data文件夹备份&#xff0c;因为data文件里存有数据库文件。然后再删除data文件。 3、cmd管理员命令进入到mysql中的bin目录下 &#xff0c;执行mysqld --initialize-…

【Python机器学习】用于回归的决策树

用于回归的决策树与用于分类的决策树类似&#xff0c;在DecisionTreeRegressor中实现。DecisionTreeRegressor不能外推&#xff0c;也不能在训练数据范围之外的数据进行预测。 利用计算机内存历史及格的数据进行实验&#xff0c;数据展示&#xff1a; import pandas as pd im…

.mkp勒索病毒数据怎么处理|数据解密恢复

导言&#xff1a; 在数字时代&#xff0c;勒索病毒如[datastorecyberfear.com].mkp [hendersoncock.li].mkp [myersairmail.cc].mkp正成为企业和个人的噩梦。本文将介绍[datastorecyberfear.com].mkp [hendersoncock.li].mkp [myersairmail.cc].mkp勒索病毒的特点、如何恢复被…

LIN总线故障检测

关注菲益科公众号—>对话窗口发送 “CANoe ”或“INCA”&#xff0c;即可获得canoe入门到精通电子书和INCA软件安装包&#xff08;不带授权码&#xff09;下载地址。 LIN总线自诊断 对LIN数据总线系统进行自诊断时&#xff0c;需使用“LIN主控模块”的“地址码”。 自诊断数…

化学DS-1040 Tosylate 抑制剂 1335138-89-0科研用途

化合物1219962-49-8是一种小分子化合物&#xff0c;分子式为C15H25N3O4&#xff0c;相对分子质量为305.37。该化合物为白色至灰白色粉末&#xff0c;不溶于水&#xff0c;易溶于有机溶剂&#xff0c;如甲醇、乙醇等。 AT791是一种与细胞周期调控相关的蛋白激酶&#xff0c;参与…

大功率TVS的基本介绍?|深圳比创达电子

在当今的电子世界里&#xff0c;电路保护变得越来越重要。那么什么是大功率TVS(Transient Voltage Suppressor)&#xff1f;它又是如何发挥作用的呢&#xff1f;TVS二极管&#xff0c;被设计用来保护敏感电子设备免受瞬态电压冲击&#xff0c;尤其是在功率较高的应用中尤为关键…

一文详解VScode 的远程开发

VS code登录服务器后进行编码和调试&#xff0c;VS code上的所有功能都可以使用&#xff0c;和在本地开发基本无区别。 一、配置免密远程登录 因为是要远程登录&#xff0c;那么需要通过使用ssh进行密钥对登录&#xff0c;这样每次登录服务器就可以不用输入密码了。 先来一句官…

五、C#与数据库交互( SQL注入与安全性)

在C#与数据库交互时&#xff0c;安全性是非常重要的一部分&#xff0c;特别是要防止SQL注入攻击。SQL注入是一种常见的网络攻击手段&#xff0c;攻击者通过在输入字段中注入恶意SQL代码来操纵数据库查询。以下是一些关于如何防止SQL注入的建议&#xff1a; 使用参数化查询: 这是…

常见测试技术都有哪些?

测试技术是用于评估系统或组件的方法&#xff0c;目的是发现它是否满足给定的要求。系统测试有助于识别缺口、错误&#xff0c;或与实际需求不同的任何类型的缺失需求。测试技术是测试团队根据给定的需求评估已开发软件所使用的最佳实践。这些技术可以确保产品或软件的整体质量…

【MySQL】utft8mb4 字符集及其排序规则(字符集校验规则)

UTF-8 是 Unicode 的一种实现方式&#xff0c;它可以表示世界上绝大多数的字符&#xff0c;包括大部分的中文字符。MySQL 从 5.5.3 版本开始支持 UTF-8 字符集&#xff0c;其中包括 UTF-8MB4。UTF-8MB4 是 MySQL 支持的最大的字符集&#xff0c;它可以表示 4 字节的 Unicode 字…

TDuckX 新功能介绍:提交后抽奖!

欢迎来到 TDuckX&#xff0c;我们最近推出了令人兴奋的新功能——提交后抽奖&#xff01;现在&#xff0c;您不仅可以收集用户的宝贵意见&#xff0c;还有机会为他们带来丰厚的奖励。让我们一起来了解这个令人期待的新功能吧&#xff01; 主要功能亮点&#xff1a; 1. 奖品设…

adb 使用的出现 adb server is out of date. killing

我知道你们肯定看到这种播客 像这种解决问题的方法是暂时的 , 因为我们Android studio 中 , 你在查看后台的时候发现 你的Android studio 也占用了一端口 , 就是说你把 Android studio 杀掉了只要打开 Android studio 打开就还是 关闭 cmd adb 看到一个新的方法 , win 10 中…

Spring——Spring AOP1(代理模式Proxy)

代理&#xff08;Proxy&#xff09;模式 1.创建工程 2.代理&#xff08;Proxy&#xff09;模式介绍 作用&#xff1a;通过代理可以控制访问某个对象的方法&#xff0c;在调用这个方法前做前置处理&#xff0c;调用这个方法后做后置处理。&#xff08;即&#xff1a; AOP的微观…

GDI+更改背景颜色

GDI更改背景颜色 CRect rectDlg;GetClientRect(rectDlg);//获得窗体的大小Graphics graphics(this->GetDC()->m_hDC);//设置背景颜色Gdiplus::SolidBrush redBrush(Color(240 , 240 , 240));//填充背景graphics.FillRectangle(&redBrush, 0, 0, rectDlg.Width(), re…

C类网络地址段通过变长子网掩码划分3个局域网

现有一个C类网络地址段192.168.1.0/24&#xff0c;局域网1有30个主机&#xff0c;局域网2有20个主机&#xff0c;局域网3有10个主机&#xff0c;请使用变长子网掩码给三个子网分别分配IP地址。 解答&#xff1a;需要先划分数量小的局域网&#xff0c;我这里从局域网3开始划分。…

208.【2023年华为OD机试真题(C卷)】停车场车辆统计(贪心算法实现-JavaPythonC++JS实现)

🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目-停车场车辆统计二.解题思路三.题解代码Python题…