博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从零实现简易播放器:4.ffmpeg 解码视频为yuv数据-使用avcodec_send_packet与avcodec_receive_frame
阅读量:4071 次
发布时间:2019-05-25

本文共 8867 字,大约阅读时间需要 29 分钟。

ffmpeg 解码视频为yuv数据

作者:史正邮箱:shizheng163@126.com如有错误还请及时指正如果有错误的描述给您带来不便还请见谅如需交流请发送邮件,欢迎联系

csdn :

github :

文章目录

简述

简单描述下视频播放的步骤:

  • 解复用:将输入的视频变为编码后的压缩数据
  • 解码: 将压缩数据变为颜色空间(YUV, RGB等)
  • 渲染: 将YUV等颜色空间绘制在显示设备上形成图像

下面对应上述流程说明下ffmpeg解码为yuv数据的接口调用。

ffmpeg版本:4.1

前文曾经提过解码后的图像数据为YUV420, 如果只需要YUV420, 那么就不需要进行图像转换,图像色彩空间转换环境也无需初始化。

另外为了对比播放器的播放速度特意使用如下命令为视频添加了水印:

  • ffmpeg -i ./Suger.mp4 -vf "drawtext=expansion=strftime: basetime=$(date +%s -d '2019-01-12 00:00:00')000000 :text='%Y-%m-%d %H\\:%M\\:%S':fontsize=30:fontcolor=white:box=1:x=10:y=10:boxcolor=black@0.5:" -strict -2 -y "SugerTime.mp4"

2019-01-12 00:00:00为起始时间戳。

错误处理

  • 处理过程中出现bad dst image pointers

    此问题是因为没有调用av_image_alloc

  • 解码过程中出现:Invalid data found when processing input

    此问题出现是因为没调用avcodec_parameters_to_contextavcodec_open2

代码实现

如果打开的url是网络流需要先调用avformat_network_init() 初始化网络环境。

完整代码可见

.h 文件

