转:XBMC源代码分析

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

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

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

转:XBMC源代码分析

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源代码进行一个整体分析:

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

转:XBMC源代码分析

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

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

docs:文档。

language:语言文件。

project:项目工程文件。

xbmc:源代码

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

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

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

ImageLib_XXX是图片处理的工程。

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

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

转:XBMC源代码分析

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

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

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

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

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

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

此处不一一例举

转:XBMC源代码分析

2:Addons(皮肤Skin)

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

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

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

转:XBMC源代码分析

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

转:XBMC源代码分析

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

转:XBMC源代码分析

皮肤“simplicity”如图所示。

转:XBMC源代码分析

皮肤“SiO2”如图所示。

转:XBMC源代码分析

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

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

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

转:XBMC源代码分析

每个文件夹的作用:

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

background:背景图片

font:字体

language:各种语言支持

media:各种图标

sound:声音

例如,background文件夹内容:

转:XBMC源代码分析

media文件夹内容:

转:XBMC源代码分析

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

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

转:XBMC源代码分析

“设置”页面对应的布局信息文件为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>与此类似。

<!--  

雷霄骅  

[email protected]  

中国传媒大学/数字电视技术  

-->

<?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)源代码结构如图所示:

转:XBMC源代码分析

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

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

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

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

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

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

转:XBMC源代码分析

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

DVDCodecs:封装各种解码器

DVDDemuxers:封装各种解复用器

DVDHeaders:封装各种Dll的头文件

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

转:XBMC源代码分析

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

转:XBMC源代码分析

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

转:XBMC源代码分析

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

 

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

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

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

转:XBMC源代码分析

解码器分为音频解码器和视频解码器。在这里我们看一下视频解码器中的FFMPEG解码器。对应DVDVideoCodecFFmpeg.h和DVDVideoCodecFFmpeg.cpp。

DVDVideoCodecFFmpeg.h源代码如下所示:

/*

 * 雷霄骅

 * [email protected]

 * 中国传媒大学/数字电视技术

 *

 */



#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源代码如下:

/*

 * 雷霄骅

 * [email protected]

 * 中国传媒大学/数字电视技术

 *

 */

#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解复用器部分文件目录如下图所示:

转:XBMC源代码分析

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

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

DVDDemuxFFmpeg.h源代码如下所示:

/*

 * 雷霄骅

 * [email protected]

 * 中国传媒大学/数字电视技术

 *

 */

#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中文件头部分文件目录结构如下图所示。

转:XBMC源代码分析

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

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

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

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

/* 

* 雷霄骅 

* [email protected] 

* 中国传媒大学/数字电视技术 

* 

*/ 

//接口的作用 

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的继承关系如下图所示。

转:XBMC源代码分析

由于自己的操作系统是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

/* 

* 雷霄骅 

* [email protected] 

* 中国传媒大学/数字电视技术 

* 

*/ 

//如果有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流媒体的各种操作。

/* 

* 雷霄骅 

* [email protected] 

* 中国传媒大学/数字电视技术 

* 

*/ 

//打开 

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;  

}

你可能感兴趣的:(源代码)