メディア項目、プレイリスト、トラック

この記事では、ローカル ファイルやリモート ファイルなど、さまざまなソースのメディアを参照および再生するための一般的な方法を提供し、基になるメディア形式に関係なく、メディア データにアクセスするための一般的なモデルを公開する MediaSource クラスの使い方について説明します。 MediaPlaybackItem クラスは、メディア項目に含まれている複数のオーディオ、ビデオ、メタデータ トラックを管理および選択できるようにして、MediaSource の機能を拡張します。 MediaPlaybackList を使用すると、1 つまたは複数のメディア再生項目から再生リストを作成できます。

MediaSource を作成および再生する

クラスによって公開されているファクトリ メソッドのいずれかを呼び出すことによって、MediaSource の新しいインスタンスを作成します。

作成した MediaSource は、Source プロパティを設定すると、MediaPlayer を使用して再生できます。 Windows 10 バージョン 1607 以降では、XAML ページでメディア プレイヤー コンテンツをレンダリングするには、SetMediaPlayer を呼び出して MediaPlayerMediaPlayerElement に割り当てます。 これは、MediaElement を使用するよりもお勧めの方法です。 MediaPlayer の使い方について詳しくは、「MediaPlayer を使ったオーディオとビデオの再生」をご覧ください。

次の例では、MediaSource を使って MediaPlayer でユーザーが選択したメディア ファイルを再生する方法を示します。

このシナリオを実現するには、Windows.Media.Core および Windows.Media.Playback 名前空間を含める必要があります。

using Windows.Media.Core;
using Windows.Media.Playback;

MediaSource 型の変数を宣言します。 この記事の例では、複数の場所からアクセスできるように、メディア ソースはクラス メンバーとして宣言されています。

MediaSource _mediaSource;

MediaPlayer オブジェクトを格納するための変数を宣言します。XAML でメディア コンテンツをレンダリングする場合は、MediaPlayerElement コントロールをページに追加します。

MediaPlayer _mediaPlayer;
<MediaPlayerElement x:Name="mediaPlayerElement"/>

ユーザーが再生するメディア ファイルを選択できるようにするには、FileOpenPicker を使います。 ピッカーの PickSingleFileAsync メソッドから返された StorageFile オブジェクトを使って、MediaSource.CreateFromStorageFile を呼び出すことにより、新しい MediaObject を初期化します。 最後に、SetPlaybackSource メソッドを呼び出すことによって、メディア ソースを、MediaElement の再生ソースとして設定します。

//Create a new picker
var filePicker = new Windows.Storage.Pickers.FileOpenPicker();

//make a collection of all video types you want to support (for testing we are adding just 3).
string[] fileTypes = new string[] {".wmv", ".mp4", ".mkv"};   
   
//Add your fileTypes to the FileTypeFilter list of filePicker.
foreach (string fileType in fileTypes)
{
    filePicker.FileTypeFilter.Add(fileType);
}

//Set picker start location to the video library
filePicker.SuggestedStartLocation = PickerLocationId.VideosLibrary;

//Retrieve file from picker
StorageFile file = await filePicker.PickSingleFileAsync();

if (!(file is null))
{
    _mediaSource = MediaSource.CreateFromStorageFile(file);
    _mediaPlayer = new MediaPlayer();
    _mediaPlayer.Source = _mediaSource;
    mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
}

既定では、メディア ソースが設定されても MediaPlayer では自動的に再生が開始されません。 Play を呼び出すことにより手動で再生を開始できます。

_mediaPlayer.Play();

MediaPlayerAutoPlay プロパティを true に設定して、メディア ソースが設定されたらすぐに再生を開始するようにプレイヤーに指示することもできます。

_mediaPlayer.AutoPlay = true;

DownloadOperation から MediaSource を作成する

Windows、バージョン 1803 以降では、DownloadOperation から MediaSourceオブジェクトを作成できます。

StorageFile destinationFile = await KnownFolders.VideosLibrary.CreateFileAsync("file.mp4", CreationCollisionOption.GenerateUniqueName);

var downloader = new BackgroundDownloader();
var downloadOperation = downloader.CreateDownload(new Uri("http://server.com/file.mp4"), destinationFile);
MediaSource mediaSource =
      MediaSource.CreateFromDownloadOperation(downloadOperation);

ダウンロードを開始したり、その IsRandomAccessRequired プロパティを true に設定したりしなくても、ダウンロードから MediaSource を作成できますが、MediaSourceMediaPlayerMediaPlayerElement にアタッチするには、その前にこれら両方の操作を行う必要があります。

downloadOperation.IsRandomAccessRequired = true;
var startAsyncTask = downloadOperation.StartAsync().AsTask();
mediaPlayerElement.Source = mediaSource;

MediaPlaybackItem を使って複数のオーディオ、ビデオ、メタデータ トラックを処理する

