PlayReady を使ったアダプティブ ストリーミングAdaptive streaming with PlayReady

この記事では、ユニバーサル Windows プラットフォーム (UWP) アプリに、Microsoft PlayReady コンテンツ保護を使ったマルチメディア コンテンツのアダプティブ ストリーミングを追加する方法について説明します。This article describes how to add adaptive streaming of multimedia content with Microsoft PlayReady content protection to a Universal Windows Platform (UWP) app.

現在、この機能では、Dynamic Adaptive Streaming over HTTP (DASH) コンテンツの再生がサポートされています。This feature currently supports playback of Dynamic streaming over HTTP (DASH) content.

HLS (Apple の HTTP ライブ ストリーミング) は、PlayReady ではサポートされていません。HLS (Apple's HTTP Live Streaming) is not supported with PlayReady.

Smooth Streaming も、現在、ネイティブではサポートされていません。ただし、PlayReady は拡張可能であるため、追加のコードまたはライブラリを使うことで、PlayReady で保護された Smooth Streaming をサポートして、ソフトウェアまたはハードウェアの DRM (デジタル著作権管理) を活用できます。Smooth streaming is also currently not supported natively; however, PlayReady is extensible and by using additional code or libraries, PlayReady-protected Smooth streaming can be supported, leveraging software or even hardware DRM (digital rights management).

この記事では、アダプティブ ストリーミングの PlayReady 固有の側面についてのみ扱います。This article only deals with the aspects of adaptive streaming specific to PlayReady. アダプティブ ストリーミングの実装に関する全般的な情報については、「アダプティブ ストリーミング」をご覧ください。For information about implementing adaptive streaming in general, see Adaptive streaming.

この記事では、GitHub の Microsoft の Windows-universal-samples リポジトリにあるアダプティブ ストリーミングのサンプルのコードを使っています。This article uses code from the Adaptive streaming sample in Microsoft's Windows-universal-samples repository on GitHub. PlayReady を使ったアダプティブ ストリーミングはシナリオ 4 で取り上げられています。Scenario 4 deals with using adaptive streaming with PlayReady. リポジトリを含む ZIP ファイルをダウンロードするには、リポジトリのルート レベルに移動して、 [Download ZIP] ボタンを選びます。You can download the repo in a ZIP file by navigating to the root level of the repository and selecting the Download ZIP button.

次の using ステートメントが必要です。You will need the following using statements:

using LicenseRequest;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Windows.Foundation.Collections;
using Windows.Media.Protection;
using Windows.Media.Protection.PlayReady;
using Windows.Media.Streaming.Adaptive;
using Windows.UI.Xaml.Controls;

LicenseRequest 名前空間は、Microsoft がライセンス使用者に提供する PlayReady ファイル CommonLicenseRequest.cs にあります。The LicenseRequest namespace is from CommonLicenseRequest.cs, a PlayReady file provided by Microsoft to licensees.

いくつかのグローバル変数を宣言する必要があります。You will need to declare a few global variables:

private AdaptiveMediaSource ams = null;
private MediaProtectionManager protectionManager = null;
private string playReadyLicenseUrl = "";
private string playReadyChallengeCustomData = "";

次の定数を宣言することもできます。You will also want to declare the following constant:

private const uint MSPR_E_CONTENT_ENABLING_ACTION_REQUIRED = 0x8004B895;

MediaProtectionManager の設定Setting up the MediaProtectionManager

PlayReady コンテンツ保護を UWP アプリに追加するには、MediaProtectionManager オブジェクトを設定する必要があります。To add PlayReady content protection to your UWP app, you will need to set up a MediaProtectionManager object. これは、AdaptiveMediaSource オブジェクトを初期化するときに行います。You do this when initializing your AdaptiveMediaSource object.

次のコードは、MediaProtectionManager をセットアップします。The following code sets up a MediaProtectionManager:

private void SetUpProtectionManager(ref MediaElement mediaElement)
{
    protectionManager = new MediaProtectionManager();

    protectionManager.ComponentLoadFailed += 
        new ComponentLoadFailedEventHandler(ProtectionManager_ComponentLoadFailed);

    protectionManager.ServiceRequested += 
        new ServiceRequestedEventHandler(ProtectionManager_ServiceRequested);

    PropertySet cpSystems = new PropertySet();

    cpSystems.Add(
        "{F4637010-03C3-42CD-B932-B48ADF3A6A54}", 
        "Windows.Media.Protection.PlayReady.PlayReadyWinRTTrustedInput");

    protectionManager.Properties.Add("Windows.Media.Protection.MediaProtectionSystemIdMapping", cpSystems);

    protectionManager.Properties.Add(
        "Windows.Media.Protection.MediaProtectionSystemId", 
        "{F4637010-03C3-42CD-B932-B48ADF3A6A54}");

    protectionManager.Properties.Add(
        "Windows.Media.Protection.MediaProtectionContainerGuid", 
        "{9A04F079-9840-4286-AB92-E65BE0885F95}");

    mediaElement.ProtectionManager = protectionManager;
}

コンテンツ保護の設定は必須のため、このコードはそのままアプリにコピーできます。This code can simply be copied to your app, since it is mandatory for setting up content protection.

バイナリ データの読み込みに失敗すると、ComponentLoadFailed イベントが発生します。The ComponentLoadFailed event is fired when the load of binary data fails. これを処理するにはイベント ハンドラーを追加して、読み込みが完了していないことを通知する必要があります。We need to add an event handler to handle this, signaling that the load did not complete:

private void ProtectionManager_ComponentLoadFailed(
    MediaProtectionManager sender, 
    ComponentLoadFailedEventArgs e)
{
    e.Completion.Complete(false);
}

同様に、サービスが要求されたときに発生する ServiceRequested イベントのイベント ハンドラーを追加する必要があります。Similarly, we need to add an event handler for the ServiceRequested event, which fires when a service is requested. このコードは、要求の種類を確認し、それに応じて応答します。This code checks what kind of request it is, and responds appropriately:

private async void ProtectionManager_ServiceRequested(
    MediaProtectionManager sender, 
    ServiceRequestedEventArgs e)
{
    if (e.Request is PlayReadyIndividualizationServiceRequest)
    {
        PlayReadyIndividualizationServiceRequest IndivRequest = 
            e.Request as PlayReadyIndividualizationServiceRequest;

        bool bResultIndiv = await ReactiveIndivRequest(IndivRequest, e.Completion);
    }
    else if (e.Request is PlayReadyLicenseAcquisitionServiceRequest)
    {
        PlayReadyLicenseAcquisitionServiceRequest licenseRequest = 
            e.Request as PlayReadyLicenseAcquisitionServiceRequest;

        LicenseAcquisitionRequest(
            licenseRequest, 
            e.Completion, 
            playReadyLicenseUrl, 
            playReadyChallengeCustomData);
    }
}

個別化サービス要求Individualization service requests

次のコードでは、PlayReady 個別化サービス要求を事後対応的に行います。The following code reactively makes a PlayReady individualization service request. 要求は、パラメーターとして関数に渡します。We pass in the request as a parameter to the function. 呼び出しは try/catch ブロックで囲み、例外がない場合は要求が正常に完了したと見なされます。We surround the call in a try/catch block, and if there are no exceptions, we say the request completed successfully:

async Task<bool> ReactiveIndivRequest(
    PlayReadyIndividualizationServiceRequest IndivRequest, 
    MediaProtectionServiceCompletion CompletionNotifier)
{
    bool bResult = false;
    Exception exception = null;

    try
    {
        await IndivRequest.BeginServiceRequest();
    }
    catch (Exception ex)
    {
        exception = ex;
    }
    finally
    {
        if (exception == null)
        {
            bResult = true;
        }
        else
        {
            COMException comException = exception as COMException;
            if (comException != null && comException.HResult == MSPR_E_CONTENT_ENABLING_ACTION_REQUIRED)
            {
                IndivRequest.NextServiceRequest();
            }
        }
    }

    if (CompletionNotifier != null) CompletionNotifier.Complete(bResult);
    return bResult;
}

または、個別化サービス要求を事前に行うこともできます。その場合、ProtectionManager_ServiceRequestedReactiveIndivRequest を呼び出すコードの代わりに次の関数を呼び出します。Alternatively, we may want to proactively make an individualization service request, in which case we call the following function in place of the code calling ReactiveIndivRequest in ProtectionManager_ServiceRequested:

async void ProActiveIndivRequest()
{
    PlayReadyIndividualizationServiceRequest indivRequest = new PlayReadyIndividualizationServiceRequest();
    bool bResultIndiv = await ReactiveIndivRequest(indivRequest, null);
}

ライセンス取得サービス要求License acquisition service requests

代わりに、要求が PlayReadyLicenseAcquisitionServiceRequest であった場合、次の関数を呼び出して PlayReady ライセンスを要求および取得します。If instead the request was a PlayReadyLicenseAcquisitionServiceRequest, we call the following function to request and acquire the PlayReady license. 要求が成功したかどうかを、渡した MediaProtectionServiceCompletion オブジェクトに通知し、要求を完了します。We tell the MediaProtectionServiceCompletion object that we passed in whether the request was successful or not, and we complete the request:

async void LicenseAcquisitionRequest(
    PlayReadyLicenseAcquisitionServiceRequest licenseRequest, 
    MediaProtectionServiceCompletion CompletionNotifier, 
    string Url, 
    string ChallengeCustomData)
{
    bool bResult = false;
    string ExceptionMessage = string.Empty;

    try
    {
        if (!string.IsNullOrEmpty(Url))
        {
            if (!string.IsNullOrEmpty(ChallengeCustomData))
            {
                System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
                byte[] b = encoding.GetBytes(ChallengeCustomData);
                licenseRequest.ChallengeCustomData = Convert.ToBase64String(b, 0, b.Length);
            }

            PlayReadySoapMessage soapMessage = licenseRequest.GenerateManualEnablingChallenge();

            byte[] messageBytes = soapMessage.GetMessageBody();
            HttpContent httpContent = new ByteArrayContent(messageBytes);

            IPropertySet propertySetHeaders = soapMessage.MessageHeaders;

            foreach (string strHeaderName in propertySetHeaders.Keys)
            {
                string strHeaderValue = propertySetHeaders[strHeaderName].ToString();

                if (strHeaderName.Equals("Content-Type", StringComparison.OrdinalIgnoreCase))
                {
                    httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse(strHeaderValue);
                }
                else
                {
                    httpContent.Headers.Add(strHeaderName.ToString(), strHeaderValue);
                }
            }

            CommonLicenseRequest licenseAcquision = new CommonLicenseRequest();

            HttpContent responseHttpContent = 
                await licenseAcquision.AcquireLicense(new Uri(Url), httpContent);

            if (responseHttpContent != null)
            {
                Exception exResult = licenseRequest.ProcessManualEnablingResponse(
                                         await responseHttpContent.ReadAsByteArrayAsync());

                if (exResult != null)
                {
                    throw exResult;
                }
                bResult = true;
            }
            else
            {
                ExceptionMessage = licenseAcquision.GetLastErrorMessage();
            }
        }
        else
        {
            await licenseRequest.BeginServiceRequest();
            bResult = true;
        }
    }
    catch (Exception e)
    {
        ExceptionMessage = e.Message;
    }

    CompletionNotifier.Complete(bResult);
}

AdaptiveMediaSource の初期化Initializing the AdaptiveMediaSource

最後に、特定の UriMediaElement から作成された AdaptiveMediaSource を初期化するための関数が必要になります。Finally, you will need a function to initialize the AdaptiveMediaSource, created from a given Uri and MediaElement. Uri は、メディア ファイル (HLS または DASH) へのリンクです。MediaElement は、XAML で定義されます。The Uri should be the link to the media file (HLS or DASH); the MediaElement should be defined in your XAML.

async private void InitializeAdaptiveMediaSource(System.Uri uri, MediaElement m)
{
    AdaptiveMediaSourceCreationResult result = await AdaptiveMediaSource.CreateFromUriAsync(uri);
    if (result.Status == AdaptiveMediaSourceCreationStatus.Success)
    {
        ams = result.MediaSource;
        SetUpProtectionManager(ref m);
        m.SetMediaStreamSource(ams);
    }
    else
    {
        // Error handling
    }
}

この関数は、アダプティブ ストリーミングの開始をどのイベント (たとえば、ボタン クリック イベント) で処理する場合でも呼び出すことができます。You can call this function in whichever event handles the start of adaptive streaming; for example, in a button click event.

関連項目See also