Consumption of a WTV file in DirectShow

Pasha Glozman – Windows Media Center

September 16, 2008

1.   Scope

The intention of this document is to introduce developers to the new WTV file format.  Below you will find a brief discussion on how to consume WTV files in a DirectShow graph for various operations on a WTV file such as trans-coding, playback, editing, etc.

The main audience of this document is developers familiar with C++ and DirectShow concepts.

2.   Overview

WTV file format replaces the previously used DVR-MS file format. Video is encoded using the MPEG-2 standard and audio using MPEG-1 Layer II or Dolby Digital AC-3 (ATSC A/52). The format extends these standards by including metadata about the content and digital rights management.  Access to a WTV file content is made using Stream Buffer Engine DirectShow source filter. Unlike DVR-MS files, produced by previous versions of Media Center and based on ASF file format, WTV files are based on a new file format.

Below sections describe how to access audio/visual content inside WTV file.

3.   Consuming WTV file

The below section provide details on how to consume audio and video from a WTV file using Stream Buffer Engine (SBE) DirectShow filter. There are several basic steps that need to be followed:

3.1   Source filter

The preferred way to add a source filter to a DirectShow graph is to call IGraphBuilder::AddSourceFilter with a name of .WTV file as a parameter.

Graph builder object will automatically find source filter (SBE) according to a file extension (.WTV)

Source filter can also be inserted manually into the graph by creating a SBE COM object with CLSID CLSID_StreamBufferSource and adding this filter to a graph by calling IFilterGraph::AddFilter.

3.2   Connecting to a consuming filter

SBE source filter will expose a variable number of pins, depending on the way the file was authored. Be default there is up to 1 audio pin (major type is MEDIATYPE_Audio) and up to 1 video pin (major type is MEDIATYPE_Video) that is exposed. The overall number and order of exposed audio/video and other pins is not predetermined – the application must enumerate all exposed pins and find the pins (by major type) it wants to consume data from.

3.2.1      Transform filter

If the consuming filter is a transform filter, its input pin(s) should be connected to the appropriate output SBE pin(s) (having the same major types) by calling IGraphBuilder::Connect. This will use the “Intelligent Connect” mechanism in order to insert needed intermediate filters between SBE and consuming transform filter. Please see MSDN for more information regarding “Intelligent Connect” mechanism.

3.2.2      Renderer filter

If the consuming filter is a renderer filter that consumes audio, video or both, it can be inserted into the graph, and then appropriate SBE audio/video output pin(s) should be automatically rendered using IGraphBuilder::Render. Above mentioned approach for transform filters will also work for renderer filters - output SBE pin may be passed along with renderer filter’s input pin to IGraphBuilder::Connect.

3.3   Consuming the content

New features in WTV file format allow dynamic format changes, e.g. video compression type, video resolution, audio compression type, etc. Consuming filter should support dynamic media format changes coming out of SBE, signaling change in video and/or audio media type change (update on media subtype, format block, etc.).

3.3.1      Video handling

If a consuming filter was connected downstream from video decoder, it should support IPin::ReceiveConnection calls during the time graph is running and not rely on default implementation in DirectShow base classes. DirectShow base class implementation is to return and error from IPin::ReceiveConnection on a connected pin.

The default format block for MPEG-2 video stream will always indicate NTSC 720x480 resolution. For TV content having resolution other than NTSC 720x480, SBE will not send dynamic format updates and it is up to downstream filters to parse video stream and discover actual video resolution. When the Microsoft MPEG-2 Video Decoder filter is connected downstream from SBE, its output pin will initially expose the same resolution in its media type format block as the one exposed from SBE (NTSC 720x480 for MPEG-2 video). When graph is pre-rolled (on Pause), it will parse the video steam and issue a video format update on its output pin with the actual video resolution. It will call IPin::ReceiveConnection on a downstream filter with new media type at that time. See MSDN page for IPin::ReceiveConnection method for more information about this method.

4.   Sample

The following pseudo-code shows how to build a WTV -> ASF trans-coding graph.

HRESULT BuildTranscodeGraph()
{
    bool fIntellegentConnect = true;
    // This example builds a simple transcode graph from WTV file
    // to .ASF file using ASF Writer filter.
    // We will use "intellegent connect" mechanism in this example
    // as well as just rendering pins to existing renderer in the graph
    CComPtr <IGraphBuilder> spGraph;
    CComPtr <IBaseFilter>   spSBE;
    CComPtr <IBaseFilter>   spASFWriter;
    CComPtr <IPin>          spSbeVideoOutputPin;
    CComPtr <IPin>          spSbeAudioOutputPin;
    CComPtr <IPin>          spRendererInputPin;
    // Create Graph Builder object.
    CoCreateInstance(
                CLSID_FilterGraph,
                NULL,
                CLSCTX_INPROC_SERVER,
                IID_IGraphBuilder,
                (void**)&spGraph
        );
    // ask Graph Builder to find approproate source filter
    // for .wtv file and insert it into the graph.
    spGraph->AddSourceFilter(sSourceFileName, L"Source Filter", &spSBE);
    // Create consuming ASF Writer filter.
    // Create ASF Writer renderer filter and add it to the graph
    CoCreateInstance(
                CLSID_WMAsfWriter,
                NULL,
                CLSCTX_INPROC_SERVER,
                IID_IBaseFilter,
                (void**)&spASFWriter
        );
    spGraph->AddFilter(spASFWriter, L"ASF Writer");
    // Find SBE output pin having MEDIATYPE_Video major type
    // and MEDIATYPE_Audio major type
    spSbeVideoOutputPin = FindOutputPinByType(spSBE, MEDIATYPE_Video);
    spSbeAudioOutputPin = FindOutputPinByType(spSBE, MEDIATYPE_Audio);
    // as an example we will use both techniques
    if (fIntellegentConnect)
    {
        spRendererInputPin = FindInputPinByType(spASFWriter, MEDIATYPE_Video);
        // Connect the pins , using intellegent connect.
        spGraph->Connect(spSbeVideoOutputPin, spRendererInputPin);
        spRendererInputPin = FindInputPinByType(spASFWriter, MEDIATYPE_Audio);
        // Connect the pins , using intellegent connect.
        spGraph->Connect(spSbeAudioOutputPin, spRendererInputPin);
    }
    else
    {
        spGraph->Render(spSbeVideoOutputPin);
        spGraph->Render(spSbeAudioOutputPin);
    }
    return S_OK;
}

5.   Next steps

Stream Buffer Engine Source filter supports all documented interfaces with WTV files.

Please go to https://msdn.microsoft.com/en-us/library/ms787604(VS.85).aspx to learn more about interfaces supported by Stream Buffer Engine Source. These interfaces include the following:

IAMFilterMiscFlags

IBaseFilter

IFileSourceFilter

ISpecifyPropertyPages

IStreamBufferMediaSeeking

IStreamBufferMediaSeeking2

IStreamBufferInitialize

IStreamBufferSource.