XBMC源代码分析

1:整体结构以及编译方法

XBMC(全称是XBOX Media Center)是一个开源的媒体中心软件。XBMC最初为Xbox而开发,可以运行在Linux、OSX、Windows、Android4.0系统。我自己下载了一个然后体验了一下,感觉确实不错,和Windows自带的媒体中心差不多。

XBMC项目首页:http://xbmc.org/

XBMC差不多是我接触到的开源多媒体项目中体积最大的了。但是它的编译方法却出乎意料的简单。我按照它的Wiki上面说的步骤去做,非常顺利的完成了编译,没有遇到任何错误,赞一个。

下面简述一下它的编译方法。

前提条件

1.Visual C++ 2010

2.Microsoft DirectX SDK (August 2009 或更晚的版本)

3.Git

4.JRE

编译

注意:需要下载很多东西,所以需要联网

1.使用Git下载源代码。Git地址:git://github.com/xbmc/xbmc.git

2.运行DownloadBuildDeps.bat (所在目录 project\BuildDependencies):下载编译项目所需要的依赖项

3.运行DownloadMingwBuildEnv.bat (所在目录 project\BuildDependencies) :下载编译ffmpeg库所需要的依赖项

4.运行buildmingwlibs.bat (所在目录 project\Win32BuildSetup): 编译ffmpeg库
5.以下二选一。一般情况下选第二个就可以了。

(1)BuildSetup.bat (所在目录 project\Win32BuildSetup):只有需要直接编译一个打包文件的时候,才推荐使用该批处理。

(2)extract_git_rev.bat : 如果是为了调试,并且使用 VC++ 2010 进行编译,推荐使用该批处理。

6.打开project\VS2010Express\XBMC for Windows.sln,就可以编译了。

下面对XBMC源代码进行一个整体分析:

源代码的目录结构如下图所示。我把其中比较主要的文件夹下面标记了一条红线。

这几个主要文件夹的作用如下(其他文件夹就不再细说了):

addons:附加元件。比如说XBMC的皮肤文件,屏幕保护文件,可视化效果文件等等。

docs:文档。

language:语言文件。

project:项目工程文件。

xbmc:源代码

lib:调用的各个库。比如说libavcodec。

XBMC项目解决方案的目录如下图所示。可以看出项目工程数量是极其巨大的。

其中名字为“XBMC”的工程是主程序。

ImageLib_XXX是图片处理的工程。

libXBMC_XXX是完成XBMC各种功能的工程。

visXXX是各种可视化效果的工程。

我们来看一下“XBMC”工程的目录。该工程下源文件的数量也是十分庞大的。不同功能的类被放到了不同的文件夹中,显得还是比较井然有续的:

其中“core”文件夹中存放核心的类

“addon”文件夹中存放和addon相关的类

“music”文件夹中存放和音乐功能相关的类

“video”文件夹中存放和影视功能相关的类

“settings”文件夹中存放和设置功能相关的类

此处不一一例举

2:Addons(皮肤Skin)

从这篇文章开始,就要对XBMC源代码进行具体分析了。首先先不分析其C++代码,分析一下和其皮肤相关的代码。

XBMC 的和皮肤相关的代码位于 "根目录/addons" 里面。可以从官方网站上下载皮肤文件的压缩包,然后解压到该目录下面即可。皮肤文件夹名称一般是“skin.XXXX”形式的,即以“skin.”开头。

XBMC自带的皮肤存储在文件夹“skin.confluence”中。我从网上下载了4个皮肤,解压后,如下图所示。

系统默认的皮肤:confluence如图所示。

可以在“skin”选项里面选择皮肤,如图所示。

皮肤“simplicity”如图所示。

皮肤“SiO2”如图所示。

可以看出。不同皮肤之间差距非常的大。皮肤囊括了XBMC所有可以看见的界面元素。可以说不修改源代码,只制作皮肤,也可以完全定制出一套非常个性化的系统。

下面我们以系统自带的皮肤“confluence”为例,分析一下皮肤的构成。

skin.confluence文件夹中,目录结构如图所示:

每个文件夹的作用:

720p:界面存放于文件夹里

background:背景图片

font:字体

language:各种语言支持

media:各种图标

sound:声音

例如,background文件夹内容:

media文件夹内容:

下面重点研究720p文件夹中的内容。这个文件夹中存储了界面的布局信息。系统会根据这个文件夹中的布局信息(xml形式)设置窗口的大小,并去其他文件夹中查找相关的素材。

我们以系统的“设置”页面为例研究一下布局信息。系统的布局页面如下图所示。

“设置”页面对应的布局信息文件为Settings.xml。

时间所限,就不逐行注释了。语法理解起来还是比较容易的。总结以下几点:

1.语法与HTML类似。最外层的<window>相当于<html>。<controls>相当于<body>。<control>类似于<div>,是可以嵌套的。<content>相当于<ul>,<item>相当于<li>。当然,这只是打个比方,方便理解。

2.各种组件都是<control>,就是属性“type”不一样。例如“image”,“group”等等。<control>中

<left>,<top>,<width>,<height>表示窗口位置;

<animation>表示其动画效果;

<onleft>2</onleft>表示遥控器按向左键时如果焦点还在控件里面,并且己经是最左边一个元素时,将焦点切换到ID为2的控件;<onright>,<onup>,<ondown>与此类似。

