共用方式為


在 DirectShow 中讀取DRM-Protected ASF 檔案

[與此頁面相關的功能 DirectShow是舊版功能。 它已被 MediaPlayerIMFMediaEngineMedia Foundation 中的音訊/視訊擷取取代。 這些功能已針對Windows 10和Windows 11進行優化。 Microsoft 強烈建議新程式碼盡可能使用 MediaPlayerIMFMediaEngine音訊/視訊擷取 ,而不是 DirectShow。 Microsoft 建議使用舊版 API 的現有程式碼盡可能重寫為使用新的 API。

本主題描述如何使用 DirectShow 播放受 Windows Media Digital Rights Management 保護的媒體檔案, (DRM) 。

DRM 概念

使用 Windows 媒體 DRM 保護媒體檔案牽涉到兩個不同的步驟:

  • 內容提供者會封裝檔案,也就是加密檔案,並將授權資訊附加至 ASF 檔案標頭。 授權資訊包含用戶端可取得授權的 URL。
  • 用戶端應用程式會取得內容的授權。

封裝會在檔案散發之前發生。 當使用者嘗試播放或複製檔案時,就會發生授權取得。 授權取得可能是 無訊息非無訊息。 無訊息擷取不需要與使用者互動。 非無訊息取得需要應用程式開啟瀏覽器視窗,並向使用者顯示網頁。 此時,使用者可能需要提供某種資訊給內容提供者。 不過,這兩種類型的授權取得都需要用戶端向授權伺服器提交 HTTP 要求。

DRM 版本

有數個版本的 Windows 媒體 DRM 存在。 從用戶端應用程式的觀點來看,它們可以分成兩個類別:DRM 第 1 版和 DRM 第 7 版或更新版本。 (第二個類別包含 DRM 版本 9 和 10,以及版本 7.) 如此分類 DRM 版本的原因,就是第 1 版授權的處理方式與第 7 版或更新版本的授權稍有不同。 在本檔中, 第 7 版授權 一詞表示第 7 版或更新版本。

請務必區分 DRM 封裝與 DRM 授權。 如果使用 Windows Media Rights Manager 第 7 版或更新版本封裝檔案,DRM 標頭除了第 7 版授權 URL 之外,還可以包含第 1 版授權 URL。 第 1 版授權 URL 可讓不支援第 7 版的舊版玩家取得內容的授權。 不過,相反的不是真,因此具有第 1 版封裝的檔案不能有第 7 版授權 URL。

應用程式安全性層級

若要播放受 DRM 保護的檔案,用戶端應用程式必須連結至 Microsoft 以二進位格式提供的靜態程式庫。 此程式庫可唯一識別應用程式,有時稱為存根程式庫。 存根程式庫具有指派的安全性層級,其值取決於您取得存根程式庫時所簽署的授權合約。

內容提供者會設定取得授權所需的最低安全性層級。 如果存根程式庫的安全性層級小於授權伺服器所需的最低安全性層級,應用程式就無法取得授權。

個人化

為了提高安全性,應用程式可能會更新用戶端電腦上的 DRM 元件。 此更新稱為個別化,可區分使用者的應用程式複本與相同應用程式所有其他複本。 受保護檔案的 DRM 標頭可以指定最小個別化層級。 (如需詳細資訊,請參閱 Windows 媒體版權管理員 SDK.) 中的 WMRMHeader.IndividualizedVersion 檔

Microsoft 個人化服務會處理使用者的資訊。 因此,在應用程式個人化之前,您必須顯示 Microsoft 隱私權聲明,或提供其連結, (請參閱 Microsoft 隱私權聲明) 。

提供軟體憑證

若要讓應用程式能夠使用 DRM 授權,應用程式必須將軟體憑證或 金鑰 提供給 Filter Graph 管理員。 此金鑰包含在應用程式個別化的靜態程式庫中。 如需取得個別化程式庫的詳細資訊,請參閱 Windows 媒體格式 SDK 檔中的 取得必要的 DRM 程式庫

若要提供軟體金鑰,請執行下列步驟:

  1. 連結至靜態程式庫。
  2. 實作 IServiceProvider 介面。
  3. 查詢 IObjectWithSite 介面的 Filter Graph 管理員。
  4. 使用IServiceProvider實作的指標呼叫IObjectWithSite::SetSite
  5. Filter Graph 管理員會呼叫 IServiceProvider::QueryService,並為服務識別碼指定 IID_IWMReader
  6. 在您的 QueryService實作中,呼叫 WMCreateCertificate 來建立軟體金鑰。

下列程式碼示範如何實作 QueryService 方法:

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;
}

下列程式碼示範如何在 Filter Graph Manager 上呼叫 SetSite

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;
}

建置播放圖表

若要播放受 DRM 保護的 ASF 檔案,請執行下列步驟:

  1. 建立 Filter Graph 管理員 ,並使用 IMediaEventEx 介面來註冊圖形事件。
  2. 呼叫 CoCreateInstance 以建立 WM ASF 讀取器 篩選器的新實例。
  3. 呼叫 IFilterGraph::AddFilter 以將篩選新增至篩選圖形。
  4. 查詢 IFileSourceFilter 介面的篩選準則。
  5. 使用檔案的 URL 呼叫 IFileSourceFilter::Load
  6. 處理 EC_WMT_EVENT 事件。
  7. 在第一個EC_WMT_EVENT事件上,查詢IServiceProvider介面的WM ASF 讀取器篩選。
  8. 呼叫 IServiceProvider::QueryService 以取得 IWMDRMReader 介面的指標。
  9. 呼叫 IGraphBuilder::Render 來轉譯 WM ASF 讀取器的 輸出針腳。

注意

開啟受 DRM 保護的檔案時,請勿呼叫 IGraphBuilder::RenderFile 來建立篩選圖形。 在取得 DRM 授權之前,WM ASF 讀取器篩選器無法連線到任何其他篩選。 此步驟需要應用程式使用必須從篩選取得的 IWMDRMReader 介面,如步驟 7–8 中所述。 因此,您必須建立篩選,並將它新增至圖形

 

注意

在將 WM ASF 讀取器 篩選新增至圖形 (步驟 3) 之前,請務必先 (步驟 1) 註冊圖形事件,因為應用程式必須處理 EC_WMT_EVENT 事件。 呼叫 Load 時會傳送事件 (步驟 5) 。

 

下列程式碼示範如何建置圖形:

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;}

在先前的程式碼中,函 RenderOutputPins 式會列舉 WM ASF 讀取器 篩選上的輸出針腳,並針對每個針腳呼叫 IGraphBuilder::Render

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;
}

下列程式碼示範如何從WM ASF 讀取器取得IWMDRMReader介面的指標:

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;
}

在 DirectShow 中讀取 ASF 檔案