Como reproduzir arquivos de mídia protegidos

Um arquivo de mídia protegido é qualquer arquivo de mídia que tenha regras associadas para usar o conteúdo. Em alguns casos, um arquivo de mídia protegido é criptografado usando alguma forma de criptografia DRM (gerenciamento de direitos digitais). Para reproduzir um arquivo de mídia protegido, a reprodução deve ocorrer dentro do PMP (caminho de mídia protegido). Além disso, o usuário pode ter que adquirir direitos para o conteúdo.

O termo aquisição de direitos refere-se a qualquer ação que o aplicativo deve executar antes que o usuário possa reproduzir o conteúdo. O exemplo mais comum é obter uma licença drm, mas o Media Foundation define um mecanismo genérico que pode dar suporte a outros tipos de aquisição de direitos. A interface IMFContentEnabler define esse mecanismo genérico.

A aquisição de direitos deve ser feita fora do PMP, a partir do processo de aplicativo. A Sessão de Mídia notifica o aplicativo por meio da interface IMFContentProtectionManager , que é implementada pelo aplicativo. A Sessão de Mídia usa a interface IMFContentProtectionManager para encaminhar um objeto de habilitador de conteúdo para o aplicativo. Os habilitadores de conteúdo implementam a interface IMFContentEnabler . O aplicativo usa essa interface para adquirir os direitos necessários.

Um habilitador de conteúdo pode dar suporte à aquisição automática de direitos; nesse caso, o habilitador de conteúdo implementa todo o processo e o aplicativo simplesmente monitora o status. Caso contrário, o aplicativo deve usar a aquisição de direitos não silenciosos, que é um processo em que o aplicativo envia dados HTTP POST para uma URL fornecida pelo habilitador de conteúdo.

Para reproduzir mídia protegida, um aplicativo segue as mesmas etapas fornecidas no tópico How to Play Media Files with Media Foundation, com as seguintes etapas adicionais:

  1. Consulte se a fonte de mídia contém conteúdo protegido. (Opcional).
  2. Crie a Sessão de Mídia no processo PMP, em vez do processo do aplicativo.
  3. Execute a aquisição de direitos, se notificado para fazer isso pela Sessão de Mídia. Essa operação é executada de forma assíncrona pelo aplicativo.
  4. Conclua a operação assíncrona.

Consulta de conteúdo protegido

Para consultar se uma fonte de mídia contém conteúdo protegido, chame a função MFRequireProtectedEnvironment no descritor de apresentação da fonte de mídia. Se a função retornar S_OK, você deverá usar o PMP para reproduzir o conteúdo. Se a função retornar S_FALSE, o PMP não será necessário e você poderá criar a Sessão de Mídia no processo do aplicativo. Como alternativa, você pode usar o PMP para reproduzir os dois tipos de conteúdo, protegidos e desprotegidos. Se você fizer isso, não precisará chamar MFRequireProtectedEnvironment.

Para obter mais informações sobre descritores de apresentação, consulte Descritores de apresentação.

Criar a sessão de mídia PMP

Para criar a Sessão de Mídia no PMP, chame MFCreatePMPMediaSession. Essa função é semelhante a MFCreateMediaSession, mas em vez de criar a Sessão de Mídia no processo do aplicativo, ela cria a Sessão de Mídia no processo pmp. O aplicativo recebe um ponteiro para um objeto proxy para a Sessão de Mídia. O aplicativo chama métodos IMFMediaSession no objeto proxy, assim como faria na Sessão de Mídia. O objeto proxy encaminha as chamadas para a Sessão de Mídia no limite do processo.

Crie a Sessão de Mídia pmp da seguinte maneira:

  1. Crie um novo repositório de atributos chamando MFCreateAttributes.
  2. Defina o atributo MF_SESSION_CONTENT_PROTECTION_MANAGER no repositório de atributos. O valor desse atributo é um ponteiro para a implementação de IMFContentProtectionManager do aplicativo. Chame IMFAttributes::SetUnknown para definir o atributo.
  3. Chame MFCreatePMPMediaSession para criar a Sessão de Mídia no processo pmp. O parâmetro pConfiguration é um ponteiro para a interface IMFAttributes do repositório de atributos.
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).

Em seguida, crie uma topologia de reprodução e enfileira-a na Sessão de Mídia, conforme descrito em Criando Topologias de Reprodução.

