Elementi multimediali, playlist e tracce

Questo articolo illustra come usare la classe MediaSource che offre un modo comune per fare riferimento e riprodurre supporti da origini diverse, ad esempio file locali o remoti, ed espone un modello comune per l'accesso ai dati multimediali, indipendentemente dal formato multimediale sottostante. La classe MediaPlaybackItem estende la funzionalità di MediaSource, consentendo di gestire e selezionare da più tracce audio, video e metadati contenute in un elemento multimediale. MediaPlaybackList consente di creare elenchi di riproduzione da uno o più elementi di riproduzione multimediale.

Creare e riprodurre un oggetto MediaSource

Creare una nuova istanza di MediaSource chiamando uno dei metodi factory esposti dalla classe:

Dopo aver creato un oggetto MediaSource si può riprodurlo con un MediaPlayer impostando la proprietà Source. A partire da Windows 10, versione 1607, puoi assegnare un MediaPlayer a un MediaPlayerElement chiamando SetMediaPlayer per eseguire il rendering del contenuto del lettore multimediale in una pagina XAML. Questo è il metodo preferito rispetto all'uso di MediaElement. Per altre informazioni sull'uso di MediaPlayer, see Riprodurre audio e video con MediaPlayer.

L'esempio seguente mostra come riprodurre un file multimediale selezionato dall'utente in un MediaPlayer usando MediaSource.

Per completare questo scenario, si dovranno includere gli spazi dei nomi Windows.Media.Core e Windows.Media.Playback.

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

Dichiarare una variabile di tipo MediaSource. Per gli esempi in questo articolo, l'origine multimediale viene dichiarata come membro di classe in modo che sia accessibile da più posizioni.

MediaSource _mediaSource;

Dichiarare una variabile per archiviare l'oggetto MediaPlayer e, se si vuole eseguire il rendering del contenuto multimediale in XAML, aggiungere un controllo MediaPlayerElement alla pagina.

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

Per consentire all'utente di selezionare un file multimediale da riprodurre, usare un oggetto FileOpenPicker. Con l'oggetto StorageFile restituito dal metodo selettorePickSingleFileAsync, inizializzare un nuovo MediaObject chiamando MediaSource.CreateFromStorageFile. Impostare infine l'origine multimediale come origine di riproduzione per MediaElement chiamando il metodo SetPlaybackSource.

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

Per impostazione predefinita, MediaPlayer non inizia automaticamente la riproduzione quando è impostata l'origine multimediale. Si può iniziare manualmente la riproduzione chiamando Play.

_mediaPlayer.Play();

Si può anche impostare la proprietà AutoPlay di MediaPlayer su true per indicare al lettore di iniziare la riproduzione non appena viene impostata l'origine multimediale.

_mediaPlayer.AutoPlay = true;

Creare un oggetto MediaSource da un oggetto DownloadOperation

A partire da Windows, versione 1803, è possibile creare un oggetto MediaSource da un oggetto DownloadOperation.

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

Tenere presente che mentre si può creare un MediaSource da un download senza avviarlo o impostarne la proprietà IsRandomAccessRequired su true, si devono eseguire entrambe queste operazioni prima di tentare di collegare MediaSource a un MediaPlayer o MediaPlayerElement per la riproduzione.

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

Gestire più tracce audio, video e metadati con MediaPlaybackItem

L'uso di un MediaSource per la riproduzione è pratico perché offre un modo comune per riprodurre elementi multimediali da diversi tipi di origini, ma è possibile accedere a un comportamento più avanzato creando un oggetto MediaPlaybackItem da MediaSource. Ciò include la possibilità di accedere e gestire più tracce audio, video e dati per un elemento multimediale.

Dichiarare una variabile per archiviare MediaPlaybackItem.

MediaPlaybackItem _mediaPlaybackItem;

Creare un oggetto MediaPlaybackItem chiamando il costruttore e passando un oggetto MediaSource inizializzato.

Se l'app supporta più tracce audio, video o dati in un elemento di riproduzione multimediale, registrare i gestori eventi per gli eventi AudioTracksChanged, VideoTracksChanged o TimedMetadataTracksChanged.