MediaSource を使った再生では、さまざまな種類のソースからメディアを再生するための共通の方法が提供されるため便利ですが、MediaSource から MediaPlaybackItem を作成することにより、さらに高度な動作を実現できます。 これには、メディアの項目の複数のオーディオ、ビデオ、データのトラックにアクセスして管理する機能が含まれます。

MediaPlaybackItem を格納するための変数を宣言します。

MediaPlaybackItem _mediaPlaybackItem;

コンストラクターを呼び出して、初期化された MediaSource オブジェクトを渡すことによって、MediaPlaybackItem を作成します。

アプリがメディア再生項目で複数のオーディオ、ビデオ、データのトラックをサポートしている場合は、AudioTracksChangedVideoTracksChanged、または TimedMetadataTracksChanged イベントのイベント ハンドラーを登録します。

最後に、MediaElement または MediaPlayer の再生ソースを MediaPlaybackItem に設定します。

_mediaSource = MediaSource.CreateFromStorageFile(file);
_mediaPlaybackItem = new MediaPlaybackItem(_mediaSource);

_mediaPlaybackItem.AudioTracksChanged += PlaybackItem_AudioTracksChanged;
_mediaPlaybackItem.VideoTracksChanged += MediaPlaybackItem_VideoTracksChanged;
_mediaPlaybackItem.TimedMetadataTracksChanged += MediaPlaybackItem_TimedMetadataTracksChanged;

_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = _mediaPlaybackItem;
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);

注意

MediaSource は、単一の MediaPlaybackItem にのみ関連付けることができます。 ソースから MediaPlaybackItem を作成した後、同じソースから別の再生項目を作成しようとすると、エラーが発生します。 また、メディア ソースから MediaPlaybackItem を作成した後、MediaSource オブジェクトを MediaPlayer のソースとして直接設定することはできません。この場合は、代わりに MediaPlaybackItem を使う必要があります。

複数のビデオ トラックを含む MediaPlaybackItem が再生ソースとして割り当てられると、VideoTracksChanged イベントが発生します。項目の変更としてビデオ トラックのリストが変更された場合は、再びイベントが発生することがあります。 このイベントのハンドラーを使うと、ユーザーが利用可能なトラックを切り替えることができるように UI を更新する機会が提供されます。 この例では、ComboBox を使って利用可能なビデオ トラックを表示します。

<ComboBox x:Name="videoTracksComboBox" SelectionChanged="videoTracksComboBox_SelectionChanged"/>

VideoTracksChanged ハンドラーで、再生の項目の VideoTracks のリストにあるすべてのトラックをループ処理します。 各トラックについて、新しい ComboBoxItem が作成されます。 トラックにまだラベルがない場合、トラックのインデックスからラベルが生成されます。 コンボ ボックス項目の Tag プロパティが、後で識別できるようにトラックのインデックスに設定されます。 最後に、コンボ ボックスに項目が追加されます。 これらの処理は CoreDispatcher.RunAsync の呼び出し内で実行されます。UI の変更はすべて UI スレッドで実行される必要があり、このイベントは別のスレッドで発生しているためです。

private async void MediaPlaybackItem_VideoTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        videoTracksComboBox.Items.Clear();
        for (int index = 0; index < sender.VideoTracks.Count; index++)
        {
            var videoTrack = sender.VideoTracks[index];
            ComboBoxItem item = new ComboBoxItem();
            item.Content = String.IsNullOrEmpty(videoTrack.Label) ? $"Track {index}" : videoTrack.Label;
            item.Tag = index;
            videoTracksComboBox.Items.Add(item);
        }
    });
}

コンボ ボックスの SelectionChanged ハンドラーで、選択した項目の Tag プロパティからトラックのインデックスが取得されます。 メディア再生項目の VideoTracks リストの SelectedIndex プロパティを設定すると、MediaElement または MediaPlayer はアクティブなビデオ トラックを、指定されたインデックスに切り替えます。

private void videoTracksComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    int trackIndex = (int)((ComboBoxItem)((ComboBox)sender).SelectedItem).Tag;
    _mediaPlaybackItem.VideoTracks.SelectedIndex = trackIndex;
}

複数のオーディオ トラックを含むメディア項目の管理は、ビデオ トラックの場合とまったく同じです。 AudioTracksChanged を処理し、再生項目の AudioTracks リストで見つかったオーディオ トラックを使って、UI を更新します。 ユーザーがオーディオ トラックを選んだときに、AudioTracks リストの SelectedIndex プロパティを設定すると、MediaElement または MediaPlayer はアクティブなオーディオ トラックを、指定されたインデックスに切り替えます。

