转:XBMC源代码分析

时间:2023-03-08 22:18:47

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>与此类似。

<!--
雷霄骅
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)源代码结构如图所示:

转: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源代码如下所示:

/*
* 雷霄骅
* 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) = ;
virtual int Decode (AVCodecContext* avctx, AVFrame* frame) = ;
virtual bool GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture) = ;
virtual int Check (AVCodecContext* avctx) = ;
virtual void Reset () {}
virtual unsigned GetAllowedReferences() { return ; }
virtual const std::string Name() = ;
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[];
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 = ;
m_pCodecContext->debug = ;
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 > )
{
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 != )
{
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(), );
} int num_threads = std::min( /*MAX_THREADS*/, g_cpuInfo.getCPUCount());
if( num_threads > && !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) < )
{
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 = ;
}
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 = , len = ; 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 = ;
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 + )
m_iLastKeyframe = m_pCodecContext->has_b_frames + ; if (len < )
{
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 + ;
} /* put a limit on convergence count to avoid huge mem usage on streams without keyframes */
if(m_iLastKeyframe > )
m_iLastKeyframe = ; /* 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[]->format != m_pCodecContext->pix_fmt
|| m_pFilterIn->outputs[]->w != m_pCodecContext->width
|| m_pFilterIn->outputs[]->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) < )
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源代码如下所示:

/*
* 雷霄骅
* 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() > )
{
std::string content = m_pInput->GetContent(); /* check if we can get a hint from content */
if ( content.compare("video/x-vobsub") == )
iformat = m_dllAvFormat.av_find_input_format("mpeg");
else if( content.compare("video/x-dvd-mpeg") == )
iformat = m_dllAvFormat.av_find_input_format("mpeg");
else if( content.compare("video/x-mpegts") == )
iformat = m_dllAvFormat.av_find_input_format("mpegts");
else if( content.compare("multipart/x-mixed-replace") == )
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(); 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=-;
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 < )
{
url.SetProtocol("mmst");
strFile = url.Get();
}
}
//真正地打开
if (result < && m_dllAvFormat.avformat_open_input(&m_pFormatContext, strFile.c_str(), iformat, &options) < )
{
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, , 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(, SEEK_POSSIBLE) == )
m_ioContext->seekable = ; 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, , ); // 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") == ))
{
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 <= )
{
CLog::Log(LOGERROR, "%s - error reading from input stream, %s", __FUNCTION__, CURL::GetRedacted(strFile).c_str());
return false;
}
memset(pd.buf+pd.buf_size, , AVPROBE_PADDING_SIZE); // restore position again
m_dllAvFormat.avio_seek(m_ioContext , , 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") == && !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 / )
{
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 / )
{
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") == )
iformat = m_dllAvFormat.av_find_input_format("aac");
else if( content.compare("audio/aac") == )
iformat = m_dllAvFormat.av_find_input_format("aac");
else if( content.compare("video/flv") == )
iformat = m_dllAvFormat.av_find_input_format("flv");
else if( content.compare("video/x-flv") == )
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) < )
{
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 == )
m_pFormatContext->fps_probe_size = ; // analyse very short to speed up mjpeg playback start
if (iformat && (strcmp(iformat->name, "mjpeg") == ) && m_ioContext->seekable == )
m_pFormatContext->max_analyze_duration = ; // we need to know if this is matroska or avi later
m_bMatroska = strncmp(m_pFormatContext->iformat->name, "matroska", ) == ; // for "matroska.webm"
m_bAVI = strcmp(m_pFormatContext->iformat->name, "avi") == ; if (streaminfo)
{
/* too speed up dvd switches, only analyse very short */
if(m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD))
m_pFormatContext->max_analyze_duration = ; CLog::Log(LOGDEBUG, "%s - avformat_find_stream_info starting", __FUNCTION__);
int iErr = m_dllAvFormat.avformat_find_stream_info(m_pFormatContext, NULL);
if (iErr < )
{
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 == && m_pFormatContext->streams[]->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, , strFile.c_str(), ); UpdateCurrentPTS(); CreateStreams(); return true;
}

Dispose()