Infine, impostare l'origine di riproduzione di MediaElement o MediaPlayer per 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);

Nota

Un oggetto MediaSource può essere associato solo a un singolo Oggetto MediaPlaybackItem. Dopo aver creato un oggetto MediaPlaybackItem da un'origine, il tentativo di creare un altro elemento di riproduzione dalla stessa origine genererà un errore. Inoltre, dopo aver creato un oggetto MediaPlaybackItem da un'origine multimediale, non è possibile impostare l'oggetto MediaSource direttamente come sorgente di un MediaPlayer ma si deve invece usare MediaPlaybackItem.

L'evento VideoTracksChanged viene generato dopo l'assegnazione di un oggetto MediaPlaybackItem contenente più tracce video come origine di riproduzione e può essere generato di nuovo se l'elenco di tracce video cambia per l'elemento. Il gestore per questo evento offre la possibilità di aggiornare l'interfaccia utente per consentire all'utente di passare da una traccia all'altra disponibile. In questo esempio viene usato un controllo ComboBox per visualizzare le tracce video disponibili.

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

Nel gestore VideoTracksChanged scorrere tutte le tracce nell'elenco VideoTracks dell'elemento di riproduzione. Per ogni traccia viene creato un nuovo oggetto ComboBoxItem. Se la traccia non ha già un'etichetta, viene generata un'etichetta dall'indice di traccia. La proprietà Tag dell'elemento casella combinata viene impostata sull'indice di traccia in modo che possa essere identificato in un secondo momento. Infine, l'elemento viene aggiunto alla casella combinata. Si noti che queste operazioni vengono eseguite all'interno di una chiamata CoreDispatcher.RunAsync perché tutte le modifiche dell'interfaccia utente devono essere apportate nel thread dell'interfaccia utente e questo evento viene generato in un thread diverso.

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

Nel gestore SelectionChanged per la casella combinata, l'indice di traccia viene recuperato dalla proprietà Tag dell'elemento selezionato. L'impostazione della proprietà SelectedIndex dell'elemento di riproduzione multimediale dell'elenco VideoTracks fa sì che MediaElement o MediaPlayer cambi la traccia video attiva sull'indice specificato.

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

La gestione degli elementi multimediali con più tracce audio funziona esattamente come con le tracce video. Gestire AudioTracksChanged per aggiornare l'interfaccia utente con le tracce audio trovate nell'elenco di AudioTracks. Quando l'utente seleziona una traccia audio, imposta la proprietà SelectedIndex dell'elenco AudioTracks per fare in modo che MediaElement o MediaPlayer cambi la traccia audio attiva sull'indice specificato.

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

Oltre all'audio e al video, un oggetto MediaPlaybackItem può contenere zero o più oggetti TimedMetadataTrack. Una traccia dei metadati a tempo può contenere sottotitolo o testo didascalia oppure può contenere dati personalizzati proprietari dell'app. Una traccia di metadati a tempo contiene un elenco di segnali rappresentati da oggetti che ereditano da IMediaCue, come un DataCue o un TimedTextCue. Ogni segnale ha un'ora di inizio e una durata che determina quando il segnale viene attivato e per quanto tempo.

Analogamente alle tracce audio e alle tracce video, è possibile individuare le tracce di metadati temporali per un elemento multimediale gestendo l'evento TimedMetadataTracksChanged di un MediaPlaybackItem. Con le tracce dei metadati a tempo, tuttavia, l'utente potrebbe voler abilitare più di una traccia dei metadati alla volta. Inoltre, a seconda dello scenario dell'app, è possibile abilitare o disabilitare automaticamente le tracce dei metadati, senza l'intervento dell'utente. A scopo illustrativo, in questo esempio viene aggiunto un controllo ToggleButton per ogni traccia di metadati in un elemento multimediale per consentire all'utente di abilitare e disabilitare la traccia. La proprietà Tag di ogni pulsante è impostata sull'indice della traccia dei metadati associata in modo che possa essere identificata quando il pulsante viene attivato o disattivato.

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

