Flusso adattivo
Questo articolo descrive come aggiungere la riproduzione di contenuti multimediali in streaming adattivo a un'app UWP (piattaforma UWP (Universal Windows Platform)). Questa funzionalità supporta la riproduzione di contenuti Http Live Streaming (HLS) e Dynamic Streaming su HTTP (DASH).
A partire da Windows 10, versione 1803, Smooth Streaming è supportato da AdaptiveMediaSource. Si noti che per lo streaming Smooth sono supportati solo i codec H264 e WVC1. Altri tipi di manifesto non hanno questa limitazione.
Per un elenco dei tag di protocollo HLS supportati, vedere Supporto dei tag HLS.
Per un elenco dei profili DASH supportati, vedere Supporto del profilo DASH.
Nota
Il codice in questo articolo è stato adattato dall'esempio di streaming adattivo.
Streaming adattivo semplice con MediaPlayer e MediaPlayerElement
Per riprodurre contenuti multimediali di streaming adattivi in un'app UWP, creare un oggetto URI che punta a un file manifesto DASH o HLS. Creare un'istanza della classe MediaPlayer. Chiama MediaSource.CreateFromUri per creare un nuovo oggetto MediaSource e quindi impostarlo sulla proprietà Source di MediaPlayer. Chiama Riproduci per avviare la riproduzione del contenuto multimediale.
MediaPlayer _mediaPlayer;
System.Uri manifestUri = new Uri("http://amssamples.streaming.mediaservices.windows.net/49b57c87-f5f3-48b3-ba22-c55cfdffa9cb/Sintel.ism/manifest(format=m3u8-aapl)");
_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = MediaSource.CreateFromUri(manifestUri);
_mediaPlayer.Play();
L'esempio precedente riproduce l'audio del contenuto multimediale, ma non esegue automaticamente il rendering del contenuto nell'interfaccia utente. La maggior parte delle app che riproduce contenuto video vuole eseguire il rendering del contenuto in una pagina XAML. A tale scopo, aggiungere un controllo MediaPlayerElement alla pagina XAML.
<MediaPlayerElement x:Name="mediaPlayerElement" HorizontalAlignment="Stretch" AreTransportControlsEnabled="True"/>
Chiamare MediaSource.CreateFromUri per creare un oggetto MediaSource dall'URI di un file manifesto DASH o HLS. Impostare quindi la proprietà Source di MediaPlayerElement. MediaPlayerElement creerà automaticamente un nuovo oggetto MediaPlayer per il contenuto. Puoi chiamare Play su MediaPlayer per avviare la riproduzione del contenuto.
System.Uri manifestUri = new Uri("http://amssamples.streaming.mediaservices.windows.net/49b57c87-f5f3-48b3-ba22-c55cfdffa9cb/Sintel.ism/manifest(format=m3u8-aapl)");
mediaPlayerElement.Source = MediaSource.CreateFromUri(manifestUri);
mediaPlayerElement.MediaPlayer.Play();
Nota
A partire da Windows 10, versione 1607, è consigliabile usare la classe MediaPlayer per riprodurre elementi multimediali. MediaPlayerElement è un controllo XAML leggero usato per eseguire il rendering del contenuto di un MediaPlayer in una pagina XAML. Il controllo MediaElement continua a essere supportato per la compatibilità con le versioni precedenti. Per altre informazioni sull'uso di MediaPlayer e MediaPlayerElement per riprodurre contenuti multimediali, vedi Riprodurre audio e video con MediaPlayer. Per informazioni sull'uso di MediaSource e sulle API correlate per l'uso del contenuto multimediale, vedere Elementi multimediali, playlist e tracce.
Streaming adattivo con AdaptiveMediaSource
Se l'app richiede funzionalità di streaming adattive più avanzate, ad esempio la fornitura di intestazioni HTTP personalizzate, il monitoraggio delle velocità in bit di download e riproduzione correnti o la regolazione dei rapporti che determinano quando il sistema commuta le velocità in bit del flusso adattivo, usa l'oggetto AdaptiveMediaSource .
Le API di streaming adattivo si trovano nello spazio dei nomi Windows.Media.Streaming.Adaptive. Gli esempi in questo articolo usano le API degli spazi dei nomi seguenti.
using Windows.Media.Streaming.Adaptive;
using System.Threading.Tasks;
using Windows.Storage.Streams;
using Windows.Media.Playback;
using Windows.Media.Core;
Inizializzare un oggetto AdaptiveMediaSource da un URI.
Inizializzare AdaptiveMediaSource con l'URI di un file manifesto del flusso adattivo chiamando CreateFromUriAsync. Il valore AdaptiveMediaSourceCreationStatus restituito da questo metodo consente di sapere se l'origine multimediale è stata creata correttamente. In tal caso, puoi impostare l'oggetto come origine di flusso per MediaPlayer creando un oggetto MediaSource chiamando MediaSource.CreateFromAdaptiveMediaSource e quindi assegnandolo alla proprietà Source del lettore multimediale. In questo esempio viene eseguita una query sulla proprietà AvailableBitrates per determinare la velocità in bit massima supportata per questo flusso e quindi tale valore viene impostato come velocità in bit iniziale. Questo esempio registra anche i gestori per i diversi eventi AdaptiveMediaSource descritti più avanti in questo articolo.
async private void InitializeAdaptiveMediaSource(System.Uri uri)
{
AdaptiveMediaSourceCreationResult result = await AdaptiveMediaSource.CreateFromUriAsync(uri);
if (result.Status == AdaptiveMediaSourceCreationStatus.Success)
{
ams = result.MediaSource;
mediaPlayerElement.SetMediaPlayer(new MediaPlayer());
mediaPlayerElement.MediaPlayer.Source = MediaSource.CreateFromAdaptiveMediaSource(ams);
mediaPlayerElement.MediaPlayer.Play();
ams.InitialBitrate = ams.AvailableBitrates.Max<uint>();
//Register for download requests
ams.DownloadRequested += DownloadRequested;
//Register for download failure and completion events
ams.DownloadCompleted += DownloadCompleted;
ams.DownloadFailed += DownloadFailed;
//Register for bitrate change events
ams.DownloadBitrateChanged += DownloadBitrateChanged;
ams.PlaybackBitrateChanged += PlaybackBitrateChanged;
//Register for diagnostic event
ams.Diagnostics.DiagnosticAvailable += DiagnosticAvailable;
}
else
{
// Handle failure to create the adaptive media source
MyLogMessageFunction($"Adaptive source creation failed: {uri} - {result.ExtendedError}");
}
}
Inizializzare un oggetto AdaptiveMediaSource usando HttpClient
Se è necessario impostare intestazioni HTTP personalizzate per ottenere il file manifesto, è possibile creare un oggetto HttpClient , impostare le intestazioni desiderate e quindi passare l'oggetto all'overload di CreateFromUriAsync.
httpClient = new Windows.Web.Http.HttpClient();
httpClient.DefaultRequestHeaders.TryAppendWithoutValidation("X-CustomHeader", "This is a custom header");
AdaptiveMediaSourceCreationResult result = await AdaptiveMediaSource.CreateFromUriAsync(manifestUri, httpClient);
L'evento DownloadRequested viene generato quando il sistema sta per recuperare una risorsa dal server. AdaptiveMediaSourceDownloadRequestedEventArgs è passato nel gestore eventi espone proprietà che forniscono informazioni sulla risorsa richiesta, ad esempio il tipo e l'URI della risorsa.
Modificare le proprietà della richiesta di risorsa usando l'evento DownloadRequested
È possibile usare il gestore eventi DownloadRequested per modificare la richiesta di risorsa aggiornando le proprietà dell'oggetto AdaptiveMediaSourceDownloadResult fornito dagli argomenti dell'evento. Nell'esempio seguente l'URI da cui verrà recuperata la risorsa viene modificato aggiornando le proprietà ResourceUri dell'oggetto risultato. È anche possibile riscrivere l'offset e la lunghezza dell'intervallo di byte per i segmenti multimediali oppure, come illustrato nell'esempio seguente, modificare l'URI della risorsa per scaricare la risorsa completa e impostare l'offset e la lunghezza dell'intervallo di byte su Null.
È possibile eseguire l'override del contenuto della risorsa richiesta impostando le proprietà Buffer o InputStream dell'oggetto risultato. Nell'esempio seguente il contenuto della risorsa manifesto viene sostituito impostando la proprietà Buffer . Si noti che se si aggiorna la richiesta di risorsa con i dati ottenuti in modo asincrono, ad esempio il recupero di dati da un server remoto o l'autenticazione utente asincrona, è necessario chiamare AdaptiveMediaSourceDownloadRequestedEventArgs.GetDeferral per ottenere un differimento e quindi chiamare Completa al termine dell'operazione per segnalare al sistema che l'operazione di richiesta di download può continuare.
private async void DownloadRequested(AdaptiveMediaSource sender, AdaptiveMediaSourceDownloadRequestedEventArgs args)
{
// rewrite key URIs to replace http:// with https://
if (args.ResourceType == AdaptiveMediaSourceResourceType.Key)
{
string originalUri = args.ResourceUri.ToString();
string secureUri = originalUri.Replace("http:", "https:");
// override the URI by setting property on the result sub object
args.Result.ResourceUri = new Uri(secureUri);
}
if (args.ResourceType == AdaptiveMediaSourceResourceType.Manifest)
{
AdaptiveMediaSourceDownloadRequestedDeferral deferral = args.GetDeferral();
args.Result.Buffer = await CreateMyCustomManifest(args.ResourceUri);
deferral.Complete();
}
if (args.ResourceType == AdaptiveMediaSourceResourceType.MediaSegment)
{
var resourceUri = args.ResourceUri.ToString() + "?range=" +
args.ResourceByteRangeOffset + "-" + (args.ResourceByteRangeLength - 1);
// override the URI by setting a property on the result sub object
args.Result.ResourceUri = new Uri(resourceUri);
// clear the byte range properties on the result sub object
args.Result.ResourceByteRangeOffset = null;
args.Result.ResourceByteRangeLength = null;
}
}
Usare gli eventi a velocità in bit per gestire e rispondere alle modifiche a velocità in bit
L'oggetto AdaptiveMediaSource fornisce eventi che consentono di reagire quando cambiano le velocità in bit di download o riproduzione. In questo esempio le velocità in bit correnti vengono semplicemente aggiornate nell'interfaccia utente. Si noti che è possibile modificare i rapporti che determinano quando il sistema cambia velocità in bit del flusso adattivo. Per altre informazioni, vedere la proprietà AdvancedSettings.
private async void DownloadBitrateChanged(AdaptiveMediaSource sender, AdaptiveMediaSourceDownloadBitrateChangedEventArgs args)
{
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
{
txtDownloadBitrate.Text = args.NewValue.ToString();
}));
}
private async void PlaybackBitrateChanged(AdaptiveMediaSource sender, AdaptiveMediaSourcePlaybackBitrateChangedEventArgs args)
{
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
{
txtPlaybackBitrate.Text = args.NewValue.ToString();
}));
}
Gestire gli eventi di completamento del download e di errore
L'oggetto AdaptiveMediaSource genera l'evento DownloadFailed quando il download di una risorsa richiesta ha esito negativo. È possibile usare questo evento per aggiornare l'interfaccia utente in risposta all'errore. È anche possibile usare l'evento per registrare informazioni statistiche sull'operazione di download e sull'errore.
L'oggetto AdaptiveMediaSourceDownloadFailedEventArgs passato al gestore eventi contiene metadati relativi al download di risorse non riuscite, ad esempio il tipo di risorsa, l'URI della risorsa e la posizione all'interno del flusso in cui si è verificato l'errore. RequestId ottiene un identificatore univoco generato dal sistema per la richiesta che può essere usata per correlare le informazioni sullo stato relative a una singola richiesta in più eventi.
La proprietà Statistics restituisce un oggetto AdaptiveMediaSourceDownloadStatistics che fornisce informazioni dettagliate sul numero di byte ricevuti al momento dell'evento e sull'intervallo di varie attività cardine nell'operazione di download. È possibile registrare queste informazioni per identificare i problemi di performance nell'implementazione del flusso adattivo.
private void DownloadFailed(AdaptiveMediaSource sender, AdaptiveMediaSourceDownloadFailedEventArgs args)
{
var statistics = args.Statistics;
MyLogMessageFunction("download failed for: " + args.ResourceType +
" - " + args.ResourceUri +
" – Error:" + args.ExtendedError.HResult +
" - RequestId" + args.RequestId +
" – Position:" + args.Position +
" - Duration:" + args.ResourceDuration +
" - ContentType:" + args.ResourceContentType +
" - TimeToHeadersReceived:" + statistics.TimeToHeadersReceived +
" - TimeToFirstByteReceived:" + statistics.TimeToFirstByteReceived +
" - TimeToLastByteReceived:" + statistics.TimeToLastByteReceived +
" - ContentBytesReceivedCount:" + statistics.ContentBytesReceivedCount);
}
L'evento DownloadCompleted si verifica quando il download di una risorsa viene completato e visualizza i dati simili all'evento DownloadFailed. Anche in questo caso, viene fornito un RequestId per correlare gli eventi per una singola richiesta. Inoltre, viene fornito un oggetto AdaptiveMediaSourceDownloadStatistics per abilitare la registrazione delle statistiche di download.
private void DownloadCompleted(AdaptiveMediaSource sender, AdaptiveMediaSourceDownloadCompletedEventArgs args)
{
var statistics = args.Statistics;
MyLogMessageFunction("download completed for: " + args.ResourceType + " - " +
args.ResourceUri +
" – RequestId:" + args.RequestId +
" – Position:" + args.Position +
" - Duration:" + args.ResourceDuration +
" - ContentType:" + args.ResourceContentType +
" - TimeToHeadersReceived:" + statistics.TimeToHeadersReceived +
" - TimeToFirstByteReceived:" + statistics.TimeToFirstByteReceived +
" - TimeToLastByteReceived:" + statistics.TimeToLastByteReceived +
" - ContentBytesReceivedCount:" + statistics.ContentBytesReceivedCount);
}
Raccogliere dati di telemetria di streaming adattivo con AdaptiveMediaSourceDiagnostics
AdaptiveMediaSource espone una proprietà Diagnostics che restituisce un oggetto AdaptiveMediaSourceDiagnostics. Utilizzare questo oggetto per eseguire la registrazione per l'evento DiagnosticAvailable . Questo evento deve essere usato per la raccolta di dati di telemetria e non deve essere usato per modificare il comportamento dell'app in fase di esecuzione. Questo evento di diagnostica viene generato per molti motivi diversi. Controllare la proprietà DiagnosticType dell'oggetto AdaptiveMediaSourceDiagnosticAvailableEventArgs passato all'evento per determinare il motivo per cui è stato generato l'evento. I potenziali motivi includono errori durante l'accesso alla risorsa richiesta ed errori durante l'analisi del file manifesto di streaming. Per un elenco di situazioni che possono attivare un evento di diagnostica, vedere AdaptiveMediaSourceDiagnosticType. Analogamente agli argomenti per altri eventi di streaming adattivo, AdaptiveMediaSourceDiagnosticAvailableEventArgs fornisce una proprietà RequestId per correlare le informazioni sulle richieste tra eventi diversi.
private void DiagnosticAvailable(AdaptiveMediaSourceDiagnostics sender, AdaptiveMediaSourceDiagnosticAvailableEventArgs args)
{
MySendTelemetryFunction(args.RequestId, args.Position,
args.DiagnosticType, args.SegmentId,
args.ResourceType, args.ResourceUri,
args.ResourceDuration, args.ResourceContentType,
args.ResourceByteRangeOffset,
args.ResourceByteRangeLength,
args.Bitrate,
args.ExtendedError);
}
Rinviare l'associazione del contenuto di streaming adattivo per gli elementi in un elenco di riproduzione tramite MediaBinder
La classe MediaBinder consente di rinviare l'associazione del contenuto multimediale in MediaPlaybackList. A partire da Windows 10, versione 1703, puoi fornire adaptiveMediaSource come contenuto associato. Il processo di associazione posticipata di un'origine multimediale adattiva è in gran parte uguale all'associazione di altri tipi di supporti, descritti in Elementi multimediali, playlist e tracce.
Creare un'istanza di MediaBinder, impostare una stringa token definita dall'app per identificare il contenuto da associare e registrarsi per l'evento Binding. Creare un oggetto MediaSource da Binder chiamando MediaSource.CreateFromMediaBinder. Creare quindi un oggetto MediaPlaybackItem da MediaSource e aggiungerlo all'elenco di riproduzione.
_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);
Nel gestore eventi Binding usare la stringa del token per identificare il contenuto da associare e quindi creare l'origine multimediale adattiva chiamando uno degli overload di CreateFromStreamAsync o CreateFromUriAsync. Poiché si tratta di metodi asincroni, è necessario chiamare prima di tutto il metodo MediaBindingEventArgs.GetDeferral per indicare al sistema di attendere il completamento dell'operazione prima di continuare. Impostare l'origine multimediale adattiva come contenuto associato chiamando SetAdaptiveMediaSource. Infine, chiamare Deferral.Complete al termine dell'operazione per indicare al sistema di continuare.
private async void Binder_Binding_AdaptiveMediaSource(MediaBinder sender, MediaBindingEventArgs args)
{
var deferral = args.GetDeferral();
var contentUri = new Uri($"http://contoso.com/media/{args.MediaBinder.Token}");
AdaptiveMediaSourceCreationResult result = await AdaptiveMediaSource.CreateFromUriAsync(contentUri);
if (result.MediaSource != null)
{
args.SetAdaptiveMediaSource(result.MediaSource);
}
args.SetUri(contentUri);
deferral.Complete();
}
Se vuoi registrare i gestori eventi per l'origine multimediale adattiva associata, puoi farlo nel gestore per l'evento CurrentItemChanged di MediaPlaybackList. La proprietà CurrentMediaPlaybackItemChangedEventArgs.NewItem contiene il nuovo oggetto MediaPlaybackItem attualmente in riproduzione nell'elenco. Ottenere un'istanza di AdaptiveMediaSource che rappresenta il nuovo elemento accedendo alla proprietà Source dell'oggetto MediaPlaybackItem e quindi alla proprietà AdaptiveMediaSource dell'origine multimediale. Questa proprietà sarà Null se il nuovo elemento di riproduzione non è AdaptiveMediaSource, quindi è consigliabile verificare la presenza di valori Null prima di tentare di registrare i gestori per uno degli eventi dell'oggetto.
private void AMSMediaPlaybackList_CurrentItemChanged(MediaPlaybackList sender, CurrentMediaPlaybackItemChangedEventArgs args)
{
if (!(args.NewItem is null))
{
var ams = args.NewItem.Source.AdaptiveMediaSource;
if (!(ams is null))
{
ams.PlaybackBitrateChanged += Ams_PlaybackBitrateChanged;
}
}
}