<ComboBox x:Name="audioTracksComboBox" SelectionChanged="audioTracksComboBox_SelectionChanged"/>
private async void PlaybackItem_AudioTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        audioTracksComboBox.Items.Clear();
        for (int index = 0; index < sender.AudioTracks.Count; index++)
        {
            var audioTrack = sender.AudioTracks[index];
            ComboBoxItem item = new ComboBoxItem();
            item.Content = String.IsNullOrEmpty(audioTrack.Label) ? $"Track {index}" : audioTrack.Label;
            item.Tag = index;
            videoTracksComboBox.Items.Add(item);
        }
    });
}
private void audioTracksComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    int trackIndex = (int)((ComboBoxItem)((ComboBox)sender).SelectedItem).Tag;
    _mediaPlaybackItem.AudioTracks.SelectedIndex = trackIndex;
}

オーディオとビデオに加え、MediaPlaybackItem オブジェクトは 0 個以上の TimedMetadataTrack オブジェクトを格納する場合があります。 タイミングが設定されたメタデータ トラックは、サブタイトルまたはキャプション テキストを含めることができます。またはアプリに固有のカスタム データを含めることができます。 タイミングが設定されたメタデータ トラックには、DataCueTimedTextCue など、IMediaCue を継承するオブジェクトで表されるキューのリストが含まれます。 各キューには開始時刻と継続時間があり、キューがいつアクティブ化され、どのくらいの時間アクティブ化されているかを決定します。

オーディオ トラックやビデオ トラックと同様に、MediaPlaybackItemTimedMetadataTracksChanged イベントを処理することによって、メディア項目のタイミングが設定されたメタデータ トラックを検出できます。 ただし、タイミングが設定されたメタデータ トラックを使って、ユーザーは一度に複数のメタデータ トラックを有効にすることができます。 また、アプリ シナリオに応じて、ユーザーの介入なしに、自動的にメタデータ トラックを有効または無効にすることもできます。 この例では、わかりやすくするために、メディア項目で各メタデータ トラック用の ToggleButton を追加することで、ユーザーがトラックを有効または無効に設定できるようにしています。の各メタデータ トラックを無効にするユーザーを許可するように、メディア項目で追跡します。各ボタンの Tag プロパティは、ボタンが切り替えられたときに識別できるように、関連付けられたメタデータ トラックのインデックスに設定されます。

<StackPanel x:Name="MetadataButtonPanel" Orientation="Horizontal"/>
private async void MediaPlaybackItem_TimedMetadataTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        for (int index = 0; index < sender.TimedMetadataTracks.Count; index++)
        {
            var timedMetadataTrack = sender.TimedMetadataTracks[index];

            ToggleButton toggle = new ToggleButton()
            {
                Content = String.IsNullOrEmpty(timedMetadataTrack.Label) ? $"Track {index}" : timedMetadataTrack.Label,
                Tag = (uint)index
            };
            toggle.Checked += Toggle_Checked;
            toggle.Unchecked += Toggle_Unchecked;

            MetadataButtonPanel.Children.Add(toggle);
        }
    });
}

一度に複数のメタデータ トラックをアクティブ化することができるため、メタデータ トラック リストについて、単にアクティブなインデックスを設定しません。 代わりに、MediaPlaybackItem オブジェクトの SetPresentationMode メソッドを呼び出して、切り替えるトラックのインデックスを渡し、TimedMetadataTrackPresentationMode 列挙体から値を指定します。 選択する表示モードは、アプリの実装によって異なります。 この例では、メタデータ トラックは、有効な場合 PlatformPresented に設定されます。 テキスト ベースのトラックの場合、これは、システムが自動的にトラックにテキスト キューを表示することを意味します。トグル ボタンをオフにすると、表示モードは Disabled に設定されます。つまり、テキストは表示されず、キュー イベントは発生しません。 キュー イベントについては、この記事で後述します。

private void Toggle_Checked(object sender, RoutedEventArgs e) =>         
    _mediaPlaybackItem.TimedMetadataTracks.SetPresentationMode((uint)((ToggleButton)sender).Tag,
        TimedMetadataTrackPresentationMode.PlatformPresented);
private void Toggle_Unchecked(object sender, RoutedEventArgs e) =>         
    _mediaPlaybackItem.TimedMetadataTracks.SetPresentationMode((uint)((ToggleButton)sender).Tag,
        TimedMetadataTrackPresentationMode.Disabled);

メタデータ トラックを処理している場合は、Cues プロパティまたは ActiveCues プロパティにアクセスすることで、トラック内の一連のキューにアクセスできます。 こうすることで、UI を更新して、メディア項目のキューの場所を表示することができます。

メディア項目を開く際にサポートされていないコーデックと不明なエラーを処理する