Poiché più tracce di metadati possono essere attive alla volta, non è sufficiente impostare l'indice attivo per l'elenco di tracce dei metadati. Chiamare invece il metodo SetPresentationMode dell'oggetto MediaPlaybackItem, passando l'indice della traccia che si desidera attivare o disattivare e quindi specificando un valore dall'enumerazione TimedMetadataTrackPresentationMode. La modalità presentazione scelta dipende dall'implementazione dell'app. In questo esempio, la traccia dei metadati è impostata su PlatformPresented se abilitata. Per le tracce basate su testo, questo significa che il sistema visualizzerà automaticamente i segnali di testo nella traccia. Quando l'interruttore è disattivato, la modalità presentazione è impostata su Disabilitato, il che significa che non viene visualizzato alcun testo e non vengono generati eventi di segnale. Gli eventi cue vengono descritti più avanti in questo articolo.

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

Durante l'elaborazione delle tracce dei metadati, è possibile accedere al set di segnali all'interno della traccia accedendo alle proprietà Cues o ActiveCues. È possibile eseguire questa operazione per aggiornare l'interfaccia utente per visualizzare i percorsi dei segnali per un elemento multimediale.

Gestire codec non supportati ed errori sconosciuti durante l'apertura di elementi multimediali

A partire da Windows 10 versione 1607, è possibile verificare se il codec necessario per riprodurre un elemento multimediale è supportato o parzialmente supportato nel dispositivo in cui è in esecuzione l'app. Nel gestore eventi per gli eventi modificati delle tracce MediaPlaybackItem, ad esempio AudioTracksChanged, verificare innanzitutto se la modifica della traccia è un inserimento di una nuova traccia. In tal caso, è possibile ottenere un riferimento alla traccia inserita usando l'indice passato nel parametro IVectorChangedEventArgs.Index con la traccia appropriata della raccolta di parametri MediaPlaybackItem, come la raccolta di AudioTracks.

Dopo aver ottenuto un riferimento alla traccia inserita, controllare il DecoderStatus della proprietà SupportInfo della traccia. Se il valore è FullySupported, il codec appropriato necessario per riprodurre la traccia è presente nel dispositivo. Se il valore è Degraded, la traccia può essere riprodotta dal sistema, ma la riproduzione sarà danneggiata in qualche modo. Ad esempio, una traccia audio 5.1 può essere riprodotta come stereo a 2 canali. In questo caso, potrebbe essere necessario aggiornare l'interfaccia utente per avvisare l'utente della riduzione delle prestazioni. Se il valore è UnsupportedSubtype o UnsupportedEncoderProperties, la traccia non può essere riprodotta affatto con i codec correnti nel dispositivo. È possibile avvisare l'utente e ignorare la riproduzione dell'elemento o implementare l'interfaccia utente per consentire all'utente di scaricare il codec corretto. Il metodo GetEncodingProperties della traccia può essere usato per determinare il codec necessario per la riproduzione.

Infine, è possibile registrarsi per l'evento OpenFailed della traccia, che verrà generato se la traccia è supportata nel dispositivo ma non è riuscita ad aprire a causa di un errore sconosciuto nella pipeline.

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

}

Nel gestore eventi OpenFailed è possibile verificare se lo stato di MediaSource è sconosciuto e, in tal caso, è possibile selezionare a livello di programmazione una traccia diversa da riprodurre, consentire all'utente di scegliere una traccia diversa o abbandonare la riproduzione.

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

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

Impostare le proprietà di visualizzazione utilizzate dai controlli di trasporto multimediale di sistema

