미디어 항목, 재생 목록 및 트랙

이 문서에서는 MediaSource를 사용하는 법, 즉 이 문서에서는 로컬 또는 원격 파일과 같은 다양한 소스의 미디어를 참조하고 재생하는 공통된 방법을 제공하고 기본 미디어 형식에 관계없이 미디어 데이터에 액세스하는 공통 모델을 노출하는 법을 설명합니다. MediaPlaybackItem 클래스는 MediaSource의 기능을 확장하여 미디어 항목에 포함된 여러 오디오, 비디오 및 메타데이터 트랙을 관리하고 선택할 수 있습니다. MediaPlaybackList를 사용하면 하나 이상의 미디어 재생 항목에서 재생 목록을 만들 수 있습니다.

MediaSource 만들기 및 재생

클래스에서 노출하는 팩터리 메서드 중 하나를 호출하여 MediaSource의 새 인스턴스를 만듭니다.

MediaSource를 만든 후 Source 속성을 설정하여 MediaPlayer로 재생할 수 있습니다. Windows 10 버전 1607부터는 XAML 페이지에서 미디어 플레이어 콘텐츠를 렌더링하기 위해 SetMediaPlayer를 호출하여 MediaPlayerMediaPlayerElement에 할당할 수 있습니다. MediaElement를 사용하는 방법보다 기본 설정 방법입니다. MediaPlayer 사용 방법은 MediaPlayer를 사용하여 오디오 및 비디오 재생을 참조하세요.

다음 예제에서는 MediaSource를 사용하여 MediaPlayer에서 사용자가 선택한 미디어 파일을 재생하는 방법을 보여줍니다.

이 시나리오를 완료하려면 Windows.Media.CoreWindows.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;

Create a MediaSource from a DownloadOperation

Windows, 버전 1803부터 DownloadOperationMediaSource 개체를 만들 수 있습니다.

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

MediaSource을 시작하지 않거나 그 IsRandomAccessRequired 속성을 true로 설정하지 않고 다운로드하여 만들 수 있지만, MediaPlayer 또는 MediaPlayerElementMediaSource을 첨부하여 재생하기 전에 이 두 가지 작업을 모두 수행해야 합니다.

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

MediaPlaybackItem을 사용하여 여러 오디오, 비디오 및 메타데이터 트랙 처리

다른 종류의 원본에서 미디어를 재생하는 일반적인 방법을 제공하기 때문에 재생에 MediaSource를 사용하는 것이 편리하지만 MediaSource에서 MediaPlaybackItem을 만들어 고급 동작에 액세스할 수 있습니다. 여기에는 미디어 항목에 대한 여러 오디오, 비디오 및 데이터 트랙에 액세스하고 관리하는 기능이 포함됩니다.

MediaPlaybackItem을 저장할 변수를 선언합니다.

MediaPlaybackItem _mediaPlaybackItem;

생성자를 호출하고 초기화된 MediaSource 개체를 전달하여 MediaPlaybackItem을 만듭니다.

앱이 미디어 재생 항목에서 여러 오디오, 비디오 또는 데이터 트랙을 지원하는 경우 AudioTracksChanged, VideoTracksChanged, or 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을 사용해야 합니다.

VideoTracksChanged 이벤트는 여러 비디오 트랙이 포함된 MediaPlaybackItem이 재생 원본으로 할당된 후 발생하며, 비디오 목록이 항목 변경에 대한 변경 내용을 추적하는 경우 다시 발생할 수 있습니다. 이 이벤트의 처리기는 사용자가 사용 가능한 트랙 간에 전환할 수 있도록 UI를 업데이트할 수 있는 기회를 제공합니다. 이 예제에서는 ComboBox를 사용하여 사용 가능한 비디오 트랙을 표시합니다.

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

VideoTracksChanged 처리기에서 재생 항목의 VideoTracks 목록에 있는 모든 트랙을 반복합니다. 각 트랙에 대해 새 ComboBoxItem이 만들어집니다. 트랙에 레이블이 아직 없으면 트랙 인덱스에서 레이블이 생성됩니다. 나중에 식별할 수 있도록 콤보 상자 항목의 Tag 속성이 트랙 인덱스로 설정됩니다. 마지막으로 항목이 콤보 상자에 추가됩니다. 이러한 작업은 UI 스레드에서 모든 UI를 변경해야 하고 이 이벤트는 다른 스레드에서 발생하므로 CoreDispatcher.RunAsync 호출 내에서 수행됩니다.

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를 업데이트합니다. 사용자가 오디오 트랙을 선택하면 MediaElement 또는 MediaPlayer가 활성 오디오 트랙을 지정된 인덱스로 전환하도록 AudioTracks 목록의 SelectedIndex 속성을 설정합니다.