Windows 10 バージョン 1607 以降では、アプリが実行されているデバイスでメディア項目の再生に必要なコーデックがサポートされているかどうか、または部分的にサポートされているかどうかを確認できます。 まず、AudioTracksChanged などの MediaPlaybackItem トラック変更イベントのイベント ハンドラーで、その変更が新しいトラックの挿入かどうかを確認します。そうである場合は、AudioTracks コレクションなど、MediaPlaybackItem パラメーターの適切なトラック コレクションと共に、IVectorChangedEventArgs.Index パラメーターで渡されるインデックスを使用することにより、挿入されるトラックへの参照を取得できます。

挿入されるトラックへの参照を取得したら、そのトラックの SupportInfo プロパティの DecoderStatus を確認します。 この値が FullySupported の場合は、トラックの再生に必要な適切なコーデックがデバイス上に存在します。 この値が Degraded の場合は、システムでトラックを再生することはできるものの、何らかの方法で再生が劣化することになります。 たとえば、5.1 オーディオ トラックが、2 チャンネル ステレオで再生される可能性があります。 このような場合は、UI を更新して、ユーザーに対し品質の低下を通知することをお勧めします。 この値が UnsupportedSubtype または UnsupportedEncoderProperties の場合は、デバイス上に存在する現在のコーデックではトラックを再生できません。 ユーザーに通知してその項目の再生をスキップするか、ユーザーが正しいコーデックをダウンロードできる UI の実装が必要になる場合があります。 トラックの GetEncodingProperties メソッドは、再生に必要なコーデックを確認するために使用します。

最後に、トラックの OpenFailed イベントを登録します。このイベントは、デバイス上でトラックがサポートされているものの、パイプラインの不明なエラーによってトラックを開けなかった場合に発生します。

private async void SnippetAudioTracksChanged_CodecCheck(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    if (args.CollectionChange == CollectionChange.ItemInserted)
    {
        var insertedTrack = sender.AudioTracks[(int)args.Index];

        var decoderStatus = insertedTrack.SupportInfo.DecoderStatus;
        if (decoderStatus != MediaDecoderStatus.FullySupported)
        {
            if (decoderStatus == MediaDecoderStatus.Degraded)
            {
                ShowMessageToUser($"Track {insertedTrack.Name} can play but playback will be degraded. {insertedTrack.SupportInfo.DegradationReason}");
            }
            else
            {
                // status is MediaDecoderStatus.UnsupportedSubtype or MediaDecoderStatus.UnsupportedEncoderProperties
                ShowMessageToUser($"Track {insertedTrack.Name} uses an unsupported media format.");
            }

            Windows.Media.MediaProperties.AudioEncodingProperties props = insertedTrack.GetEncodingProperties();
            await HelpUserInstallCodec(props);
        }
        else
        {
            insertedTrack.OpenFailed += InsertedTrack_OpenFailed;
        }
    }

}

OpenFailed イベント ハンドラーでは、MediaSource ステータスが不明かどうかを確認できます。不明な場合は、プログラムによって別のトラックを選択して再生できるようにしたり、ユーザーが別のトラックを選べるようにしたり、再生を無視することができます。

private async void InsertedTrack_OpenFailed(AudioTrack sender, AudioTrackOpenFailedEventArgs args)
{
    LogError(args.ExtendedError.HResult);

    if (sender.SupportInfo.MediaSourceStatus == MediaSourceStatus.Unknown)
    {
        await SelectAnotherTrackOrSkipPlayback(sender.PlaybackItem);
    }
}

システム メディア トランスポート コントロールで使用する表示プロパティを設定する

Windows 10 バージョン 1607 以降、MediaPlayer で再生されるメディアは既定では自動的にシステム メディア トランスポート コントロール (SMTC) に統合されます。 SMTC で表示されるメタデータを指定するには、MediaPlaybackItem の表示プロパティを更新します。 項目の表示プロパティを表すオブジェクトを取得するには、GetDisplayProperties を呼び出します。 Type プロパティを設定することによって、再生項目が音楽かビデオかを設定し、 次に、オブジェクトの VideoProperties または MusicProperties を設定します。 項目のプロパティを与えた値に更新するには、ApplyDisplayProperties を呼び出します。 通常、アプリは Web サービスから表示値を動的に取得しますが、次の例はこのプロセスをハードコードされた値を使って示しています。

MediaItemDisplayProperties props = mediaPlaybackItem.GetDisplayProperties();
props.Type = Windows.Media.MediaPlaybackType.Video;
props.VideoProperties.Title = "Video title";
props.VideoProperties.Subtitle = "Video subtitle";
props.VideoProperties.Genres.Add("Documentary");
mediaPlaybackItem.ApplyDisplayProperties(props);
props = mediaPlaybackItem.GetDisplayProperties();
props.Type = Windows.Media.MediaPlaybackType.Music;
props.MusicProperties.Title = "Song title";
props.MusicProperties.Artist = "Song artist";
props.MusicProperties.Genres.Add("Polka");
mediaPlaybackItem.ApplyDisplayProperties(props);

TimedTextSource を使って外部のタイミングが設定されたテキストを追加する

