您的位置:首页 > 编程语言

LAV Filter 源代码分析 2: LAV Splitter

2013-10-14 21:06 495 查看
LAV Filter 中最著名的就是 LAV Splitter,支持Matroska /WebM,MPEG-TS/PS,MP4/MOV,FLV,OGM / OGG,AVI等其他格式,广泛存在于各种视频播放器(暴风影音这类的)之中。
本文分析一下它的源代码。在分析之前,先看看它是什么样的。
使用GraphEdit随便打开一个视频文件,就可以看见LAV Filter:



可以右键点击这个Filter看一下它的属性页面,如图所示:
属性设置页面:



支持输入格式:



下面我们在 VC 2010 中看一下它的源代码:



从何看起呢?就先从directshow的注册函数看起吧,位于dllmain.cpp之中。部分代码的含义已经用注释标注上了。从代码可以看出,和普通的DirectShow Filter没什么区别。
dllmain.cpp
/*
* Copyright (C) 2010-2013 Hendrik Leppkes
* http://www.1f0.de *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

// Based on the SampleParser Template by GDCL
// --------------------------------------------------------------------------------
// Copyright (c) GDCL 2004. All Rights Reserved.
// You are free to re-use this as the basis for your own filter development,
// provided you retain this copyright notice in the source.
// http://www.gdcl.co.uk // --------------------------------------------------------------------------------
//各种定义。。。
#include "stdafx.h"

// Initialize the GUIDs
#include <InitGuid.h>

#include <qnetwork.h>
#include "LAVSplitter.h"
#include "moreuuids.h"

#include "registry.h"
#include "IGraphRebuildDelegate.h"

// The GUID we use to register the splitter media types
DEFINE_GUID(MEDIATYPE_LAVSplitter,
0x9c53931c, 0x7d5a, 0x4a75, 0xb2, 0x6f, 0x4e, 0x51, 0x65, 0x4d, 0xb2, 0xc0);

// --- COM factory table and registration code --------------
//注册时候的信息
const AMOVIESETUP_MEDIATYPE
sudMediaTypes[] = {
{ &MEDIATYPE_Stream, &MEDIASUBTYPE_NULL },
};
//注册时候的信息(PIN)
const AMOVIESETUP_PIN sudOutputPins[] =
{
{
L"Output", // pin name
FALSE, // is rendered?
TRUE, // is output?
FALSE, // zero instances allowed?
TRUE, // many instances allowed?
&CLSID_NULL, // connects to filter (for bridge pins)
NULL, // connects to pin (for bridge pins)
0, // count of registered media types
NULL // list of registered media types
},
{
L"Input", // pin name
FALSE, // is rendered?
FALSE, // is output?
FALSE, // zero instances allowed?
FALSE, // many instances allowed?
&CLSID_NULL, // connects to filter (for bridge pins)
NULL, // connects to pin (for bridge pins)
1, // count of registered media types
&sudMediaTypes[0] // list of registered media types
}
};
//注册时候的信息(名称等)
//CLAVSplitter
const AMOVIESETUP_FILTER sudFilterReg =
{
&__uuidof(CLAVSplitter), // filter clsid
L"LAV Splitter", // filter name
MERIT_PREFERRED + 4, // merit
2, // count of registered pins
sudOutputPins, // list of pins to register
CLSID_LegacyAmFilterCategory
};
//注册时候的信息(名称等)
//CLAVSplitterSource
const AMOVIESETUP_FILTER sudFilterRegSource =
{
&__uuidof(CLAVSplitterSource), // filter clsid
L"LAV Splitter Source", // filter name
MERIT_PREFERRED + 4, // merit
1, // count of registered pins
sudOutputPins, // list of pins to register
CLSID_LegacyAmFilterCategory
};

// --- COM factory table and registration code --------------

// DirectShow base class COM factory requires this table,
// declaring all the COM objects in this DLL
// 注意g_Templates名称是固定的
CFactoryTemplate g_Templates[] = {
// one entry for each CoCreate-able object
{
sudFilterReg.strName,
sudFilterReg.clsID,
CreateInstance<CLAVSplitter>,
CLAVSplitter::StaticInit,
&sudFilterReg
},
{
sudFilterRegSource.strName,
sudFilterRegSource.clsID,
CreateInstance<CLAVSplitterSource>,
NULL,
&sudFilterRegSource
},
// This entry is for the property page.
// 属性页
{
L"LAV Splitter Properties",
&CLSID_LAVSplitterSettingsProp,
CreateInstance<CLAVSplitterSettingsProp>,
NULL, NULL
},
{
L"LAV Splitter Input Formats",
&CLSID_LAVSplitterFormatsProp,
CreateInstance<CLAVSplitterFormatsProp>,
NULL, NULL
}
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);

// self-registration entrypoint
STDAPI DllRegisterServer()
{
std::list<LPCWSTR> chkbytes;

// BluRay
chkbytes.clear();
chkbytes.push_back(L"0,4,,494E4458"); // INDX (index.bdmv)
chkbytes.push_back(L"0,4,,4D4F424A"); // MOBJ (MovieObject.bdmv)
chkbytes.push_back(L"0,4,,4D504C53"); // MPLS
RegisterSourceFilter(__uuidof(CLAVSplitterSource),
MEDIASUBTYPE_LAVBluRay, chkbytes, NULL);

// base classes will handle registration using the factory template table
return AMovieDllRegisterServer2(true);
}

STDAPI DllUnregisterServer()
{
UnRegisterSourceFilter(MEDIASUBTYPE_LAVBluRay);

// base classes will handle de-registration using the factory template table
return AMovieDllRegisterServer2(false);
}

// if we declare the correct C runtime entrypoint and then forward it to the DShow base
// classes we will be sure that both the C/C++ runtimes and the base classes are initialized
// correctly
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
BOOL WINAPI DllMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpReserved)
{
return DllEntryPoint(reinterpret_cast<HINSTANCE>(hDllHandle), dwReason, lpReserved);
}

void CALLBACK OpenConfiguration(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)
{
HRESULT hr = S_OK;
CUnknown *pInstance = CreateInstance<CLAVSplitter>(NULL, &hr);
IBaseFilter *pFilter = NULL;
pInstance->NonDelegatingQueryInterface(IID_IBaseFilter, (void **)&pFilter);
if (pFilter) {
pFilter->AddRef();
CBaseDSPropPage::ShowPropPageDialog(pFilter);
}
delete pInstance;
}

接下来就要进入正题了,看一看核心的分离器(解封装器)的类CLAVSplitter的定义文件LAVSplitter.h。乍一看这个类确实了得,居然继承了那么多的父类,实在是碉堡了。先不管那么多,看看里面都有什么函数吧。主要的函数上面都加了注释。注意还有一个类CLAVSplitterSource继承了CLAVSplitter。
LAVSplitter.h

/*
* Copyright (C) 2010-2013 Hendrik Leppkes
* http://www.1f0.de *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Initial design and concept by Gabest and the MPC-HC Team, copyright under GPLv2
* Contributions by Ti-BEN from the XBMC DSPlayer Project, also under GPLv2
*/

