Implement a DirectShow Filter (Compact 2013)

3/26/2014

In your DirectShow filter, you must implement a number of interfaces and methods even for a minimal implementation. To reduce the amount of code that you have to write, the DirectShow base classes provide a large amount of common code. We recommend that you use the base classes wherever possible.

Parts of a Filter

The main functional parts of a filter include the input pin, output pin, and the logic for the filter itself. However, remember that a particular filter may have no input pin or no output pin, for example, in the case of a source filter and a renderer filter, respectively. Typically, you implement a class for each of these parts while deriving from some corresponding base class.

The following Unified Modeling Language (UML) class diagram shows the basics of a sample implementation of a decoder filter. Note that it is not a comprehensive class diagram; it shows the layout of only some of the classes and interfaces that are discussed in the following sections in this topic. The shaded boxes contain the classes that the decoder developer implements. In your actual implementation, there will certainly be additional members and methods defined in each class, and possibly additional classes that you have to include for your specific decoder. The unshaded boxes are the base classes that implement several necessary interfaces, which are not shown in the diagram.

UML Diagram for DirectShow Classes

The steps that follow provide details about how to implement the classes in a DirectShow filter.

  • Step 1: Implement the decoder filter class
  • Step 2: Implement the pin classes to connect the filters
  • Step 3: Allocate a buffer between the demultiplexer and the decoder
  • Step 4: Specify the types of media streams

Step 1: Implement the decoder filter class

Your DirectShow filters must implement the IBaseFilter Interface. The CBaseFilter Class has already implemented the interface, so derive your filter’s main filter implementation class from CBaseFilter. Because your filter will create pins for input and output, override the CBaseFilter::GetPin and CBaseFilter::GetPinCount methods.

The following example code shows a basic sample filter class for a decoder filter:

// Filter class for sample decoder filter
class CFilter : public CBaseFilter
{
public:
    DECLARE_IUNKNOWN;
    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
    
    // CBaseFilter overridden functions
    CBasePin *GetPin(int n);
    int GetPinCount();

    STDMETHODIMP Stop();
    STDMETHODIMP Pause();
    STDMETHODIMP Run(REFERENCE_TIME tStart);
   
    CFilter(LPUNKNOWN lpunk, HRESULT *phr);
    ~CFilter();

    // Other methods
    …

private:
    CInputPin *m_InputPins[];
    COutputPin *m_OutputPins[];
}

Step 2: Implement the pin classes to connect the filters

After you implement the decoder filter class, you have to connect the demultiplexer filter to the decoder filter using pins. All protocols of connection negotiation in a DirectShow filter graph apply to Windows Embedded Compact 2013. For more information, see How Filters Connect on MSDN.

Create the pins to connect the filters

In DirectShow, all pins must implement the IPin Interface. The demultiplexers derive their output pin classes from the CBaseOutputPin Class, which derives from the CBasePin Class. Therefore, you also need to adhere to the implementation requirements for those base classes.

Both the input and output pin must override the CBasePin::Active and CBasePin::Inactive methods, which are called by the Filter Graph Manager before and after the streaming. These methods allocate and deallocate resources required for streaming.

The following example code shows a basic input pin class:

class CInputPin : public CBasePin
{
public:
    DECLARE_IUNKNOWN;
    HRESULT SetMediaType(const CMediaType *pmt);
    HRESULT CheckMediaType(const CMediaType *pMediaType);
    HRESULT GetMediaType(int iPosition,CMediaType *pMediaType);
    HRESULT CompleteConnect(IPin* pPin);
    HRESULT BreakConnect(void);
    HRESULT BeginFlush();
    HRESULT EndFlush();
    HRESULT Active();
    HRESULT Inactive();
    HRESULT ReadBytes(QWORD offset, DWORD size, BYTE *pDestination);

    CInputPin(HRESULT *phr, CFilter *pParent, LPCWSTR pPinName);
    ~CInputPin();
    …
}