A partire da Windows 10, versione 1607, i supporti riprodotti in MediaPlayer vengono integrati automaticamente nei controlli SMTC (System Media Transport Controls) per impostazione predefinita. È possibile specificare i metadati che verranno visualizzati dai controlli SMTC aggiornando le proprietà di visualizzazione per un oggetto MediaPlaybackItem. Ottenere un oggetto che rappresenta le proprietà di visualizzazione per un elemento chiamando GetDisplayProperties. Impostare se l'elemento di riproduzione è musica o video impostando la proprietà Type. Impostare quindi le proprietà VideoProperties o MusicProperties dell'oggetto. Chiamare ApplyDisplayProperties per aggiornare le proprietà dell'elemento ai valori specificati. In genere, un'app recupererà i valori visualizzati in modo dinamico da un servizio Web, ma l'esempio seguente illustra questo processo con valori hardcoded.

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

Aggiungere testo a tempo esterno con TimedTextSource

Per alcuni scenari, è possibile che siano presenti file esterni che contengono testo programmato associato a un elemento multimediale, ad esempio file separati che contengono sottotitoli per impostazioni locali diverse. Usare la classe TimedTextSource per caricare in file di testo a tempo esterno da un flusso o un URI.

In questo esempio viene utilizzata una raccolta Dictionary per archiviare un elenco delle origini di testo programmate per l'elemento multimediale usando l'URI di origine e l'oggetto TimedTextSource come coppia chiave/valore per identificare le tracce dopo la risoluzione.

Dictionary<TimedTextSource, Uri> timedTextSourceMap;

Creare un nuovo TimedTextSource per ogni file di testo timed esterno chiamando CreateFromUri. Aggiungere una voce al Dictionary per l'origine del testo programmata. Aggiungere un gestore per l'evento TimedTextSource.Resolved da gestire se l'elemento non è riuscito a caricare o impostare proprietà aggiuntive dopo che l'elemento è stato caricato correttamente.

Registrare tutti gli oggetti TimedTextSource con MediaSource aggiungendoli alla raccolta ExternalTimedTextSources. Si noti che le origini di testo a tempo esterno vengono aggiunte direttamente a MediaSource e non all'oggetto MediaPlaybackItem creato dall'origine. Per aggiornare l'interfaccia utente in modo da riflettere le tracce di testo esterne, registrare e gestire l'evento TimedMetadataTracksChanged come descritto in precedenza in questo articolo.

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

Nel gestore per l'evento TimedTextSource.Resolved controllare la proprietà Error dell'oggetto TimedTextSourceResolveResultEventArgs passato nel gestore per determinare se si è verificato un errore durante il tentativo di caricare i dati di testo temporizzato. Se l'elemento è stato risolto correttamente, è possibile usare questo gestore per aggiornare proprietà aggiuntive della traccia risolta. In questo esempio viene aggiunta un'etichetta per ogni traccia in base all'URI archiviato in precedenza nel Dictionary.

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";
    }
}

Per un elenco dei formati di testo a tempo supportati in Windows, vedere Codec supportati.

Aggiungere tracce di metadati aggiuntive

È possibile creare in modo dinamico tracce di metadati personalizzate nel codice e associarle a un'origine multimediale. Le tracce create possono contenere sottotitolo o testo didascalia oppure possono contenere i dati dell'app proprietari.

Creare un nuovo TimedMetadataTrack chiamando il costruttore e specificando un ID, l'identificatore di lingua e un valore dall'enumerazione TimedMetadataKind. Registrare i gestori per gli eventi CueEntered e CueExited. Questi eventi vengono generati quando è stata raggiunta l'ora di inizio di un segnale e quando la durata di un segnale è scaduta, rispettivamente.

Creare un nuovo oggetto segnale, appropriato per il tipo di traccia dei metadati creato e impostare l'ID, l'ora di inizio e la durata per la traccia. Questo esempio crea una traccia dei dati, quindi viene generato un set di oggetti DataCue e viene fornito un buffer contenente dati specifici dell'app per ogni segnale. Per registrare la nuova traccia, aggiungerla all'insieme ExternalTimedMetadataTracks dell'oggetto MediaSource.

A partire da Windows 10, versione 1703, la proprietà DataCue.Properties espone un PropertySet che si può usare per archiviare proprietà personalizzate in coppie chiave/dati che possono essere recuperate negli eventi CueEntered e CueExited.

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