<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 개체에는 오디오 및 비디오 외에도 TimedMetadataTrack 개체가 0개 이상 포함될 수 있습니다. 시간 제한 메타데이터 트랙은 자막 또는 캡션 텍스트를 포함하거나 앱에 독점적인 사용자 지정 데이터를 포함할 수 있습니다. 시간 제한 메타데이터 트랙에는 DataCue 또는 TimedTextCue와 같이 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(시스템 미디어 전송 컨트롤)에 자동으로 통합됩니다. MediaPlaybackItem의 표시 속성을 업데이트하여 SMTC에서 표시할 메타데이터를 지정할 수 있습니다. GetDisplayProperties를 호출하여 항목의 표시 속성을 나타내는 개체를 가져옵니다. Type 속성을 설정하여 재생 항목이 음악인지 비디오인지를 설정합니다. 그런 다음 개체의 VideoProperties 또는 MusicProperties의 속성을 설정합니다. ApplyDisplayProperties를 호출하여 항목의 속성을 제공한 값으로 업데이트합니다. 일반적으로 앱은 웹 서비스에서 표시 값을 동적으로 검색하지만 다음 예제에서는 하드 코딩된 값을 사용하여 이 프로세스를 보여 줍니다.

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를 사용하여 외부 시간 제한 텍스트 추가

일부 시나리오에서는 미디어 항목과 연결된 시간 제한 텍스트를 포함하는 외부 파일(예: 다른 로캘에 대한 자막이 포함된 별도의 파일)이 있을 수 있습니다. TimedTextSource 클래스를 사용하여 스트림 또는 URI에서 외부 시간 제한 텍스트 파일을 로드합니다.

이 예제에서는 Dictionary 컬렉션을 사용하여 원본 URI 및 TimedTextSource 개체를 키/값 쌍으로 사용하여 미디어 항목의 시간 제한 텍스트 원본 목록을 저장하여 확인된 후 트랙을 식별합니다.

Dictionary<TimedTextSource, Uri> timedTextSourceMap;

CreateFromUri를 호출하여 각 외부 시간 제한 텍스트 파일에 대해 새 TimedTextSource를 만듭니다. 시간 제한 텍스트 원본의 사전에 항목을 추가합니다. 항목이 로드되지 않은 경우 처리하거나 항목이 성공적으로 로드된 후 추가 속성을 설정할 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에서 지원되는 시간 제한 텍스트 형식 목록은 지원되는 코덱을 참조하세요.

추가 메타데이터 트랙 추가

코드에서 사용자 지정 메타데이터 트랙을 동적으로 만들고 미디어 원본과 연결할 수 있습니다. 만든 트랙에는 부제 또는 캡션 텍스트가 포함되거나 소유 앱 데이터가 포함될 수 있습니다.

생성자를 호출하고 TimedMetadataKind 열거형의 ID, 언어 식별자 및 값을 지정하여 새 TimedMetadataTrack을 만듭니다. CueEnteredCueExited 이벤트에 대한 핸들러를 등록합니다. 이러한 이벤트는 큐에 대한 시작 시간에 도달하고 큐의 기간이 만료될 때 각각 발생합니다.

생성한 메타데이터 트랙 유형에 적합한 새 신호 개체를 만들고 트랙의 ID, 시작 시간, 지속 시간을 설정합니다. 이 예에서는 데이터 트랙을 만들어서 DataCue 개체 세트를 생성하고 각 신호별로 앱별 데이터를 포함하는 버퍼를 제공합니다. 새 트랙을 등록하려면 MediaSource 개체의 ExternalTimedMetadataTracks 컬렉션에 추가합니다.

Windows 10 버전 1703부터 DataCue.Properties 속성은 CueEnteredCueExited 이벤트에서 검색할 수 있는 키/데이터 쌍으로 사용자 지정 속성을 저장하는 데 사용할 수 있는 PropertySet를 노출합니다.

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 이벤트는 관련된 트랙에 ApplicationPresented, Hidden 또는 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 개체로 표시되는 미디어 항목의 재생 목록을 만들 수 있습니다.

MediaPlaybackListNote 항목은 간격 없는 재생을 사용하여 렌더링됩니다. 시스템은 MP3 또는 AAC로 인코딩된 파일에서 제공된 메타데이터를 사용하여 간격 없는 재생에 필요한 지연 또는 패딩 보정을 결정합니다. MP3 또는 AAC로 인코딩된 파일이 이 메타데이터를 제공하지 않는 경우 시스템은 지연 또는 패딩을 추론적으로 결정합니다. PCM, FLAC 또는 ALAC와 같은 무손실 형식의 경우 이러한 인코더는 지연 또는 패딩을 도입하지 않으므로 시스템에서 아무런 조치도 취하지 않습니다.