The following example code shows a basic output pin class:

class COutputPin : public CBaseOutputPin
{
public:
    DECLARE_IUNKNOWN;
    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
    HRESULT CheckMediaType(const CMediaType *pMediaType);
    HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
    HRESULT CompleteConnect(IPin *pPin);
    HRESULT BreakConnect();
    HRESULT BeginFlush();
    HRESULT EndFlush();
    HRESULT Active();
    HRESULT Inactive();

    COutputPin(HRESULT *phr, CFilter *pParent, LPCWSTR pPinName);
    ~COutputPin();
    …
}

Set up the decoder pin to receive samples

The decoder must implement the IMemInputPin Interface on its input pin to connect to the upstream filter. The easiest way to implement this interface is to derive the input pin from the CBaseInputPin Class which adds support for IMemInputPin.

IMemInputPin::Receive and IMemInputPin::ReceiveMultiple are the two functions defined to receive samples from upstream. The first function is used to receive a single sample per call, and the second function is used to receive multiple samples. The decoder should override CBaseInputPin::Receive to receive samples from the upstream filter. It can also override and implement the CBaseInputPin::ReceiveMultiple function to receive batches of samples. If not overridden, the default implementation will call the CBaseInputPin::Receive function multiple times. Each sample is a COM object that exposes the IMediaSample Interface, which is reference counted. Therefore, you should release the sample as soon as the decoder has finished processing it so that it can be reused by the pipeline for subsequent use.

Step 3: Allocate a buffer between the demultiplexer and the decoder

To pass data between the demultiplexer and decoder filter, you have to set up a buffer.

An allocator object is negotiated between the output pin of the demultiplexer and the input pin of the decoder. You use this object to allocate buffers that hold the samples as they are passed downstream to the decoder. Allocators in DirectShow must implement the IMemAllocator Interface.

CBaseInputPin::GetAllocator provides a default allocator of type CMemAllocator Class. The default allocator works in most cases. However, if the hardware decoder on the device has requirements, such as using a particular region of memory for better performance, the decoder filter must have its own allocator class that implements the IMemInputPin Interface. A simple way to implement a custom allocator is to derive it from the CBaseAllocator Class. In this custom allocator class, the decoder filter must manage the special region of the memory. The decoder filter must also override IMemInputPin::GetAllocator on its input pin and return an instance of the allocator class.

Step 4: Specify the types of media streams

When you connect DirectShow pins to each other, you must provide information about the type of media that will flow from the output pin to the input pin. The type of the media is described through the AM_MEDIA_TYPE data type, which has the following structure:

typedef struct _MediaType{
  GUID majortype;
  GUID subtype;
  BOOL bFixedSizeSamples;
  BOOL bTemporalCompression;
  ULONG lSampleSize;
  GUID formattype;
  IUnknown* pUnk;
  ULONG cbFormat;
  BYTE __RPC_FAR* pbFormat;
} AM_MEDIA_TYPE;

Among the fields of AM_MEDIA_TYPE, the majortype field is almost always MEDIATYPE_Video or MEDIATYPE_Audio, although some demultiplexers can support other types, such as subtitle. The subtype field depends on the encoding or format of the stream. Typically, you derive the subtype from a four-character code (called FourCC) that defines the encoding type. For information about how to create a GUID from a FourCC, see the FOURCCMap Class. For more information about media types, see DirectShow Media Types.

The pbFormat pointer links to another data structure with additional format specific information that you will need for decoding the media. The cbFormat field specifies the length of that structure. The formattype field identifies the GUID specifying the format type.

The following tables describe the media types and subtypes and the type of the format structures as implemented in Windows Embedded Compact 2013. The comments for specific formats may be useful for implementing the decoder. Note that the decoder must check the formattype and cbFormat fields to ensure the correct type and length before interpreting data pointed to by pbFormat. Also, it is common not to populate all of the fields of the format structures. You can find the details of each format structure by looking up the corresponding documentation on MSDN.

