Reading DRM-Protected ASF Files in DirectShow

[The feature associated with this page, DirectShow, is a legacy feature. It has been superseded by MediaPlayer, IMFMediaEngine, and Audio/Video Capture in Media Foundation. Those features have been optimized for Windows 10 and Windows 11. Microsoft strongly recommends that new code use MediaPlayer, IMFMediaEngine and Audio/Video Capture in Media Foundation instead of DirectShow, when possible. Microsoft suggests that existing code that uses the legacy APIs be rewritten to use the new APIs if possible.]

This topic describes how to use DirectShow to play media files that are protected with Windows Media Digital Rights Management (DRM).

DRM Concepts

Protecting a media file with Windows Media DRM involves two distinct steps:

  • The content provider packages the file—that is, encrypts the file and attaches licensing information to the ASF file header. The licensing information includes a URL where the client can obtain the license.
  • The client application acquires a license for the content.

Packaging happens before the file is distributed. License acquisition occurs when the user tries to play or copy the file. License acquisition may be either silent or non-silent. Silent acquisition does not require any interaction with the user. Non-silent acquisition requires the application to open a browser window and display a web page to the user. At that point, the user might need to provide some kind of information to the content provider. However, both types of license acquisition require the client to submit an HTTP request to a license server.

DRM Versions

Several versions of Windows Media DRM exist. From the perspective of a client application, they can be grouped into two categories: DRM version 1, and DRM version 7 or later. (The second category includes DRM versions 9 and 10, as well as version 7.) The reason for categorizing DRM versions this way is that version 1 licenses are handled somewhat differently than version 7 or later licenses. In this documentation, the term version 7 license means version 7 or later.

It is also important to distinguish the DRM packaging from the DRM license. If the file is packaged using Windows Media Rights Manager version 7 or later, the DRM header can contain a version 1 license URL in addition to the version 7 license URL. The version 1 license URL enables older players that do not support version 7 to obtain a license for the content. However, the converse is not true, so a file with version 1 packaging cannot have a version 7 license URL.

Application Security Level

To play DRM-protected files, the client application must be linked to a static library that is provided in binary form by Microsoft. This library, which uniquely identifies the application, is sometimes called the stub library. The stub library has an assigned security level, the value of which is determined by the license agreement that you signed when you obtained the stub library.

The content provider sets a minimum security level needed to acquire the license. If the security level of your stub library is less than the minimum security level required by the license server, the application cannot obtain the license.

Individualization

To increase security, an application may update the DRM components on the client's computer. This update, called individualization, differentiates the user's copy of the application from all other copies of the same application. The DRM header of a protected file may specify may specify a minimum individualization level. (For more information, see the documentation for WMRMHeader.IndividualizedVersion in the Windows Media Rights Manager SDK.)

The Microsoft Individualization Service handles information from the user. So before your application individualizes, you must display the Microsoft Privacy Statement, or provide a link to it (see Microsoft Privacy Statement).

Provide the Software Certificate

To enable the application to use the DRM license, the application must provide a software certificate or key to the Filter Graph Manager. This key is contained in a static library that is individualized for the application. For information about obtaining the individualized library, see Obtaining the Required DRM Library in the Windows Media Format SDK documentation.

To provide the software key, perform the following steps:

  1. Link to the static library.
  2. Implement the IServiceProvider interface.
  3. Query the Filter Graph Manager for the IObjectWithSite interface.
  4. Call IObjectWithSite::SetSite with a pointer to your implementation of IServiceProvider.
  5. The Filter Graph Manager will call IServiceProvider::QueryService, specifying IID_IWMReader for the service identifier.
  6. In your implementation of QueryService, call WMCreateCertificate to create the software key.

The following code shows how to implement the QueryService method:

STDMETHODIMP Player::QueryService(REFIID siid, REFIID riid, void **ppv)
{
    if (ppv == NULL ) 
    { 
        return E_POINTER; 
    }

    if (siid == __uuidof(IWMReader) && riid == __uuidof(IUnknown))
    {
        IUnknown *punkCert;

        HRESULT hr = WMCreateCertificate(&punkCert);

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

        return hr;
    }
    return E_NOINTERFACE;
}

The following code shows how to call SetSite on the Filter Graph Manager:

HRESULT Player::CreateFilterGraph()
{
    CComPtr<IObjectWithSite> pSite;

    HRESULT hr = pGraph.CoCreateInstance(CLSID_FilterGraph);

    if (FAILED(hr))
    {
        goto done;
    }

    // Register the application as a site (service).
    hr = pGraph->QueryInterface(&pSite);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pSite->SetSite(this);


done:
    return hr;
}

Building the Playback Graph

