Media Player Classic - HC 源代码分析 10:Filter链的自动建立过程(CFGManager)

 今天我们重点介绍一下Filte链的自动建立过程,前面在介绍RenderFile函数的时候大概讲解了一下,但是感觉不够详细,所以这里准备单独拿出来讲解一下。

我们先了看一下整个链建立过程中需要调用的函数:

  1. 需要先根据文件创建源Filter对象。
  2. 调用IGraphBuilder2扩展接口的方法:STDMETHODIMP ConnectFilter(IBaseFilter* pBF, IPin* pPinIn);
  3. 调用IGraphBuilder接口的方法: STDMETHODIMP Connect(IPin* pPinOut, IPin* pPinIn);
  4. 调用IGraphBuilder2接口的方法:STDMETHODIMP ConnectFilterDirect(IPin* pPinOut, IBaseFilter* pBF, const AM_MEDIA_TYPE* pmt);
  5. 调用IFilterGraph接口的方法:STDMETHODIMP ConnectDirect(IPin* pPinOut, IPin* pPinIn, const AM_MEDIA_TYPE* pmt);
  6. 如果ConnectFilterDirect方法调用成功,则获取到下一个Filter对象,调用第二步中的ConnectFilter继续连接过程。

 接下来我们详细了解一下每一个函数。先来看看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. 遍历传进来的Filter的输出Pin.
  2. 根据每个输出Pin调用STDMETHODIMP Connect(IPin* pPinOut, IPin* pPinIn)方法向下连接。

按CFGManager的习惯,本来Connect这个函数应该转接到DirectShow 实现的Filter Graph Manager对象上,但是MPC-HC没有这样干,而是对这个函数重新实现了一遍。具体情况看源码:

HRESULT CFGManager::Connect(IPin* pPinOut, IPin* pPinIn, bool bContinueRender)
{
    CAutoLock cAutoLock(this);

    CheckPointer(pPinOut, E_POINTER);

    HRESULT hr;

    if (S_OK != IsPinDirection(pPinOut, PINDIR_OUTPUT)
            || pPinIn && S_OK != IsPinDirection(pPinIn, PINDIR_INPUT)) {
        return VFW_E_INVALID_DIRECTION;
    }

    if (S_OK == IsPinConnected(pPinOut)
            || pPinIn && S_OK == IsPinConnected(pPinIn)) {
        return VFW_E_ALREADY_CONNECTED;
    }

    bool fDeadEnd = true;

    if (pPinIn) {
        // 1. Try a direct connection between the filters, with no intermediate filters

        if (SUCCEEDED(hr = ConnectDirect(pPinOut, pPinIn, nullptr))) {
            return hr;
        }
    } else {
        // 1. Use IStreamBuilder

        if (CComQIPtr<IStreamBuilder> pSB = pPinOut) {
            if (SUCCEEDED(hr = pSB->Render(pPinOut, this))) {
                return hr;
            }

            pSB->Backout(pPinOut, this);
        }
    }

    // 2. Try cached filters

    if (CComQIPtr<IGraphConfig> pGC = (IGraphBuilder2*)this) {
        BeginEnumCachedFilters(pGC, pEF, pBF) {
            if (pPinIn && GetFilterFromPin(pPinIn) == pBF) {
                continue;
            }

            hr = pGC->RemoveFilterFromCache(pBF);

            // does RemoveFilterFromCache call AddFilter like AddFilterToCache calls RemoveFilter ?

            if (SUCCEEDED(hr = ConnectFilterDirect(pPinOut, pBF, nullptr))) {
                if (!IsStreamEnd(pBF)) {
                    fDeadEnd = false;
                }

                if (SUCCEEDED(hr = ConnectFilter(pBF, pPinIn))) {
                    return hr;
                }
            }

            hr = pGC->AddFilterToCache(pBF);
        }
        EndEnumCachedFilters;
    }

    // 3. Try filters in the graph

    {
        CInterfaceList<IBaseFilter> pBFs;

        BeginEnumFilters(this, pEF, pBF) {
            if (pPinIn && GetFilterFromPin(pPinIn) == pBF
                    || GetFilterFromPin(pPinOut) == pBF) {
                continue;
            }

            // HACK: ffdshow - audio capture filter
            if (GetCLSID(pPinOut) == GUIDFromCString(_T("{04FE9017-F873-410E-871E-AB91661A4EF7}"))
                    && GetCLSID(pBF) == GUIDFromCString(_T("{E30629D2-27E5-11CE-875D-00608CB78066}"))) {
                continue;
            }

            pBFs.AddTail(pBF);
        }
        EndEnumFilters;

        POSITION pos = pBFs.GetHeadPosition();
        while (pos) {
            IBaseFilter* pBF = pBFs.GetNext(pos);

            if (SUCCEEDED(hr = ConnectFilterDirect(pPinOut, pBF, nullptr))) {
                if (!IsStreamEnd(pBF)) {
                    fDeadEnd = false;
                }

                if (SUCCEEDED(hr = ConnectFilter(pBF, pPinIn))) {
                    return hr;
                }
            }

            EXECUTE_ASSERT(SUCCEEDED(Disconnect(pPinOut)));
        }
    }

    // 4. Look up filters in the registry

    {
        CFGFilterList fl;

        CAtlArray<GUID> types;
        ExtractMediaTypes(pPinOut, types);

        POSITION pos = m_transform.GetHeadPosition();
        while (pos) {
            CFGFilter* pFGF = m_transform.GetNext(pos);
            if (pFGF->GetMerit() < MERIT64_DO_USE || pFGF->CheckTypes(types, false)) {
                fl.Insert(pFGF, 0, pFGF->CheckTypes(types, true), false);
            }
        }

        pos = m_override.GetHeadPosition();
        while (pos) {
            CFGFilter* pFGF = m_override.GetNext(pos);
            if (pFGF->GetMerit() < MERIT64_DO_USE || pFGF->CheckTypes(types, false)) {
                fl.Insert(pFGF, 0, pFGF->CheckTypes(types, true), false);
            }
        }

        CComPtr<IEnumMoniker> pEM;
        if (!types.IsEmpty()
                && SUCCEEDED(m_pFM->EnumMatchingFilters(
                                 &pEM, 0, FALSE, MERIT_DO_NOT_USE + 1,
                                 TRUE, (DWORD)types.GetCount() / 2, types.GetData(), nullptr, nullptr, FALSE,
                                 !!pPinIn, 0, nullptr, nullptr, nullptr))) {
            for (CComPtr<IMoniker> pMoniker; S_OK == pEM->Next(1, &pMoniker, nullptr); pMoniker = nullptr) {
                CFGFilterRegistry* pFGF = DEBUG_NEW CFGFilterRegistry(pMoniker);
                fl.Insert(pFGF, 0, pFGF->CheckTypes(types, true));
            }
        }

        // let's check whether the madVR allocator presenter is in our list
        // it should be if madVR is selected as the video renderer
        CFGFilter* pMadVRAllocatorPresenter = nullptr;
        pos = fl.GetHeadPosition();
        while (pos) {
            CFGFilter* pFGF = fl.GetNext(pos);
            if (pFGF->GetCLSID() == CLSID_madVRAllocatorPresenter) {
                // found it!
                pMadVRAllocatorPresenter = pFGF;
                break;
            }
        }

        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();
        }
    }

    if (fDeadEnd) {
        CAutoPtr<CStreamDeadEnd> psde(DEBUG_NEW CStreamDeadEnd());
        psde->AddTailList(&m_streampath);
        int skip = 0;
        BeginEnumMediaTypes(pPinOut, pEM, pmt) {
            if (pmt->majortype == MEDIATYPE_Stream && pmt->subtype == MEDIASUBTYPE_NULL) {
                skip++;
            }
            psde->mts.AddTail(CMediaType(*pmt));
        }
        EndEnumMediaTypes(pmt);
        if (skip < (int)psde->mts.GetCount()) {
            m_deadends.Add(psde);
        }
    }

    return pPinIn ? VFW_E_CANNOT_CONNECT : VFW_E_CANNOT_RENDER;
}

这个函数代码有点长,但是重要执行代码就几行,截图如下:

这里就不详细介绍了,截图已经解释的很清楚了。从截图可知,连接的过程主要是调用了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;
}

 从源代码可知,这个函数也就干了2件事情:

  1. 遍历传入的Filter的输入Pin.
  2. 调用Conectdirect连接上一个Filter的输出Pin和步骤1 中遍历出的输入Pin.

所以这个函数将Pin和Filter的连接最终转成了PIN和PIN之间的连接。我们再来看看最终连接函数Conectdirect的源码:

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);
}

从源码可知,最终PIN和PIN之间的连接还是转接到DirectShow的Filter Graph Manager上了。

我们再回到Connect函数当中,看一下调用ConnectFilterDirect函数连接下一个Filter成功以后如何建立下一个Filter的连接。源码截图如下:

 截图可知,如果上一个Filter连接成功,则继续往下连接下一个Filter。

总结:以上就是对Filter Graph Manager中Filter链自动建立过程的详细介绍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值