Realizar aquisição de direitos

Se a reprodução exigir aquisição de direitos, a Sessão de Mídia chamará IMFContentProtectionManager::BeginEnableContent. O parâmetro pEnablerActivate desse método é um ponteiro para a interface IMFActivate . Use essa interface para criar o objeto habilitador de conteúdo, que expõe a interface IMFContentEnabler . Em seguida, use o habilitador de conteúdo para executar a etapa de aquisição de direitos.

Para criar o habilitador de conteúdo, chame IMFActivate::ActivateObject:

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

Consulte o ponteiro IMFContentEnabler retornado para a interface IMFMediaEventGenerator . Use essa interface para obter eventos do objeto habilitador de conteúdo. Para obter mais informações sobre eventos, consulte Geradores de eventos de mídia.

Para descobrir se o habilitador de conteúdo dá suporte à aquisição automática, chame IMFContentEnabler::IsAutomaticSupported. Se esse método retornar o valor TRUE, o aplicativo deverá usar a aquisição automática. Caso contrário, use a aquisição não silenciosa.

O método BeginEnableContent é assíncrono. O aplicativo deve executar a etapa de aquisição no thread do aplicativo. Uma abordagem é postar uma mensagem de janela privada na janela main do aplicativo, notificando o thread do aplicativo para executar a aquisição. Enquanto a operação está pendente, o aplicativo deve armazenar o ponteiro de retorno de chamada e o objeto de estado que recebeu nos parâmetros pCallback e punkState de BeginEnableContent. Eles serão usados para concluir a operação assíncrona.

Aquisição automática

Para realizar a aquisição automática, chame IMFContentEnabler::AutomaticEnable. Esse método é assíncrono. Quando a operação é concluída, o habilitador de conteúdo envia um evento MEEnablerCompleted . O código de status do evento indica se a operação foi bem-sucedida. Se o código de status do evento MEEnablerCompleted for NS_E_DRM_LICENSE_NOTACQUIRED, o aplicativo deverá tentar usar a aquisição não silenciosa.

Enquanto a operação de aquisição está em andamento, o objeto enabler pode enviar o evento MEEnablerProgress para indicar o progresso da operação. Para cancelar a operação, chame IMFContentEnabler::Cancel.

Aquisição não silenciosa

Se o método IsAutomaticSupported retornar FALSE ou o método AutomaticEnable falhar com o código de erro NS_E_DRM_LICENSE_NOTACQUIRED, o aplicativo deverá executar a aquisição não silenciosa, conforme descrito nas seguintes etapas:

  1. Chame IMFContentEnabler::GetEnableURL para obter a URL para a aquisição de direitos. Esse método também retorna um sinalizador que indica se a URL é confiável.

  2. Chame IMFContentEnabler::GetEnableData para obter os dados HTTP POST.

  3. Chame IMFContentEnabler::MonitorEnable. Esse método faz com que o habilitador de conteúdo monitore o progresso da ação de aquisição de direitos.

  4. Envie os dados para a URL de aquisição de direitos usando uma ação HTTP POST. Você pode usar o controle de internet Explorer ou as APIs da Internet do Windows (WinINet).

O código a seguir mostra as etapas 1 a 3. A etapa 4 depende dos requisitos específicos do aplicativo.

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

Quando a operação é concluída, o habilitador de conteúdo envia um evento MEEnablerCompleted .

Concluir a operação assíncrona

Quando a aquisição de direitos for concluída, com êxito ou não, o aplicativo deverá notificar a Sessão de Mídia invocando o ponteiro de retorno de chamada fornecido no método BeginEnableContent .

  1. Crie um objeto de resultado assíncrono chamando MFCreateAsyncResult.
  2. Invoque o retorno de chamada da Sessão de Mídia chamando MFInvokeCallback.
  3. A Sessão de Mídia chamará IMFContentProtectionManager::EndEnableContent. Em sua implementação desse método, libere quaisquer ponteiros ou recursos alocados dentro de BeginEnableContent. Retornar um HRESULT que indica o sucesso geral da operação. Se a aquisição de direitos falhou ou o usuário cancelou antes de ser concluída, retorne um código de erro.

O código a seguir mostra como criar o resultado assíncrono e invocar o retorno de chamada.

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

Sessão de Mídia

Reprodução de áudio/vídeo