Write a Transform Filter in C/C++


A transform filter takes media input and alters it in some way. When you design a transform filter, your filter class derives from one of the transform base classes, CTransformFilter, CTransInPlaceFilter, or CVideoTransformFilter, or from the more generic CBaseFilter class. Which base class you choose depends on whether your filter must copy media samples or can transform them in place. See Determine Which Base Classes to Use for more information.

The filter graph manager can use the functions of the base classes your filter derives from to fit your filter into the filter graph and automatically create the connections between your filters. The filter mapper uses your filter's registry information to configure the filter graph.

For the simplest transform filter (for example, one that has only one input pin and one output pin), you can derive your filter class from CTransformFilter and override only the Transform and CheckInputType functions. If you need custom features, you can override additional functions to create your own connections, pins, and other filter features and capabilities. See Override the Base Class Member Functions for more information. You can also derive your filter class from CBaseFilter and override its methods.

This section discusses how to:

Every transform filter must implement code to perform all the preceding steps except access additional interfaces.

For background information about transform filters, see:

For information on building a filter, see Build a Filter or Application with Visual C++ 5.x. For information on registering a filter or making it self-registering, see Register DirectShow Objects.

Define and Instantiate Your Filter Class

The following steps show you how to define and instantiate your filter class.

  1. Determine the base classes from which to derive your filter class (and pin classes, if necessary). Typically, your transform filter class derives from the CTransformFilter, CTransInPlaceFilter, or CVideoTransformFilter transform base classes, or from the more generic CBaseFilter class. If you want to transform video media (especially AVI data), derive from CVideoTransformFilter. If your filter must copy the input media samples, derive from CTransformFilter. If you filter can transform the sampled media in place, derive from CTransInPlaceFilter. If you don't want the simple transform filter support provided in the transform base classes, but prefer to implement your own member functions, derive from CBaseFilter. See Determine Which Base Classes to Use for more information.

    In the following example, the filter class derives from CTransInPlaceFilter.

    
    	class CMyFilter  : public CTransInPlaceFilter
    
  2. Implement the IUnknown interface for your object.

    In the public section of your filter class definition, create an instance of CUnknown, and then call the DECLARE_IUNKNOWN macro.

    
    	public:
    		static CUnknown *WINAPI CreateInstance(LPUNKNOWN punk, HRESULT 
    	*phr);
    		DECLARE_IUNKNOWN;
    
  3. Define your constructor. Also, define your Transform and CheckInputType functions (this does not apply if your filter class is derived from CBaseFilter).

    In the private section of your filter class definition, define your constructor by calling the constructor of the transform filter class you derived from, and then add code to perform the transform and check the input type. For example:

    
    	//Define your constructor by calling the constructor of 
    	//CTransInPlaceFilter
    	CMyFilter(TCHAR *tszName, LPUNKNOWN punk, HRESULT *phr)
    	: CTransInPlaceFilter (tszName, punk, CLSID_MyFilter, phr)
    	{ }
    
    	//Add the transform code
    	HRESULT Transform(IMediaSample *pSample){ 
    	//Transform code here
    	}
    
    	//Add code to check the input type
    	HRESULT CheckInputType(const CMediaType* mtIn) { 
    	//Input checking code here
    	}
    
  4. Implement CreateInstance for your filter object. Typically, CreateInstance calls the constructor of your filter class. For example:
    
    	CUnknown * WINAPI CMyFilter::CreateInstance(LPUNKNOWN punk, HRESULT *phr) {
    	CMyFilter *pNewObject = new CMyFilter(NAME("Description of My Filter"), punk, phr );
    	if (pNewObject == NULL) {
    		*phr = E_OUTOFMEMORY;
    	}
    	return pNewObject;
    	} 
    
  5. Declare a global array g_Templates of CFactoryTemplate objects to inform the default class factory code how to access the CreateInstance function:
    
    	CFactoryTemplate g_Templates[]=
          {   { L"My Filter"          
    		, &CLSID_MyFilter
    		, CMyFilter::CreateInstance //Function called by class factory
    		, NULL
    		, &sudMyFilter } //Address of the AMOVIESETUP_FILTER structure,
    				     //or NULL if no structure exists
    	};
        int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]);  
    

    The g_cTemplates variable defines the number of class factory templates for the filter. Each of these templates provides a link between COM and the filter and are used to create the base object for the filter. At a minimum, the filter has one template that provides the address of its own CreateInstance function, which, when called, creates the base object.

    You can add additional parameters to the CFactoryTemplate templates to add property pages. See the Gargle sample for example code. See Register DirectShow Objects for information about using CFactoryTemplate in registration.

  6. Generate a GUID for your filter object.

    For information about generating GUIDs in general, see "GUID Creation and Optimizations" and "The uuidgen Utility" in the Platform SDK.

    To generate a GUID in Microsoft® Visual C++® 5.x, choose Create GUID from the Tools menu. By default, the GUID is in DEFINE_GUID format, which is the format you want. Click the Copy button. Put the cursor in your source file beneath the include statements, and choose Paste from the Edit menu. The inserted code will look like the following example, except that it will have its own unique number and CLSID. Insert the code before your class definition in the header file or main file.

    
    	// {3FA5D260-AF2F-11d0-AE9C-00A0C91F0841}
    	DEFINE_GUID(CLSID_MyFilter, 
    	0x3fa5d260, 0xaf2f, 0x11d0, 0xae, 0x9c, 0x0, 0xa0, 0xc9, 0x1f, 0x8, 0x41);
    

