前面有两篇文章分析了Media Player Classic - HC(mpc-hc)的源代码中的核心类 CMainFrame:

Media Player Classic - HC 源代码分析 2:核心类 (CMainFrame)(1)

Media Player Classic - HC 源代码分析 3:核心类 (CMainFrame)(2)

主要介绍了CMainFrame类中的以下几个函数(“->”代表调用关系):

OpenMedia()->OpenMediaPrivate()->OpenFile()

今天我们重点介绍一下在OpenFile中调用的DirectShow函数:

HRESULT hr = m_pGB->RenderFile(CStringW(fn), nullptr);

此函数在DirectShow中的作用很重要,负责完成创建整个渲染链,我们来看看它的源代码,了解下它是如何完成这个渲染链的创建的,RenderFile函数源代码如下:

STDMETHODIMP CFGManager::RenderFile(LPCWSTR lpcwstrFileName, LPCWSTR lpcwstrPlayList)
{TRACE(_T("--> CFGManager::RenderFile on thread: %lu\n"), GetCurrentThreadId());CAutoLock cAutoLock(this);m_streampath.RemoveAll();m_deadends.RemoveAll();HRESULT hr;HRESULT hrRFS = S_OK;/*CComPtr<IBaseFilter> pBF;if (FAILED(hr = AddSourceFilter(lpcwstrFile, lpcwstrFile, &pBF)))return hr;return ConnectFilter(pBF, nullptr);*/CFGFilterList fl;if (FAILED(hr = EnumSourceFilters(lpcwstrFileName, fl))) {return hr;}CAutoPtrArray<CStreamDeadEnd> deadends;hr = VFW_E_CANNOT_RENDER;POSITION pos = fl.GetHeadPosition();while (pos) {CComPtr<IBaseFilter> pBF;CFGFilter* pFG = fl.GetNext(pos);if (SUCCEEDED(hr = AddSourceFilter(pFG, lpcwstrFileName, pFG->GetName(), &pBF))) {m_streampath.RemoveAll();m_deadends.RemoveAll();if (SUCCEEDED(hr = ConnectFilter(pBF, nullptr))) {return hr;}NukeDownstream(pBF);RemoveFilter(pBF);deadends.Append(m_deadends);} else if (pFG->GetCLSID() == __uuidof(CRARFileSource) && HRESULT_FACILITY(hr) == FACILITY_ITF) {hrRFS = hr;}}m_deadends.Copy(deadends);// If RFS was part of the graph, return its error code instead of the last error code.// TODO: Improve filter error reporting to graph manager.return hrRFS != S_OK ? hrRFS : hr;
}

从上面代码分析,我们可以看到它主要调用了3个函数:

  1. EnumSourceFilters //根据文件找到对应支持的sourceFilter.
  2. AddSourceFilter //将找到的sourceFilter加入到渲染链当中
  3. ConnectFilter //根据sourceFilter推测出后面的Filter并连接

接下来我们详细了解一下这3个函数,首先看下EnumSourceFilters,源码如下:

HRESULT CFGManager::EnumSourceFilters(LPCWSTR lpcwstrFileName, CFGFilterList& fl)
{// TODO: use overridesCheckPointer(lpcwstrFileName, E_POINTER);fl.RemoveAll();CStringW fn = CStringW(lpcwstrFileName).TrimLeft();CStringW protocol = fn.Left(fn.Find(':') + 1).TrimRight(':').MakeLower();CStringW ext = CPathW(fn).GetExtension().MakeLower();HANDLE hFile = INVALID_HANDLE_VALUE;if (protocol.GetLength() <= 1 || protocol == L"file") {hFile = CreateFile(CString(fn), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)nullptr);// In case of audio CDs with extra content, the audio tracks// cannot be accessed directly so we have to try opening itif (hFile == INVALID_HANDLE_VALUE && ext != L".cda") {return VFW_E_NOT_FOUND;}}if (hFile == INVALID_HANDLE_VALUE) {// internal / protocolPOSITION pos = m_source.GetHeadPosition();while (pos) {CFGFilter* pFGF = m_source.GetNext(pos);if (pFGF->m_protocols.Find(CString(protocol))) {fl.Insert(pFGF, 0, false, false);}}} else {// internal / check bytesPOSITION pos = m_source.GetHeadPosition();while (pos) {CFGFilter* pFGF = m_source.GetNext(pos);POSITION pos2 = pFGF->m_chkbytes.GetHeadPosition();while (pos2) {if (CheckBytes(hFile, pFGF->m_chkbytes.GetNext(pos2))) {fl.Insert(pFGF, 1, false, false);break;}}}}if (!ext.IsEmpty()) {// internal / file extensionPOSITION pos = m_source.GetHeadPosition();while (pos) {CFGFilter* pFGF = m_source.GetNext(pos);if (pFGF->m_extensions.Find(CString(ext))) {fl.Insert(pFGF, 2, false, false);}}}{// internal / the restPOSITION pos = m_source.GetHeadPosition();while (pos) {CFGFilter* pFGF = m_source.GetNext(pos);if (pFGF->m_protocols.IsEmpty() && pFGF->m_chkbytes.IsEmpty() && pFGF->m_extensions.IsEmpty()) {fl.Insert(pFGF, 3, false, false);}}}TCHAR buff[256];ULONG len;if (hFile == INVALID_HANDLE_VALUE) {// protocolCRegKey key;if (ERROR_SUCCESS == key.Open(HKEY_CLASSES_ROOT, CString(protocol), KEY_READ)) {CRegKey exts;if (ERROR_SUCCESS == exts.Open(key, _T("Extensions"), KEY_READ)) {len = _countof(buff);if (ERROR_SUCCESS == exts.QueryStringValue(CString(ext), buff, &len)) {fl.Insert(LookupFilterRegistry(GUIDFromCString(buff), m_override), 4);}}len = _countof(buff);if (ERROR_SUCCESS == key.QueryStringValue(_T("Source Filter"), buff, &len)) {fl.Insert(LookupFilterRegistry(GUIDFromCString(buff), m_override), 5);}}fl.Insert(DEBUG_NEW CFGFilterRegistry(CLSID_URLReader), 6);} else {// check bytesCRegKey key;if (ERROR_SUCCESS == key.Open(HKEY_CLASSES_ROOT, _T("Media Type"), KEY_READ)) {FILETIME ft;len = _countof(buff);for (DWORD i = 0; ERROR_SUCCESS == key.EnumKey(i, buff, &len, &ft); i++, len = _countof(buff)) {GUID majortype;if (FAILED(GUIDFromCString(buff, majortype))) {continue;}CRegKey majorkey;if (ERROR_SUCCESS == majorkey.Open(key, buff, KEY_READ)) {len = _countof(buff);for (DWORD j = 0; ERROR_SUCCESS == majorkey.EnumKey(j, buff, &len, &ft); j++, len = _countof(buff)) {GUID subtype;if (FAILED(GUIDFromCString(buff, subtype))) {continue;}CRegKey subkey;if (ERROR_SUCCESS == subkey.Open(majorkey, buff, KEY_READ)) {len = _countof(buff);if (ERROR_SUCCESS != subkey.QueryStringValue(_T("Source Filter"), buff, &len)) {continue;}GUID clsid = GUIDFromCString(buff);TCHAR buff2[256];ULONG len2;len = _countof(buff);len2 = sizeof(buff2);for (DWORD k = 0, type;clsid != GUID_NULL && ERROR_SUCCESS == RegEnumValue(subkey, k, buff2, &len2, 0, &type, (BYTE*)buff, &len);k++, len = _countof(buff), len2 = sizeof(buff2)) {if (CheckBytes(hFile, CString(buff))) {CFGFilter* pFGF = LookupFilterRegistry(clsid, m_override);pFGF->AddType(majortype, subtype);fl.Insert(pFGF, 9);break;}}}}}}}}if (!ext.IsEmpty()) {// file extensionCRegKey key;if (ERROR_SUCCESS == key.Open(HKEY_CLASSES_ROOT, _T("Media Type\\Extensions\\") + CString(ext), KEY_READ)) {len = _countof(buff);ZeroMemory(buff, sizeof(buff));LONG ret = key.QueryStringValue(_T("Source Filter"), buff, &len); // QueryStringValue can return ERROR_INVALID_DATA on bogus strings (radlight mpc v1003, fixed in v1004)if (ERROR_SUCCESS == ret || ERROR_INVALID_DATA == ret && GUIDFromCString(buff) != GUID_NULL) {GUID clsid = GUIDFromCString(buff);GUID majortype = GUID_NULL;GUID subtype = GUID_NULL;len = _countof(buff);if (ERROR_SUCCESS == key.QueryStringValue(_T("Media Type"), buff, &len)) {majortype = GUIDFromCString(buff);}len = _countof(buff);if (ERROR_SUCCESS == key.QueryStringValue(_T("Subtype"), buff, &len)) {subtype = GUIDFromCString(buff);}CFGFilter* pFGF = LookupFilterRegistry(clsid, m_override);pFGF->AddType(majortype, subtype);fl.Insert(pFGF, 7);}}}if (hFile != INVALID_HANDLE_VALUE) {CloseHandle(hFile);}CFGFilter* pFGF = LookupFilterRegistry(CLSID_AsyncReader, m_override);pFGF->AddType(MEDIATYPE_Stream, MEDIASUBTYPE_NULL);fl.Insert(pFGF, 9);return S_OK;
}