#pragma once

#include <string>
#include <list>
#include <set>
#include <vector>
#include <map>
#include "PacketQueue.h"

#include "BaseDemuxer.h"

#include "LAVSplitterSettingsInternal.h"
#include "SettingsProp.h"
#include "IBufferInfo.h"

#include "ISpecifyPropertyPages2.h"

#include "LAVSplitterTrayIcon.h"

#define LAVF_REGISTRY_KEY L"Software\\LAV\\Splitter"
#define LAVF_REGISTRY_KEY_FORMATS LAVF_REGISTRY_KEY L"\\Formats"
#define LAVF_LOG_FILE L"LAVSplitter.txt"

#define MAX_PTS_SHIFT 50000000i64

class CLAVOutputPin;
class CLAVInputPin;

#ifdef _MSC_VER
#pragma warning(disable: 4355)
#endif
//核心解复用(分离器)
//暴漏的接口在ILAVFSettings中
[uuid("171252A0-8820-4AFE-9DF8-5C92B2D66B04")]
class CLAVSplitter
: public CBaseFilter
, public CCritSec
, protected CAMThread
, public IFileSourceFilter
, public IMediaSeeking
, public IAMStreamSelect
, public IAMOpenProgress
, public ILAVFSettingsInternal
, public ISpecifyPropertyPages2
, public IObjectWithSite
, public IBufferInfo
{
public:
CLAVSplitter(LPUNKNOWN pUnk, HRESULT* phr);
virtual ~CLAVSplitter();

static void CALLBACK StaticInit(BOOL bLoading, const CLSID *clsid);

// IUnknown
//
DECLARE_IUNKNOWN;
//暴露接口,使外部程序可以QueryInterface,关键!
//翻译(“没有代表的方式查询接口”)
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);

