LAV Filter 原始碼分析 2: LAV Splitter
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()等獲取到流資訊,這裡就不多說了。
相關推薦
LAV Filter 原始碼分析 2: LAV Splitter
LAV Filter 中最著名的就是 LAV Splitter,支援Matroska /WebM,MPEG-TS/PS,MP4/MOV,FLV,OGM / OGG,AVI等其他格式,廣泛存在於各種視訊播放器(暴風影音這類的)之中。本文分析一下它的原始碼。在分析之前,先看看它是
LAV Filter 原始碼分析 3: LAV Video (1)
LAV Video 是使用很廣泛的DirectShow Filter。它封裝了FFMPEG中的libavcodec,支援十分廣泛的視訊格式的解碼。在這裡對其原始碼進行詳細的分析。LAV Video 工程程式碼的結構如下圖所示直接看LAV Video最主要的類CLAVVideo
LAV Filter 原始碼分析 1: 總體結構
LAV Filter 是一款視訊分離和解碼軟體,他的分離器封裝了FFMPEG中的libavformat,解碼器則封裝了FFMPEG中的libavcodec。它支援十分廣泛的視音訊格式。本文分析了LAV Filter原始碼的總體架構。使用git獲取LAV filter原始碼之後
Android 5.0 Camera系統原始碼分析(2):Camera開啟流程
1. 前言 本文將分析android系統原始碼,從frameworks層到hal層,暫不涉及app層和kernel層。由於某些函式比較複雜,在貼出程式碼時會適當對其進行簡化。本文屬於自己對原始碼的總結,僅僅是貫穿程式碼流程,不會深入分析各個細節。歡迎聯絡討論,QQ:1026
SpringMVC原始碼分析2:SpringMVC設計理念與DispatcherServlet
轉自:https://my.oschina.net/lichhao/blog SpringMVC簡介 SpringMVC作為Struts2之後異軍突起的一個表現層框架,正越來越流行,相信javaee的開發者們就算沒使用過SpringMVC,也應該對其略有耳聞。我試圖通過對SpringMVC的設計思想和原始碼實
Netty Pipeline原始碼分析(2)
原文連結:wangwei.one/posts/netty… 前面 ,我們分析了Netty Pipeline的初始化及節點新增與刪除邏輯。接下來,我們將來分析Pipeline的事件傳播機制。 Netty版本:4.1.30 inBound事件傳播 示例 我們通過下面這個例子來演示Ne
lucene原始碼分析(2)讀取過程例項
1.官方提供的程式碼demo Analyzer analyzer = new StandardAnalyzer(); // Store the index in memory: Directory directory = new RAMDirec
x264裡的2pass指的是什麼意思 x264原始碼分析2 encode
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
Shiro原始碼分析(2) - 會話管理器(SessionManager)
本文在於分析Shiro原始碼,對於新學習的朋友可以參考 [開濤部落格](http://jinnianshilongnian.iteye.com/blog/2018398)進行學習。 本文對Shiro中的SessionManager進行分析,SessionMan
《2.uboot和系統移植-第6部分-2.6.uboot原始碼分析2-啟動第二階段》
《2.uboot和系統移植-第6部分-2.6.uboot原始碼分析2-啟動第二階段》 第一部分、章節目錄 2.6.1.start_armboot函式簡介 2.6.2.start_armboot解析1 2.6.3.記憶體使用排布 2.6.4.start_armboot解析2 2.6.5.s
30.以太坊原始碼分析(30)eth-bloombits和filter原始碼分析
以太坊的布隆過濾器 以太坊的區塊頭中包含了一個叫做logsBloom的區域。 這個區域儲存了當前區塊中所有的收據的日誌的布隆過濾器,一共是2048個bit。也就是256個位元組。 而我們的一個交易的收據包含了很多的日誌記錄。 每個日誌記錄包含了 合約的地址, 多個Topic。 而在我
Glide 原始碼分析(一):圖片壓縮
關於圖片的那點事兒 Q: 一張大小為 55KB, 解析度為 1080 * 480 的 PNG 圖片,它載入近記憶體時所佔的大小是多少呢? 圖片記憶體大小 圖片佔用記憶體大小 = 解析度 * 畫素點大小 其中資料格式不同畫素點大小也不同: ALPHA_8: 1B RGB_565: 2B
Caffe原始碼理解2:SyncedMemory CPU和GPU間的資料同步
目錄 寫在前面 成員變數的含義及作用 構造與析構 記憶體同步管理 參考 部落格:blog.shinelee.me | 部落格園 | CSDN 寫在前面 在Caffe原始碼理解1中介紹了Blob類,其中的資料成員有 shared_ptr<SyncedMemory>
am335x 核心原始碼分析2 LCD移植
1、/arch/arm/mach-omap2/board-am335xevm.c/lcdc_init(){得到LCD硬體引數struct da8xx_lcdc_platform_data} -> am33xx_register_lcdc() -> omap_device_
MyBatis原始碼分析-2-基礎支援層-反射模組-TypeParameterResolver/ObjectFactory
TypeParameterResolver: TypeParameterResolver的功能是:當存在複雜的繼承關係以及泛型定義時, TypeParameterResolver 可以幫助我們解析欄位、方法引數或方法返回值的型別。TypeParameterResolver 是在Refelctor
muduo原始碼分析(2) --記憶體分配
寫在前面: 這個原始碼是分析libevent-2.0.20-stable, 並非最新版本的libevent,作者並沒有全看原始碼,在這裡會推薦以下參考的一些網站,也歡迎大家在不足的地方提出來進行討論。 什麼都沒包裝的記憶體管理 預設情況下,l
Minix3原始碼分析(2)——系統初始化
minix3的啟動牽扯到幾次控制權轉移,它們發生在mpx386.s中的組合語言例程和start.c及main.c中的C語言例程之間。 彙編程式碼需要做許多工作,包括建立一個 棧幀 以便為C編譯器編譯的程式碼提供適當的環境,複製處理器所使用的表格來定義儲存器段,建
Spring5原始碼分析系列(四)Spring5原始碼分析2
本文緊接上文Spring5原始碼分析1,講解基於XML的依賴注入,文章參考自Tom老師視訊,下一篇文章將介紹基於Annotation的依賴注入。 基於XML的依賴注入 1、依賴注入發生的時間 當SpringIOC容器完成了Bean定義資源的定位、載入和解析註冊以後,IO
JDK1.8ArrayList原始碼分析2
E get(int index) 因為ArrayList是採用陣列結構來儲存的,所以它的get方法非常簡單,先是判斷一下有沒有越界,之後就可以直接通過陣列下標來獲取元素了,所以get的時間複雜度是O(1)。 /** * Returns the
谷歌瀏覽器的原始碼分析 2
這麼大的工程,我從哪裡開始呢?我認為從介面開始,這樣才可以快速地深入研究。下面就可以先嚐試修改一個chrome的關於對話方塊,上一次看到它是英語的,那麼我就來把它改成中文的吧,這樣有目標了。從chrome的工程裡可以看到它是支援多種語言的,在Windows平臺上支援多語言的標準做法,就是寫多個語言的DL