<!--  
雷霄骅  
leixiaohua1020@126.com  
中国传媒大学/数字电视技术  
-->
<?xml version="1.0" encoding="UTF-8"?>
<window>
<defaultcontrol always="true">9000</defaultcontrol>
<allowoverlay>no</allowoverlay>
<controls>
<include>CommonBackground</include>
<control type="image">
<left>0</left>
<top>100r</top>
<width>1280</width>
<height>100</height>
<texture>floor.png</texture>
<animation effect="slide" start="0,10" end="0,0" time="200" condition="Window.Previous(Home)">WindowOpen</animation>
<animation effect="slide" start="0,0" end="0,10" time="200" condition="Window.Next(Home)">WindowClose</animation>
</control>
<control type="group">
<left>90</left>
<top>30</top>
<animation type="WindowOpen" reversible="false">
<effect type="zoom" start="80" end="100" center="640,360" easing="out" tween="back" time="300"/>
<effect type="fade" start="0" end="100" time="300"/>
</animation>
<animation type="WindowClose" reversible="false">
<effect type="zoom" start="100" end="80" center="640,360" easing="in" tween="back" time="300"/>
<effect type="fade" start="100" end="0" time="300"/>
</animation>
<control type="image">
<left>5</left>
<top>5</top>
<width>1090</width>
<height>630</height>
<texture border="15">ContentPanel.png</texture>
</control>
<control type="image">
<left>5</left>
<top>625</top>
<width>1090</width>
<height>64</height>
<texture border="15">ContentPanelMirror.png</texture>
</control>
<control type="button">
<description>Close Window button</description>
<left>980</left>
<top>11</top>
<width>64</width>
<height>32</height>
<label>-</label>
<font>-</font>
<onclick>PreviousMenu</onclick>
<texturefocus>DialogCloseButton-focus.png</texturefocus>
<texturenofocus>DialogCloseButton.png</texturenofocus>
<onleft>1</onleft>
<onright>1</onright>
<onup>1</onup>
<ondown>1</ondown>
<visible>system.getbool(input.enablemouse)</visible>
</control>
<control type="image">
<description>LOGO</description>
<left>30</left>
<top>15</top>
<width>220</width>
<height>80</height>
<aspectratio>keep</aspectratio>
<texture>Confluence_Logo.png</texture>
</control>
<control type="list" id="9000">
<left>10</left>
<top>82</top>
<width>260</width>
<height>541</height>
<onleft>9000</onleft>
<onright>9001</onright>
<onup>9000</onup>
<ondown>9000</ondown>
<pagecontrol>-</pagecontrol>
<scrolltime>300</scrolltime>
<itemlayout height="54" width="260">
<control type="image">
<left>0</left>
<top>0</top>
<width>260</width>
<height>55</height>
<texture border="5">MenuItemNF.png</texture>
</control>
<control type="label">
<left>250</left>
<top>0</top>
<width>380</width>
<height>55</height>
<font>font24_title</font>
<textcolor>grey3</textcolor>
<align>right</align>
<aligny>center</aligny>
<label>$INFO[ListItem.Label]</label>
</control>
</itemlayout>
<focusedlayout height="54" width="260">
<control type="image">
<left>0</left>
<top>0</top>
<width>260</width>
<height>55</height>
<texture border="5">MenuItemFO.png</texture>
</control>
<control type="label">
<left>250</left>
<top>0</top>
<width>380</width>
<height>55</height>
<font>font24_title</font>
<textcolor>white</textcolor>
<align>right</align>
<aligny>center</aligny>
<label>$INFO[ListItem.Label]</label>
</control>
</focusedlayout>
<content>
<item id="1">
<label>480</label>
<label2>31400</label2>
<onclick>ActivateWindow(AppearanceSettings)</onclick>
<icon>-</icon>
</item>
<item id="2">
<label>157</label>
<label2>31401</label2>
<onclick>ActivateWindow(VideosSettings)</onclick>
<icon>-</icon>
</item>
<item id="3">
<label>31502</label>
<label2>31409</label2>
<onclick>ActivateWindow(PVRSettings)</onclick>
<icon>special://skin/backgrounds/tv.jpg</icon>
</item>
<item id="4">
<label>2</label>
<label2>31402</label2>
<onclick>ActivateWindow(MusicSettings)</onclick>
<icon>-</icon>
</item>
<item id="5">
<label>1</label>
<label2>31403</label2>
<onclick>ActivateWindow(PicturesSettings)</onclick>
<icon>-</icon>
</item>
<item id="6">
<label>8</label>
<label2>31404</label2>
<onclick>ActivateWindow(WeatherSettings)</onclick>
<icon>-</icon>
</item>
<item id="7">
<label>24001</label>
<label2>31408</label2>
<onclick>ActivateWindow(AddonBrowser)</onclick>
<icon>-</icon>
</item>
<item id="8">
<label>14036</label>
<label2>31410</label2>
<onclick>ActivateWindow(ServiceSettings)</onclick>
<icon>-</icon>
</item>
<item id="9">
<label>13000</label>
<label2>31406</label2>
<onclick>ActivateWindow(SystemSettings)</onclick>
<icon>-</icon>
</item>
</content>
</control>
<control type="image">
<left>268</left>
<top>10</top>
<width>804</width>
<height>50</height>
<texture border="5">black-back2.png</texture>
</control>
<control type="image">
<left>268</left>
<top>10</top>
<width>804</width>
<height>70</height>
<aspectratio>stretch</aspectratio>
<texture>GlassTitleBar.png</texture>
</control>
<control type="label">
<description>header label</description>
<left>300</left>
<top>20</top>
<width>740</width>
<height>30</height>
<font>font16</font>
<label>$LOCALIZE[31000] $LOCALIZE[5]</label>
<align>left</align>
<aligny>center</aligny>
<textcolor>white</textcolor>
<shadowcolor>black</shadowcolor>
</control>
<control type="image">
<left>270</left>
<top>60</top>
<width>800</width>
<height>450</height>
<texture border="5">button-nofocus.png</texture>
</control>
<control type="image">
<left>272</left>
<top>62</top>
<width>796</width>
<height>446</height>
<aspectratio>stretch</aspectratio>
<fadetime>600</fadetime>
<texture background="true">special://skin/backgrounds/settings.jpg</texture>
</control>
<control type="image">
<left>272</left>
<top>62</top>
<width>600</width>
<height>340</height>
<aspectratio>stretch</aspectratio>
<texture>GlassOverlay.png</texture>
<colordiffuse>AAFFFFFF</colordiffuse>
</control>
<control type="image">
<left>268</left>
<top>510</top>
<width>804</width>
<height>118</height>
<texture border="5">black-back2.png</texture>
</control>
<control type="textbox">
<description>Appearance Description</description>
<left>300</left>
<top>520</top>
<width>740</width>
<height>100</height>
<font>font12</font>
<label>$INFO[Container(9000).ListItem.Label2]</label>
<align>left</align>
<textcolor>white</textcolor>
<shadowcolor>black</shadowcolor>
</control>
</control>
<include>CommonNowPlaying</include>
<include>MainWindowMouseButtons</include>
<include>BehindDialogFadeOut</include>
<control type="image">
<description>Section header image</description>
<left>20</left>
<top>3</top>
<width>35</width>
<height>35</height>
<aspectratio>keep</aspectratio>
<texture>icon_system.png</texture>
</control>
<control type="grouplist">
<left>65</left>
<top>5</top>
<width>1000</width>
<height>30</height>
<orientation>horizontal</orientation>
<align>left</align>
<itemgap>5</itemgap>
<control type="label">
<include>WindowTitleCommons</include>
<label>$LOCALIZE[5]</label>
</control>
</control>
<include>Clock</include>
</controls>
</window>

 

3:核心部分(core)-综述

本文以及以后的文章主要分析XBMC的VC工程中的源代码。XBMC源代码体积庞大,想要完全分析所有代码是比较困难的。在这里我们选择它和音视频编解码有关的部分进行分析。在本文里,我们主要分析其核心部分(core)代码。

核心部分(core)源代码结构如图所示:

我目前理解的有以下3个,其他的有时间研究后再补上:

AudioEngine:音频引擎。其封装了所有不同的媒体类型的混音、采样率转换、格式转换、编码、上混、缩混等。

dvdplayer:视频播放器。其中封装了FFMPEG等一些库,是我们分析的重点。

paplayer:XBMC自行开发出来的音频播放器。

本系列文章将会重点分析dvdplayer这个播放器。

下面我们先来看看dvdplayer的代码结构:

先不说一大堆cpp文件。dvdplayer包含以下5个文件夹,我们分析以下3个文件夹中的内容

DVDCodecs:封装各种解码器

DVDDemuxers:封装各种解复用器

DVDHeaders:封装各种Dll的头文件

DVDCodecs里面包含各种解码器的封装,下图列出了封装视频解码器的文件。

DVDDemuxers里面包含了各种解复用器(视音频分离器)的封装,如下图所示。

DVDHeaders里面包含了封装各种Dll的头文件,如下图所示。

详细的分析会在后续文章中完成。

 

4:视频播放器(dvdplayer)-解码器(以ffmpeg为例)

本文我们分析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();  
}

 

5:视频播放器(dvdplayer)-解复用器(以ffmpeg为例)

本文我们分析XBMC中视频播放器(dvdplayer)中的解复用器部分。由于解复用器种类很多,不可能一一分析,因此以ffmpeg解复用器为例进行分析。

XBMC解复用器部分文件目录如下图所示:

在这里我们看一下解复用器中的FFMPEG解复用器。对应DVDDemuxFFmpeg.h和DVDDemuxFFmpeg.cpp

之前的分析类文章在解复用器这方面已经做过详细的分析了。在此就不多叙述了,代码很清晰。重点的地方已经标上了注释。

DVDDemuxFFmpeg.h源代码如下所示:

/*
 * 雷霄骅
 * leixiaohua1020@126.com
 * 中国传媒大学/数字电视技术
 *
 */
#include "DVDDemux.h"
#include "DllAvFormat.h"
#include "DllAvCodec.h"
#include "DllAvUtil.h"

#include "threads/CriticalSection.h"
#include "threads/SystemClock.h"

#include <map>

