How To: Wrap an existing DirectShow filter with a private video source filter (COM aggregation)

See beginning in microsoft.public.win32.programmer.directx.video newsgroup.

This sample is demonstrating COM aggregation to embed an existing filter an re-expose it as a new filter having inner filter pre-initialized.

The Visual Studio C++.NET 2008 projects contains a DirectShow filter class that registers itself under Video Capture Sources category and embeds File Source (Async) Filter inside initialized to stream clock.avi file from Windows directory.

The code of interest is in Filter.h. CFilter class is the implementation of the DirectShow filter.

CFilter registers itself as a filter through static UpdateRegistry method, which overrides stock DECLARE_REGISTRY_RESOURCEID macro and adds IFilterMapper2 interface calls to register/unregister the filter.

To take an advantage of aggregating another object we need DECLARE_PROTECT_FINAL_CONSTRUCT macro to be able to instantiate inner (that is aggregated) object on the very start (we cannot do this right in constructor), DECLARE_GET_CONTROLLING_UNKNOWN macro to declare member menthod that exposes controlling unknown.

In a FinalConstruct method we instantiate aggregated object (note GetControllingUnknown() argument which indicates instantiating as aggregated):

ATLENSURE_SUCCEEDED(m_pInnerUnknown.CoCreateInstance(CLSID_AsyncReader, GetControllingUnknown()));

and initialize the object through IFileSourceFilter::Load. The inner object is ready and we are to expose its functionality as if implemented natively. We need to update object’s COM MAP for this:

BEGIN_COM_MAP(CFilter)
	COM_INTERFACE_ENTRY(IFilter)
	// NOTE: We still implement IDispatch through IFilter but we hide it to hopefully expose inner filter's IDispatch
	//COM_INTERFACE_ENTRY(IDispatch)
	// NOTE: We are hiding inner filter IFileSourceFilter to avoid GraphEdit prompts for file path on filter insertion
	COM_INTERFACE_ENTRY_NOINTERFACE(IFileSourceFilter)
	COM_INTERFACE_ENTRY_AGGREGATE_BLIND(m_pInnerUnknown)
END_COM_MAP()

COM_INTERFACE_ENTRY_AGGREGATE_BLIND will expose all inner object interfaces (except mentioned higher on the map) to the outside world. And we explicitly suppress IFileSourceFilter through COM_INTERFACE_ENTRY_NOINTERFACE to prevent GraphEdit from discovering it and popping up a file dialog to select a source file.

Source code: DirectShowWrapperSourceFilterSample.01.zip (note that Release build binary is included)