シナリオによっては、さまざまなロケールのサブタイトルが含まれる個別のファイルなど、メディア項目に関連付けられているタイミングが設定されたテキストが外部のファイルに含まれている場合があります。 ストリームや URI から外部のタイミングが設定されたテキスト ファイルを読み込むには、TimedTextSource クラスを使います。

この例では、Dictionary コレクションでソース URI を使ってメディア項目のタイミングが設定されたテキスト ソースのリストを格納し、解決された後のトラックを識別するためにキーと値のペアとして TimedTextSource オブジェクトを使用します。

Dictionary<TimedTextSource, Uri> timedTextSourceMap;

CreateFromUri を呼び出すことによって、外部のタイミングが設定されたテキスト ファイルごとに新しい TimedTextSource を作成します。 タイミングが設定されたテキスト ソース用のエントリを Dictionary に追加します。 TimedTextSource.Resolved イベントのハンドラーを追加して、項目の読み込みに失敗した場合の処理をしたり、項目が正常に読み込まれた後で追加のプロパティを設定したりします。

TimedTextSource オブジェクトを ExternalTimedTextSources コレクションに追加して、すべてのオブジェクトを MediaSource に登録します。 タイミングが設定された外部のテキスト ソースは、ソースから作成された MediaPlaybackItem ではなく、MediaSource に直接追加されることに注意してください。 外部のテキスト トラックを反映するように UI を更新するには、この記事で既に説明したように、TimedMetadataTracksChanged イベントを登録して処理します。

// Create the TimedTextSource and add entry to URI map
var timedTextSourceUri_En = new Uri("http://contoso.com/MyClipTimedText_en.srt");
var timedTextSource_En = TimedTextSource.CreateFromUri(timedTextSourceUri_En);
timedTextSourceMap[timedTextSource_En] = timedTextSourceUri_En;
timedTextSource_En.Resolved += TimedTextSource_Resolved;

var timedTextSourceUri_Pt = new Uri("http://contoso.com/MyClipTimedText_pt.srt");
var timedTextSource_Pt = TimedTextSource.CreateFromUri(timedTextSourceUri_Pt);
timedTextSourceMap[timedTextSource_Pt] = timedTextSourceUri_Pt;
timedTextSource_Pt.Resolved += TimedTextSource_Resolved;

// Add the TimedTextSource to the MediaSource
_mediaSource.ExternalTimedTextSources.Add(timedTextSource_En);
_mediaSource.ExternalTimedTextSources.Add(timedTextSource_Pt);

_mediaPlaybackItem = new MediaPlaybackItem(_mediaSource);
_mediaPlaybackItem.TimedMetadataTracksChanged += MediaPlaybackItem_TimedMetadataTracksChanged;

_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = _mediaPlaybackItem;
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);

TimedTextSource.Resolved イベントのハンドラーで、ハンドラーに渡された TimedTextSourceResolveResultEventArgsError プロパティを確認して、タイミングが設定されたテキスト データの読み込み中にエラーが発生したかどうかを判断します。 項目が正しく解決された場合は、このハンドラーを使って、解決されたトラックの他のプロパティを更新できます。この例では、以前に Dictionary に格納された URI に基づいて、各トラックのラベルを追加します。

private void TimedTextSource_Resolved(TimedTextSource sender, TimedTextSourceResolveResultEventArgs args)
{
    var timedTextSourceUri = timedTextSourceMap[sender];

    if (!(args.Error is null))
    {
        // Show that there was an error in your UI
        ShowMessageToUser($"There was an error resolving track: {timedTextSourceUri}");
        return;
    }

    // Add a label for each resolved track
    var timedTextSourceUriString = timedTextSourceUri.AbsoluteUri;
    if (timedTextSourceUriString.Contains("_en"))
    {
        args.Tracks[0].Label = "English";
    }
    else if (timedTextSourceUriString.Contains("_pt"))
    {
        args.Tracks[0].Label = "Portuguese";
    }
}

Windows でサポートされている時間指定テキスト形式の一覧については、「サポートされているコーデック」を参照してください。

その他のメタデータ トラックを追加する

コードで動的にカスタム メタデータ トラックを作成し、メディア ソースを関連付けることができます。 作成するトラックにサブタイトルやキャプション テキストを含めたり、独自のアプリ データを含めたりすることができます。

コンストラクターを呼び出して、ID、言語識別子、および TimedMetadataKind 列挙体からの値を指定することによって、新しい TimedMetadataTrack を作成します。 CueEntered イベントと CueExited イベントのハンドラーを登録します。 これらのイベントはそれぞれ、キューの開始時刻になったときと、キューの継続時間が終了したときに発生します。