MPEG-1 media types

The following two tables list the MPEG-1 media subtypes for media types MEDIATYPE_Audio and MEDIATYPE_Video, respectively.

MPEG-1 MEDIATYPE_Audio

Media subtype

Format structure

Comment

MEDIASUBTYPE_MPEG1Packet

MPEG1WAVEFORMAT

None

MPEGLAYER3WAVEFORMAT

None

MEDIASUBTYPE_MPEG1Payload

MPEG1WAVEFORMAT

None

MPEGLAYER3WAVEFORMAT

None

MPEG-1 MEDIATYPE_Video

Media subtype

Format structure

Comment

MEDIASUBTYPE_MPEG1Packet

MPEG1VIDEOINFO

None

MEDIASUBTYPE_MPEG1Payload

MPEG1VIDEOINFO

None

MPEG-2 media types

The following two tables list the MPEG-2 media subtypes for media types MEDIATYPE_Audio and MEDIATYPE_Video, respectively.

MPEG-2 MEDIATYPE_Audio

Media subtype

Format structure

Comment

MEDIASUBTYPE_MPEG1AudioPayload

MPEG1WAVEFORMAT

None

MEDIASUBTYPE_MPEG2_AUDIO

WAVEFORMATEX

None

MEDIASUBTYPE_DOLBY_AC3

WAVEFORMATEX

AC3 audio from MPEG-2 container.

MEDIASUBTYPE_DVD_LPCM_AUDIO

WAVEFORMATEX

None

MEDIASUBTYPE_MPEG_ADTS_AAC

WAVEFORMATEX

None

MPEG-2 MEDIATYPE_Video

Media subtype

Format structure

Comment

MEDIASUBTYPE_MPEG1Video

MPEG1VIDEOINFO

None

MEDIASUBTYPE_MPEG2_VIDEO

MPEG2VIDEOINFO

None

MEDIASUBTYPE_H264

VIDEOINFOHEADER

H.264 content in MPEG-2 TS container.

MEDIASUBTYPE_WVC1

VIDEOINFOHEADER

None

MPEG-4 media types

The following tables list the MPEG-4 media subtypes for media types MEDIATYPE_Audio and MEDIATYPE_Video, respectively.

MPEG-4 MEDIATYPE_Audio

Media subtype

Format structure

Comment

MEDIASUBTYPE_MPEG_ADTS_AAC

WAVEFORMATEX

AAC audio from MPEG-4 container. Each audio sample is prepended with a 7-byte Audio Data Transport Stream (ADTS) frame header.

MEDIASUBTYPE_DOLBY_AC3

WAVEFORMATEX

AC3 audio from MPEG-4 container.

MPEG-4 MEDIATYPE_Video

Media subtype

Format structure

Comment

MEDIASUBTYPE_MP4V

MPEG1VIDEOINFO

MPEG-4 Part 2 video from MPEG-4 container.

VIDEOINFOHEADER2

MEDIASUBTYPE_h264

VIDEOINFOHEADER2

MPEG-4 Part 10 (also known as H.264 or AVC) video from MPEG-4 container. Video frames have start codes.

ASF media types

The following two tables list the Advanced Systems Format (ASF) media subtypes for media types MEDIATYPE_Audio and MEDIATYPE_Video, respectively.

ASF MEDIATYPE_Audio

Media subtype

Format structure

Comment

(Content Dependent)

WAVEFORMATEX

Subtype depends on the stream. Can be any subtype from ASF spec.

ASF MEDIATYPE_Video

Media subtype

Format structure

Comment

(Content Dependent)

VIDEOINFOHEADER

Subtype depends on the stream. Can be any subtype from ASF spec.

VIDEOINFOHEADER2

See Also

Concepts

DirectShow Decoder Filter for Windows Embedded Compact