// CBaseFilter methods
//输入是一个,输出就不一定了!
int GetPinCount();
CBasePin *GetPin(int n);
STDMETHODIMP GetClassID(CLSID* pClsID);

STDMETHODIMP Stop();
STDMETHODIMP Pause();
STDMETHODIMP Run(REFERENCE_TIME tStart);

STDMETHODIMP JoinFilterGraph(IFilterGraph * pGraph, LPCWSTR pName);

// IFileSourceFilter
// 源Filter的接口方法
STDMETHODIMP Load(LPCOLESTR pszFileName, const AM_MEDIA_TYPE * pmt);
STDMETHODIMP GetCurFile(LPOLESTR *ppszFileName, AM_MEDIA_TYPE *pmt);

// IMediaSeeking
STDMETHODIMP GetCapabilities(DWORD* pCapabilities);
STDMETHODIMP CheckCapabilities(DWORD* pCapabilities);
STDMETHODIMP IsFormatSupported(const GUID* pFormat);
STDMETHODIMP QueryPreferredFormat(GUID* pFormat);
STDMETHODIMP GetTimeFormat(GUID* pFormat);
STDMETHODIMP IsUsingTimeFormat(const GUID* pFormat);
STDMETHODIMP SetTimeFormat(const GUID* pFormat);
STDMETHODIMP GetDuration(LONGLONG* pDuration);
STDMETHODIMP GetStopPosition(LONGLONG* pStop);
STDMETHODIMP GetCurrentPosition(LONGLONG* pCurrent);
STDMETHODIMP ConvertTimeFormat(LONGLONG* pTarget, const GUID* pTargetFormat, LONGLONG Source, const GUID* pSourceFormat);
STDMETHODIMP SetPositions(LONGLONG* pCurrent, DWORD dwCurrentFlags, LONGLONG* pStop, DWORD dwStopFlags);
STDMETHODIMP GetPositions(LONGLONG* pCurrent, LONGLONG* pStop);
STDMETHODIMP GetAvailable(LONGLONG* pEarliest, LONGLONG* pLatest);
STDMETHODIMP SetRate(double dRate);
STDMETHODIMP GetRate(double* pdRate);
STDMETHODIMP GetPreroll(LONGLONG* pllPreroll);

// IAMStreamSelect
STDMETHODIMP Count(DWORD *pcStreams);
STDMETHODIMP Enable(long lIndex, DWORD dwFlags);
STDMETHODIMP Info(long lIndex, AM_MEDIA_TYPE **ppmt, DWORD *pdwFlags, LCID *plcid, DWORD *pdwGroup, WCHAR **ppszName, IUnknown **ppObject, IUnknown **ppUnk);

// IAMOpenProgress
STDMETHODIMP QueryProgress(LONGLONG *pllTotal, LONGLONG *pllCurrent);
STDMETHODIMP AbortOperation();

// ISpecifyPropertyPages2
STDMETHODIMP GetPages(CAUUID *pPages);
STDMETHODIMP CreatePage(const GUID& guid, IPropertyPage** ppPage);

// IObjectWithSite
STDMETHODIMP SetSite(IUnknown *pUnkSite);
STDMETHODIMP GetSite(REFIID riid, void **ppvSite);

// IBufferInfo
STDMETHODIMP_(int) GetCount();
STDMETHODIMP GetStatus(int i, int& samples, int& size);
STDMETHODIMP_(DWORD) GetPriority();