作成するメタデータ トラックの種類に適切な新しいキュー オブジェクトを作成し、トラックの ID、開始時刻、継続時間を設定します。この例では、データ トラックが作成されるため、一連の DataCue オブジェクトが生成され、アプリ固有のデータを格納するバッファーが各キューに提供されます。 新しいトラックを登録するには、MediaSource オブジェクトの ExternalTimedMetadataTracks コレクションにトラックを追加します。

Windows 10 Version 1703 以降では、DataCue.Properties プロパティが PropertySet を公開します。これを使用して、CueEnteredCueExited イベントで取得できるキー/データのペアにカスタム プロパティを格納することができます。

TimedMetadataTrack metadataTrack = new TimedMetadataTrack("ID_0", "en-us", TimedMetadataKind.Data);
metadataTrack.Label = "Custom data track";
metadataTrack.CueEntered += MetadataTrack_DataCueEntered;
metadataTrack.CueExited += MetadataTrack_CueExited;

// Example cue data
string data = "Cue data";
byte[] bytes = new byte[data.Length * sizeof(char)];
System.Buffer.BlockCopy(data.ToCharArray(), 0, bytes, 0, bytes.Length);
Windows.Storage.Streams.IBuffer buffer = bytes.AsBuffer();

for (int i = 0; i < 10; i++)
{
    DataCue cue = new DataCue();
    cue.Id = "ID_" + i;
    cue.Data = buffer;
    cue.Properties["AdUrl"] = "http://contoso.com/ads/123";
    cue.StartTime = TimeSpan.FromSeconds(3 + i * 3);
    cue.Duration = TimeSpan.FromSeconds(2);

    metadataTrack.AddCue(cue);
}

_mediaSource.ExternalTimedMetadataTracks.Add(metadataTrack);

CueEntered イベントは、関連付けられたトラックの表示モードが ApplicationPresentedHidden、または PlatformPresented である限り、キューの開始時間になると発生します。トラックの表示モードが Disabled である場合、メタデータ トラックのキュー イベントは発生しません。 この例では、キューに関連付けられているカスタム データを、単にデバッグ ウィンドウに出力します。

private void MetadataTrack_DataCueEntered(TimedMetadataTrack sender, MediaCueEventArgs args)
{
    DataCue cue = (DataCue)args.Cue;
    string data = System.Text.Encoding.Unicode.GetString(cue.Data.ToArray());
    System.Diagnostics.Debug.WriteLine("Cue entered: " + data);
    System.Diagnostics.Debug.WriteLine("Custom prop value: " + cue.Properties["AdUrl"]);
}

この例では、トラックを作成するときに TimedMetadataKind.Caption を指定し、TimedTextCue オブジェクトを使ってトラックにキューを追加することによって、カスタム テキスト トラックを追加します。

TimedMetadataTrack metadataTrack = new TimedMetadataTrack("TrackID_0", "en-us", TimedMetadataKind.Caption);
metadataTrack.Label = "Custom text track";
metadataTrack.CueEntered += MetadataTrack_TextCueEntered;

for (int i = 0; i < 10; i++)
{
    TimedTextCue cue = new TimedTextCue()
    {
        Id = "TextCueID_" + i,
        StartTime = TimeSpan.FromSeconds(i * 3),
        Duration = TimeSpan.FromSeconds(2)
    };

    cue.Lines.Add(new TimedTextLine() { Text = "This is a custom timed text cue." });
    metadataTrack.AddCue(cue);
}

_mediaSource.ExternalTimedMetadataTracks.Add(metadataTrack);
private void MetadataTrack_TextCueEntered(TimedMetadataTrack sender, MediaCueEventArgs args)
{
    TimedTextCue cue = (TimedTextCue)args.Cue;
    System.Diagnostics.Debug.WriteLine("Cue entered: " + cue.Id + " " + cue.Lines[0].Text);
}

MediaPlaybackList を使ってメディア項目のリストを再生する

MediaPlaybackList を使うことにより、MediaPlaybackItem オブジェクトによって表されるメディア項目の再生リストを作成できます。

注:MediaPlaybackList 内の項目は、ギャップレス再生を使用してレンダリングされます。 システムは、MP3 または AAC でエンコードされたファイルで提供されるメタデータを使用して、ギャップレス再生に必要な遅延またはパディングの補正を決定します。 MP3 または AAC でエンコードされているファイルでこのメタデータが提供されない場合は、システムがヒューリスティックによって遅延またはパディングを決定します。 ロスレス形式 (PCM、FLAC、ALAC など) の場合、これらのエンコーダーによる遅延やパディングが発生しないため、システムが実行する処理はありません。

最初に、MediaPlaybackList を格納するための変数を宣言します。

MediaPlaybackList _mediaPlaybackList;