class CDVDDemuxFFmpeg;  
class CURL;  

class CDemuxStreamVideoFFmpeg  
  : public CDemuxStreamVideo  
{  
  CDVDDemuxFFmpeg *m_parent;  
  AVStream*        m_stream;  
public:  
  CDemuxStreamVideoFFmpeg(CDVDDemuxFFmpeg *parent, AVStream* stream)  
    : m_parent(parent)  
    , m_stream(stream)  
  {}  
virtual void GetStreamInfo(std::string& strInfo);  
};  


class CDemuxStreamAudioFFmpeg  
  : public CDemuxStreamAudio  
{  
  CDVDDemuxFFmpeg *m_parent;  
  AVStream*        m_stream;  
public:  
  CDemuxStreamAudioFFmpeg(CDVDDemuxFFmpeg *parent, AVStream* stream)  
    : m_parent(parent)  
    , m_stream(stream)  
  {}  
  std::string m_description;  

virtual void GetStreamInfo(std::string& strInfo);  
virtual void GetStreamName(std::string& strInfo);  
};  

class CDemuxStreamSubtitleFFmpeg  
  : public CDemuxStreamSubtitle  
{  
  CDVDDemuxFFmpeg *m_parent;  
  AVStream*        m_stream;  
public:  
  CDemuxStreamSubtitleFFmpeg(CDVDDemuxFFmpeg *parent, AVStream* stream)  
    : m_parent(parent)  
    , m_stream(stream)  
  {}  
  std::string m_description;  

virtual void GetStreamInfo(std::string& strInfo);  
virtual void GetStreamName(std::string& strInfo);  

};  

#define FFMPEG_FILE_BUFFER_SIZE   32768 // default reading size for ffmpeg
#define FFMPEG_DVDNAV_BUFFER_SIZE 2048  // for dvd's
//FFMPEG解复用
class CDVDDemuxFFmpeg : public CDVDDemux  
{  
public:  
  CDVDDemuxFFmpeg();  
virtual ~CDVDDemuxFFmpeg();  
//打开一个流
bool Open(CDVDInputStream* pInput);  
void Dispose();//关闭
void Reset();//复位
void Flush();  
void Abort();  
void SetSpeed(int iSpeed);  
virtual std::string GetFileName();  

  DemuxPacket* Read();  

bool SeekTime(int time, bool backwords = false, double* startpts = NULL);  
bool SeekByte(int64_t pos);  
int GetStreamLength();  
  CDemuxStream* GetStream(int iStreamId);  
int GetNrOfStreams();  

bool SeekChapter(int chapter, double* startpts = NULL);  
int GetChapterCount();  
int GetChapter();  
void GetChapterName(std::string& strChapterName);  
virtual void GetStreamCodecName(int iStreamId, CStdString &strName);  

bool Aborted();  

  AVFormatContext* m_pFormatContext;  
  CDVDInputStream* m_pInput;  

protected:  
friend class CDemuxStreamAudioFFmpeg;  
friend class CDemuxStreamVideoFFmpeg;  
friend class CDemuxStreamSubtitleFFmpeg;  

int ReadFrame(AVPacket *packet);  
  CDemuxStream* AddStream(int iId);  
void AddStream(int iId, CDemuxStream* stream);  
  CDemuxStream* GetStreamInternal(int iStreamId);  
void CreateStreams(unsigned int program = UINT_MAX);  
void DisposeStreams();  

  AVDictionary *GetFFMpegOptionsFromURL(const CURL &url);  
double ConvertTimestamp(int64_t pts, int den, int num);  
void UpdateCurrentPTS();  
bool IsProgramChange();  

  CCriticalSection m_critSection;  
  std::map<int, CDemuxStream*> m_streams;  
  std::vector<std::map<int, CDemuxStream*>::iterator> m_stream_index;  

  AVIOContext* m_ioContext;  
//各种封装的Dll
  DllAvFormat m_dllAvFormat;  
  DllAvCodec  m_dllAvCodec;  
  DllAvUtil   m_dllAvUtil;  

double   m_iCurrentPts; // used for stream length estimation
bool     m_bMatroska;  
bool     m_bAVI;  
int      m_speed;  
  unsigned m_program;  
  XbmcThreads::EndTime  m_timeout;  

// Due to limitations of ffmpeg, we only can detect a program change
// with a packet. This struct saves the packet for the next read and
// signals STREAMCHANGE to player
struct
  {  
    AVPacket pkt;       // packet ffmpeg returned
int      result;    // result from av_read_packet
  }m_pkt;  
};

该类中以下几个函数包含了解复用器的几个功能。

  bool Open(CDVDInputStream* pInput);//打开
  void Dispose();//关闭
  void Reset();//复位
  void Flush();

我们查看一下这几个函数的源代码。

Open()

//打开一个流 
bool CDVDDemuxFFmpeg::Open(CDVDInputStream* pInput)  
{  
  AVInputFormat* iformat = NULL;  
  std::string strFile;  
  m_iCurrentPts = DVD_NOPTS_VALUE;  
  m_speed = DVD_PLAYSPEED_NORMAL;  
  m_program = UINT_MAX;  
const AVIOInterruptCB int_cb = { interrupt_cb, this };  

if (!pInput) return false;  

if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load() || !m_dllAvFormat.Load())  {  
    CLog::Log(LOGERROR,"CDVDDemuxFFmpeg::Open - failed to load ffmpeg libraries");  
return false;  
  }  
//注册解复用器 
// register codecs 
  m_dllAvFormat.av_register_all();  

  m_pInput = pInput;  
  strFile = m_pInput->GetFileName();  

bool streaminfo = true; /* set to true if we want to look for streams before playback*/ 

if( m_pInput->GetContent().length() > 0 )  
  {  
    std::string content = m_pInput->GetContent();  

/* check if we can get a hint from content */ 
if     ( content.compare("video/x-vobsub") == 0 )  
      iformat = m_dllAvFormat.av_find_input_format("mpeg");  
else if( content.compare("video/x-dvd-mpeg") == 0 )  
      iformat = m_dllAvFormat.av_find_input_format("mpeg");  
else if( content.compare("video/x-mpegts") == 0 )  
      iformat = m_dllAvFormat.av_find_input_format("mpegts");  
else if( content.compare("multipart/x-mixed-replace") == 0 )  
      iformat = m_dllAvFormat.av_find_input_format("mjpeg");  
  }  

// open the demuxer 
  m_pFormatContext  = m_dllAvFormat.avformat_alloc_context();  
  m_pFormatContext->interrupt_callback = int_cb;  

// try to abort after 30 seconds 
  m_timeout.Set(30000);  

if( m_pInput->IsStreamType(DVDSTREAM_TYPE_FFMPEG) )  
  {  
// special stream type that makes avformat handle file opening 
// allows internal ffmpeg protocols to be used 
    CURL url = m_pInput->GetURL();  
    CStdString protocol = url.GetProtocol();  

    AVDictionary *options = GetFFMpegOptionsFromURL(url);  

int result=-1;  
if (protocol.Equals("mms"))  
    {  
// try mmsh, then mmst 
      url.SetProtocol("mmsh");  
      url.SetProtocolOptions("");  
//真正地打开 
      result = m_dllAvFormat.avformat_open_input(&m_pFormatContext, url.Get().c_str(), iformat, &options);  
if (result < 0)  
      {  
        url.SetProtocol("mmst");  
        strFile = url.Get();  
      }   
    }  
//真正地打开 
if (result < 0 && m_dllAvFormat.avformat_open_input(&m_pFormatContext, strFile.c_str(), iformat, &options) < 0 )  
    {  
      CLog::Log(LOGDEBUG, "Error, could not open file %s", CURL::GetRedacted(strFile).c_str());  
      Dispose();  
      m_dllAvUtil.av_dict_free(&options);  
return false;  
    }  
    m_dllAvUtil.av_dict_free(&options);  
  }  
