前面有两篇文章分析了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 overrides
CheckPointer(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 it
if (hFile == INVALID_HANDLE_VALUE && ext != L".cda") {
return VFW_E_NOT_FOUND;
}
}
if (hFile == INVALID_HANDLE_VALUE) {
// internal / protocol
POSITION 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 bytes
POSITION 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 extension
POSITION 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 rest
POSITION 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) {
// protocol
CRegKey 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 bytes
CRegKey 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 extension
CRegKey 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 AviSynth
if (FAILED(hr = pFSF->Load(lpcwstrFileName, pmt))) {
RemoveFilter(pBF);
return hr;
}
// doh :P
BeginEnumMediaTypes(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;
}
// TODO
hr = 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 VideoDecoder
else 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;
return
nRendered == 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 time
CComPtr<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 filter
pFGF = 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 only
for (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是如何创建的,以及如何建立连接的。