// ILAVFSettings
STDMETHODIMP SetRuntimeConfig(BOOL bRuntimeConfig);
STDMETHODIMP GetPreferredLanguages(LPWSTR *ppLanguages);
STDMETHODIMP SetPreferredLanguages(LPCWSTR pLanguages);
STDMETHODIMP GetPreferredSubtitleLanguages(LPWSTR *ppLanguages);
STDMETHODIMP SetPreferredSubtitleLanguages(LPCWSTR pLanguages);
STDMETHODIMP_(LAVSubtitleMode) GetSubtitleMode();
STDMETHODIMP SetSubtitleMode(LAVSubtitleMode mode);
STDMETHODIMP_(BOOL) GetSubtitleMatchingLanguage();
STDMETHODIMP SetSubtitleMatchingLanguage(BOOL dwMode);
STDMETHODIMP_(BOOL) GetPGSForcedStream();
STDMETHODIMP SetPGSForcedStream(BOOL bFlag);
STDMETHODIMP_(BOOL) GetPGSOnlyForced();
STDMETHODIMP SetPGSOnlyForced(BOOL bForced);
STDMETHODIMP_(int) GetVC1TimestampMode();
STDMETHODIMP SetVC1TimestampMode(int iMode);
STDMETHODIMP SetSubstreamsEnabled(BOOL bSubStreams);
STDMETHODIMP_(BOOL) GetSubstreamsEnabled();
STDMETHODIMP SetVideoParsingEnabled(BOOL bEnabled);
STDMETHODIMP_(BOOL) GetVideoParsingEnabled();
STDMETHODIMP SetFixBrokenHDPVR(BOOL bEnabled);
STDMETHODIMP_(BOOL) GetFixBrokenHDPVR();
STDMETHODIMP_(HRESULT) SetFormatEnabled(LPCSTR strFormat, BOOL bEnabled);
STDMETHODIMP_(BOOL) IsFormatEnabled(LPCSTR strFormat);
STDMETHODIMP SetStreamSwitchRemoveAudio(BOOL bEnabled);
STDMETHODIMP_(BOOL) GetStreamSwitchRemoveAudio();
STDMETHODIMP GetAdvancedSubtitleConfig(LPWSTR *ppAdvancedConfig);
STDMETHODIMP SetAdvancedSubtitleConfig(LPCWSTR pAdvancedConfig);
STDMETHODIMP SetUseAudioForHearingVisuallyImpaired(BOOL bEnabled);
STDMETHODIMP_(BOOL) GetUseAudioForHearingVisuallyImpaired();
STDMETHODIMP SetMaxQueueMemSize(DWORD dwMaxSize);
STDMETHODIMP_(DWORD) GetMaxQueueMemSize();
STDMETHODIMP SetTrayIcon(BOOL bEnabled);
STDMETHODIMP_(BOOL) GetTrayIcon();
STDMETHODIMP SetPreferHighQualityAudioStreams(BOOL bEnabled);
STDMETHODIMP_(BOOL) GetPreferHighQualityAudioStreams();
STDMETHODIMP SetLoadMatroskaExternalSegments(BOOL bEnabled);
STDMETHODIMP_(BOOL) GetLoadMatroskaExternalSegments();
STDMETHODIMP GetFormats(LPSTR** formats, UINT* nFormats);
STDMETHODIMP SetNetworkStreamAnalysisDuration(DWORD dwDuration);
STDMETHODIMP_(DWORD) GetNetworkStreamAnalysisDuration();

// ILAVSplitterSettingsInternal
STDMETHODIMP_(LPCSTR) GetInputFormat() { if (m_pDemuxer) return m_pDemuxer->GetContainerFormat(); return NULL; }
STDMETHODIMP_(std::set<FormatInfo>&) GetInputFormats();
STDMETHODIMP_(BOOL) IsVC1CorrectionRequired();
STDMETHODIMP_(CMediaType *) GetOutputMediatype(int stream);
STDMETHODIMP_(IFilterGraph *) GetFilterGraph() { if (m_pGraph) { m_pGraph->AddRef(); return m_pGraph; } return NULL; }

STDMETHODIMP_(DWORD) GetStreamFlags(DWORD dwStream) { if (m_pDemuxer) return m_pDemuxer->GetStreamFlags(dwStream); return 0; }
STDMETHODIMP_(int) GetPixelFormat(DWORD dwStream) { if (m_pDemuxer) return m_pDemuxer->GetPixelFormat(dwStream); return AV_PIX_FMT_NONE; }
STDMETHODIMP_(int) GetHasBFrames(DWORD dwStream){ if (m_pDemuxer) return m_pDemuxer->GetHasBFrames(dwStream); return -1; }

// Settings helper
std::list<std::string> GetPreferredAudioLanguageList();
std::list<CSubtitleSelector> GetSubtitleSelectors();

