XBMC源代码分析 4:视频播放器(dvdplayer)-解码器(以ffmpeg为例)
XBMC分析系列文章:
本文我们分析XBMC中视频播放器(dvdplayer)中的解码器部分。由于解码器种类很多,不可能一一分析,因此以ffmpeg解码器为例进行分析。
XBMC解码器部分文件目录如下图所示:
解码器分为音频解码器和视频解码器。在这里我们看一下视频解码器中的FFMPEG解码器。对应DVDVideoCodecFFmpeg.h和DVDVideoCodecFFmpeg.cpp。
DVDVideoCodecFFmpeg.h源代码如下所示:
/*
* 雷霄骅
* leixiaohua1020@126.com
* 中国传媒大学/数字电视技术
*
*/
#include "DVDVideoCodec.h"
#include "DVDResource.h"
#include "DllAvCodec.h"
#include "DllAvFormat.h"
#include "DllAvUtil.h"
#include "DllSwScale.h"
#include "DllAvFilter.h"
#include "DllPostProc.h"
class CCriticalSection;
//封装的FFMPEG视频解码器
class CDVDVideoCodecFFmpeg : public CDVDVideoCodec
{
public:
class IHardwareDecoder : public IDVDResourceCounted<IHardwareDecoder>
{
public:
IHardwareDecoder() {}
virtual ~IHardwareDecoder() {};
virtual bool Open (AVCodecContext* avctx, const enum PixelFormat, unsigned int surfaces) = 0;
virtual int Decode (AVCodecContext* avctx, AVFrame* frame) = 0;
virtual bool GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture) = 0;
virtual int Check (AVCodecContext* avctx) = 0;
virtual void Reset () {}
virtual unsigned GetAllowedReferences() { return 0; }
virtual const std::string Name() = 0;
virtual CCriticalSection* Section() { return NULL; }
};
CDVDVideoCodecFFmpeg();
virtual ~CDVDVideoCodecFFmpeg();
virtual bool Open(CDVDStreamInfo &hints, CDVDCodecOptions &options);//打开
virtual void Dispose();//关闭
virtual int Decode(uint8_t* pData, int iSize, double dts, double pts);//解码
virtual void Reset();
bool GetPictureCommon(DVDVideoPicture* pDvdVideoPicture);
virtual bool GetPicture(DVDVideoPicture* pDvdVideoPicture);
virtual void SetDropState(bool bDrop);
virtual unsigned int SetFilters(unsigned int filters);
virtual const char* GetName() { return m_name.c_str(); }; // m_name is never changed after open
virtual unsigned GetConvergeCount();
virtual unsigned GetAllowedReferences();
bool IsHardwareAllowed() { return !m_bSoftware; }
IHardwareDecoder * GetHardware() { return m_pHardware; };
void SetHardware(IHardwareDecoder* hardware)
{
SAFE_RELEASE(m_pHardware);
m_pHardware = hardware;
UpdateName();
}
protected:
static enum PixelFormat GetFormat(struct AVCodecContext * avctx, const PixelFormat * fmt);
int FilterOpen(const CStdString& filters, bool scale);
void FilterClose();
int FilterProcess(AVFrame* frame);
void UpdateName()
{
if(m_pCodecContext->codec->name)
m_name = CStdString("ff-") + m_pCodecContext->codec->name;
else
m_name = "ffmpeg";
if(m_pHardware)
m_name += "-" + m_pHardware->Name();
}
AVFrame* m_pFrame;
AVCodecContext* m_pCodecContext;
CStdString m_filters;
CStdString m_filters_next;
AVFilterGraph* m_pFilterGraph;
AVFilterContext* m_pFilterIn;
AVFilterContext* m_pFilterOut;
#if defined(LIBAVFILTER_AVFRAME_BASED)
AVFrame* m_pFilterFrame;
#else
AVFilterBufferRef* m_pBufferRef;
#endif
int m_iPictureWidth;
int m_iPictureHeight;
int m_iScreenWidth;
int m_iScreenHeight;
int m_iOrientation;// orientation of the video in degress counter clockwise
unsigned int m_uSurfacesCount;
//封装Dll的各种类
DllAvCodec m_dllAvCodec;
DllAvUtil m_dllAvUtil;
DllSwScale m_dllSwScale;
DllAvFilter m_dllAvFilter;
DllPostProc m_dllPostProc;
std::string m_name;
bool m_bSoftware;
bool m_isHi10p;
IHardwareDecoder *m_pHardware;
int m_iLastKeyframe;
double m_dts;
bool m_started;
std::vector<PixelFormat> m_formats;
};
该类中以下几个函数包含了解码器的几种功能:
virtual bool Open(CDVDStreamInfo &hints, CDVDCodecOptions &options);//打开
virtual void Dispose();//关闭
virtual int Decode(uint8_t* pData, int iSize, double dts, double pts);//解码
virtual void Reset();//复位
为了说明这一点,我们可以看一下视频解码器中的libmpeg2解码器,对应DVDVideoCodecLibMpeg2.h。可以看出这几个函数是一样的。
DVDVideoCodecLibMpeg2.h源代码如下:
/*
* 雷霄骅
* leixiaohua1020@126.com
* 中国传媒大学/数字电视技术
*
*/
#include "DVDVideoCodec.h"
#include "DllLibMpeg2.h"
class CDVDVideoCodecLibMpeg2 : public CDVDVideoCodec
{
public:
CDVDVideoCodecLibMpeg2();
virtual ~CDVDVideoCodecLibMpeg2();
virtual bool Open(CDVDStreamInfo &hints, CDVDCodecOptions &options);
virtual void Dispose();
virtual int Decode(uint8_t* pData, int iSize, double dts, double pts);
virtual void Reset();
virtual bool GetPicture(DVDVideoPicture* pDvdVideoPicture);
virtual bool GetUserData(DVDVideoUserData* pDvdVideoUserData);
virtual void SetDropState(bool bDrop);
virtual const char* GetName() { return "libmpeg2"; }
protected:
DVDVideoPicture* GetBuffer(unsigned int width, unsigned int height);
inline void ReleaseBuffer(DVDVideoPicture* pPic);
inline void DeleteBuffer(DVDVideoPicture* pPic);
static int GuessAspect(const mpeg2_sequence_t *sequence, unsigned int *pixel_width, unsigned int *pixel_height);
mpeg2dec_t* m_pHandle;
const mpeg2_info_t* m_pInfo;
DllLibMpeg2 m_dll;
unsigned int m_irffpattern;
bool m_bFilm; //Signals that we have film material
bool m_bIs422;
int m_hurry;
double m_dts;
double m_dts2;
//The buffer of pictures we need
DVDVideoPicture m_pVideoBuffer[3];
DVDVideoPicture* m_pCurrentBuffer;
};
现在回到DVDVideoCodecFFmpeg.h。我们可以看一下上文所示的4个函数。
Open()
//打开
bool CDVDVideoCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
{
AVCodec* pCodec;
if(!m_dllAvUtil.Load()
|| !m_dllAvCodec.Load()
|| !m_dllSwScale.Load()
|| !m_dllPostProc.Load()
|| !m_dllAvFilter.Load()
) return false;
//注册解码器
m_dllAvCodec.avcodec_register_all();
m_dllAvFilter.avfilter_register_all();
m_bSoftware = hints.software;
m_iOrientation = hints.orientation;
for(std::vector<ERenderFormat>::iterator it = options.m_formats.begin(); it != options.m_formats.end(); ++it)
{
m_formats.push_back((PixelFormat)CDVDCodecUtils::PixfmtFromEFormat(*it));
if(*it == RENDER_FMT_YUV420P)
m_formats.push_back(PIX_FMT_YUVJ420P);
}
m_formats.push_back(PIX_FMT_NONE); /* always add none to get a terminated list in ffmpeg world */
pCodec = NULL;
m_pCodecContext = NULL;
if (hints.codec == AV_CODEC_ID_H264)
{
switch(hints.profile)
{
case FF_PROFILE_H264_HIGH_10:
case FF_PROFILE_H264_HIGH_10_INTRA:
case FF_PROFILE_H264_HIGH_422:
case FF_PROFILE_H264_HIGH_422_INTRA:
case FF_PROFILE_H264_HIGH_444_PREDICTIVE:
case FF_PROFILE_H264_HIGH_444_INTRA:
case FF_PROFILE_H264_CAVLC_444:
// this is needed to not open the decoders
m_bSoftware = true;
// this we need to enable multithreading for hi10p via advancedsettings
m_isHi10p = true;
break;
}
}
//查找解码器
if(pCodec == NULL)
pCodec = m_dllAvCodec.avcodec_find_decoder(hints.codec);
if(pCodec == NULL)
{
CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to find codec %d", hints.codec);
return false;
}
CLog::Log(LOGNOTICE,"CDVDVideoCodecFFmpeg::Open() Using codec: %s",pCodec->long_name ? pCodec->long_name : pCodec->name);
if(m_pCodecContext == NULL)
m_pCodecContext = m_dllAvCodec.avcodec_alloc_context3(pCodec);
m_pCodecContext->opaque = (void*)this;
m_pCodecContext->debug_mv = 0;
m_pCodecContext->debug = 0;
m_pCodecContext->workaround_bugs = FF_BUG_AUTODETECT;
m_pCodecContext->get_format = GetFormat;
m_pCodecContext->codec_tag = hints.codec_tag;
/* Only allow slice threading, since frame threading is more
* sensitive to changes in frame sizes, and it causes crashes
* during HW accell - so we unset it in this case.
*
* When we detect Hi10p and user did not disable hi10pmultithreading
* via advancedsettings.xml we keep the ffmpeg default thread type.
* */
if(m_isHi10p && !g_advancedSettings.m_videoDisableHi10pMultithreading)
{
CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Keep default threading for Hi10p: %d",
m_pCodecContext->thread_type);
}
else if (CSettings::Get().GetBool("videoplayer.useframemtdec"))
{
CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Keep default threading %d by videoplayer.useframemtdec",
m_pCodecContext->thread_type);
}
else
m_pCodecContext->thread_type = FF_THREAD_SLICE;
#if defined(TARGET_DARWIN_IOS)
// ffmpeg with enabled neon will crash and burn if this is enabled
m_pCodecContext->flags &= CODEC_FLAG_EMU_EDGE;
#else
if (pCodec->id != AV_CODEC_ID_H264 && pCodec->capabilities & CODEC_CAP_DR1
&& pCodec->id != AV_CODEC_ID_VP8
)
m_pCodecContext->flags |= CODEC_FLAG_EMU_EDGE;
#endif
// if we don't do this, then some codecs seem to fail.
m_pCodecContext->coded_height = hints.height;
m_pCodecContext->coded_width = hints.width;
m_pCodecContext->bits_per_coded_sample = hints.bitsperpixel;
if( hints.extradata && hints.extrasize > 0 )
{
m_pCodecContext->extradata_size = hints.extrasize;
m_pCodecContext->extradata = (uint8_t*)m_dllAvUtil.av_mallocz(hints.extrasize + FF_INPUT_BUFFER_PADDING_SIZE);
memcpy(m_pCodecContext->extradata, hints.extradata, hints.extrasize);
}
// advanced setting override for skip loop filter (see avcodec.h for valid options)
// TODO: allow per video setting?
if (g_advancedSettings.m_iSkipLoopFilter != 0)
{
m_pCodecContext->skip_loop_filter = (AVDiscard)g_advancedSettings.m_iSkipLoopFilter;
}
// set any special options
for(std::vector<CDVDCodecOption>::iterator it = options.m_keys.begin(); it != options.m_keys.end(); ++it)
{
if (it->m_name == "surfaces")
m_uSurfacesCount = std::atoi(it->m_value.c_str());
else
m_dllAvUtil.av_opt_set(m_pCodecContext, it->m_name.c_str(), it->m_value.c_str(), 0);
}
int num_threads = std::min(8 /*MAX_THREADS*/, g_cpuInfo.getCPUCount());
if( num_threads > 1 && !hints.software && m_pHardware == NULL // thumbnail extraction fails when run threaded
&& ( pCodec->id == AV_CODEC_ID_H264
|| pCodec->id == AV_CODEC_ID_MPEG4 ))
m_pCodecContext->thread_count = num_threads;
//打开解码器
if (m_dllAvCodec.avcodec_open2(m_pCodecContext, pCodec, NULL) < 0)
{
CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to open codec");
return false;
}
//初始化AVFrame
m_pFrame = m_dllAvCodec.avcodec_alloc_frame();
if (!m_pFrame) return false;
#if defined(LIBAVFILTER_AVFRAME_BASED)
m_pFilterFrame = m_dllAvUtil.av_frame_alloc();
if (!m_pFilterFrame) return false;
#endif
UpdateName();
return true;
}
Dispose()
//关闭
void CDVDVideoCodecFFmpeg::Dispose()
{
//释放
if (m_pFrame) m_dllAvUtil.av_free(m_pFrame);
m_pFrame = NULL;
#if defined(LIBAVFILTER_AVFRAME_BASED)
m_dllAvUtil.av_frame_free(&m_pFilterFrame);
#endif
if (m_pCodecContext)
{
//关闭解码器
if (m_pCodecContext->codec) m_dllAvCodec.avcodec_close(m_pCodecContext);
if (m_pCodecContext->extradata)
{
m_dllAvUtil.av_free(m_pCodecContext->extradata);
m_pCodecContext->extradata = NULL;
m_pCodecContext->extradata_size = 0;
}
m_dllAvUtil.av_free(m_pCodecContext);
m_pCodecContext = NULL;
}
SAFE_RELEASE(m_pHardware);
FilterClose();
m_dllAvCodec.Unload();
m_dllAvUtil.Unload();
m_dllAvFilter.Unload();
m_dllPostProc.Unload();
}
Decode()
//解码
int CDVDVideoCodecFFmpeg::Decode(uint8_t* pData, int iSize, double dts, double pts)
{
int iGotPicture = 0, len = 0;
if (!m_pCodecContext)
return VC_ERROR;
if(pData)
m_iLastKeyframe++;
shared_ptr<CSingleLock> lock;
if(m_pHardware)
{
CCriticalSection* section = m_pHardware->Section();
if(section)
lock = shared_ptr<CSingleLock>(new CSingleLock(*section));
int result;
if(pData)
result = m_pHardware->Check(m_pCodecContext);
else
result = m_pHardware->Decode(m_pCodecContext, NULL);
if(result)
return result;
}
if(m_pFilterGraph)
{
int result = 0;
if(pData == NULL)
result = FilterProcess(NULL);
if(result)
return result;
}
m_dts = dts;
m_pCodecContext->reordered_opaque = pts_dtoi(pts);
//初始化AVPacket
AVPacket avpkt;
m_dllAvCodec.av_init_packet(&avpkt);
avpkt.data = pData;
avpkt.size = iSize;
/* We lie, but this flag is only used by pngdec.c.
* Setting it correctly would allow CorePNG decoding. */
avpkt.flags = AV_PKT_FLAG_KEY;
//解码
len = m_dllAvCodec.avcodec_decode_video2(m_pCodecContext, m_pFrame, &iGotPicture, &avpkt);
if(m_iLastKeyframe < m_pCodecContext->has_b_frames + 2)
m_iLastKeyframe = m_pCodecContext->has_b_frames + 2;
if (len < 0)
{
CLog::Log(LOGERROR, "%s - avcodec_decode_video returned failure", __FUNCTION__);
return VC_ERROR;
}
if (!iGotPicture)
return VC_BUFFER;
if(m_pFrame->key_frame)
{
m_started = true;
m_iLastKeyframe = m_pCodecContext->has_b_frames + 2;
}
/* put a limit on convergence count to avoid huge mem usage on streams without keyframes */
if(m_iLastKeyframe > 300)
m_iLastKeyframe = 300;
/* h264 doesn't always have keyframes + won't output before first keyframe anyway */
if(m_pCodecContext->codec_id == AV_CODEC_ID_H264
|| m_pCodecContext->codec_id == AV_CODEC_ID_SVQ3)
m_started = true;
if(m_pHardware == NULL)
{
bool need_scale = std::find( m_formats.begin()
, m_formats.end()
, m_pCodecContext->pix_fmt) == m_formats.end();
bool need_reopen = false;
if(!m_filters.Equals(m_filters_next))
need_reopen = true;
if(m_pFilterIn)
{
if(m_pFilterIn->outputs[0]->format != m_pCodecContext->pix_fmt
|| m_pFilterIn->outputs[0]->w != m_pCodecContext->width
|| m_pFilterIn->outputs[0]->h != m_pCodecContext->height)
need_reopen = true;
}
// try to setup new filters
if (need_reopen || (need_scale && m_pFilterGraph == NULL))
{
m_filters = m_filters_next;
if(FilterOpen(m_filters, need_scale) < 0)
FilterClose();
}
}
int result;
if(m_pHardware)
result = m_pHardware->Decode(m_pCodecContext, m_pFrame);
else if(m_pFilterGraph)
result = FilterProcess(m_pFrame);
else
result = VC_PICTURE | VC_BUFFER;
if(result & VC_FLUSHED)
Reset();
return result;
}
Reset()
//复位
void CDVDVideoCodecFFmpeg::Reset()
{
m_started = false;
m_iLastKeyframe = m_pCodecContext->has_b_frames;
m_dllAvCodec.avcodec_flush_buffers(m_pCodecContext);
if (m_pHardware)
m_pHardware->Reset();
m_filters = "";
FilterClose();
}
XBMC源代码分析 4:视频播放器(dvdplayer)-解码器(以ffmpeg为例)的更多相关文章
- XBMC源代码分析 7:视频播放器(dvdplayer)-输入流(以libRTMP为例)
前文分析了XBMC的基本结构: XBMC源代码分析 1:整体结构以及编译方法 XBMC源代码分析 2:Addons(皮肤Skin) XBMC源代码分析 3:核心部分(core)-综述 XBMC源代码分 ...
- XBMC源代码分析 6:视频播放器(dvdplayer)-文件头(以ffmpeg为例)
XBMC分析系列文章: XBMC源代码分析 1:整体结构以及编译方法 XBMC源代码分析 2:Addons(皮肤Skin) XBMC源代码分析 3:核心部分(core)-综述 XBMC源代码分析 4: ...
- XBMC源代码分析 3:核心部分(core)-综述
前文分析了XBMC的整体结构以及皮肤部分: XBMC源代码分析 1:整体结构以及编译方法 XBMC源代码分析 2:Addons(皮肤Skin) 本文以及以后的文章主要分析XBMC的VC工程中的源代码. ...
- XBMC源代码分析 2:Addons(皮肤Skin)
前文已经对XBMC源代码的整体架构进行了分析: XBMC源代码分析 1:整体结构以及编译方法 从这篇文章开始,就要对XBMC源代码进行具体分析了.首先先不分析其C++代码,分析一下和其皮肤相关的代码. ...
- ffdshow 源代码分析 7: libavcodec视频解码器类(TvideoCodecLibavcodec)
===================================================== ffdshow源代码分析系列文章列表: ffdshow 源代码分析 1: 整体结构 ffds ...
- LIRe 源代码分析 5:提取特征向量[以颜色布局为例]
===================================================== LIRe源代码分析系列文章列表: LIRe 源代码分析 1:整体结构 LIRe 源代码分析 ...
- 转:XBMC源代码分析
1:整体结构以及编译方法 XBMC(全称是XBOX Media Center)是一个开源的媒体中心软件.XBMC最初为Xbox而开发,可以运行在Linux.OSX.Windows.Android4.0 ...
- XBMC源代码分析 1:整体结构以及编译方法
XBMC(全称是XBOX Media Center)是一个开源的媒体中心软件.XBMC最初为Xbox而开发,可以运行在Linux.OSX.Windows.Android4.0系统.我自己下载了一个然后 ...
- 从源代码分析modelDriven拦截器和params拦截器和拦截器prepare 和paramsPrepareParamsStack拦截器栈(让你的Struts2代码更简洁——如何培养框架设计能力
源代码文件:Web App Libraries/struts2-core-2.3.15.3.jar/struts-default.xml 拦截器modelDriven: <interceptor ...
随机推荐
- python while条件和if判断的总练习
输出123456 89的数字 num =1 while num < 11: if num == 7: pass else: print(num) num = num + 1 输出1-100的奇数 ...
- 浅谈static其一之不死变量
在学习汇编的过程中,小有所悟,遂把自己所思所想记下,以便日后查阅. 首先说说我对这个关键字的理解.static字面上就是静止的.静态的.不变的之类的意思,所以在被它修饰之后,应该也会带有这样的一些特点 ...
- C++笔记001:Microsoft Visual Studio 2010软件的安装与建立第一个cpp文件
原创笔记,转载请注明出处! 点击[关注],关注也是一种美德~ 我学习C++使用软件为Microsoft Visual Studio 2010. 首先,软件的安装包 链接:https://pan.bai ...
- 纪念 参与GitHub上第一个组织
颇为起伏的一天. 今天大连的风, 甚是喧嚣. 不过,很高兴,小项目被fork了,也成功成为了一个开源贡献者. https://github.com/HostsTools 组织 上的那个Windows- ...
- log4j日志记录级别是如何工作?
级别p的级别使用q,在记录日志请求时,如果p>=q启用.这条规则是log4j的核心.它假设级别是有序的.对于标准级别它们关系如下:ALL < DEBUG < INFO < WA ...
- 解决 oracle IO占用率很高的问题
突然user io占用率很很高,看了一个AWR报告,发现direct path read temp,direct path write temp的的数率很高,后来怀疑是临时表空间不够了,就试着设了一下 ...
- [boost][filesystem] 扫描给定目录下所有项
Intro. Boost的filesystem可以用来扫描给定目录下的所有项. 实现 具体实现代码如下: 需要包含的头文件和使用的命名空间: #include <boost/filesystem ...
- 分析RunTime执行命令以及得到返回值
RunTime执行命令得到返回值 我们有在好好几篇博客里提到过RunTime,比如 JAVA之旅(二十三)--System,RunTime,Date,Calendar,Math的数学运算 Androi ...
- 关于[[NSNotificationCenter defaultCenter] addObserver不remove后续又收到通知crash问题
今天试了一个小demo,测出一个现象,同步出来:object 作为 observer 监听了通知 A,然后 object 中途被释放执行了dealloc,随后app发出这个通知 A:iOS 6.iOS ...
- Hive-ORC文件存储格式(续)
本文在Hive-ORC文件存储格式的理论基础上,进一步分析一个实际的Hive ORC表中的数据存储形式. 一.表结构 库名+表名:fileformat.test_orc 字段 类型 category_ ...