else 
  {  
    unsigned char* buffer = (unsigned char*)m_dllAvUtil.av_malloc(FFMPEG_FILE_BUFFER_SIZE);  
    m_ioContext = m_dllAvFormat.avio_alloc_context(buffer, FFMPEG_FILE_BUFFER_SIZE, 0, this, dvd_file_read, NULL, dvd_file_seek);  
    m_ioContext->max_packet_size = m_pInput->GetBlockSize();  
if(m_ioContext->max_packet_size)  
      m_ioContext->max_packet_size *= FFMPEG_FILE_BUFFER_SIZE / m_ioContext->max_packet_size;  

if(m_pInput->Seek(0, SEEK_POSSIBLE) == 0)  
      m_ioContext->seekable = 0;  

if( iformat == NULL )  
    {  
// let ffmpeg decide which demuxer we have to open 

bool trySPDIFonly = (m_pInput->GetContent() == "audio/x-spdif-compressed");  

if (!trySPDIFonly)  
        m_dllAvFormat.av_probe_input_buffer(m_ioContext, &iformat, strFile.c_str(), NULL, 0, 0);  

// Use the more low-level code in case we have been built against an old 
// FFmpeg without the above av_probe_input_buffer(), or in case we only 
// want to probe for spdif (DTS or IEC 61937) compressed audio 
// specifically, or in case the file is a wav which may contain DTS or 
// IEC 61937 (e.g. ac3-in-wav) and we want to check for those formats. 
if (trySPDIFonly || (iformat && strcmp(iformat->name, "wav") == 0))  
      {  
        AVProbeData pd;  
        uint8_t probe_buffer[FFMPEG_FILE_BUFFER_SIZE + AVPROBE_PADDING_SIZE];  

// init probe data 
        pd.buf = probe_buffer;  
        pd.filename = strFile.c_str();  

// read data using avformat's buffers 
        pd.buf_size = m_dllAvFormat.avio_read(m_ioContext, pd.buf, m_ioContext->max_packet_size ? m_ioContext->max_packet_size : m_ioContext->buffer_size);  
if (pd.buf_size <= 0)  
        {  
          CLog::Log(LOGERROR, "%s - error reading from input stream, %s", __FUNCTION__, CURL::GetRedacted(strFile).c_str());  
return false;  
        }  
        memset(pd.buf+pd.buf_size, 0, AVPROBE_PADDING_SIZE);  

// restore position again 
        m_dllAvFormat.avio_seek(m_ioContext , 0, SEEK_SET);  

// the advancedsetting is for allowing the user to force outputting the 
// 44.1 kHz DTS wav file as PCM, so that an A/V receiver can decode 
// it (this is temporary until we handle 44.1 kHz passthrough properly) 
if (trySPDIFonly || (iformat && strcmp(iformat->name, "wav") == 0 && !g_advancedSettings.m_dvdplayerIgnoreDTSinWAV))  
        {  
// check for spdif and dts 
// This is used with wav files and audio CDs that may contain 
// a DTS or AC3 track padded for S/PDIF playback. If neither of those 
// is present, we assume it is PCM audio. 
// AC3 is always wrapped in iec61937 (ffmpeg "spdif"), while DTS 
// may be just padded. 
          AVInputFormat *iformat2;  
          iformat2 = m_dllAvFormat.av_find_input_format("spdif");  

if (iformat2 && iformat2->read_probe(&pd) > AVPROBE_SCORE_MAX / 4)  
          {  
            iformat = iformat2;  
          }  
else 
          {  
// not spdif or no spdif demuxer, try dts 
            iformat2 = m_dllAvFormat.av_find_input_format("dts");  

if (iformat2 && iformat2->read_probe(&pd) > AVPROBE_SCORE_MAX / 4)  
            {  
              iformat = iformat2;  
            }  
else if (trySPDIFonly)  
            {  
// not dts either, return false in case we were explicitely 
// requested to only check for S/PDIF padded compressed audio 
              CLog::Log(LOGDEBUG, "%s - not spdif or dts file, fallbacking", __FUNCTION__);  
return false;  
            }  
          }  
        }  
      }  

if(!iformat)  
      {  
        std::string content = m_pInput->GetContent();  

/* check if we can get a hint from content */ 
if( content.compare("audio/aacp") == 0 )  
          iformat = m_dllAvFormat.av_find_input_format("aac");  
else if( content.compare("audio/aac") == 0 )  
          iformat = m_dllAvFormat.av_find_input_format("aac");  
else if( content.compare("video/flv") == 0 )  
          iformat = m_dllAvFormat.av_find_input_format("flv");  
else if( content.compare("video/x-flv") == 0 )  
          iformat = m_dllAvFormat.av_find_input_format("flv");  
      }  

if (!iformat)  
      {  
        CLog::Log(LOGERROR, "%s - error probing input format, %s", __FUNCTION__, CURL::GetRedacted(strFile).c_str());  
return false;  
      }  
else 
      {  
if (iformat->name)  
          CLog::Log(LOGDEBUG, "%s - probing detected format [%s]", __FUNCTION__, iformat->name);  
else 
          CLog::Log(LOGDEBUG, "%s - probing detected unnamed format", __FUNCTION__);  
      }  
    }  


    m_pFormatContext->pb = m_ioContext;  

if (m_dllAvFormat.avformat_open_input(&m_pFormatContext, strFile.c_str(), iformat, NULL) < 0)  
    {  
      CLog::Log(LOGERROR, "%s - Error, could not open file %s", __FUNCTION__, CURL::GetRedacted(strFile).c_str());  
      Dispose();  
return false;  
    }  
  }  

// Avoid detecting framerate if advancedsettings.xml says so 
if (g_advancedSettings.m_videoFpsDetect == 0)   
      m_pFormatContext->fps_probe_size = 0;  

// analyse very short to speed up mjpeg playback start 
if (iformat && (strcmp(iformat->name, "mjpeg") == 0) && m_ioContext->seekable == 0)  
    m_pFormatContext->max_analyze_duration = 500000;  

// we need to know if this is matroska or avi later 
  m_bMatroska = strncmp(m_pFormatContext->iformat->name, "matroska", 8) == 0; // for "matroska.webm" 
  m_bAVI = strcmp(m_pFormatContext->iformat->name, "avi") == 0;  

if (streaminfo)  
  {  
/* too speed up dvd switches, only analyse very short */ 
if(m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD))  
      m_pFormatContext->max_analyze_duration = 500000;  


    CLog::Log(LOGDEBUG, "%s - avformat_find_stream_info starting", __FUNCTION__);  
int iErr = m_dllAvFormat.avformat_find_stream_info(m_pFormatContext, NULL);  
if (iErr < 0)  
    {  
      CLog::Log(LOGWARNING,"could not find codec parameters for %s", CURL::GetRedacted(strFile).c_str());  
if (m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD)  
      ||  m_pInput->IsStreamType(DVDSTREAM_TYPE_BLURAY)  
      || (m_pFormatContext->nb_streams == 1 && m_pFormatContext->streams[0]->codec->codec_id == AV_CODEC_ID_AC3))  
      {  
// special case, our codecs can still handle it. 
      }  
else 
      {  
        Dispose();  
return false;  
      }  
    }  
    CLog::Log(LOGDEBUG, "%s - av_find_stream_info finished", __FUNCTION__);  
  }  
// reset any timeout 
  m_timeout.SetInfinite();  

// if format can be nonblocking, let's use that 
  m_pFormatContext->flags |= AVFMT_FLAG_NONBLOCK;  

// print some extra information 
  m_dllAvFormat.av_dump_format(m_pFormatContext, 0, strFile.c_str(), 0);  

  UpdateCurrentPTS();  

  CreateStreams();  