根据以上的代码我们大概可以看出这个函数完成了如下工作:

  1. 从 CAtlList<CFGFilter*> m_source中找到和文件匹配的CFGFilter对象。(在CFGManager构造的时候就往m_source中添加了一些内部支持的默认CFGFilter对象,后面会重点介绍CFGManager构造函数)
  2. 从注册表中找到和文件匹配的CFGFilter对象的描述信息,并根据描述信息创建CFGFilter对象。
  3. 将找到匹配的CFGFilter对象全部返回给RenderFile函数。

我们接下来看一下AddSourceFilter的源码:

HRESULT CFGManager::AddSourceFilter(CFGFilter* pFGF, LPCWSTR lpcwstrFileName, LPCWSTR lpcwstrFilterName, IBaseFilter** ppBF)
{TRACE(_T("FGM: AddSourceFilter trying '%s'\n"), CStringFromGUID(pFGF->GetCLSID()).GetString());CheckPointer(lpcwstrFileName, E_POINTER);CheckPointer(ppBF, E_POINTER);ASSERT(*ppBF == nullptr);HRESULT hr;CComPtr<IBaseFilter> pBF;CInterfaceList<IUnknown, &IID_IUnknown> pUnks;if (FAILED(hr = pFGF->Create(&pBF, pUnks))) {return hr;}CComQIPtr<IFileSourceFilter> pFSF = pBF;if (!pFSF) {return E_NOINTERFACE;}if (FAILED(hr = AddFilter(pBF, lpcwstrFilterName))) {return hr;}const AM_MEDIA_TYPE* pmt = nullptr;CMediaType mt;const CAtlList<GUID>& types = pFGF->GetTypes();if (types.GetCount() == 2 && (types.GetHead() != GUID_NULL || types.GetTail() != GUID_NULL)) {mt.majortype = types.GetHead();mt.subtype = types.GetTail();pmt = &mt;}// sometimes looping with AviSynthif (FAILED(hr = pFSF->Load(lpcwstrFileName, pmt))) {RemoveFilter(pBF);return hr;}// doh :PBeginEnumMediaTypes(GetFirstPin(pBF, PINDIR_OUTPUT), pEMT, pmt2) {static const GUID guid1 ={ 0x640999A0, 0xA946, 0x11D0, { 0xA5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };static const GUID guid2 ={ 0x640999A1, 0xA946, 0x11D0, { 0xA5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };static const GUID guid3 ={ 0xD51BD5AE, 0x7548, 0x11CF, { 0xA5, 0x20, 0x00, 0x80, 0xC7, 0x7E, 0xF5, 0x8A } };if (pmt2->subtype == guid1 || pmt2->subtype == guid2 || pmt2->subtype == guid3) {RemoveFilter(pBF);pFGF = DEBUG_NEW CFGFilterRegistry(CLSID_NetShowSource);hr = AddSourceFilter(pFGF, lpcwstrFileName, lpcwstrFilterName, ppBF);delete pFGF;return hr;}}EndEnumMediaTypes(pmt2);*ppBF = pBF.Detach();m_pUnks.AddTailList(&pUnks);return S_OK;
}

我们可以看到上面有一行代码为:

CComPtr<IBaseFilter> pBF;
CInterfaceList<IUnknown, &IID_IUnknown> pUnks;
if (FAILED(hr = pFGF->Create(&pBF, pUnks))) {return hr;
}

