Microsoft DirectX 8.0

Creating ASF Files in DirectShow

The ASF File Format and the Windows Media Format

This section describes how to use the Microsoft® DirectShow® ASF Writer filter to create files in Advanced Streaming Format (ASF). ASF is a file format that can contain any type of compressed or uncompressed data. One advantage of ASF over formats such as AVI and WAV is that data in ASF files is time-stamped. Microsoft® Windows Media™ Format files are ASF files that use certain codecs as specified in the Windows Media Format Software Development Kit (SDK). These files use the extensions "wma" for Windows Media Audio files and "wmv" for Windows Media Video files. For more information about Windows Media Format, see the Windows Media Technologies home page.

The DirectShow SDK and the Windows Media Format SDK

DirectShow and the Windows Media Format SDK offer complementary solutions for writing applications that create and play back Windows Media Format streams. For more information, read the white paper at http://www.microsoft.com/hwdev/vidcap/WM_DS.htm.

The ASF Writer filter accepts any number of input streams and creates an ASF file. The filter uses the Windows Media Format SDK to convert uncompressed audio or video files to Windows Media Format for storage in an ASF file, as the Windows Media Format specification requires. You can bypass the filter's compression mechanism when you wish to use other codecs or save uncompressed data in ASF format.

You can use the ASF Writer in various scenarios including digital video (DV) capture, audio recompression, and conversion of Audio-Video Interleaved (AVI) or MPEG multimedia files for network streaming. This filter provides the only way to create Microsoft® Windows Media™ Audio (WMA) and Windows Media Video (WMV) files in DirectShow®. The filter can also create Windows Media Format files that are secured by Digital Rights Management (DRM).

The ASF Writer filter can be used only in conjunction with the Windows Media Format SDK. To develop applications that use this filter, you must have the Windows Media Format SDK installed on your development machine. When you download the SDK, Microsoft will send you a software certificate by e-mail. This certificate will enable your application to unlock that SDK, and to read or write any Windows Media Audio or Windows Media Video file that does not have Digital Rights Management (DRM) (without this certificate, an application cannot add the ASF Writer to a filter graph). To read or write DRM-enabled files, you need to obtain a different certificate from Microsoft, as described on the Windows Media Technologies Web site.

This topic contains the following sections:

Configuring the ASF Writer

When the ASF Writer filter is created, it is configured automatically with a default system profile. System profiles are discussed in detail in the Windows Media Format SDK; basically, they describe various attributes of a Windows Media Format file such as bit rate, number and type of streams, compression quality, and so on. If the default profile is not suitable, an application can modify it using the filter's IConfigAsfWriter interface methods. The filter uses the profile to determine what kind of Windows Media Format file to write, how many input pins it must set up, and what media types they can accept. The filter must be added to the graph before it can be configured, and it must be configured before it can be connected to upstream filters.

There are various ways to specify a profile to the ASF Writer. The ASF Writer has a property page that lists the profiles available on the system. This list shows what kinds of streams the filter can create based on the specified input streams. The profile descriptions appear as human-readable strings such as "250 Video—Creates video content for clients with 250 Kbps network connections, such as cable modems, DSL, and LAN connections" or "128 CD Quality Audio—creates CD transparency quality audio content for clients with high-speed Internet and LAN connections. Also suited for local playback." An application can expose this property page to the end user to enable the user to select a profile. For information on how to display a filter's property page, see Displaying a Filter's Property Pages. If you don't want to use the property page, you can get the list of profiles directly by using the Windows Media Format SDK and present these to the end user in your user interface. You can also create a custom profile and pass it directly to the filter, or pass the filter a globally unique identifier (GUID) for a profile from the Wmsysprf.h header file in the Windows Media Format SDK.

The filter allows profiles to be reset while its input pins are connected, but if the profile is changed—for example, from a one-input audio stream profile to a two-input audio and video profile—only the previously connected audio stream is reconnected.

By default, the ASF Writer expects uncompressed audio and video streams on its input pins, and handles the compression by using the Windows Media Format SDK. Because any data format can be converted to ASF, there might be occasions when you want to convert a data format for which the Windows Media Format SDK does not have an appropriate codec. (The resulting file will be a perfectly valid ASF file but it will not qualify as a WMA or WMV file.) In such cases, you need to compress the stream in the filter graph upstream of the ASF Writer and bypass the ASF Writer compression mechanism. To do this, ensure that the media type of the upstream output pin that connects to the ASF Writer exactly matches the output stream format used in the current ASF Writer profile. When the ASF Writer detects that the input streams are identical to the profile it was configured with, it converts the data to ASF without attempting to compress it further.

The ASF Writer always does the compression internally whenever filters are inserted automatically between the source filter and the ASF Writer (that is, by calling the IGraphBuilder::Connect) method. Therefore, to bypass the ASF Writer compression mechanism, you must create the entire graph manually, as described in the following section.

