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个函数:

  1. EnumSourceFilters //根据文件找到对应支持的sourceFilter.
  2. AddSourceFilter //将找到的sourceFilter加入到渲染链当中
  3. 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;
}

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

  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 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是如何创建的,以及如何建立连接的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值