この記事で既に説明した手順と同じ手順で、リストに追加する各メディア項目について MediaPlaybackItem を作成します。 MediaPlaybackList オブジェクトを初期化し、メディア再生項目を追加します。 CurrentItemChanged イベントのハンドラーを登録します。 このイベントによって、UI を更新して現在再生中のメディア項目を反映できます。 リスト内の項目が正常に開かれたときに発生する ItemOpenedイベントや、リスト内の項目を開くことができないときに発生する ItemFailed イベントに登録することもできます。

Windows 10 Version 1703 以降では、MaxPlayedItemsToKeepOpen プロパティを設定することによって、システムが再生後に開いたままにしておく、MediaPlaybackList 内の MediaPlaybackItem オブジェクトの最大数を指定できます。 MediaPlaybackItem を開いたままにしておくと、ユーザーがその項目に切り替えたときに、項目を再び読み込む必要がないため、すぐに項目の再生を開始できます。 ただし、項目を開いたままにしておくと、アプリのメモリ消費量も増加するため、この値を設定する場合は、応答性とメモリ使用量のバランスを考慮する必要があります。

リストの再生を有効にするには、MediaPlayer の再生ソースを MediaPlaybackList に設定します。

_mediaPlaybackList = new MediaPlaybackList();

var files = await filePicker.PickMultipleFilesAsync();

foreach (var file in files)
{
    var mediaPlaybackItem = new MediaPlaybackItem(MediaSource.CreateFromStorageFile(file));
    _mediaPlaybackList.Items.Add(mediaPlaybackItem);
}

_mediaPlaybackList.CurrentItemChanged += MediaPlaybackList_CurrentItemChanged;
_mediaPlaybackList.ItemOpened += MediaPlaybackList_ItemOpened;
_mediaPlaybackList.ItemFailed += MediaPlaybackList_ItemFailed;

_mediaPlaybackList.MaxPlayedItemsToKeepOpen = 3;

_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = _mediaPlaybackList;
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);

CurrentItemChanged イベント ハンドラーで、UI を更新して現在再生中の項目を反映します。再生中の項目は、イベントに渡された CurrentMediaPlaybackItemChangedEventArgs オブジェクトの NewItem プロパティを使って取得できます。 このイベントから UI を更新する場合は、更新が UI スレッドで行われるように、CoreDispatcher.RunAsync の呼び出し内で実行する必要があります。

Windows 10 Version 1703 以降では、CurrentMediaPlaybackItemChangedEventArgs.Reason プロパティを調べて、項目が変更された理由 (アプリがプログラムによって項目を切り替えた、以前に再生していた項目の末尾に到達した、エラーが発生したなど) を示す値を取得できます。

private void MediaPlaybackList_CurrentItemChanged(MediaPlaybackList sender, CurrentMediaPlaybackItemChangedEventArgs args) => 
    LogTelemetryData($"CurrentItemChanged reason: {args.Reason.ToString()}");

メディア プレーヤーで MediaPlaybackList の前の項目や次の項目を再生するには、MovePrevious または MoveNext を呼び出します。

private void prevButton_Click(object sender, RoutedEventArgs e) =>  _mediaPlaybackList.MovePrevious();
private void nextButton_Click(object sender, RoutedEventArgs e) => _mediaPlaybackList.MoveNext();

ShuffleEnabled プロパティを設定して、メディア プレーヤーがリスト内の項目をランダムな順序で再生するかどうかを指定します。

private async void shuffleButton_Click(object sender, RoutedEventArgs e)
{
    _mediaPlaybackList.ShuffleEnabled = !_mediaPlaybackList.ShuffleEnabled;

    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        shuffleButton.FontWeight =
            _mediaPlaybackList.ShuffleEnabled ? Windows.UI.Text.FontWeights.Bold : Windows.UI.Text.FontWeights.Light;
    });
}

AutoRepeatEnabled プロパティを設定して、メディア プレーヤーがリストをループ再生するかどうかを指定します。

private async void autoRepeatButton_Click(object sender, RoutedEventArgs e)
{
    _mediaPlaybackList.AutoRepeatEnabled = !_mediaPlaybackList.AutoRepeatEnabled;

    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        autoRepeatButton.FontWeight =
            _mediaPlaybackList.AutoRepeatEnabled ? Windows.UI.Text.FontWeights.Bold : Windows.UI.Text.FontWeights.Light;
    });
}

再生リスト内のメディア項目のエラーへの対処

ItemFailed イベントは、リスト内の項目を開けない場合に発生します。 ハンドラーに渡された MediaPlaybackItemError オブジェクトの ErrorCode プロパティは、ネットワーク エラー、デコード エラー、暗号化エラーなど、可能であればエラーの具体的な原因を列挙します。

private void MediaPlaybackList_ItemFailed(MediaPlaybackList sender, MediaPlaybackItemFailedEventArgs args)
{
    LogError(args.Error.ErrorCode.ToString());
    LogError(args.Error.ExtendedError.HResult);
}

再生リストの項目の再生の無効化