Because the Windows Media Format SDK requires an audio stream to work, the ASF Writer must always have an input audio pin, even if it is for a dummy stream—that is, a muted, low-bit-rate audio stream. All input data must be time-stamped, and all input pins must be connected before the filter can be run or paused.

Building a Graph

You can build a filter graph using the ASF Writer in two ways. The first way is to manually add each filter to the graph and connect the pins. After adding the ASF Writer, configure it through the IConfigAsfWriter methods, if the default profile is not suitable, and connect the ASF Writer input pins to the corresponding output pins on the upstream filters.

The second way is to call the IGraphBuilder::RenderFile method with the name of the file to convert to ASF. RenderFile automatically builds a complete graph for rendering the file. In your application code, you then manually remove the renderer filters and insert the ASF Writer. This involves five basic steps:

  1. Add the ASF Writer to the graph.
  2. Disconnect the renderer filters and store the output pins they were connected to.
  3. Remove the renderer filters from the graph.
  4. Configure the ASF Writer.
  5. Connect the ASF Writer input pins to the output pins by using IGraphBuilder::Connect.

The following illustration shows typical ASF Writer filter graph configurations.

ASF Writer filter graph

Unlocking the Windows Media Format SDK

To access the Windows Media Format SDK, an application must provide a software certificate, also called a key, at run time. For details on obtaining the certificate, see the Windows Media Format SDK. A DirectShow application provides its certificate to the ASF Writer when it is added to the filter graph. The application must register as a key provider using the Component Object Model (COM) IServiceProvider and IObjectWithSite interfaces. Using this technique, the application implements a key provider class derived from IServiceProvider. This class implements the three standard COM methods—AddRef, QueryInterface, and Release—along with one additional method, QueryService, which is called by the filter graph manager. QueryService calls the Windows Media Format SDK method WMCreateCertificate and returns to the filter graph manager a pointer to the certificate that was created. If the certificate is valid, the filter graph manager allows the graph-building process to proceed.

Note  To build an application, include Wmsdkidl.h for the prototype for WMCreateCertificate, and link to the Wmstub.lib that you received from Microsoft.

The following code example illustrates the basic steps in this process:

// Declare and implement a key provider class derived from IServiceProvider.

class CKeyProvider : public IServiceProvider {
public:
    // IUnknown interface
    STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

    CKeyProvider();

    // IServiceProvider
    STDMETHODIMP QueryService(REFIID siid, REFIID riid, void **ppv);
    
private:
    ULONG m_cRef;
};

CKeyProvider::CKeyProvider() : m_cRef(0)
{
}

//////////////////////////////////////////////////////////////////////////
//
// IUnknown methods
//
//////////////////////////////////////////////////////////////////////////

ULONG CKeyProvider::AddRef()
{
    return ++m_cRef;
}

ULONG CKeyProvider::Release()
{
    ASSERT(m_cRef > 0);

    m_cRef--;

    if (m_cRef == 0) {
        delete this;

        // Don't return m_cRef, because the object doesn't exist anymore.
        return((ULONG) 0);
    }

    return(m_cRef);
}


// We only support IUnknown and IServiceProvider.
HRESULT CKeyProvider::QueryInterface(REFIID riid, void ** ppv)
{
    if (riid == IID_IServiceProvider || riid == IID_IUnknown) {
        *ppv = (void *) static_cast<IServiceProvider *>(this);
        AddRef();
        return NOERROR;
    }
    
    return E_NOINTERFACE;
}

STDMETHODIMP CKeyProvider::QueryService(REFIID siid, REFIID riid, void **ppv)
{
    if (siid == __uuidof(IWMReader) && riid == IID_IUnknown) {
        IUnknown *punkCert;
        
        HRESULT hr = WMCreateCertificate(&punkCert);

        if (SUCCEEDED(hr)) {
            *ppv = (void *) punkCert;
        }

        return hr;
    }
    return E_NOINTERFACE;
}

////////////////////////////////////////////////////////////////////
//
// These examples illustrate the sequence of method calls
// in your application. Error checking is omitted for brevity.
//
///////////////////////////////////////////////////////////////////

// Create the filter graph manager, but don't add any filters.
IGraphBuilder *pGraph;
hr = CreateFilterGraph(&pGraph);

...

// Instantiate the key provider class, and AddRef it
// so that COM doesn't try to free our static object.

CKeyProvider prov;
prov.AddRef();  // Don't let COM try to free our static object.

// Give the graph an IObjectWithSite pointer to us for callbacks & QueryService.
IObjectWithSite* pObjectWithSite = NULL;

hr = pGraph->QueryInterface(IID_IObjectWithSite, (void**)&pObjectWithSite);
if (SUCCEEDED(hr))
   {
// Use the IObjectWithSite pointer to specify our key provider object.
// The filter graph manager will use this pointer to call
// QueryService to do the unlocking.
// If the unlocking succeeds, then we can build our graph.
	
pObjectWithSite->SetSite((IUnknown *) (IServiceProvider *) &prov);
	pObjectWithSite->Release();
   }

// Now build the graph as described above in "Building A Graph."