19 Replies to “How To: Wrap an existing DirectShow filter with a private video source filter (COM aggregation)”

  1. hi,Alax
    I compiled sucessfully with your src code On Vista32 Sp1+ VC2008, but run with a graphedit error message when try insert your source filter:
    The Filter Could not be created. Resource used by this filter may be already be in use.

    Return code: 0x80070002

    after that, I did the same thing on another Comp which has Xp Sp2 + VC2008 SP1,
    ,this time it’s Ok to display the clock with pulsed sound.

    RGS!

  2. Hello ..thanks for posting this …I am successfully able to compile and see the filter in graph edit and play clock.avi file.

    Now in WMEncoder, I can see this ‘Alax.InfoWrapper Source Sample’ entry in the Video source. But when I select this, it is showing error dialog

    ‘One or more codecs required to open this content could not be found. (0xC00D1B83)’

    I would like this filter to work as a source in WMEncoder and stream the video thru the filter.

    Please help…

  3. One or more codecs required to open this content could not be found

    The error is quite descriptive but I am not sure how it is related to this source filter. My guess would be however that this kind of sample is nicely illustrating COM aggregation but AVI file source is quite far from real video source.

    File source is delivering raw file contents. OK, it may supply data with AVI subtype, but that’s all. On the other hand even simplest video source is expected to at least deliver video samples with media type of MEDIATYPE_Video. I think it is the cause. WMEncoder just does not see what it can do with this lind of data. And a video source is also generally expected to implement IAMStreamConfig, so what is actually required is to take a better base for aggregation.

  4. Alax

    I am new to DirectX Filters. Are you telling to use different base filter instead of AsyncReader, if so, which filter?

    WMEncoder might be using the same filters requires to play avi, i.e AsyncReader->AVISplitter->ColorSpaceConverter->Renderer or streamer.

    Thanks again..

  5. I am new to DirectX Filters. Are you telling to use different base filter instead of AsyncReader, if so, which filter?

    WMEncoder might be using the same filters requires to play avi, i.e AsyncReader->AVISplitter->ColorSpaceConverter->Renderer or streamer.

    There was no other suitable filter to wrap, so I wrapped that AVI. There should have been a proper inner filter for a solid demo. Actually I still have plans to make one but there has been no time so far.

    If such filter wrapped AsyncReader and AVISplitter together and showed these two as a single source filter with an output pin corresponding to splitter’s video output pin – this would work, including WMEcnoder. But with COM aggregation you can only embed one filter, not the chain.

    So perhaps stay tuned here and there will be another sample, compatible with Windows Media Encoder

    P.S. Filters are DirectShow, not DirectX. DirectShow now is a part of Windows/Platform SDK, that is “the core”. DirectX is not.

  6. “But with COM aggregation you can only embed one filter, not the chain.”

    Hi Alax:
    Can you give me some advice to wrap a filter chain in a sigle directshow filter?

    In my project, i want play dvb by windows media player. I think i should write a filter which contains NetworkProvoder–>tuner–>capturer–>mpeg2demux, and assotiate it with a cumtom file extension(.dvb). Is that right and what is your advice for that?

    thanks.

  7. Can you give me some advice to wrap a filter chain in a sigle directshow filter?

    You need to create a full filter which internally embeds graph of your interest. For source filter that you mention, you will have the chain you mention + some sink renderer filter (some prefer Sample Grabber Filter + Null Renderer for this purpose) and each time this renderer generates a media sample, you delivery it from output pin of your top level filter.

    Yes, you can associate your filter with a file extension or a protocol, so that WMP pick it up.

    Also, I see your post on MSDN Forums: http://social.msdn.microsoft.com/Forums/en-US/windowsdirectshowdevelopment/thread/c651939d-c530-4a0a-a0b4-7bb32b70b847/

  8. Hi Roman:

    I wrote a DVBSrcFilter(a fake src) derived from CBaseFilter with a output pin derived from CBaseOutputPin. It do nothing and just for connecting to downstream filter

    filter information code like these:
    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    const AMOVIESETUP_MEDIATYPE sudOpPinTypes =
    {
    &MEDIATYPE_Video,
    &MEDIASUBTYPE_MPEG2_VIDEO
    };

    const AMOVIESETUP_PIN sudOpPin =

    { L”VideoOutput” // strName
    , FALSE // bRendered
    , TRUE // bOutput
    , FALSE // bZero
    , FALSE // bMany
    , &CLSID_NULL // clsConnectsToFilter
    , NULL // strConnectsToPin
    , 1 // nTypes
    , &sudOpPinTypes } ; // lpTypes;

    const AMOVIESETUP_FILTER DVBSrcFilter =
    { &CLSID_DVBSrcFilter // clsID
    , L”DVBSrcFilter” // strName
    , MERIT_UNLIKELY // dwMerit
    , 1 // nPins
    , &sudOpPin };

    #endif
    //
    // Object creation template
    //
    CFactoryTemplate g_Templates[1] = {
    { L”DVBSrcFilter”
    , &CLSID_DVBSrcFilter
    , CDVBSrcFilter::CreateInstance
    , NULL
    , &DVBSrcFilter }
    };

    int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);

    <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ffdshow Video Decoder in graphedit,
    visual studio pop warning like this:

    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    Windows has triggered a breakpoint in graphedt.exe.

    This may be due to a corruption of the heap, and indicates a bug in graphedt.exe or any of the DLLs it has loaded.

    The output window may have more diagnostic information
    <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>
    These filters cannot agree on a connection. Verify type compatibility of input pin and output pin. No combination of intermediate filters could be found to make the connection
    (Return code: 0x80040217)
    <<<<<<<<<Sample Grabber
    It was ok;

    I’m new for directshow development, please help me, thanks a lot.

  9. Hi Roman:
    I am sorry for above puzzel comments. I cannot edit comment after submit, so i post it again as following:

    I wrote a DVBSrcFilter(a fake src) derived from CBaseFilter with a output pin derived from CBaseOutputPin. It do nothing and just for connecting to downstream filter

    filter information code like this:

    ===================begin==============================

    const AMOVIESETUP_MEDIATYPE sudOpPinTypes =
    {
    &MEDIATYPE_Video,
    &MEDIASUBTYPE_MPEG2_VIDEO
    };

    const AMOVIESETUP_PIN sudOpPin =

    { L”VideoOutput” // strName
    , FALSE // bRendered
    , TRUE // bOutput
    , FALSE // bZero
    , FALSE // bMany
    , &CLSID_NULL // clsConnectsToFilter
    , NULL // strConnectsToPin
    , 1 // nTypes
    , &sudOpPinTypes } ; // lpTypes;

    const AMOVIESETUP_FILTER DVBSrcFilter =
    { &CLSID_DVBSrcFilter // clsID
    , L”DVBSrcFilter” // strName
    , MERIT_UNLIKELY // dwMerit
    , 1 // nPins
    , &sudOpPin };

    #endif
    //
    // Object creation template
    //
    CFactoryTemplate g_Templates[1] = {
    { L”DVBSrcFilter”
    , &CLSID_DVBSrcFilter
    , CDVBSrcFilter::CreateInstance
    , NULL
    , &DVBSrcFilter }
    };

    int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);

    ========================end=====================

    When i attemp to connect DVBSrcFilter –>ffdshow Video Decoder in graphedit,
    visual studio pop warning like this:

    ========================begin===========================
    Windows has triggered a breakpoint in graphedt.exe.

    This may be due to a corruption of the heap, and indicates a bug in graphedt.exe or any of the DLLs it has loaded.

    The output window may have more diagnostic information
    =======================end===============

    if chose continue, pop something like this:

    ========================begin=================================
    These filters cannot agree on a connection. Verify type compatibility of input pin and output pin. No combination of intermediate filters could be found to make the connection
    (Return code: 0x80040217)
    ========================end=====================================

    I verified that output pin and input pin both have the media type of MEDIATYPE_Video/MEDIASUBTYPE_MPEG2_VIDEO, why cannot they connect?

    ps :
    if connect like this :
    DVBSrcFilter –>Sample Grabber
    It was ok;

    I’m new for directshow development, please help me, thanks a lot.

  10. Hi Roman:

    The first warning:
    ========================begin===========================
    Windows has triggered a breakpoint in graphedt.exe.

    This may be due to a corruption of the heap, and indicates a bug in graphedt.exe or any of the DLLs it has loaded.

    The output window may have more diagnostic information
    =======================end===============
    may be caused by DVBworld software :M2AToM1A.ax.
    I have rename it to M2AToM1A.ax.renamed, and this warning was handled.

    But, the second warning still pop.

  11. “This may be due to a corruption of the heap, and indicates a bug in…” indicates that memory heap is damaged, e.g. something wrote to a memory block out side of allocated size and destroyed fields responsible for memory heap integrity. It is likely that it is your filter, but you provided only registration code so I have no ideas as for what code could destroy the heap. There is no other way but find it and fix it so that memory is properly used.

  12. Roman:
    Thanks for your reply.
    Here is my complete code.

    /**********
    DVBSrcFilter.h
    ***********/

    #include
    #include
    #include
    #include

    // {68DD561A-E069-4c75-BB76-6D2FE30234D1}
    DEFINE_GUID(CLSID_DVBSrcFilter,
    0x68dd561a, 0xe069, 0x4c75, 0xbb, 0x76, 0x6d, 0x2f, 0xe3, 0x2, 0x34, 0xd1);

    class CVideoOutputPin ;
    class CDVBSrcFilter ;

    class CVideoOutputPin :
    public CBaseOutputPin
    {
    public :

    CVideoOutputPin (
    IN TCHAR * szName,
    IN CBaseFilter * pFilter,
    IN CCritSec * pLock,
    OUT HRESULT * pHr,
    IN LPCWSTR pszName
    ) ;

    ~CVideoOutputPin () ;

    HRESULT
    GetMediaType (
    IN int iPosition,
    OUT CMediaType * pmt
    ) ;

    HRESULT
    CheckMediaType (
    IN const CMediaType * pmt
    ) ;

    HRESULT
    DecideBufferSize (
    IN IMemAllocator *,
    OUT ALLOCATOR_PROPERTIES *
    ) ;
    } ;

    class CDVBSrcFilter : public CBaseFilter
    {

    CCritSec m_crtFilterLock ; // filter lock
    CVideoOutputPin * m_pVideoOutput ; // video output pin

    public :

    CDVBSrcFilter (
    IN TCHAR * tszName,
    IN LPUNKNOWN punk,
    OUT HRESULT * phr
    ) ;

    ~CDVBSrcFilter (
    ) ;

    void LockFilter () { m_crtFilterLock.Lock () ; }
    void UnlockFilter () { m_crtFilterLock.Unlock () ; }

    // ——————————————————————–
    // CBaseFilter methods

    int GetPinCount ()
    { return 1 ; }

    CBasePin * GetPin (IN int Index) ;

    DECLARE_IUNKNOWN
    STDMETHODIMP NonDelegatingQueryInterface (
    IN REFIID riid,
    OUT void ** ppv
    )
    {
    return CBaseFilter::NonDelegatingQueryInterface(riid, ppv);

    }

    static
    CUnknown *
    CreateInstance (
    IN LPUNKNOWN punk,
    OUT HRESULT * phr
    ) ;
    } ;

    /*************
    DVBSrcFilter.cpp
    **************/
    #include “DVBSrcFilter.h”

    const AMOVIESETUP_MEDIATYPE sudOpPinTypes =
    {
    &MEDIATYPE_Video,
    &MEDIASUBTYPE_MPEG2_VIDEO
    };

    const AMOVIESETUP_PIN sudOpPin =

    { L”VideoOutput” // strName
    , FALSE // bRendered
    , TRUE // bOutput
    , FALSE // bZero
    , FALSE // bMany
    , &CLSID_NULL // clsConnectsToFilter
    , NULL // strConnectsToPin
    , 1 // nTypes
    , &sudOpPinTypes } ; // lpTypes;

    const AMOVIESETUP_FILTER DVBSrcFilter =
    { &CLSID_DVBSrcFilter // clsID
    , L”DVBSrcFilter” // strName
    , MERIT_UNLIKELY // dwMerit
    , 1 // nPins
    , &sudOpPin };

    //
    // Object creation template
    //
    CFactoryTemplate g_Templates[1] = {
    { L”DVBSrcFilter”
    , &CLSID_DVBSrcFilter
    , CDVBSrcFilter::CreateInstance
    , NULL
    , &DVBSrcFilter }
    };

    int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);

    STDAPI DllRegisterServer()
    {
    return AMovieDllRegisterServer2(TRUE);
    }

    STDAPI DllUnregisterServer()
    {
    return AMovieDllRegisterServer2(FALSE);
    }

    //
    // DllEntryPoint
    //
    extern “C” BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);

    BOOL APIENTRY DllMain(HANDLE hModule,
    DWORD dwReason,
    LPVOID lpReserved)
    {
    return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved);
    }

    CVideoOutputPin::CVideoOutputPin (
    IN TCHAR * szName,
    IN CBaseFilter * pFilter,
    IN CCritSec * pLock,
    OUT HRESULT * pHr,
    IN LPCWSTR pszName
    ) : CBaseOutputPin (szName,
    pFilter,
    pLock,
    pHr,
    pszName
    )
    {
    }

    CVideoOutputPin::~CVideoOutputPin (
    )
    {
    }

    HRESULT
    CVideoOutputPin::GetMediaType (
    IN int iPosition,
    OUT CMediaType * pmt
    )
    {
    HRESULT hr ;

    if (iPosition == 0) {
    ASSERT (pmt) ;
    pmt -> InitMediaType () ;

    pmt -> SetType (& MEDIATYPE_Video) ;
    pmt -> SetSubtype (& MEDIASUBTYPE_MPEG2_VIDEO) ;

    hr = S_OK ;
    }
    else {
    hr = VFW_S_NO_MORE_ITEMS ;
    }

    return hr ;
    }

    HRESULT
    CVideoOutputPin::CheckMediaType (
    IN const CMediaType * pmt
    )
    {
    HRESULT hr ;

    ASSERT (pmt) ;

    if (pmt -> majortype == MEDIATYPE_Video &&
    pmt -> subtype == MEDIASUBTYPE_MPEG2_VIDEO) {

    hr = S_OK ;
    }
    else {
    hr = S_FALSE ;
    }

    return hr ;
    }

    HRESULT
    CVideoOutputPin::DecideBufferSize (
    IN IMemAllocator * pIMemAllocator,
    OUT ALLOCATOR_PROPERTIES * pProp
    )
    {
    HRESULT hr ;

    ALLOCATOR_PROPERTIES pActual;

    hr = pIMemAllocator->SetProperties (pProp, &pActual);

    if (hr != S_OK) {
    printf (“%s: SetProperties return hr(%d)\n”, __FUNCTION__, hr);
    }

    return hr;
    }

    CDVBSrcFilter::CDVBSrcFilter (
    IN TCHAR * tszName,
    IN LPUNKNOWN punk,
    OUT HRESULT * phr
    ) : CBaseFilter (
    tszName,
    punk,
    & m_crtFilterLock,
    CLSID_DVBSrcFilter
    ),
    m_pVideoOutput (NULL)

    {
    // instantiate the output pin
    m_pVideoOutput = new CVideoOutputPin (
    NAME (“xxVideoOutputPin”),
    this,
    & m_crtFilterLock,
    phr,
    L”yyMPEG-2 Video”
    ) ;
    if (m_pVideoOutput == NULL ||
    FAILED (* phr)) {

    (* phr) = (FAILED (* phr) ? * phr : E_OUTOFMEMORY) ;
    return ;
    }
    }

    CDVBSrcFilter::~CDVBSrcFilter ()
    {
    delete m_pVideoOutput ;
    }

    CBasePin *
    CDVBSrcFilter::GetPin (
    IN int Index
    )
    {
    CBasePin * pPin ;

    LockFilter () ;

    if (Index == 0) {
    pPin = m_pVideoOutput ;
    }
    #ifdef USE_AUDIO
    else if (Index == 1) {
    pPin = m_pAudioOutput;

    }
    #endif
    else {
    pPin = NULL ;
    }

    UnlockFilter () ;

    return pPin ;
    }

    CUnknown * WINAPI CDVBSrcFilter::CreateInstance (
    IN LPUNKNOWN punk,
    OUT HRESULT * phr
    )
    {

    ASSERT(phr);
    return new CDVBSrcFilter (
    NAME (“CDVBSrcFilter”),
    punk,
    phr
    ) ;
    }

  13. My CDVBSrcFilter just has 1 output pin for mpeg2 video, m_pAudioOutput is not a member of CDVBSrcFilter. So, “don’t having USE_AUDIO defined” is what i expect.

    I modified the code and it is now:

    CBasePin *
    CDVBSrcFilter::GetPin (IN int Index)
    {

    CBasePin * pPin ;

    LockFilter () ;

    if (Index == 0) {
    pPin = m_pVideoOutput ;
    }
    else {
    pPin = NULL ;
    }

    UnlockFilter () ;

    return pPin ;
    }

  14. Follow-up from bikekiller:

    I did some debug and found that ffdshow video decoder dont propose any prefered MediaType, it let the upstream filter to suggest connection MediaType and check this type in ReceiveConnect() method of its input pin. I set DVBSrcFilter output pin’s Format Type to FORMAT_MPEG2Video and fill the Format Structure, at this time, connection succeeded.

Leave a Reply