/* * copyright (c) 2018-2019 shizheng. All Rights Reserved. * Please retain author information while you reference code * date:   2019-01-13 * author: shizheng * email:  shizheng163@126.com */#ifndef FFDECODER_H#define FFDECODER_H#include 
#include
#include
#include
#include
struct AVFormatContext;struct AVFrame;struct AVCodecContext;namespace ffmpegutil {
typedef fileutil::PictureFilePtr YuvDataPtr;class FFDecoder{
public: typedef std::function
ProcessYuvDataCallback; typedef std::function
DecodeThreadExitCallback; FFDecoder(); ~FFDecoder(); bool InitializeDecoder(std::string url); bool StartDecodeThread(); void StopDecodeThread(); /** * @brief 设置解码数据回调函数 */ void SetProcessDataCallback(ProcessYuvDataCallback callback); /** * @brief 设置解码线程退出的回调函数 */ void SetDecodeThreadExitCallback(DecodeThreadExitCallback callback); /** * @brief 获取错误原因 * @note 当错误发生时, 错误原因会被记录 */ std::string ErrName() const { return m_szErrName;} /** * @brief 获取视频帧率 */ float GetVideoFrameRate();private: void decodeInThread(); std::string m_szUrl; AVFormatContext *m_pInputFormatContext; int m_nVideoStreamIndex; AVCodecContext *m_pCodecContext; //处理解码后数据的回调函数 std::mutex m_mutexForFnProcessYuvData; ProcessYuvDataCallback m_fnProcssYuvData; std::thread m_threadForDecode; bool m_bIsRunDecodeThread; std::string m_szErrName; //处理解码线程退出的回调函数 std::mutex m_mutexForFnThreadExit; DecodeThreadExitCallback m_fnThreadExit;};}//namespace ffmpegutil#endif // FFDECODER_H

cpp文件

/* * copyright (c) 2018-2019 shizheng. All Rights Reserved. * Please retain author information while you reference code * date:   2019-01-13 * author: shizheng * email:  shizheng163@126.com */#include "ffdecoder.h"#include 
extern "C"{
#include
#include
#include
}#include "logutil.h"#include "ffmpegutil.h"using namespace ffmpegutil;using namespace std;using namespace logutil;typedef std::shared_ptr
AVPacketPtr;typedef std::shared_ptr
AVFramePtr;FFDecoder::FFDecoder() :m_pInputFormatContext(NULL) ,m_nVideoStreamIndex(-1) ,m_pCodecContext(NULL){ }FFDecoder::~FFDecoder(){ if(m_threadForDecode.joinable()) m_threadForDecode.join(); if(m_pInputFormatContext) { avformat_close_input(&m_pInputFormatContext); avformat_free_context(m_pInputFormatContext); m_pInputFormatContext = NULL; } if(m_pCodecContext) { avcodec_free_context(&m_pCodecContext); m_pCodecContext = NULL; }}bool FFDecoder::InitializeDecoder(string url){ m_szUrl = url; int ret = 0; ret = avformat_open_input(&m_pInputFormatContext, m_szUrl.c_str(), NULL, NULL); //GetStrError是对av_strerror的一次封装。 if(ret < 0) { m_szErrName = MySprintf("FFDecoder open input failed, url = %s, err: %s", m_szUrl.c_str(), GetStrError(ret).c_str()); return false; } //先探测文件信息 ret = avformat_find_stream_info(m_pInputFormatContext, NULL); if(ret < 0) { m_szErrName = MySprintf("FFDecoder find stream info failed, url = %s, err: %s", m_szUrl.c_str(), GetStrError(ret).c_str()); return false; } for(unsigned i = 0; i < m_pInputFormatContext->nb_streams; i++) { if(m_pInputFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { m_nVideoStreamIndex = i; break; } } if( m_nVideoStreamIndex == -1) { m_szErrName = MySprintf("FFDecoder could find video stream, url = %s", m_szUrl.c_str()); return false; }// av_dump_format(m_pInputFormatContext, m_nVideoStreamIndex, NULL, 0); //InitAVDecoder AVCodec * pCodec = avcodec_find_decoder(m_pInputFormatContext->streams[m_nVideoStreamIndex]->codecpar->codec_id); if(!pCodec) { m_szErrName = MySprintf("FFDecoder find AVCodec failed, url = %s, codec: %s", m_szUrl.c_str(), avcodec_get_name(m_pInputFormatContext->streams[m_nVideoStreamIndex]->codecpar->codec_id)); return false; } m_pCodecContext = avcodec_alloc_context3(pCodec); //复制解码器参数 ret = avcodec_parameters_to_context(m_pCodecContext, m_pInputFormatContext->streams[m_nVideoStreamIndex]->codecpar); if(ret < 0) { m_szErrName = MySprintf("FFDecoder avcodec_parameters_to_context failed, url = %s, err = %s", m_szUrl.c_str(), avcodec_get_name(m_pInputFormatContext->streams[m_nVideoStreamIndex]->codecpar->codec_id), GetStrError(ret).c_str()); return false; } ret = avcodec_open2(m_pCodecContext, pCodec, NULL); if(ret < 0) { m_szErrName = MySprintf("FFDecoder AVCodec Open failed, url = %s, codec: %s, err = %s", m_szUrl.c_str(), avcodec_get_name(m_pInputFormatContext->streams[m_nVideoStreamIndex]->codecpar->codec_id), GetStrError(ret).c_str()); return false; } return true;}bool FFDecoder::StartDecodeThread(){ if(!m_pCodecContext) { m_szErrName = "decode context not init!"; return false; } m_bIsRunDecodeThread = true; m_threadForDecode = std::thread(&FFDecoder::decodeInThread, this); return true;}void FFDecoder::SetProcessDataCallback(FFDecoder::ProcessYuvDataCallback callback){ std::unique_lock
locker(m_mutexForFnProcessYuvData); m_fnProcssYuvData = callback;}void FFDecoder::SetDecodeThreadExitCallback(FFDecoder::DecodeThreadExitCallback callback){ std::unique_lock
locker(m_mutexForFnThreadExit); m_fnThreadExit = callback;}void FFDecoder::StopDecodeThread(){ m_bIsRunDecodeThread = false;}float FFDecoder::GetVideoFrameRate(){ if(m_pInputFormatContext) { return (float)m_pInputFormatContext->streams[m_nVideoStreamIndex]->avg_frame_rate.num / m_pInputFormatContext->streams[m_nVideoStreamIndex]->avg_frame_rate.den ; } return 0;}void FFDecoder::decodeInThread(){ bool isEof = false; AVFramePtr pFrameScale(av_frame_alloc(), [](AVFrame * ptr){ av_frame_free(&ptr); //释放调用av_image_alloc申请的内存 av_free(&ptr->data[0]); }); int ret = 0; //初始化图像转换器 AVStream * pVideoStream = m_pInputFormatContext->streams[m_nVideoStreamIndex]; SwsContext * pswsContext = sws_getContext(pVideoStream->codecpar->width, pVideoStream->codecpar->height, (AVPixelFormat)pVideoStream->codecpar->format, pVideoStream->codecpar->width, pVideoStream->codecpar->height, AV_PIX_FMT_YUV420P, SWS_FAST_BILINEAR, 0, 0, 0); //申请图像存储内存 av_image_alloc(pFrameScale->data, pFrameScale->linesize, pVideoStream->codecpar->width, pVideoStream->codecpar->height, AV_PIX_FMT_YUV420P, 1); while(m_bIsRunDecodeThread) { AVPacketPtr ptrAVPacket(av_packet_alloc(), [](AVPacket * ptr){ av_packet_free(&ptr); }); av_init_packet(ptrAVPacket.get()); ret = av_read_frame(m_pInputFormatContext, ptrAVPacket.get()); if(ret < 0) { string szErrName = GetStrError(ret).c_str(); if(szErrName != "End of file") m_szErrName = MySprintf("FFMpeg Read Frame Failed, Err = %s", szErrName.c_str()); else { MyLog(info, "FFMpeg Read Frame Complete!"); isEof = true; } break; } if(ptrAVPacket->stream_index == m_nVideoStreamIndex) { ret = avcodec_send_packet(m_pCodecContext, ptrAVPacket.get()); AVFramePtr pTempFrame(av_frame_alloc(), [](AVFrame * ptr){ av_frame_free(&ptr); }); while(1) { ret = avcodec_receive_frame(m_pCodecContext, pTempFrame.get()); if(ret != 0) break; ret= sws_scale(pswsContext, (const uint8_t* const*)pTempFrame->data, pTempFrame->linesize, 0, pTempFrame->height, (uint8_t* const*)pFrameScale->data, pFrameScale->linesize); if(ret > 0) { fileutil::FileRawData data; data.AppendData(pFrameScale->data[0], pVideoStream->codecpar->width * pVideoStream->codecpar->height); data.AppendData(pFrameScale->data[1], pVideoStream->codecpar->width * pVideoStream->codecpar->height / 4); data.AppendData(pFrameScale->data[2], pVideoStream->codecpar->width * pVideoStream->codecpar->height / 4); YuvDataPtr pYuvData(new fileutil::PictureFile(data, pVideoStream->codecpar->width, pVideoStream->codecpar->height, fileutil::PictureFile::kFormatYuv)); pYuvData->m_filename = to_string(pVideoStream->codec_info_nb_frames); std::unique_lock
locker(m_mutexForFnProcessYuvData); if(m_fnProcssYuvData) m_fnProcssYuvData(pYuvData); } } } } MyLog(m_bIsRunDecodeThread && !isEof ? err : info, "FFDecoder %s exit!\n", m_bIsRunDecodeThread && !isEof ? "abnormal" : "normal"); std::unique_lock
locker(m_mutexForFnThreadExit); if(m_fnThreadExit) m_fnThreadExit(m_bIsRunDecodeThread && !isEof);}

参考文章

转载地址:http://prhji.baihongyu.com/

你可能感兴趣的文章
Oracle 异机恢复
查看>>
Oracle 12C DG 搭建(RAC-RAC/RAC-单机)
查看>>
Truncate 表之恢复
查看>>
Oracle DG failover 后恢复
查看>>
mysql 主从同步配置
查看>>
为什么很多程序员都选择跳槽?
查看>>
mongdb介绍
查看>>
mongdb在java中的应用
查看>>
Yotta企业云盘更好的为媒体广告业服务
查看>>
Yotta企业云盘助力科技行业创高峰
查看>>
Yotta企业云盘更好地为教育行业服务
查看>>
Yotta企业云盘怎么帮助到能源化工行业
查看>>
企业云盘如何助力商业新发展
查看>>
医疗行业运用企业云盘可以带来什么样的提升
查看>>
能源化工要怎么管控核心数据
查看>>
媒体广告业如何运用云盘提升效率
查看>>
企业如何运用企业云盘进行数字化转型-实现新发展
查看>>
司法如何运用电子智能化加快现代化建设
查看>>
iSecret&nbsp;1.1&nbsp;正在审核中
查看>>
IOS开发的开源库
查看>>