return true;  
}

Dispose()

//关闭 
void CDVDDemuxFFmpeg::Dispose()  
{  
  m_pkt.result = -1;  
  m_dllAvCodec.av_free_packet(&m_pkt.pkt);  

if (m_pFormatContext)  
  {  
if (m_ioContext && m_pFormatContext->pb && m_pFormatContext->pb != m_ioContext)  
    {  
      CLog::Log(LOGWARNING, "CDVDDemuxFFmpeg::Dispose - demuxer changed our byte context behind our back, possible memleak");  
      m_ioContext = m_pFormatContext->pb;  
    }  
    m_dllAvFormat.avformat_close_input(&m_pFormatContext);  
  }  

if(m_ioContext)  
  {  
    m_dllAvUtil.av_free(m_ioContext->buffer);  
    m_dllAvUtil.av_free(m_ioContext);  
  }  

  m_ioContext = NULL;  
  m_pFormatContext = NULL;  
  m_speed = DVD_PLAYSPEED_NORMAL;  

  DisposeStreams();  

  m_pInput = NULL;  

  m_dllAvFormat.Unload();  
  m_dllAvCodec.Unload();  
  m_dllAvUtil.Unload();  
}

Reset()

//复位 
void CDVDDemuxFFmpeg::Reset()  
{  
  CDVDInputStream* pInputStream = m_pInput;  
  Dispose();  
  Open(pInputStream);  
}

Flush()

void CDVDDemuxFFmpeg::Flush()  
{  
// naughty usage of an internal ffmpeg function 
if (m_pFormatContext)  
    m_dllAvFormat.av_read_frame_flush(m_pFormatContext);  

  m_iCurrentPts = DVD_NOPTS_VALUE;  

  m_pkt.result = -1;  
  m_dllAvCodec.av_free_packet(&m_pkt.pkt);  
}

6:视频播放器(dvdplayer)-文件头(以ffmpeg为例)

本文我们分析XBMC中视频播放器(dvdplayer)中的文件头部分。文件头部分里包含的是封装Dll用到的头文件。由于文件头种类很多,不可能一一分析,因此还是以ffmpeg文件头为例进行分析。

XBMC中文件头部分文件目录结构如下图所示。

在这里我们看一下封装AVCodec和AVFormat结构体的头文件,分别是DllAvCodec.h和DllAvFormat.h。

DllAvFormat.h内容如下。其中包含了2个主要的类:DllAvFormatInterface和DllAvFormat。

其中DllAvFormatInterface是一个纯虚类,里面全是纯虚函数。

DllAvFormat中包含很多已经定义过的宏,稍后我们分析一下这些宏的含义。

/* 
* 雷霄骅 
* leixiaohua1020@126.com 
* 中国传媒大学/数字电视技术 
* 
*/ 
//接口的作用 
class DllAvFormatInterface  
{  
public:  
virtual ~DllAvFormatInterface() {}  
virtual void av_register_all_dont_call(void)=0;  
virtual void avformat_network_init_dont_call(void)=0;  
virtual void avformat_network_deinit_dont_call(void)=0;  
virtual AVInputFormat *av_find_input_format(const char *short_name)=0;  
virtual void avformat_close_input(AVFormatContext **s)=0;  
virtual int av_read_frame(AVFormatContext *s, AVPacket *pkt)=0;  
virtual void av_read_frame_flush(AVFormatContext *s)=0;  
virtual int av_read_play(AVFormatContext *s)=0;  
virtual int av_read_pause(AVFormatContext *s)=0;  
virtual int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)=0;  
#if (!defined USE_EXTERNAL_FFMPEG) && (!defined TARGET_DARWIN) 
virtual int avformat_find_stream_info_dont_call(AVFormatContext *ic, AVDictionary **options)=0;  
#endif 
virtual int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options)=0;  
virtual AVIOContext *avio_alloc_context(unsigned char *buffer, int buffer_size, int write_flag, void *opaque,  
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),  
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),  
                            offset_t (*seek)(void *opaque, offset_t offset, int whence))=0;  
virtual AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened)=0;  
virtual AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max)=0;  
virtual int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, const char *filename, void *logctx, unsigned int offset, unsigned int max_probe_size)=0;  
virtual void av_dump_format(AVFormatContext *ic, int index, const char *url, int is_output)=0;  
virtual int avio_open(AVIOContext **s, const char *filename, int flags)=0;  
virtual int avio_close(AVIOContext *s)=0;  
virtual int avio_open_dyn_buf(AVIOContext **s)=0;  
virtual int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer)=0;  
virtual offset_t avio_seek(AVIOContext *s, offset_t offset, int whence)=0;  
virtual int avio_read(AVIOContext *s, unsigned char *buf, int size)=0;  
virtual void avio_w8(AVIOContext *s, int b)=0;  
virtual void avio_write(AVIOContext *s, const unsigned char *buf, int size)=0;  
virtual void avio_wb24(AVIOContext *s, unsigned int val)=0;  
virtual void avio_wb32(AVIOContext *s, unsigned int val)=0;  
virtual void avio_wb16(AVIOContext *s, unsigned int val)=0;  
virtual AVFormatContext *avformat_alloc_context(void)=0;  
virtual int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat, const char *format_name, const char *filename) = 0;  
virtual AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c)=0;  
virtual AVOutputFormat *av_guess_format(const char *short_name, const char *filename, const char *mime_type)=0;  
virtual int avformat_write_header (AVFormatContext *s, AVDictionary **options)=0;  
virtual int av_write_trailer(AVFormatContext *s)=0;  
virtual int av_write_frame  (AVFormatContext *s, AVPacket *pkt)=0;  
#if defined(AVFORMAT_HAS_STREAM_GET_R_FRAME_RATE) 
virtual AVRational av_stream_get_r_frame_rate(const AVStream *s)=0;  
#endif 
};  

//封装的Dll,继承了DllDynamic,以及接口 
class DllAvFormat : public DllDynamic, DllAvFormatInterface  
{  
  DECLARE_DLL_WRAPPER(DllAvFormat, DLL_PATH_LIBAVFORMAT)  

  LOAD_SYMBOLS()  