bool IsAnyPinDrying();
void SetFakeASFReader(BOOL bFlag) { m_bFakeASFReader = bFlag; }
protected:
// CAMThread
enum {CMD_EXIT, CMD_SEEK};
DWORD ThreadProc();

HRESULT DemuxSeek(REFERENCE_TIME rtStart);
HRESULT DemuxNextPacket();
HRESULT DeliverPacket(Packet *pPacket);

void DeliverBeginFlush();
void DeliverEndFlush();

STDMETHODIMP Close();
STDMETHODIMP DeleteOutputs();
//初始化解复用器
STDMETHODIMP InitDemuxer();

friend class CLAVOutputPin;
STDMETHODIMP SetPositionsInternal(void *caller, LONGLONG* pCurrent, DWORD dwCurrentFlags, LONGLONG* pStop, DWORD dwStopFlags);

public:
CLAVOutputPin *GetOutputPin(DWORD streamId, BOOL bActiveOnly = FALSE);
STDMETHODIMP RenameOutputPin(DWORD TrackNumSrc, DWORD TrackNumDst, std::vector<CMediaType> pmts);
STDMETHODIMP UpdateForcedSubtitleMediaType();

STDMETHODIMP CompleteInputConnection();
STDMETHODIMP BreakInputConnection();

protected:
//相关的参数设置
STDMETHODIMP LoadDefaults();
STDMETHODIMP ReadSettings(HKEY rootKey);
STDMETHODIMP LoadSettings();
STDMETHODIMP SaveSettings();
//创建图标
STDMETHODIMP CreateTrayIcon();

protected:
CLAVInputPin *m_pInput;

private:
CCritSec m_csPins;
//用vector存储输出PIN(解复用的时候是不确定的)
std::vector<CLAVOutputPin *> m_pPins;
//活动的
std::vector<CLAVOutputPin *> m_pActivePins;
//不用的
std::vector<CLAVOutputPin *> m_pRetiredPins;
std::set<DWORD> m_bDiscontinuitySent;

std::wstring m_fileName;
std::wstring m_processName;
//有很多纯虚函数的基本解复用类
//注意:绝大部分信息都是从这获得的
//这里的信息是由其派生类从FFMPEG中获取到的
CBaseDemuxer *m_pDemuxer;

BOOL m_bPlaybackStarted;
BOOL m_bFakeASFReader;

// Times
REFERENCE_TIME m_rtStart, m_rtStop, m_rtCurrent, m_rtNewStart, m_rtNewStop;
REFERENCE_TIME m_rtOffset;
double m_dRate;
BOOL m_bStopValid;

// Seeking
REFERENCE_TIME m_rtLastStart, m_rtLastStop;
std::set<void *> m_LastSeekers;

// flushing
bool m_fFlushing;
CAMEvent m_eEndFlush;

std::set<FormatInfo> m_InputFormats;

// Settings
//设置
struct Settings {
BOOL TrayIcon;
std::wstring prefAudioLangs;
std::wstring prefSubLangs;
std::wstring subtitleAdvanced;
LAVSubtitleMode subtitleMode;
BOOL PGSForcedStream;
BOOL PGSOnlyForced;
int vc1Mode;
BOOL substreams;

BOOL MatroskaExternalSegments;

BOOL StreamSwitchRemoveAudio;
BOOL ImpairedAudio;
BOOL PreferHighQualityAudio;
DWORD QueueMaxSize;
DWORD NetworkAnalysisDuration;

std::map<std::string, BOOL> formats;
} m_settings;

BOOL m_bRuntimeConfig;

IUnknown *m_pSite;

CBaseTrayIcon *m_pTrayIcon;
};

[uuid("B98D13E7-55DB-4385-A33D-09FD1BA26338")]
class CLAVSplitterSource : public CLAVSplitter
{
public:
// construct only via class factory
CLAVSplitterSource(LPUNKNOWN pUnk, HRESULT* phr);
virtual ~CLAVSplitterSource();

// IUnknown
DECLARE_IUNKNOWN;
//暴露接口,使外部程序可以QueryInterface,关键!
//翻译(“没有代表的方式查询接口”)
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
};