Override CheckInputType

You must override the CheckInputType function to determine if the proposed input to your filter is valid. (This does not apply to filter classes derived from CBaseFilter.) Your implementation should return an error for media types it can't support. The media types your filter supports are listed in the AMOVIESETUP_MEDIATYPE structure. For example:


	HRESULT CMyFilter::CheckInputType(const CMediaType *pmt)
	{
   	if (pmt->majortype != MEDIATYPE_Video) {
   		return S_FALSE;
   	} 
   		else return S_OK;
   	}

Override the Transform Function

To perform the desired transformation on your input media, your must override the Transform function of your transform base class, or implement your own transformation functions. (This does not apply to filter classes derived from CBaseFilter.) Examples of transformations are MPEG audio/video decoders (see the MPGAudio and MPGVideo samples), visual effects (see the Contrast and EzRGB24 samples), and audio effects (see the Gargle sample).

For example, consider the following code from the Contrast sample. You override the CContrast::Transform function as follows:


	HRESULT CContrast::Transform(IMediaSample *pIn, IMediaSample *pOut)
	{
    		HRESULT hr = Copy(pIn, pOut);
    	if (FAILED(hr)) {
        	return hr;
    	}
    		return Transform(pOut);

	}

The first CContrast::Transform function copies the media data, and then passes the copy (pointed to by the pOut parameter) to a second Transform function. The first Transform function in the Contrast sample is an overloaded function, and the second form of the Transform function performs an in-place transform on the copy of the input media, as shown in the following code fragment.


	HRESULT CContrast::Transform(IMediaSample *pMediaSample)
	{
    	signed char ContrastLevel;
    	ContrastLevel = m_ContrastLevel;	
    	AM_MEDIA_TYPE *pAdjustedType = NULL;

    	pMediaSample->GetMediaType(&pAdjustedType);
  	HRESULT hr = Transform(&AdjustedType, ContrastLevel);
    	pMediaSample->SetMediaType(&AdjustedType);
    	return NOERROR;
	}

Note that the second form of the overloaded Transform function calls a third form of the overloaded Transform function.

Access Additional Interfaces

If your filter implements any interfaces that aren't implemented in the base classes, you must override the NonDelegatingQueryInterface function and return pointers to the implemented interfaces.

  1. In the public section of your filter class definition, declare NonDelegatingQueryInterface:
    
    	STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
    
  2. In the implementation section of your class, implement the NonDelegatingQueryInterface function. For example:
    
    	//Reveal persistent stream and property pages
    	STDMETHODIMP CMyFilter::NonDelegatingQueryInterface(REFIID riid, void **ppv)
    	{
        	if (riid == IID_IPersistStream) {
          	AddRef( );   // Add a reference count. Be sure to release when done.
         	*ppv = (void *) (IPersistStream *) this; 
          	return NOERROR;
       	}
       	else if (riid == IID_ISpecifyPropertyPages) {
         		return GetInterface((ISpecifyPropertyPages *) this, ppv);
       	}  
       	else {
          	return CTransInPlaceFilter::NonDelegatingQueryInterface(riid, ppv);
       	}
    	}
    

Create Registry Information

The filter graph manager uses your filter's registry entries to configure your filter and its connections. You provide your filter's registry information in the AMOVIESETUP_MEDIATYPE, AMOVIESETUP_PIN, and AMOVIESETUP_FILTER structures. Typically, these structures are at the beginning of your filter implementation code. See Register DirectShow Objects for more information about using these structures.

Perform the following steps to provide the three structures you need for filter registration.

  1. Provide the AMOVIESETUP_MEDIATYPEstructure. This structure holds registry information about the media types your filter supports. For example:
    
    	const AMOVIESETUP_MEDIATYPE sudPinTypes = 
                    	{ &MEDIATYPE_Video             // MajorType
                    	, &MEDIASUBTYPE_NULL}  ;       // MinorType
    

    The possible major types are MEDIATYPE_Stream, MEDIATYPE_Video, and MEDIATYPE_Audio.

  2. Provide the AMOVIESETUP_PIN structure. This structure holds registry information about the pins your filter supports.
  3. Provide the AMOVIESETUP_FILTER structure. This structure holds registry information about your filter object: its CLSID, description, number of pins, the pin structure's name, and your filter's merit. The merit controls the order in which the filter graph manager accesses your filter. Possible merit values are MERIT_PREFERRED, MERIT_NORMAL, MERIT_UNLIKELY, and MERIT_DO_NOT_USE. See IFilterMapper::RegisterFilter for a description of merit values. The following code shows an example of an AMOVIESETUP_FILTER structure.
    
    	const AMOVIESETUP_FILTER
    	sudMyFilter = { &CLSID_MyFilter			// clsID
                	, L"My Filter Description"		// strName
                	, MERIT_UNLIKELY				// dwMerit
                	, 2						// nPins
                	, sudpPins };				// lpPin
    

© 1997 Microsoft Corporation. All rights reserved. Terms of Use.