L'evento CueEntered viene generato quando viene raggiunta l'ora di inizio di un segnale finché la traccia associata ha una modalità di presentazione di ApplicationPresented, Hidden o PlatformPresented. Gli eventi cue non vengono generati per le tracce di metadati mentre la modalità presentazione per la traccia è Disabilitata. In questo esempio vengono semplicemente restituiti i dati personalizzati associati alla finestra di debug.

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"]);
}

In questo esempio viene aggiunta una traccia di testo personalizzata specificando TimedMetadataKind.Caption durante la creazione della traccia e l'uso di oggetti TimedTextCue per aggiungere segnali alla traccia.

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

Riprodurre un elenco di elementi multimediali con MediaPlaybackList

MediaPlaybackList consente di creare una playlist di elementi multimediali, rappresentati da oggetti MediaPlaybackItem.

Nota Gli elementi in un oggetto MediaPlaybackList vengono visualizzati usando la riproduzione senza interruzioni. Il sistema userà i metadati forniti nei file con codifica MP3 o AAC per determinare il ritardo o la compensazione della spaziatura interna necessaria per la riproduzione senza interruzioni. Se i file con codifica MP3 o AAC non forniscono questi metadati, il sistema determina il ritardo o la spaziatura interna in modo euristico. Per i formati senza perdita, ad esempio PCM, FLAC o ALAC, il sistema non esegue alcuna azione perché questi codificatori non introducono ritardi o spaziatura interna.

Per iniziare, dichiarare una variabile per archiviare MediaPlaybackList.

MediaPlaybackList _mediaPlaybackList;

Creare un oggetto MediaPlaybackItem per ogni elemento multimediale da aggiungere all'elenco usando la stessa procedura descritta in precedenza in questo articolo. Inizializzare l'oggetto MediaPlaybackList e aggiungervi gli elementi di riproduzione multimediale. Registrare un gestore per l'evento CurrentItemChanged. Questo evento consente di aggiornare l'interfaccia utente in modo da riflettere l'elemento multimediale attualmente in riproduzione. È anche possibile registrarsi per l'evento ItemOpened, generato quando un elemento nell'elenco viene aperto correttamente e l'evento ItemFailed, generato quando non è possibile aprire un elemento nell'elenco.

A partire da Windows 10, versione 1703, puoi specificare il numero massimo di oggetti MediaPlaybackItem in MediaPlaybackList che il sistema manterrà aperto dopo che sono stati riprodotti impostando la proprietà MaxPlayedItemsToKeepOpen. Quando un oggetto MediaPlaybackItem viene mantenuto aperto, la riproduzione dell'elemento può essere avviata istantaneamente quando l'utente passa a tale elemento perché l'elemento non deve essere ricaricato. Ma mantenere aperti gli elementi aumenta anche il consumo di memoria dell'app, quindi è consigliabile considerare l'equilibrio tra velocità di risposta e utilizzo della memoria quando si imposta questo valore.

Per abilitare la riproduzione dell'elenco, impostare l'origine di riproduzione di MediaPlayer su 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);

Nell'evento CurrentItemChanged aggiornare l'interfaccia utente in modo da riflettere l'elemento attualmente in riproduzione, che può essere recuperato usando la proprietà NewItem dell'oggetto CurrentMediaPlaybackItemChangedEventArgs passato all'evento. Tenere presente che se si aggiorna l'interfaccia utente da questo evento, è consigliabile eseguire questa operazione all'interno di una chiamata a CoreDispatcher.RunAsync in modo che gli aggiornamenti vengano eseguiti nel thread dell'interfaccia utente.

A partire da Windows 10, versione 1703, puoi controllare la proprietà purrentMediaPlaybackItemChangedEventArgs.Reason per ottenere un valore che indica il motivo per cui l'elemento è stato modificato, ad esempio l'app che cambia elementi a livello di programmazione, l'elemento in riproduzione precedentemente che raggiunge la fine o si verifica un errore.

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

Chiamare MovePrevious o MoveNext per fare in modo che il lettore multimediale giochi l'elemento precedente o successivo in MediaPlaybackList.

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