先来看一下查询接口的函数NonDelegatingQueryInterface()吧
//暴露接口,使外部程序可以QueryInterface,关键!
STDMETHODIMP CLAVSplitter::NonDelegatingQueryInterface(REFIID riid, void** ppv)
{
CheckPointer(ppv, E_POINTER);

*ppv = NULL;

if (m_pDemuxer && (riid == __uuidof(IKeyFrameInfo) || riid == __uuidof(ITrackInfo) || riid == IID_IAMExtendedSeeking || riid == IID_IAMMediaContent)) {
return m_pDemuxer->QueryInterface(riid, ppv);
}
//写法好特别啊,意思是一样的
return
QI(IMediaSeeking)
QI(IAMStreamSelect)
QI(ISpecifyPropertyPages)
QI(ISpecifyPropertyPages2)
QI2(ILAVFSettings)
QI2(ILAVFSettingsInternal)
QI(IObjectWithSite)
QI(IBufferInfo)
__super::NonDelegatingQueryInterface(riid, ppv);
}
这个NonDelegatingQueryInterface()的写法确实够特别的,不过其作用还是一样的:根据不同的REFIID,获得不同的接口指针。在这里就不多说了。
再看一下Load()函数
// IFileSourceFilter
// 打开
STDMETHODIMP CLAVSplitter::Load(LPCOLESTR pszFileName, const AM_MEDIA_TYPE * pmt)
{
CheckPointer(pszFileName, E_POINTER);

m_bPlaybackStarted = FALSE;

m_fileName = std::wstring(pszFileName);

HRESULT hr = S_OK;
SAFE_DELETE(m_pDemuxer);
LPWSTR extension = PathFindExtensionW(pszFileName);

DbgLog((LOG_TRACE, 10, L"::Load(): Opening file '%s' (extension: %s)", pszFileName, extension));

// BDMV uses the BD demuxer, everything else LAVF
if (_wcsicmp(extension, L".bdmv") == 0 || _wcsicmp(extension, L".mpls") == 0) {
m_pDemuxer = new CBDDemuxer(this, this);
} else {
m_pDemuxer = new CLAVFDemuxer(this, this);
}
//打开
if(FAILED(hr = m_pDemuxer->Open(pszFileName))) {
SAFE_DELETE(m_pDemuxer);
return hr;
}
m_pDemuxer->AddRef();

return InitDemuxer();
}
在这里我们要注意CLAVSplitter的一个变量:m_pDemuxer。这是一个指向 CBaseDemuxer的指针。因此在这里CLAVSplitter实际上调用了 CBaseDemuxer中的方法。
从代码中的逻辑我们可以看出:
1.寻找文件后缀
2.当文件后缀是:".bdmv"或者".mpls"的时候,m_pDemuxer指向一个CBDDemuxer(我推测这代表目标文件是蓝光文件什么的),其他情况下m_pDemuxer指向一个CLAVFDemuxer。
3.然后m_pDemuxer会调用Open()方法。
4.最后会调用一个InitDemuxer()方法。
在这里我们应该看看m_pDemuxer->Open()这个方法里面有什么。我们先考虑m_pDemuxer指向CLAVFDemuxer的情况。
// Demuxer Functions
// 打开(就是一个封装)
STDMETHODIMP CLAVFDemuxer::Open(LPCOLESTR pszFileName)
{
return OpenInputStream(NULL, pszFileName, NULL, TRUE);
}发现是一层封装,于是果断决定层层深入。
//实际的打开,使用FFMPEG
STDMETHODIMP CLAVFDemuxer::OpenInputStream(AVIOContext *byteContext, LPCOLESTR pszFileName, const char *format, BOOL bForce)
{
CAutoLock lock(m_pLock);
HRESULT hr = S_OK;

int ret; // return code from avformat functions

// Convert the filename from wchar to char for avformat
char fileName[4100] = {0};
if (pszFileName) {
ret = WideCharToMultiByte(CP_UTF8, 0, pszFileName, -1, fileName, 4096, NULL, NULL);
}

if (_strnicmp("mms:", fileName, 4) == 0) {
memmove(fileName+1, fileName, strlen(fileName));
memcpy(fileName, "mmsh", 4);
}

AVIOInterruptCB cb = {avio_interrupt_cb, this};

trynoformat:
// Create the avformat_context
// FFMPEG中的函数
m_avFormat = avformat_alloc_context();
m_avFormat->pb = byteContext;
m_avFormat->interrupt_callback = cb;

if (m_avFormat->pb)
m_avFormat->flags |= AVFMT_FLAG_CUSTOM_IO;

LPWSTR extension = pszFileName ? PathFindExtensionW(pszFileName) : NULL;

AVInputFormat *inputFormat = NULL;
//如果指定了格式
if (format) {
//查查有木有
inputFormat = av_find_input_format(format);
} else if (pszFileName) {
LPWSTR extension = PathFindExtensionW(pszFileName);
for (int i = 0; i < countof(wszImageExtensions); i++) {
if (_wcsicmp(extension, wszImageExtensions[i]) == 0) {
if (byteContext) {
inputFormat = av_find_input_format("image2pipe");
} else {
inputFormat = av_find_input_format("image2");
}
break;
}
}
for (int i = 0; i < countof(wszBlockedExtensions); i++) {
if (_wcsicmp(extension, wszBlockedExtensions[i]) == 0) {
goto done;
}
}
}

// Disable loading of external mkv segments, if required
if (!m_pSettings->GetLoadMatroskaExternalSegments())
m_avFormat->flags |= AVFMT_FLAG_NOEXTERNAL;

m_timeOpening = time(NULL);
//实际的打开
ret = avformat_open_input(&m_avFormat, fileName, inputFormat, NULL);
//出错了
if (ret < 0) {
DbgLog((LOG_ERROR, 0, TEXT("::OpenInputStream(): avformat_open_input failed (%d)"), ret));
if (format) {
DbgLog((LOG_ERROR, 0, TEXT(" -> trying again without specific format")));
format = NULL;
//实际的关闭
avformat_close_input(&m_avFormat);
goto trynoformat;
}
goto done;
}
DbgLog((LOG_TRACE, 10, TEXT("::OpenInputStream(): avformat_open_input opened file of type '%S' (took %I64d seconds)"), m_avFormat->iformat->name, time(NULL) - m_timeOpening));
m_timeOpening = 0;
//初始化AVFormat
CHECK_HR(hr = InitAVFormat(pszFileName, bForce));

return S_OK;
done:
CleanupAVFormat();
return E_FAIL;
}