//关闭
void CDVDDemuxFFmpeg::Dispose()
{
m_pkt.result = -;
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 = -;
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中包含很多已经定义过的宏,稍后我们分析一下这些宏的含义。

/*
* 雷霄骅
* leixiaohua1020@126.com
* 中国传媒大学/数字电视技术
*
*/
//接口的作用
class DllAvFormatInterface
{
public:
virtual ~DllAvFormatInterface() {}
virtual void av_register_all_dont_call(void)=;
virtual void avformat_network_init_dont_call(void)=;
virtual void avformat_network_deinit_dont_call(void)=;
virtual AVInputFormat *av_find_input_format(const char *short_name)=;
virtual void avformat_close_input(AVFormatContext **s)=;
virtual int av_read_frame(AVFormatContext *s, AVPacket *pkt)=;
virtual void av_read_frame_flush(AVFormatContext *s)=;
virtual int av_read_play(AVFormatContext *s)=;
virtual int av_read_pause(AVFormatContext *s)=;
virtual int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)=;
#if (!defined USE_EXTERNAL_FFMPEG) && (!defined TARGET_DARWIN)
virtual int avformat_find_stream_info_dont_call(AVFormatContext *ic, AVDictionary **options)=;
#endif
virtual int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options)=;
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))=;
virtual AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened)=;
virtual AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max)=;
virtual int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, const char *filename, void *logctx, unsigned int offset, unsigned int max_probe_size)=;
virtual void av_dump_format(AVFormatContext *ic, int index, const char *url, int is_output)=;
virtual int avio_open(AVIOContext **s, const char *filename, int flags)=;
virtual int avio_close(AVIOContext *s)=;
virtual int avio_open_dyn_buf(AVIOContext **s)=;
virtual int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer)=;
virtual offset_t avio_seek(AVIOContext *s, offset_t offset, int whence)=;
virtual int avio_read(AVIOContext *s, unsigned char *buf, int size)=;
virtual void avio_w8(AVIOContext *s, int b)=;
virtual void avio_write(AVIOContext *s, const unsigned char *buf, int size)=;
virtual void avio_wb24(AVIOContext *s, unsigned int val)=;
virtual void avio_wb32(AVIOContext *s, unsigned int val)=;
virtual void avio_wb16(AVIOContext *s, unsigned int val)=;
virtual AVFormatContext *avformat_alloc_context(void)=;
virtual int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat, const char *format_name, const char *filename) = ;
virtual AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c)=;
virtual AVOutputFormat *av_guess_format(const char *short_name, const char *filename, const char *mime_type)=;
virtual int avformat_write_header (AVFormatContext *s, AVDictionary **options)=;
virtual int av_write_trailer(AVFormatContext *s)=;
virtual int av_write_frame (AVFormatContext *s, AVPacket *pkt)=;
#if defined(AVFORMAT_HAS_STREAM_GET_R_FRAME_RATE)
virtual AVRational av_stream_get_r_frame_rate(const AVStream *s)=;
#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 == && loaded)
avformat_network_init_dont_call();
return loaded;
} virtual void Unload()
{
CSingleLock lock(DllAvCodec::m_critSection);
if (--m_avformat_refcnt == && 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()=;
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() = ;
virtual void Unload() = ; virtual int ResolveExport(const char* symbol, void** ptr, bool logging = true) = ;
virtual int ResolveOrdinal(unsigned long ordinal, void** ptr);
virtual bool IsSystemDll() = ;
virtual HMODULE GetHModule() = ;
virtual bool HasSymbols() = ; 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, , (LPTSTR) &lpMsgBuf, , 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源代码分析

从目录中文件的名称我们可以看出,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) = ;
virtual bool PrevChannel(bool preview = false) = ;
virtual bool SelectChannelByNumber(unsigned int channel) = ;
virtual bool SelectChannel(const PVR::CPVRChannel &channel) { return false; };
virtual bool GetSelectedChannel(PVR::CPVRChannelPtr&) { return false; };
virtual bool UpdateItem(CFileItem& item) = ;
virtual bool CanRecord() = ;
virtual bool IsRecording() = ;
virtual bool Record(bool bOnOff) = ;
virtual bool CanPause() = ;
virtual bool CanSeek() = ;
}; class IDisplayTime
{
public:
virtual ~IDisplayTime() {};
virtual int GetTotalTime() = ;
virtual int GetTime() = ;
}; class ISeekTime
{
public:
virtual ~ISeekTime() {};
virtual bool SeekTime(int ms) = ;
}; class IChapter
{
public:
virtual ~IChapter() {};
virtual int GetChapter() = ;
virtual int GetChapterCount() = ;
virtual void GetChapterName(std::string& name) = ;
virtual bool SeekChapter(int ch) = ;
}; class IMenus
{
public:
virtual ~IMenus() {};
virtual void ActivateButton() = ;
virtual void SelectButton(int iButton) = ;
virtual int GetCurrentButton() = ;
virtual int GetTotalButtons() = ;
virtual void OnUp() = ;
virtual void OnDown() = ;
virtual void OnLeft() = ;
virtual void OnRight() = ;
virtual void OnMenu() = ;
virtual void OnBack() = ;
virtual void OnNext() = ;
virtual void OnPrevious() = ;
virtual bool OnMouseMove(const CPoint &point) = ;
virtual bool OnMouseClick(const CPoint &point) = ;
virtual bool IsInMenu() = ;
virtual void SkipStill() = ;
virtual double GetTimeStampCorrection() = ;
virtual bool GetState(std::string &xmlstate) = ;
virtual bool SetState(const std::string &xmlstate) = ; }; class ISeekable
{
public:
virtual ~ISeekable() {};
virtual bool CanSeek() = ;
virtual bool CanPause() = ;
}; 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() = ;//关闭
virtual int Read(uint8_t* buf, int buf_size) = ;//读取
virtual int64_t Seek(int64_t offset, int whence) = ;//跳转
virtual bool Pause(double dTime) = ;//暂停
virtual int64_t GetLength() = ;
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 ; }
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() = ;
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)+,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=; 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, ))
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 < )
m_eof = true; return i;
}
//跳转到
int64_t CDVDInputStreamRTMP::Seek(int64_t offset, int whence)
{
if (whence == SEEK_POSSIBLE)
return ;
else
return -;
}
//暂停
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;
}