Impostare la proprietà ShuffleEnabled per specificare se il lettore multimediale deve riprodurre gli elementi nell'elenco in ordine casuale.

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

Impostare la proprietà AutoRepeatEnabled per specificare se il lettore multimediale deve riprodurre in ciclo l'elenco.

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

Gestire l'errore degli elementi multimediali in un elenco di riproduzione

L'evento ItemFailed viene generato quando un elemento dell'elenco non viene aperto. La proprietà ErrorCode dell'oggetto MediaPlaybackItemError passata al gestore enumera la causa specifica dell'errore quando possibile, inclusi errori di rete, errori di decodifica o errori di crittografia.

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

Disabilitare la riproduzione di elementi in un elenco di riproduzione

A partire da Windows 10, versione 1703, puoi disabilitare la riproduzione di uno o più elementi in MediaPlaybackItemList impostando la proprietà IsDisabledInPlaybackList di un oggetto MediaPlaybackItem su false.

Uno scenario tipico per questa funzionalità è per le app che riproducono musica trasmessa da Internet. L'app può restare in ascolto delle modifiche apportate allo stato della connessione di rete del dispositivo e disabilitare la riproduzione di elementi non completamente scaricati. Nell'esempio seguente un gestore viene registrato per l'evento NetworkInformation.NetworkStatusChanged.

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

Nel gestore per NetworkStatusChanged verificare se GetInternetConnectionProfile restituisce null, che indica che la rete non è connessa. In questo caso, scorrere tutti gli elementi nell'elenco di riproduzione e se TotalDownloadProgress per l'elemento è minore di 1, ovvero l'elemento non è stato completamente scaricato, disabilitare l'elemento. Se la connessione di rete è abilitata, scorrere tutti gli elementi nell'elenco di riproduzione e abilitare ogni elemento.

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

Rinviare l'associazione del contenuto multimediale per gli elementi in un elenco di riproduzione tramite MediaBinder

Negli esempi precedenti viene creato un oggetto MediaSource da un file, un URL o un flusso, dopo il quale viene creato e aggiunto un oggetto MediaPlaybackItem a MediaPlaybackList. Per alcuni scenari, ad esempio se l'utente viene addebitato per la visualizzazione del contenuto, potrebbe essere necessario rinviare il recupero del contenuto di un oggetto MediaSource fino a quando l'elemento nell'elenco di riproduzione non è pronto per essere effettivamente riprodotto. Per implementare questo scenario, creare un'istanza della classe MediaBinder. Impostare la proprietà Token su una stringa definita dall'app che identifica il contenuto per il quale si desidera rinviare il recupero e quindi registrare un gestore per l'evento Binding. Quindi, creare un oggetto MediaSource da Binder chiamando MediaSource.CreateFromMediaBinder. Creare quindi un oggetto MediaPlaybackItem da MediaSource e aggiungerlo all'elenco di riproduzione come al solito.

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

Quando il sistema determina che il contenuto associato a MediaBinder deve essere recuperato, genererà l'evento Binding. Nel gestore per questo evento è possibile recuperare l'istanza MediaBinder di MediaBindingEventArgs passato all'evento. Recuperare la stringa specificata per la proprietà Token e usarla per determinare il contenuto da recuperare. MediaBindingEventArgs fornisce metodi per impostare il contenuto associato in diverse rappresentazioni, tra cui SetStorageFile, SetStream, SetStreamReference e SetUri.

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

Si noti che se si eseguono operazioni asincrone, ad esempio le richieste web, nel gestore eventi Binding è necessario chiamare il metodo MediaBindingEventArgs.GetDeferral per indicare al sistema di attendere il completamento dell'operazione prima di continuare. Chiamare Deferral.Complete al termine dell'operazione per indicare al sistema di continuare.

A partire da Windows 10 versione 1703, si può fornire AdaptiveMediaSource come contenuto associato chiamando SetAdaptiveMediaSource. Per altre informazioni sull'uso dello streaming adattivo nell'app, vedere Streaming adattivo.