看到这个函数,立马感受到了一种“拨云见日”的感觉。看到了很多FFMPEG的API函数。最重要的依据当属avformat_open_input()了,通过这个函数,打开了实际的文件。如果出现错误,则调用avformat_close_input()进行清理。
最后,还调用了InitAVFormat()函数:
//初始化AVFormat
STDMETHODIMP CLAVFDemuxer::InitAVFormat(LPCOLESTR pszFileName, BOOL bForce)
{
HRESULT hr = S_OK;
const char *format = NULL;
//获取InputFormat信息(,短名称,长名称)
lavf_get_iformat_infos(m_avFormat->iformat, &format, NULL);
if (!bForce && (!format || !m_pSettings->IsFormatEnabled(format))) {
DbgLog((LOG_TRACE, 20, L"::InitAVFormat() - format of type '%S' disabled, failing", format ? format : m_avFormat->iformat->name));
return E_FAIL;
}

m_pszInputFormat = format ? format : m_avFormat->iformat->name;

m_bVC1SeenTimestamp = FALSE;

LPWSTR extension = pszFileName ? PathFindExtensionW(pszFileName) : NULL;

m_bMatroska = (_strnicmp(m_pszInputFormat, "matroska", 8) == 0);
m_bOgg = (_strnicmp(m_pszInputFormat, "ogg", 3) == 0);
m_bAVI = (_strnicmp(m_pszInputFormat, "avi", 3) == 0);
m_bMPEGTS = (_strnicmp(m_pszInputFormat, "mpegts", 6) == 0);
m_bMPEGPS = (_stricmp(m_pszInputFormat, "mpeg") == 0);
m_bRM = (_stricmp(m_pszInputFormat, "rm") == 0);
m_bPMP = (_stricmp(m_pszInputFormat, "pmp") == 0);
m_bMP4 = (_stricmp(m_pszInputFormat, "mp4") == 0);

m_bTSDiscont = m_avFormat->iformat->flags & AVFMT_TS_DISCONT;

WCHAR szProt[24] = L"file";
if (pszFileName) {
DWORD dwNumChars = 24;
hr = UrlGetPart(pszFileName, szProt, &dwNumChars, URL_PART_SCHEME, 0);
if (SUCCEEDED(hr) && dwNumChars && (_wcsicmp(szProt, L"file") != 0)) {
m_avFormat->flags |= AVFMT_FLAG_NETWORK;
DbgLog((LOG_TRACE, 10, TEXT("::InitAVFormat(): detected network protocol: %s"), szProt));
}
}

// TODO: make both durations below configurable
// decrease analyze duration for network streams
if (m_avFormat->flags & AVFMT_FLAG_NETWORK || (m_avFormat->flags & AVFMT_FLAG_CUSTOM_IO && !m_avFormat->pb->seekable)) {
// require at least 0.2 seconds
m_avFormat->max_analyze_duration = max(m_pSettings->GetNetworkStreamAnalysisDuration() * 1000, 200000);
} else {
// And increase it for mpeg-ts/ps files
if (m_bMPEGTS || m_bMPEGPS)
m_avFormat->max_analyze_duration = 10000000;
}

av_opt_set_int(m_avFormat, "correct_ts_overflow", !m_pBluRay, 0);

if (m_bMatroska)
m_avFormat->flags |= AVFMT_FLAG_KEEP_SIDE_DATA;

m_timeOpening = time(NULL);
//获取媒体流信息
int ret = avformat_find_stream_info(m_avFormat, NULL);
if (ret < 0) {
DbgLog((LOG_ERROR, 0, TEXT("::InitAVFormat(): av_find_stream_info failed (%d)"), ret));
goto done;
}
DbgLog((LOG_TRACE, 10, TEXT("::InitAVFormat(): avformat_find_stream_info finished, took %I64d seconds"), time(NULL) - m_timeOpening));
m_timeOpening = 0;

// Check if this is a m2ts in a BD structure, and if it is, read some extra stream properties out of the CLPI files
if (m_pBluRay) {
m_pBluRay->ProcessClipLanguages();
} else if (pszFileName && m_bMPEGTS) {
CheckBDM2TSCPLI(pszFileName);
}

SAFE_CO_FREE(m_stOrigParser);
m_stOrigParser = (enum AVStreamParseType *)CoTaskMemAlloc(m_avFormat->nb_streams * sizeof(enum AVStreamParseType));
if (!m_stOrigParser)
return E_OUTOFMEMORY;

for(unsigned int idx = 0; idx < m_avFormat->nb_streams; ++idx) {
AVStream *st = m_avFormat->streams[idx];

// Disable full stream parsing for these formats
if (st->need_parsing == AVSTREAM_PARSE_FULL) {
if (st->codec->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
st->need_parsing = AVSTREAM_PARSE_NONE;
}
}

if (m_bOgg && st->codec->codec_id == AV_CODEC_ID_H264) {
st->need_parsing = AVSTREAM_PARSE_FULL;
}

// Create the parsers with the appropriate flags
init_parser(m_avFormat, st);
UpdateParserFlags(st);

#ifdef DEBUG
AVProgram *streamProg = av_find_program_from_stream(m_avFormat, NULL, idx);
DbgLog((LOG_TRACE, 30, L"Stream %d (pid %d) - program: %d, codec: %S; parsing: %S;", idx, st->id, streamProg ? streamProg->pmt_pid : -1, avcodec_get_name(st->codec->codec_id), lavf_get_parsing_string(st->need_parsing)));
#endif
m_stOrigParser[idx] = st->need_parsing;

if ((st->codec->codec_id == AV_CODEC_ID_DTS && st->codec->codec_tag == 0xA2)
|| (st->codec->codec_id == AV_CODEC_ID_EAC3 && st->codec->codec_tag == 0xA1))
st->disposition |= LAVF_DISPOSITION_SECONDARY_AUDIO;

UpdateSubStreams();

if (st->codec->codec_type == AVMEDIA_TYPE_ATTACHMENT && (st->codec->codec_id == AV_CODEC_ID_TTF || st->codec->codec_id == AV_CODEC_ID_OTF)) {
if (!m_pFontInstaller) {
m_pFontInstaller = new CFontInstaller();
}
m_pFontInstaller->InstallFont(st->codec->extradata, st->codec->extradata_size);
}
}

CHECK_HR(hr = CreateStreams());

return S_OK;
done:
//关闭输入
CleanupAVFormat();
return E_FAIL;
}
该函数通过avformat_find_stream_info()等获取到流信息,这里就不多说了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息