  DEFINE_METHOD0(void, av_register_all_dont_call)  
  DEFINE_METHOD0(void, avformat_network_init_dont_call)  
  DEFINE_METHOD0(void, avformat_network_deinit_dont_call)  
  DEFINE_METHOD1(AVInputFormat*, av_find_input_format, (const char *p1))  
  DEFINE_METHOD1(void, avformat_close_input, (AVFormatContext **p1))  
  DEFINE_METHOD1(int, av_read_play, (AVFormatContext *p1))  
  DEFINE_METHOD1(int, av_read_pause, (AVFormatContext *p1))  
  DEFINE_METHOD1(void, av_read_frame_flush, (AVFormatContext *p1))  
  DEFINE_FUNC_ALIGNED2(int, __cdecl, av_read_frame, AVFormatContext *, AVPacket *)  
  DEFINE_FUNC_ALIGNED4(int, __cdecl, av_seek_frame, AVFormatContext*, int, int64_t, int)  
  DEFINE_FUNC_ALIGNED2(int, __cdecl, avformat_find_stream_info_dont_call, AVFormatContext*, AVDictionary **)  
  DEFINE_FUNC_ALIGNED4(int, __cdecl, avformat_open_input, AVFormatContext **, const char *, AVInputFormat *, AVDictionary **)  
  DEFINE_FUNC_ALIGNED2(AVInputFormat*, __cdecl, av_probe_input_format, AVProbeData*, int)  
  DEFINE_FUNC_ALIGNED3(AVInputFormat*, __cdecl, av_probe_input_format2, AVProbeData*, int, int*)  
  DEFINE_FUNC_ALIGNED6(int, __cdecl, av_probe_input_buffer, AVIOContext *, AVInputFormat **, const char *, void *, unsigned int, unsigned int)  
  DEFINE_FUNC_ALIGNED3(int, __cdecl, avio_read, AVIOContext*, unsigned char *, int)  
  DEFINE_FUNC_ALIGNED2(void, __cdecl, avio_w8, AVIOContext*, int)  
  DEFINE_FUNC_ALIGNED3(void, __cdecl, avio_write, AVIOContext*, const unsigned char *, int)  
  DEFINE_FUNC_ALIGNED2(void, __cdecl, avio_wb24, AVIOContext*, unsigned int)  
  DEFINE_FUNC_ALIGNED2(void, __cdecl, avio_wb32, AVIOContext*, unsigned int)  
  DEFINE_FUNC_ALIGNED2(void, __cdecl, avio_wb16, AVIOContext*, unsigned int)  
  DEFINE_METHOD7(AVIOContext *, avio_alloc_context, (unsigned char *p1, int p2, int p3, void *p4,  
int (*p5)(void *opaque, uint8_t *buf, int buf_size),  
int (*p6)(void *opaque, uint8_t *buf, int buf_size),  
                  offset_t (*p7)(void *opaque, offset_t offset, int whence)))  
  DEFINE_METHOD4(void, av_dump_format, (AVFormatContext *p1, int p2, const char *p3, int p4))  
  DEFINE_METHOD3(int, avio_open, (AVIOContext **p1, const char *p2, int p3))  
  DEFINE_METHOD1(int, avio_close, (AVIOContext *p1))  
  DEFINE_METHOD1(int, avio_open_dyn_buf, (AVIOContext **p1))  
  DEFINE_METHOD2(int, avio_close_dyn_buf, (AVIOContext *p1, uint8_t **p2))  
  DEFINE_METHOD3(offset_t, avio_seek, (AVIOContext *p1, offset_t p2, int p3))  
  DEFINE_METHOD0(AVFormatContext *, avformat_alloc_context)  
  DEFINE_METHOD4(int, avformat_alloc_output_context2, (AVFormatContext **p1, AVOutputFormat *p2, const char *p3, const char *p4))  
  DEFINE_METHOD2(AVStream *, avformat_new_stream, (AVFormatContext *p1, AVCodec *p2))  
  DEFINE_METHOD3(AVOutputFormat *, av_guess_format, (const char *p1, const char *p2, const char *p3))  
  DEFINE_METHOD2(int, avformat_write_header , (AVFormatContext *p1, AVDictionary **p2))  
  DEFINE_METHOD1(int, av_write_trailer, (AVFormatContext *p1))  
  DEFINE_METHOD2(int, av_write_frame  , (AVFormatContext *p1, AVPacket *p2))  
#if defined(AVFORMAT_HAS_STREAM_GET_R_FRAME_RATE) 
  DEFINE_METHOD1(AVRational, av_stream_get_r_frame_rate, (const AVStream *p1))  
#endif 
  BEGIN_METHOD_RESOLVE()  
    RESOLVE_METHOD_RENAME(av_register_all, av_register_all_dont_call)  
    RESOLVE_METHOD_RENAME(avformat_network_init,   avformat_network_init_dont_call)  
    RESOLVE_METHOD_RENAME(avformat_network_deinit, avformat_network_deinit_dont_call)  
    RESOLVE_METHOD(av_find_input_format)  
    RESOLVE_METHOD(avformat_close_input)  
    RESOLVE_METHOD(av_read_frame)  
    RESOLVE_METHOD(av_read_play)  
    RESOLVE_METHOD(av_read_pause)  
    RESOLVE_METHOD(av_read_frame_flush)  
    RESOLVE_METHOD(av_seek_frame)  
    RESOLVE_METHOD_RENAME(avformat_find_stream_info, avformat_find_stream_info_dont_call)  
    RESOLVE_METHOD(avformat_open_input)  
    RESOLVE_METHOD(avio_alloc_context)  
    RESOLVE_METHOD(av_probe_input_format)  
    RESOLVE_METHOD(av_probe_input_format2)  
    RESOLVE_METHOD(av_probe_input_buffer)  
    RESOLVE_METHOD(av_dump_format)  
    RESOLVE_METHOD(avio_open)  
    RESOLVE_METHOD(avio_close)  
    RESOLVE_METHOD(avio_open_dyn_buf)  
    RESOLVE_METHOD(avio_close_dyn_buf)  
    RESOLVE_METHOD(avio_seek)  
    RESOLVE_METHOD(avio_read)  
    RESOLVE_METHOD(avio_w8)  
    RESOLVE_METHOD(avio_write)  
    RESOLVE_METHOD(avio_wb24)  
    RESOLVE_METHOD(avio_wb32)  
    RESOLVE_METHOD(avio_wb16)  
    RESOLVE_METHOD(avformat_alloc_context)  
    RESOLVE_METHOD(avformat_alloc_output_context2)  
    RESOLVE_METHOD(avformat_new_stream)  
    RESOLVE_METHOD(av_guess_format)  
    RESOLVE_METHOD(avformat_write_header)  
    RESOLVE_METHOD(av_write_trailer)  
    RESOLVE_METHOD(av_write_frame)  
#if defined(AVFORMAT_HAS_STREAM_GET_R_FRAME_RATE) 
    RESOLVE_METHOD(av_stream_get_r_frame_rate)  
#endif 
  END_METHOD_RESOLVE()  

/* dependencies of libavformat */ 
  DllAvCodec m_dllAvCodec;  
// DllAvUtil loaded implicitely by m_dllAvCodec 

public:  
void av_register_all()  
  {  
    CSingleLock lock(DllAvCodec::m_critSection);  
    av_register_all_dont_call();  
  }  
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)  
  {  
    CSingleLock lock(DllAvCodec::m_critSection);  
return avformat_find_stream_info_dont_call(ic, options);  
  }  

virtual bool Load()  
  {  
if (!m_dllAvCodec.Load())  
return false;  
bool loaded = DllDynamic::Load();  

    CSingleLock lock(DllAvCodec::m_critSection);  
if (++m_avformat_refcnt == 1 && loaded)  
      avformat_network_init_dont_call();  
return loaded;  
  }  

virtual void Unload()  
  {  
    CSingleLock lock(DllAvCodec::m_critSection);  
if (--m_avformat_refcnt == 0 && DllDynamic::IsLoaded())  
      avformat_network_deinit_dont_call();  

    DllDynamic::Unload();  
  }  

protected:  
static int m_avformat_refcnt;  
};  

#endif

这些宏的含义如下:

DEFINE_METHOD0(result, name)        定义一个方法(不包含参数)  
DEFINE_METHOD1(result, name, args)  定义一个方法(1个参数)  
DEFINE_METHOD2(result, name, args)  定义一个方法(2个参数)  
DEFINE_METHOD3(result, name, args)  定义一个方法(3个参数)  
DEFINE_METHOD4(result, name, args)  定义一个方法(4个参数)  
以此类推...  

DEFINE_FUNC_ALIGNED0(result, linkage, name)         定义一个方法(不包含参数)  
DEFINE_FUNC_ALIGNED1(result, linkage, name, t1)     定义一个方法(1个参数)  
DEFINE_FUNC_ALIGNED2(result, linkage, name, t1, t2)     定义一个方法(2个参数)  
以此类推...

可以看一下这些宏的定义。看了一会,感觉宏的定义太多了,好乱。在这里仅举一个例子:RESOLVE_METHOD