Windows 10 Version 1703 以降では、MediaPlaybackItemIsDisabledInPlaybackList プロパティを false に設定することにより、MediaPlaybackItemList 内の 1 つまたは複数の項目の再生を無効にすることができます。

この機能の一般的なシナリオは、インターネットからストリーム配信される音楽を再生するアプリです。 このアプリでは、デバイスのネットワーク接続状態の変化をリッスンし、完全にダウンロードされていない項目の再生を無効にすることができます。 次の例では、NetworkInformation.NetworkStatusChanged イベントのハンドラーが登録されています。

Windows.Networking.Connectivity.NetworkInformation.NetworkStatusChanged += NetworkInformation_NetworkStatusChanged;

NetworkStatusChanged のハンドラーで、GetInternetConnectionProfile が null (ネットワークが接続されていないことを示す) を返すかどうかを確認します。 これに該当する場合、再生リスト内のすべての項目をループ処理し、項目の TotalDownloadProgress が 1 未満である (つまり項目が完全にダウンロードされていない) 場合は、項目を無効にします。 ネットワーク接続が有効である場合は、再生リスト内のすべての項目をループ処理し、各項目を有効にします。

private void NetworkInformation_NetworkStatusChanged(object sender)
{
    if (Windows.Networking.Connectivity.NetworkInformation.GetInternetConnectionProfile() == null)
    {
        // Check download status of each item in the list. (TotalDownloadProgress < 1 means not completely downloaded)
        foreach (var item in _mediaPlaybackList.Items)
        {
            if (item.TotalDownloadProgress < 1)
            {
                item.IsDisabledInPlaybackList = true;
            }
        }
    }
    else
    {
        // Connected to internet, re-enable all playlist items
        foreach (var item in _mediaPlaybackList.Items)
        {
            item.IsDisabledInPlaybackList = true;
        }
    }
}

MediaBinder を使用して再生リスト内の項目に対するメディア コンテンツのバインドを延期する

前の例では、ファイル、URL、ストリームから MediaSource が作成され、その後 MediaPlaybackItem が作成され、MediaPlaybackList に追加されます。 ユーザーがコンテンツを表示する際に課金されている場合など、いくつかのシナリオでは、再生リスト内の項目を実際に再生する準備ができるまで、MediaSource のコンテンツの取得を延期することができます。 このシナリオを実装するには、MediaBinder クラスのインスタンスを作成します。 Token プロパティを、取得を延期するコンテンツを識別するための、アプリで定義された文字列に設定し、Binding イベントのハンドラーを登録します。 次に、MediaSource.CreateFromMediaBinder を呼び出すことによって、Binder から MediaSource を作成します。 その後、通常の手順で、MediaSource から MediaPlaybackItem を作成し、再生リストに追加します。

_mediaPlaybackList = new MediaPlaybackList();

var binder = new MediaBinder();
binder.Token = "MyBindingToken1";
binder.Binding += Binder_Binding;
_mediaPlaybackList.Items.Add(new MediaPlaybackItem(MediaSource.CreateFromMediaBinder(binder)));

binder = new MediaBinder();
binder.Token = "MyBindingToken2";
binder.Binding += Binder_Binding;
_mediaPlaybackList.Items.Add(new MediaPlaybackItem(MediaSource.CreateFromMediaBinder(binder)));

_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = _mediaPlaybackList;
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);

システムは、MediaBinder に関連付けられているコンテンツを取得する必要があることを特定すると、Binding イベントを発生させます。 このイベントのハンドラーで、イベントに渡された MediaBindingEventArgs から MediaBinder インスタンスを取得できます。 Token プロパティに指定された文字列を取得し、その文字列を使用して取得する必要があるコンテンツを特定します。 MediaBindingEventArgs は、いくつかの異なる表現でバインドされるコンテンツを設定するためのメソッド (SetStorageFileSetStreamSetStreamReferenceSetUri など) を提供します。

private void Binder_Binding(MediaBinder sender, MediaBindingEventArgs args)
{
    // Get a deferral if you need to perform async operations
    // var deferral = args.GetDeferral();

    var contentUri = new Uri("http://contoso.com/media/" + args.MediaBinder.Token);
    args.SetUri(contentUri);

    // Call complete after your async operations are complete
    // deferral.Complete();
}

Binding イベント ハンドラーで、Web 要求などの非同期操作を実行している場合、MediaBindingEventArgs.GetDeferral メソッドを呼び出して、操作が完了するまで待機してから続行するようにシステムに指示する必要があります。 操作が完了した後、Deferral.Complete を呼び出して、システムに続行を指示します。

Windows 10 Version 1703 以降では、SetAdaptiveMediaSource を呼び出すことによって、バインドされているコンテンツとして AdaptiveMediaSource を指定できます。 アプリでのアダプティブ ストリーミングの使用について詳しくは、「アダプティブ ストリーミング」をご覧ください。