正是通过这个Create函数创建了一个真正的IBaseFilter对象。我通过代码调试发现这个函数会从磁盘中将对应的Filter加载进来,最终加载Filter文件的函数为LoadExternalObject,整个调用堆栈如下图:

LoadExternalObject的源代码如下:

HRESULT LoadExternalObject(LPCTSTR path, REFCLSID clsid, REFIID iid, void** ppv)
{CheckPointer(ppv, E_POINTER);CAutoLock lock(&s_csExtObjs);CString fullpath = MakeFullPath(path);HINSTANCE hInst = nullptr;bool fFound = false;POSITION pos = s_extObjs.GetHeadPosition();while (pos) {ExternalObject& eo = s_extObjs.GetNext(pos);if (!eo.path.CompareNoCase(fullpath)) {hInst = eo.hInst;fFound = true;eo.bUnloadOnNextCheck = false;break;}}HRESULT hr = E_FAIL;if (!hInst) {hInst = LoadLibraryEx(fullpath, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);}if (hInst) {typedef HRESULT(__stdcall * PDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID * ppv);PDllGetClassObject p = (PDllGetClassObject)GetProcAddress(hInst, "DllGetClassObject");if (p && FAILED(hr = p(clsid, iid, ppv))) {CComPtr<IClassFactory> pCF;if (SUCCEEDED(hr = p(clsid, IID_PPV_ARGS(&pCF)))) {hr = pCF->CreateInstance(nullptr, iid, ppv);}}}if (FAILED(hr) && hInst && !fFound) {FreeLibrary(hInst);return hr;}if (hInst && !fFound) {ExternalObject eo;eo.path = fullpath;eo.hInst = hInst;eo.clsid = clsid;eo.fpDllCanUnloadNow = (fDllCanUnloadNow)GetProcAddress(hInst, "DllCanUnloadNow");eo.bUnloadOnNextCheck = false;s_extObjs.AddTail(eo);}return hr;
}

此函数通过LoadLibraryEx将对应的Filter模块加载到内存,然后通过调用模块导出函数DllGetClassObject获取到IClassFactory *pCF对象,最后通过pCF->CreateInstance函数创建真正的Filter对象。

我们接下来回到AddSourceFilter函数,看到创建完Filter以后,调用了AddFilter函数将Filter加入到graph当中。我们来看一下AddFilter的具体实现:

STDMETHODIMP CFGManager::AddFilter(IBaseFilter* pFilter, LPCWSTR pName)
{if (!m_pUnkInner) {return E_UNEXPECTED;}CAutoLock cAutoLock(this);HRESULT hr;if (FAILED(hr = CComQIPtr<IFilterGraph2>(m_pUnkInner)->AddFilter(pFilter, pName))) {return hr;}// TODOhr = pFilter->JoinFilterGraph(nullptr, nullptr);hr = pFilter->JoinFilterGraph(this, pName);return hr;
}

由上面代码可以看出,Filter不仅被加入到Graph当中,而且将Graph对象的指针也设置到Filter当中。

接下来我们回到RenderFile函数,看到调用完AddSourceFilter以后,又调用了ConnectFilter函数。在ConectFilter函数中创建了Graph的整个链。ConnectFilter源码如下:

STDMETHODIMP CFGManager::ConnectFilter(IBaseFilter* pBF, IPin* pPinIn)
{CAutoLock cAutoLock(this);CheckPointer(pBF, E_POINTER);if (pPinIn && S_OK != IsPinDirection(pPinIn, PINDIR_INPUT)) {return VFW_E_INVALID_DIRECTION;}int nTotal = 0, nRendered = 0;const CAppSettings& s = AfxGetAppSettings();BeginEnumPins(pBF, pEP, pPin) {if (S_OK == IsPinDirection(pPin, PINDIR_OUTPUT)&& S_OK != IsPinConnected(pPin)&& !((s.iDSVideoRendererType != VIDRNDT_DS_EVR_CUSTOM && s.iDSVideoRendererType != VIDRNDT_DS_EVR && s.iDSVideoRendererType != VIDRNDT_DS_SYNC) && GetPinName(pPin)[0] == '~')) {CLSID clsid;pBF->GetClassID(&clsid);// Disable DVD subtitle mixing in EVR (CP) and Sync Renderer for Microsoft DTV-DVD Video Decoder, it corrupts DVD playback.if (clsid == CLSID_CMPEG2VidDecoderDS) {if (s.iDSVideoRendererType == VIDRNDT_DS_EVR_CUSTOM || s.iDSVideoRendererType == VIDRNDT_DS_SYNC) {if (GetPinName(pPin)[0] == '~') {continue;}}}// No multiple pin for Internal MPEG2 Software Decoder, NVIDIA PureVideo Decoder, Sonic Cinemaster VideoDecoderelse if (clsid == CLSID_CMpeg2DecFilter|| clsid == CLSID_NvidiaVideoDecoder|| clsid == CLSID_SonicCinemasterVideoDecoder) {if (GetPinName(pPin)[0] == '~') {continue;}//TODO: enable multiple pins for the renderer, if the video decoder supports DXVA}m_streampath.Append(pBF, pPin);HRESULT hr = Connect(pPin, pPinIn);if (SUCCEEDED(hr)) {for (ptrdiff_t i = m_deadends.GetCount() - 1; i >= 0; i--) {if (m_deadends[i]->Compare(m_streampath)) {m_deadends.RemoveAt(i);}}nRendered++;}nTotal++;m_streampath.RemoveTail();if (SUCCEEDED(hr) && pPinIn) {return S_OK;}}}EndEnumPins;returnnRendered == nTotal ? (nRendered > 0 ? S_OK : S_FALSE) :nRendered > 0 ? VFW_S_PARTIAL_RENDER :VFW_E_CANNOT_RENDER;
}

这个函数主要做了2件事情:

1.从SourceFilter枚举出pin对象。

2.根据其中的输出Pin调用Connect方法建立整个链。

这个contect方法中有一段代码比较重要,创建了后面的各个Filter,并且将这些Filter加入到Graph当中,然后连接这些Filter,这段代码如下:

        pos = fl.GetHeadPosition();while (pos) {CFGFilter* pFGF = fl.GetNext(pos);// Checks if madVR is already in the graph to avoid two instances at the same timeCComPtr<IBaseFilter> pBFmadVR;FindFilterByName(_T("madVR Renderer"), &pBFmadVR);if (pBFmadVR && (pFGF->GetName() == _T("madVR Renderer"))) {continue;}if (pMadVRAllocatorPresenter && (pFGF->GetCLSID() == CLSID_madVR)) {// the pure madVR filter was selected (without the allocator presenter)// subtitles, OSD etc don't work correctly without the allocator presenter// so we prefer the allocator presenter over the pure filterpFGF = pMadVRAllocatorPresenter;}TRACE(_T("FGM: Connecting '%s'\n"), pFGF->GetName().GetString());CComPtr<IBaseFilter> pBF;CInterfaceList<IUnknown, &IID_IUnknown> pUnks;if (FAILED(pFGF->Create(&pBF, pUnks))) {TRACE(_T("     --> Filter creation failed\n"));continue;}if (FAILED(hr = AddFilter(pBF, pFGF->GetName()))) {TRACE(_T("     --> Adding the filter failed\n"));pUnks.RemoveAll();pBF.Release();continue;}hr = ConnectFilterDirect(pPinOut, pBF, nullptr);/*if (FAILED(hr)){if (types.GetCount() >= 2 && types[0] == MEDIATYPE_Stream && types[1] != GUID_NULL){CMediaType mt;mt.majortype = types[0];mt.subtype = types[1];mt.formattype = FORMAT_None;if (FAILED(hr)) hr = ConnectFilterDirect(pPinOut, pBF, &mt);mt.formattype = GUID_NULL;if (FAILED(hr)) hr = ConnectFilterDirect(pPinOut, pBF, &mt);}}*/if (SUCCEEDED(hr)) {if (!IsStreamEnd(pBF)) {fDeadEnd = false;}if (bContinueRender) {hr = ConnectFilter(pBF, pPinIn);}if (SUCCEEDED(hr)) {m_pUnks.AddTailList(&pUnks);// maybe the application should do this...POSITION posInterface = pUnks.GetHeadPosition();while (posInterface) {if (CComQIPtr<IMixerPinConfig, &IID_IMixerPinConfig> pMPC = pUnks.GetNext(posInterface)) {pMPC->SetAspectRatioMode(AM_ARMODE_STRETCHED);}}if (CComQIPtr<IVMRAspectRatioControl> pARC = pBF) {pARC->SetAspectRatioMode(VMR_ARMODE_NONE);}if (CComQIPtr<IVMRAspectRatioControl9> pARC = pBF) {pARC->SetAspectRatioMode(VMR_ARMODE_NONE);}if (CComQIPtr<IVMRMixerControl9> pMC = pBF) {m_pUnks.AddTail(pMC);}if (CComQIPtr<IVMRMixerBitmap9> pMB = pBF) {m_pUnks.AddTail(pMB);}if (CComQIPtr<IMFGetService, &__uuidof(IMFGetService)> pMFGS = pBF) {CComPtr<IMFVideoDisplayControl> pMFVDC;CComPtr<IMFVideoMixerBitmap>    pMFMB;CComPtr<IMFVideoProcessor>      pMFVP;if (SUCCEEDED(pMFGS->GetService(MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&pMFVDC)))) {m_pUnks.AddTail(pMFVDC);}if (SUCCEEDED(pMFGS->GetService(MR_VIDEO_MIXER_SERVICE, IID_PPV_ARGS(&pMFMB)))) {m_pUnks.AddTail(pMFMB);}if (SUCCEEDED(pMFGS->GetService(MR_VIDEO_MIXER_SERVICE, IID_PPV_ARGS(&pMFVP)))) {m_pUnks.AddTail(pMFVP);}//CComPtr<IMFWorkQueueServices> pMFWQS;//pMFGS->GetService (MF_WORKQUEUE_SERVICES, IID_PPV_ARGS(&pMFWQS));//pMFWQS->BeginRegisterPlatformWorkQueueWithMMCSS(if (pMadVRAllocatorPresenter) {// Hook DXVA to have status and logging.CComPtr<IDirectXVideoDecoderService> pDecoderService;CComPtr<IDirect3DDeviceManager9>     pDeviceManager;HANDLE hDevice = INVALID_HANDLE_VALUE;if (SUCCEEDED(pMFGS->GetService(MR_VIDEO_ACCELERATION_SERVICE, IID_PPV_ARGS(&pDeviceManager)))&& SUCCEEDED(pDeviceManager->OpenDeviceHandle(&hDevice))&& SUCCEEDED(pDeviceManager->GetVideoService(hDevice, IID_PPV_ARGS(&pDecoderService)))) {HookDirectXVideoDecoderService(pDecoderService);pDeviceManager->CloseDeviceHandle(hDevice);}pDeviceManager.Release();pDecoderService.Release();}}return hr;}}EXECUTE_ASSERT(SUCCEEDED(RemoveFilter(pBF)));TRACE(_T("     --> Failed to connect\n"));pUnks.RemoveAll();pBF.Release();}

从上面代码了解到连接Filter调用了ConnectFilterDirect这个函数,我们再来看看这个函数做了什么?源码如下:

STDMETHODIMP CFGManager::ConnectFilterDirect(IPin* pPinOut, IBaseFilter* pBF, const AM_MEDIA_TYPE* pmt)
{CAutoLock cAutoLock(this);CheckPointer(pPinOut, E_POINTER);CheckPointer(pBF, E_POINTER);if (S_OK != IsPinDirection(pPinOut, PINDIR_OUTPUT)) {return VFW_E_INVALID_DIRECTION;}const CAppSettings& s = AfxGetAppSettings();BeginEnumPins(pBF, pEP, pPin) {if (S_OK == IsPinDirection(pPin, PINDIR_INPUT)&& S_OK != IsPinConnected(pPin)&& !((s.iDSVideoRendererType != VIDRNDT_DS_EVR_CUSTOM && s.iDSVideoRendererType != VIDRNDT_DS_EVR && s.iDSVideoRendererType != VIDRNDT_DS_SYNC) && GetPinName(pPin)[0] == '~')) {HRESULT hr = ConnectDirect(pPinOut, pPin, pmt);if (SUCCEEDED(hr)) {return hr;}}}EndEnumPins;return VFW_E_CANNOT_CONNECT;
}

由上面代码我们发现ConnectFilterDirect又将Filter的连接工作交给了ConnectDirect,这个函数负责

上一级Filter的输出pin和下一级Filter的输入pin连接。我们再深入一步,看一下这个函数又干了啥?

STDMETHODIMP CFGManager::ConnectDirect(IPin* pPinOut, IPin* pPinIn, const AM_MEDIA_TYPE* pmt)
{if (!m_pUnkInner) {return E_UNEXPECTED;}CAutoLock cAutoLock(this);CComPtr<IBaseFilter> pBF = GetFilterFromPin(pPinIn);CLSID clsid = GetCLSID(pBF);// TODO: GetUpStreamFilter goes up on the first input pin onlyfor (CComPtr<IBaseFilter> pBFUS = GetFilterFromPin(pPinOut); pBFUS; pBFUS = GetUpStreamFilter(pBFUS)) {if (pBFUS == pBF) {return VFW_E_CIRCULAR_GRAPH;}if (clsid != CLSID_Proxy && GetCLSID(pBFUS) == clsid) {return VFW_E_CANNOT_CONNECT;}}return CComQIPtr<IFilterGraph2>(m_pUnkInner)->ConnectDirect(pPinOut, pPinIn, pmt);
}

从上面代码可知,最终的连接调用了CFGManager类里创建的IFilterGraph2对象的ConnectDirect方法。

总结:RenderFile函数的解析就到这里,我们大概了解了RenderFile函数中整个链中的Filter是如何创建的,以及如何建立连接的。

Media Player Classic - HC 源代码分析 8:RenderFile函数详细分析(CFGManager)相关推荐

  1. Media Player Classic - HC 源代码分析 9:CFGManager类详细分析(CFGManager)

    上一篇文章分析了Media Player Classic - HC(mpc-hc)的源代码中的CFGManager类的RenderFile函数: Media Player Classic - HC 源 ...

  2. Media Player Classic - HC 源代码分析 7:详细信息选项卡(CPPageFileInfoDetails)

    ===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...

  3. Media Player Classic - HC 源代码分析 6:MediaInfo选项卡 (CPPageFileMediaInfo)

    ===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...

  4. Media Player Classic - HC 源代码分析 5:关于对话框 (CAboutDlg)

    ===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...

  5. Media Player Classic - HC 源代码分析 4:核心类 (CMainFrame)(3)

    ===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...

  6. Media Player Classic - HC 源代码分析 3:核心类 (CMainFrame)(2)

    ===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...

  7. Media Player Classic - HC 源代码分析 2:核心类 (CMainFrame)(1)

    ===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...

  8. Media Player Classic - HC 源代码分析 1:整体结构

    ===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...

  9. Media Player Classic - HC 源代码分析 14:PIN连接过程中推模式和拉模式区别

    前面有两篇文章讲解了PIN连接过程中需要做2件事情: 媒体类型的协商 分配器的协商 推模式和拉模式关于媒体类型的协商调用的接口都一样,主要的区别还是在分配器的协商. 前面讲解的分配器的协商过程是推模式 ...

最新文章

  1. Python,OpenCV应用轮廓逼近算法,检测对象的形状
  2. 微信小店 API 手册
  3. 常用工具之zabbix
  4. ibatis轻松入门
  5. Stack around the variable 'date' was corrupted.
  6. 查询大于2分钟的数据
  7. BZOJ 1877 拆点费用流
  8. python写算法求最短路径,Python实现迪杰斯特拉算法并生成最短路径的示例代码
  9. 如何优化网页转化率?(上篇)
  10. SpringBoot系列: 单元测试2
  11. sql2012 ssrs_您必须在SQL Server Reporting Services(SSRS)中记录的十件事
  12. 如何采集企业信息公示系统
  13. oracle数据库sql语句修改表某列字段长度
  14. word参考文献交叉引用
  15. cad 选择框不是矩形 解决方法
  16. 展示数据的3大要点——web数据可视化的实现
  17. 为何服务器变成ink文件,电脑上的文件全部变成ink了, 为什么, 跪求大神回答
  18. 在excel中如何筛选重复数据_如何将Excel表中重复数据筛选出来?
  19. Lacking counting POJ.NO 2386
  20. 【基础知识】多标签分类CrossEntropyLoss 与 二分类BCELoss

热门文章

  1. 美团饿了么大额无门槛红包
  2. java词法分析器 c语言_Java编写的C语言词法分析器
  3. request.getSession.setAttribute和request.setAttribute区别
  4. Failed to create Spark client for Spark session/30041Code
  5. 2. QML使用View3D控件显示三维模型
  6. 面向6G的RIS辅助的cell-free系统
  7. Java中this、super关键字的用法
  8. 运动健身APP开发需要具备哪些功能?
  9. MindSpore进阶课程视频集锦
  10. AcWing 660. 零食