To play a DRM-protected ASF file, perform the following steps:

  1. Create the Filter Graph Manager and use the IMediaEventEx interface to register for graph events.
  2. Call CoCreateInstance to create a new instance of the WM ASF Reader filter.
  3. Call IFilterGraph::AddFilter to add the filter to the filter graph.
  4. Query the filter for the IFileSourceFilter interface.
  5. Call IFileSourceFilter::Load with the URL of the file.
  6. Handle EC_WMT_EVENT events.
  7. On the first EC_WMT_EVENT event, query the WM ASF Reader filter for the IServiceProvider interface.
  8. Call IServiceProvider::QueryService to get a pointer to the IWMDRMReader interface.
  9. Call IGraphBuilder::Render to render the output pins of the WM ASF Reader filter.

Note

When opening a DRM-protected file, do not call IGraphBuilder::RenderFile to create the filter graph. The WM ASF Reader filter cannot connect to any other filters until the DRM license is acquired. This step requires the application to use the IWMDRMReader interface, which must be obtained from the filter, as described in steps 7–8. Therefore, you must create the filter and add it to the graph

 

Note

It is important to register for graph events (step 1) before adding the WM ASF Reader filter to the graph (step 3), because the application must handle the EC_WMT_EVENT events. The events are sent when Load is called (step 5).

 

The following code shows how to build the graph:

HRESULT Player::LoadMediaFile(PCWSTR pwszFile)
{


    BOOL bIsWindowsMediaFile = IsWindowsMediaFile(pwszFile);

    HRESULT hr = S_OK;

    // If this is the first time opening the file, create the
    // filter graph and add the WM ASF Reader filter.

    if (m_DRM.State() == DRM_INITIAL)
    {
        hr = CreateFilterGraph();
        if (FAILED(hr))
        {
            goto done;
        }

        // Use special handling for Windows Media files.
        if (bIsWindowsMediaFile)
        {
            // Add the ASF Reader filter to the graph.
            hr = m_pReader.CoCreateInstance(CLSID_WMAsfReader);
            if (FAILED(hr))
            {
                goto done;
            }

            hr = pGraph->AddFilter(m_pReader, NULL);
            if (FAILED(hr))
            {
                goto done;
            }

            hr = m_pReader->QueryInterface(&m_pFileSource);
            if (FAILED(hr))
            {
                goto done;
            }
        }
    }

    if (bIsWindowsMediaFile)
    {
            hr = m_pFileSource->Load(pwszFile, NULL);
C++
            if (FAILED(hr))            {                goto done;            }            hr = RenderOutputPins(pGraph, m_pReader);    }    else    {        // Not a Windows Media file, so just render the standard way.        hr = pGraph->RenderFile(pwszFile, NULL);    }done:    return hr;}

In the previous code, the RenderOutputPins function enumerates the output pins on the WM ASF Reader filter and calls IGraphBuilder::Render for each pin.

HRESULT RenderOutputPins(IGraphBuilder *pGraph, IBaseFilter *pFilter)
{
    CComPtr<IEnumPins>  pEnumPin = NULL;
    CComPtr<IPin>       pConnectedPin;
    CComPtr<IPin>       pPin;

    // Enumerate all pins on the filter
    HRESULT hr = pFilter->EnumPins(&pEnumPin);
    if (FAILED(hr))
    {
        goto done;
    }

    // Step through every pin, looking for the output pins.
    while (S_OK == (hr = pEnumPin->Next(1, &pPin, NULL)))
    {
        // Skip connected pins.
        hr = pPin->ConnectedTo(&pConnectedPin);
        if (hr == VFW_E_NOT_CONNECTED)
        {
            PIN_DIRECTION PinDirection;
            hr = pPin->QueryDirection(&PinDirection);

            if ((S_OK == hr) && (PinDirection == PINDIR_OUTPUT))
            {
                hr = pGraph->Render(pPin);
            }
        }

        pConnectedPin.Release();
        pPin.Release();

        // If there was an error, stop enumerating.
        if (FAILED(hr))
        {
            break;
        }
    }

done:
    return hr;
}

The following code shows how to get a pointer to the IWMDRMReader interface from the WM ASF Reader:

HRESULT DrmManager::Initialize(IBaseFilter *pFilter)
{


    CComPtr<IServiceProvider> pService;
    CComPtr<IWMDRMReader> pDrmReader;

    HRESULT hr = pFilter->QueryInterface(&pService);
    if (SUCCEEDED(hr))
    {
        hr = pService->QueryService(
            __uuidof(IWMDRMReader), IID_PPV_ARGS(&m_pDrmReader));
    }
    return hr;
}

Reading ASF Files in DirectShow