#define RESOLVE_METHOD(method) \ 
if (!m_dll->ResolveExport( #method , & m_##method##_ptr )) \  
return false;

从定义中可以看出,调用了m_dll的方法ResolveExport()。但是在DllAvFormat中并没有m_dll变量。实际上m_dll位于DllAvFormat的父类DllDynamic里面。

DllAvFormat继承了DllDynamic。DllDynamic是用于加载Dll的类。我们可以看一下它的定义:

//Dll动态加载类 
class DllDynamic  
{  
public:  
  DllDynamic();  
  DllDynamic(const CStdString& strDllName);  
virtual ~DllDynamic();  
virtual bool Load();//加载 
virtual void Unload();//卸载 
virtual bool IsLoaded() const { return m_dll!=NULL; }//是否加载 
bool CanLoad();  
bool EnableDelayedUnload(bool bOnOff);  
bool SetFile(const CStdString& strDllName);//设置文件 
const CStdString &GetFile() const { return m_strDllName; }  

protected:  
virtual bool ResolveExports()=0;  
virtual bool LoadSymbols() { return false; }  
bool  m_DelayUnload;  
  LibraryLoader* m_dll;  
  CStdString m_strDllName;  
};

其中有一个变量LibraryLoader* m_dll。是用于加载Dll的。

可以看一DllDynamic中主要的几个函数,就能明白这个类的作用了。

//加载 
bool DllDynamic::Load()  
{  
if (m_dll)  
return true;  

if (!(m_dll=CSectionLoader::LoadDLL(m_strDllName, m_DelayUnload, LoadSymbols())))  
return false;  

if (!ResolveExports())  
  {  
    CLog::Log(LOGERROR, "Unable to resolve exports from dll %s", m_strDllName.c_str());  
    Unload();  
return false;  
  }  

return true;  
}
//卸载 
void DllDynamic::Unload()  
{  
if(m_dll)  
    CSectionLoader::UnloadDLL(m_strDllName);  
  m_dll=NULL;  
}

可以看看LibraryLoader的定义。LibraryLoader本身是一个纯虚类,具体方法的实现在其子类里面。

//Dll加载类 
class LibraryLoader  
{  
public:  
  LibraryLoader(const char* libraryFile);  
virtual ~LibraryLoader();  

virtual bool Load() = 0;  
virtual void Unload() = 0;  

virtual int ResolveExport(const char* symbol, void** ptr, bool logging = true) = 0;  
virtual int ResolveOrdinal(unsigned long ordinal, void** ptr);  
virtual bool IsSystemDll() = 0;  
virtual HMODULE GetHModule() = 0;  
virtual bool HasSymbols() = 0;  

char* GetName(); // eg "mplayer.dll" 
char* GetFileName(); // "special://xbmcbin/system/mplayer/players/mplayer.dll" 
char* GetPath(); // "special://xbmcbin/system/mplayer/players/" 

int IncrRef();  
int DecrRef();  
int GetRef();  

private:  
  LibraryLoader(const LibraryLoader&);  
  LibraryLoader& operator=(const LibraryLoader&);  
char* m_sFileName;  
char* m_sPath;  
int   m_iRefCount;  
};

LibraryLoader的继承关系如下图所示。

由于自己的操作系统是Windows下的,因此可以看看Win32DllLoader的定义。

//Windows下的Dll加载类 
class Win32DllLoader : public LibraryLoader  
{  
public:  
class Import  
  {  
public:  
void *table;  
DWORD function;  
  };  

  Win32DllLoader(const char *dll);  
  ~Win32DllLoader();  

virtual bool Load();//加载 
virtual void Unload();//卸载 

virtual int ResolveExport(const char* symbol, void** ptr, bool logging = true);  
virtual bool IsSystemDll();  
virtual HMODULE GetHModule();  
virtual bool HasSymbols();  

private:  
void OverrideImports(const CStdString &dll);  
void RestoreImports();  
static bool ResolveImport(const char *dllName, const char *functionName, void **fixup);  
static bool ResolveOrdinal(const char *dllName, unsigned long ordinal, void **fixup);  
bool NeedsHooking(const char *dllName);  

HMODULE m_dllHandle;  
bool bIsSystemDll;  

  std::vector<Import> m_overriddenImports;  
  std::vector<HMODULE> m_referencedDlls;  
};

其中加载Dll使用Load(),卸载Dll使用Unload()。可以看看这两个函数具体的代码。

//加载 
bool Win32DllLoader::Load()  
{  
if (m_dllHandle != NULL)  
return true;  
//文件路径 
  CStdString strFileName = GetFileName();  

  CStdStringW strDllW;  
  g_charsetConverter.utf8ToW(CSpecialProtocol::TranslatePath(strFileName), strDllW, false, false, false);  
//加载库 
  m_dllHandle = LoadLibraryExW(strDllW.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);  
if (!m_dllHandle)  
  {  
LPVOID lpMsgBuf;  
DWORD dw = GetLastError();   

    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, 0, (LPTSTR) &lpMsgBuf, 0, NULL );  
    CLog::Log(LOGERROR, "%s: Failed to load %s with error %d:%s", __FUNCTION__, CSpecialProtocol::TranslatePath(strFileName).c_str(), dw, lpMsgBuf);  
    LocalFree(lpMsgBuf);  
return false;  
  }  

// handle functions that the dll imports 
if (NeedsHooking(strFileName.c_str()))  
    OverrideImports(strFileName);  
else 
    bIsSystemDll = true;  

return true;  
}  
//卸载 
void Win32DllLoader::Unload()  
{  
// restore our imports 
  RestoreImports();  
//卸载库 
if (m_dllHandle)  
  {  
if (!FreeLibrary(m_dllHandle))  
       CLog::Log(LOGERROR, "%s Unable to unload %s", __FUNCTION__, GetName());  
  }  

  m_dllHandle = NULL;  
}

7:视频播放器(dvdplayer)-输入流(以libRTMP为例)

本文我们分析XBMC中视频播放器(dvdplayer)中的输入流部分。由于输入流种类很多,因此以RTMP输入流为例进行分析。

XBMC中输入流部分文件目录结构如下图所示。

从目录中文件的名称我们可以看出,XBMC支持多种输入方式:File,HTSP,HTTP,RTMP等等。在这里我们看看RTMP部分的源代码。对应DVDInputStreamRTMP.h和DVDInputStreamRTMP.cpp

先来看看DVDInputStreamRTMP.h

/* 
* 雷霄骅 
* leixiaohua1020@126.com 
* 中国传媒大学/数字电视技术 
* 
*/ 
//如果有libRTMP 
#ifdef HAS_LIBRTMP 

#include "DVDInputStream.h" 
#include "DllLibRTMP.h" 
//支持RTMP输入流的类,继承CDVDInputStream 
class CDVDInputStreamRTMP   
  : public CDVDInputStream  
  , public CDVDInputStream::ISeekTime  
{  
public:  
  CDVDInputStreamRTMP();  
virtual ~CDVDInputStreamRTMP();  
virtual bool    Open(const char* strFile, const std::string &content);//打开 
virtual void    Close();//关闭 
virtual int     Read(uint8_t* buf, int buf_size);//读取 
virtual int64_t Seek(int64_t offset, int whence);//跳转到 
bool            SeekTime(int iTimeInMsec);  
virtual bool Pause(double dTime);//暂停 
virtual bool    IsEOF();  
virtual int64_t GetLength();  

  CCriticalSection m_RTMPSection;  

protected:  
bool       m_eof;  
bool       m_bPaused;  
char*      m_sStreamPlaying;  
  std::vector<CStdString> m_optionvalues;  

  RTMP       *m_rtmp;  
  DllLibRTMP m_libRTMP;  
};  

#endif

该类中包含了Open(),Close(),Read(),Seek(),Pause() 这类的方法。实现了对RTMP协议的各种操作。这些方法都是CDVDInputStreamRTMP父类CDVDInputStream中的方法。可以看一下CDVDInputStream的定义,就知道了。

//输入流类 
class CDVDInputStream  
{  
public:  
class IChannel  
  {  
public:  
virtual ~IChannel() {};  
virtual bool NextChannel(bool preview = false) = 0;  
virtual bool PrevChannel(bool preview = false) = 0;  
virtual bool SelectChannelByNumber(unsigned int channel) = 0;  
virtual bool SelectChannel(const PVR::CPVRChannel &channel) { return false; };  
virtual bool GetSelectedChannel(PVR::CPVRChannelPtr&) { return false; };  
virtual bool UpdateItem(CFileItem& item) = 0;  
virtual bool CanRecord() = 0;  
virtual bool IsRecording() = 0;  
virtual bool Record(bool bOnOff) = 0;  
virtual bool CanPause() = 0;  
virtual bool CanSeek() = 0;  
  };  

class IDisplayTime  
  {  
public:  
virtual ~IDisplayTime() {};  
virtual int GetTotalTime() = 0;  
virtual int GetTime() = 0;  
  };  

class ISeekTime  
  {  
public:  
virtual ~ISeekTime() {};  
virtual bool SeekTime(int ms) = 0;  
  };  

class IChapter  
  {  
public:  
virtual ~IChapter() {};  
virtual int  GetChapter() = 0;  
virtual int  GetChapterCount() = 0;  
virtual void GetChapterName(std::string& name) = 0;  
virtual bool SeekChapter(int ch) = 0;  
  };  

class IMenus  
  {  
public:  
virtual ~IMenus() {};  
virtual void ActivateButton() = 0;  
virtual void SelectButton(int iButton) = 0;  
virtual int  GetCurrentButton() = 0;  
virtual int  GetTotalButtons() = 0;  
virtual void OnUp() = 0;  
virtual void OnDown() = 0;  
virtual void OnLeft() = 0;  
virtual void OnRight() = 0;  
virtual void OnMenu() = 0;  
virtual void OnBack() = 0;  
virtual void OnNext() = 0;  
virtual void OnPrevious() = 0;  
virtual bool OnMouseMove(const CPoint &point) = 0;  
virtual bool OnMouseClick(const CPoint &point) = 0;  
virtual bool IsInMenu() = 0;  
virtual void SkipStill() = 0;  
virtual double GetTimeStampCorrection() = 0;  
virtual bool GetState(std::string &xmlstate) = 0;  
virtual bool SetState(const std::string &xmlstate) = 0;  

  };  

class ISeekable  
  {  
public:  
virtual ~ISeekable() {};  
virtual bool CanSeek()  = 0;  
virtual bool CanPause() = 0;  
  };  

enum ENextStream  
  {  
    NEXTSTREAM_NONE,  
    NEXTSTREAM_OPEN,  
    NEXTSTREAM_RETRY,  
  };  

  CDVDInputStream(DVDStreamType m_streamType);  
virtual ~CDVDInputStream();  
virtual bool Open(const char* strFileName, const std::string& content);//打开 
virtual void Close() = 0;//关闭 
virtual int Read(uint8_t* buf, int buf_size) = 0;//读取 
virtual int64_t Seek(int64_t offset, int whence) = 0;//跳转 
virtual bool Pause(double dTime) = 0;//暂停 
virtual int64_t GetLength() = 0;  
virtual std::string& GetContent() { return m_content; };  
virtual std::string& GetFileName() { return m_strFileName; }  
virtual CURL &GetURL() { return m_url; }  
virtual ENextStream NextStream() { return NEXTSTREAM_NONE; }  
virtual void Abort() {}  
virtual int GetBlockSize() { return 0; }  
virtual void ResetScanTimeout(unsigned int iTimeoutMs) { }  

/*! \brief Indicate expected read rate in bytes per second. 
   *  This could be used to throttle caching rate. Should 
   *  be seen as only a hint 
   */ 
virtual void SetReadRate(unsigned rate) {}  

/*! \brief Get the cache status 
   \return true when cache status was succesfully obtained 
   */ 
virtual bool GetCacheStatus(XFILE::SCacheStatus *status) { return false; }  

bool IsStreamType(DVDStreamType type) const { return m_streamType == type; }  
virtual bool IsEOF() = 0;  
virtual BitstreamStats GetBitstreamStats() const { return m_stats; }  

void SetFileItem(const CFileItem& item);  

protected:  
  DVDStreamType m_streamType;  
  std::string m_strFileName;  
  CURL m_url;  
  BitstreamStats m_stats;  
  std::string m_content;  
  CFileItem m_item;  
};

回到CDVDInputStreamRTMP类本身。可以看一下Open(),Close(),Read(),Seek(),Pause()这些方法的函数体。这些方方通过调用libRTMP中相应的方法,完成了对RTMP流媒体的各种操作。

/* 
* 雷霄骅 
* leixiaohua1020@126.com 
* 中国传媒大学/数字电视技术 
* 
*/ 
//打开 
bool CDVDInputStreamRTMP::Open(const char* strFile, const std::string& content)  
{  
if (m_sStreamPlaying)  
  {  
    free(m_sStreamPlaying);  
    m_sStreamPlaying = NULL;  
  }  

if (!CDVDInputStream::Open(strFile, "video/x-flv"))  
return false;  

  CSingleLock lock(m_RTMPSection);  

// libRTMP can and will alter strFile, so take a copy of it 
  m_sStreamPlaying = (char*)calloc(strlen(strFile)+1,sizeof(char));  
  strcpy(m_sStreamPlaying,strFile);  
//libRTMP中的设置URL 
if (!m_libRTMP.SetupURL(m_rtmp, m_sStreamPlaying))  
return false;  

// SetOpt and SetAVal copy pointers to the value. librtmp doesn't use the values until the Connect() call, 
// so value objects must stay allocated until then. To be extra safe, keep the values around until Close(), 
// in case librtmp needs them again. 
  m_optionvalues.clear();  
for (int i=0; options[i].name; i++)  
  {  
    CStdString tmp = m_item.GetProperty(options[i].name).asString();  
if (!tmp.empty())  
    {  
      m_optionvalues.push_back(tmp);  
      AVal av_tmp;  
      SetAVal(av_tmp, m_optionvalues.back());  
      m_libRTMP.SetOpt(m_rtmp, &options[i].key, &av_tmp);  
    }  
  }  
//建立RTMP链接中的NetConnection和NetStream 
if (!m_libRTMP.Connect(m_rtmp, NULL) || !m_libRTMP.ConnectStream(m_rtmp, 0))  
return false;  

  m_eof = false;  

return true;  
}  
//关闭 
// close file and reset everything 
void CDVDInputStreamRTMP::Close()  
{  
  CSingleLock lock(m_RTMPSection);  
  CDVDInputStream::Close();  
//关闭连接 
  m_libRTMP.Close(m_rtmp);  

  m_optionvalues.clear();  
  m_eof = true;  
  m_bPaused = false;  
}  
//读取 
int CDVDInputStreamRTMP::Read(uint8_t* buf, int buf_size)  
{//读取 
int i = m_libRTMP.Read(m_rtmp, (char *)buf, buf_size);  
if (i < 0)  
    m_eof = true;  

return i;  
}  
//跳转到 
int64_t CDVDInputStreamRTMP::Seek(int64_t offset, int whence)  
{  
if (whence == SEEK_POSSIBLE)  
return 0;  
else 
return -1;  
}  
//暂停 
bool CDVDInputStreamRTMP::Pause(double dTime)  
{  
  CSingleLock lock(m_RTMPSection);  

  m_bPaused = !m_bPaused;  

  CLog::Log(LOGNOTICE, "RTMP Pause %s requested", m_bPaused ? "TRUE" : "FALSE");  

  m_libRTMP.Pause(m_rtmp, m_bPaused);  

return true;  
}
posted @ 2015-05-21 12:40  Mr.Rico  阅读(2595)  评论(0编辑  收藏  举报