Media Player Classic - HC 源代码分析 8:RenderFile函数详细分析(CFGManager)
前面有两篇文章分析了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个函数:
- EnumSourceFilters //根据文件找到对应支持的sourceFilter.
- AddSourceFilter //将找到的sourceFilter加入到渲染链当中
- 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;
}
根据以上的代码我们大概可以看出这个函数完成了如下工作:
- 从 CAtlList<CFGFilter*> m_source中找到和文件匹配的CFGFilter对象。(在CFGManager构造的时候就往m_source中添加了一些内部支持的默认CFGFilter对象,后面会重点介绍CFGManager构造函数)
- 从注册表中找到和文件匹配的CFGFilter对象的描述信息,并根据描述信息创建CFGFilter对象。
- 将找到匹配的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)相关推荐
- Media Player Classic - HC 源代码分析 9:CFGManager类详细分析(CFGManager)
上一篇文章分析了Media Player Classic - HC(mpc-hc)的源代码中的CFGManager类的RenderFile函数: Media Player Classic - HC 源 ...
- Media Player Classic - HC 源代码分析 7:详细信息选项卡(CPPageFileInfoDetails)
===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...
- Media Player Classic - HC 源代码分析 6:MediaInfo选项卡 (CPPageFileMediaInfo)
===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...
- Media Player Classic - HC 源代码分析 5:关于对话框 (CAboutDlg)
===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...
- Media Player Classic - HC 源代码分析 4:核心类 (CMainFrame)(3)
===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...
- Media Player Classic - HC 源代码分析 3:核心类 (CMainFrame)(2)
===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...
- Media Player Classic - HC 源代码分析 2:核心类 (CMainFrame)(1)
===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...
- Media Player Classic - HC 源代码分析 1:整体结构
===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...
- Media Player Classic - HC 源代码分析 14:PIN连接过程中推模式和拉模式区别
前面有两篇文章讲解了PIN连接过程中需要做2件事情: 媒体类型的协商 分配器的协商 推模式和拉模式关于媒体类型的协商调用的接口都一样,主要的区别还是在分配器的协商. 前面讲解的分配器的协商过程是推模式 ...
最新文章
- Python,OpenCV应用轮廓逼近算法,检测对象的形状
- 微信小店 API 手册
- 常用工具之zabbix
- ibatis轻松入门
- Stack around the variable 'date' was corrupted.
- 查询大于2分钟的数据
- BZOJ 1877 拆点费用流
- python写算法求最短路径,Python实现迪杰斯特拉算法并生成最短路径的示例代码
- 如何优化网页转化率?(上篇)
- SpringBoot系列: 单元测试2
- sql2012 ssrs_您必须在SQL Server Reporting Services(SSRS)中记录的十件事
- 如何采集企业信息公示系统
- oracle数据库sql语句修改表某列字段长度
- word参考文献交叉引用
- cad 选择框不是矩形 解决方法
- 展示数据的3大要点——web数据可视化的实现
- 为何服务器变成ink文件,电脑上的文件全部变成ink了, 为什么, 跪求大神回答
- 在excel中如何筛选重复数据_如何将Excel表中重复数据筛选出来?
- Lacking counting POJ.NO 2386
- 【基础知识】多标签分类CrossEntropyLoss 与 二分类BCELoss
热门文章
- 美团饿了么大额无门槛红包
- java词法分析器 c语言_Java编写的C语言词法分析器
- request.getSession.setAttribute和request.setAttribute区别
- Failed to create Spark client for Spark session/30041Code
- 2. QML使用View3D控件显示三维模型
- 面向6G的RIS辅助的cell-free系统
- Java中this、super关键字的用法
- 运动健身APP开发需要具备哪些功能?
- MindSpore进阶课程视频集锦
- AcWing 660. 零食