시작하려면 MediaPlaybackList를 저장할 변수를 선언합니다.

MediaPlaybackList _mediaPlaybackList;

이 문서의 앞에서 설명한 것과 동일한 절차를 사용하여 목록에 추가하려는 각 미디어 항목에 대한 MediaPlaybackItem을 만듭니다. MediaPlaybackList 개체를 초기화하고 미디어 재생 항목을 추가합니다. CurrentItemChanged 이벤트에 대한 처리기를 등록합니다. 이 이벤트를 사용하면 현재 재생 중인 미디어 항목을 반영하도록 UI를 업데이트할 수 있습니다. 또한 목록에서 항목을 성공적으로 열 때 발생하는 ItemOpened 이벤트 및 목록에서 항목을 열 수 없는 경우 발생하는 ItemFailed 이벤트를 등록할 수 있습니다.

Windows 10 버전 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 버전 1703부터 CurrentMediaPlaybackItemChangedEventArgs.Reason 속성을 확인하여 프로그래밍 방식의 앱 전환 항목, 이전에 재생된 항목의 종료, 또는 오류 발생 등 항목이 변경된 이유를 나타내는 값을 가져올 수 있습니다.

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

MovePrevious 또는 MoveNext를 호출하여 미디어 플레이어가 MediaPlaybackList에서 이전 또는 다음 항목을 재생하도록 합니다.

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 버전 1703부터 MediaPlaybackItemIsDisabledInPlaybackList 속성을 false로 설정하여 MediaPlaybackItemList에 있는 하나 이상의 항목의 재생을 사용 안 함으로 설정할 수 있습니다.

이 기능에 대한 일반적인 시나리오는 인터넷에서 스트리밍되는 음악을 재생하는 앱입니다. 앱은 디바이스의 네트워크 연결 상태 변경 내용을 수신하고 완전히 다운로드되지 않은 항목의 재생을 사용하지 않도록 설정할 수 있습니다. 다음 예에서 처리기는 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를 사용하여 재생 목록의 항목에 대한 미디어 콘텐츠 바인딩 연기

위 예에서 MediaSource는 파일, URL 또는 스트림에서 생성되며 이후 MediaPlaybackItem을 만들고 MediaPlaybackList에 추가합니다. 사용자에게 콘텐츠 열람에 대해 비용이 청구되는 등 일부 시나리오의 경우 재생 목록의 항목이 실제로 재생할 준비가 될 때까지 MediaSource의 콘텐츠 검색을 연기할 수 있습니다. 이 시나리오를 구현하려면 MediaBinder 클래스의 인스턴스를 만듭니다. 토큰 속성을 검색을 연기하고 바인딩 이벤트에 대한 처리기를 등록하고자 하는 콘텐츠를 식별하는 앱 정의 문자열로 설정합니다. 그 다음 MediaSource.CreateFromMediaBinder를 호출하여 바인더에서 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와 관련된 콘텐츠를 검색하도록 결정한 경우 바인딩 이벤트가 발생합니다. 이 이벤트에 대한 처리기에서 이벤트에 전달된 MediaBindingEventArgs로부터 MediaBinder 인스턴스를 검색할 수 있습니다. 토큰 속성에 대해 지정한 문자열을 검색하고 이를 사용하여 검색해야 하는 콘텐츠 확인할 수 있습니다. MediaBindingEventArgsSetStorageFile, SetStream, SetStreamReferenceSetUri 등 여러 가지 표현에 바인딩된 콘텐츠를 설정하는 메서드를 제공합니다.

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

웹 요청 등 비동기 작업을 수행하는 경우 바인딩 이벤트 처리기에서 MediaBindingEventArgs.GetDeferral 메서드를 호출하여 작업을 진행하기 전에 시스템이 사용자 작업이 완료될 때까지 대기하도록 지시할 수 있습니다. 작업이 완료된 후 시스템에 계속하도록 지시하기 위해 Deferral.Complete를 호출합니다.

Windows 10 버전 1703부터 SetAdaptiveMediaSource를 호출하여 AdaptiveMediaSource를 바인딩된 콘텐츠로 제공할 수 있습니다. 앱에서 적응 스트리밍을 사용하는 것에 대한 자세한 내용은 적응 스트리밍을 참조하세요.