如何播放受保护的媒体文件

受保护的媒体文件是具有使用内容的相关规则的任何媒体文件。 在某些情况下,受保护的媒体文件使用某种形式的数字版权管理 (DRM) 加密进行加密。 若要播放受保护的媒体文件,必须在 PMP) (受保护媒体路径内进行播放。 此外,用户可能必须获取对内容的权限。

术语“权限获取”是指应用程序在用户可以播放内容之前必须执行的任何操作。 最常见的示例是获取 DRM 许可证,但媒体基础定义了一种通用机制,该机制可以支持其他类型的权限获取。 IMFContentEnabler 接口定义此通用机制。

权限获取必须在 PMP 之外从申请过程中完成。 媒体会话通过 IMFContentProtectionManager 接口通知应用程序,该接口由应用程序实现。 媒体会话使用 IMFContentProtectionManager 接口将内容启用程序对象转发到应用程序。 内容启用程序实现 IMFContentEnabler 接口。 应用程序使用此接口来获取必要的权限。

内容启用程序可能支持自动获取权限,在这种情况下,内容启用程序实现整个过程,应用程序只需监视状态。 否则,应用程序必须使用非无提示权限获取,这是应用程序将 HTTP POST 数据发送到内容启用程序提供的 URL 的过程。

若要播放受保护的媒体,应用程序遵循主题 How to Play Media Files with Media Foundation 中提供的相同步骤,并执行以下附加步骤:

  1. 查询媒体源是否包含受保护的内容。 (可选。)
  2. 在 PMP 进程而不是应用程序进程中创建媒体会话。
  3. 执行权限获取(如果媒体会话通知这样做)。 此操作由应用程序异步执行。
  4. 完成异步操作。

查询受保护的内容

若要查询媒体源是否包含受保护的内容,请对媒体源的呈现描述符调用 MFRequireProtectedEnvironment 函数。 如果函数返回S_OK,则必须使用 PMP 播放内容。 如果函数返回S_FALSE,则不需要 PMP,你可以在应用程序过程中创建媒体会话。 或者,可以使用 PMP 播放两种类型的内容,即受保护和未受保护的内容。 如果这样做,则无需调用 MFRequireProtectedEnvironment

有关表示描述符的详细信息,请参阅 演示文稿描述符

创建 PMP 媒体会话

若要在 PMP 中创建媒体会话,请调用 MFCreatePMPMediaSession。 此函数类似于 MFCreateMediaSession,但它不会在应用程序的进程中创建媒体会话,而是在 PMP 进程中创建媒体会话。 应用程序接收指向媒体会话的代理对象的指针。 应用程序在代理对象上调用 IMFMediaSession 方法,就像在媒体会话上一样。 代理对象跨进程边界将调用转发到媒体会话。

按如下所示创建 PMP 媒体会话:

  1. 通过调用 MFCreateAttributes 创建新的属性存储。
  2. 在属性存储中设置 MF_SESSION_CONTENT_PROTECTION_MANAGER 属性。 此属性的值是指向应用程序实现 IMFContentProtectionManager 的指针。 调用 IMFAttributes::SetUnknown 以设置 属性。
  3. 调用 MFCreatePMPMediaSession 以在 PMP 进程中创建媒体会话。 pConfiguration 参数是指向属性存储的 IMFAttributes 接口的指针。
IMFAttributes *pAttributes = NULL;
IMFMediaSession *pSession = NULL;

// Create the attribute store.
hr = MFCreateAttributes(&pAttributes, 1);

// Set the IMFContentProtectionManager pointer.
if (SUCCEEDED(hr))
{
    hr = pAttributes->SetUnknown(
        MF_SESSION_CONTENT_PROTECTION_MANAGER, 
        pCPM  // Your implementation of IMFContentProtectionManager.
        );
}

// Create the Media Session.
if (SUCCEEDED(hr))
{
    hr = MFCreatePMPMediaSession(
        0,
        pAttributes, 
        &pSession,
        NULL
    );
}

SAFE_RELEASE(pAttributes); // Release the attribute store.
// Use the Media Session to control playback (not shown).

接下来,按照 创建播放拓扑中所述,创建播放拓扑并将其排入媒体会话。

执行权限获取

如果播放需要获取权限,媒体会话将调用 IMFContentProtectionManager::BeginEnableContent。 此方法的 pEnablerActivate 参数是指向 IMFActivate 接口的指针。 使用此接口创建内容启用程序对象,该对象公开 IMFContentEnabler 接口。 然后使用内容启用程序执行权限获取步骤。

若要创建内容启用程序,请调用 IMFActivate::ActivateObject

IMFContentEnabler *pEnabler = NULL;
hr = pEnablerActivate->ActivateObject(
    IID_IMFContentEnabler, 
    (void**)&pEnabler
    );

查询 IMFMediaEventGenerator 接口返回的 IMFContentEnabler 指针。 使用此接口可从内容启用程序对象获取事件。 有关事件的详细信息,请参阅 媒体事件生成器

若要了解内容启用程序是否支持自动获取,请调用 IMFContentEnabler::IsAutomaticSupported。 如果此方法返回值 TRUE,则应用程序应使用自动获取。 否则,请使用非无提示获取。

BeginEnableContent 方法是异步的。 应用程序应在应用程序的线程上执行获取步骤。 一种方法是将专用窗口消息发布到应用程序的main窗口,通知应用程序线程执行购置。 当操作处于挂起状态时,应用程序必须将回调指针和它收到的状态对象存储在 BeginEnableContentpCallbackpunkState 参数中。 这些将用于完成异步操作。

自动获取

若要执行自动获取,请调用 IMFContentEnabler::AutomaticEnable。 此方法是异步方法。 操作完成后,内容启用程序将发送 MEEnablerCompleted 事件。 事件的状态代码指示操作是否成功。 如果 MEEnablerCompleted 事件的状态代码NS_E_DRM_LICENSE_NOTACQUIRED,则应用程序应尝试使用非无提示获取。

在获取操作正在进行时,enabler 对象可能会发送 MEEnablerProgress 事件以指示操作的进度。 若要取消操作,请调用 IMFContentEnabler::Cancel

非无提示获取

如果 IsAutomaticSupported 方法返回 FALSEAutomaticEnable 方法失败并出现错误代码NS_E_DRM_LICENSE_NOTACQUIRED,则应用程序应执行非无提示获取,如以下步骤所述:

  1. 调用 IMFContentEnabler::GetEnableURL 以获取获取权限的 URL。 此方法还返回一个标志,指示 URL 是否受信任。

  2. 调用 IMFContentEnabler::GetEnableData 以获取 HTTP POST 数据。

  3. 调用 IMFContentEnabler::MonitorEnable。 此方法使内容启用程序监视权限获取操作的进度。

  4. 使用 HTTP POST 操作将数据提交到权限获取 URL。 可以使用 Internet Explorer 控件或 Windows Internet (WinINet) API。

以下代码显示了步骤 1-3。 步骤 4 取决于应用程序的特定要求。

WCHAR   *sURL = NULL;  // URL.
DWORD   cchURL = 0;    // Size of the URL in characters.

// Trust status of the URL.
MF_URL_TRUST_STATUS  trustStatus = MF_LICENSE_URL_UNTRUSTED;

BYTE    *pPostData = NULL;  // Buffer to hold HTTP POST data.
DWORD   cbPostDataSize = 0; // Size of the buffer, in bytes.

HRESULT hr = S_OK;

// Get the URL. 
hr = m_pEnabler->GetEnableURL(&sURL, &cchURL, &trustStatus);

if (SUCCEEDED(hr))
{
    if (trustStatus != MF_LICENSE_URL_TRUSTED)
    {
        // The URL is not trusted. Do not proceed.
        hr = E_FAIL;
    }
}

// Monitor the rights acquisition. 
if (SUCCEEDED(hr))
{
    hr = m_pEnabler->MonitorEnable();
}

// Get the HTTP POST data.
if (SUCCEEDED(hr))
{
    hr = m_pEnabler->GetEnableData(&pPostData, &cbPostDataSize);
}

// Open the URL and send the HTTP POST data. (Not shown.)

// Release the buffers.
CoTaskMemFree(pPostData);
CoTaskMemFree(sURL);

操作完成后,内容启用程序将发送 MEEnablerCompleted 事件。

完成异步操作

成功完成权限获取或其他操作后,应用程序必须通过调用 BeginEnableContent 方法中给定的回调指针来通知媒体会话。

  1. 通过调用 MFCreateAsyncResult 创建异步结果对象。
  2. 通过调用 MFInvokeCallback 调用媒体会话的回调。
  3. 媒体会话将调用 IMFContentProtectionManager::EndEnableContent。 在此方法的实现中,释放 在 BeginEnableContent 中分配的任何指针或资源。 返回一个 HRESULT ,指示操作的总体成功。 如果权限获取失败或用户在完成之前已取消,则返回错误代码。

以下代码演示如何创建异步结果并调用回调。

IMFAsyncResult  *pResult = NULL;

// Create the asynchronous result object.
hr = MFCreateAsyncResult(NULL, pCallback, punkState, &pResult);

// Invoke the callback.
if (SUCCEEDED(hr))
{
    pResult->SetStatus(hrStatus);
    hr = MFInvokeCallback(pResult);
}
SAFE_RELEASE(pResult);

媒体会话

音频/视频播放