Condividi tramite


Riprodurre audio e video con MediaPlayer

Questo articolo illustra come riprodurre contenuti multimediali nella tua app di Windows universale usando la classe MediaPlayer. Con Windows 10, versione 1607, sono stati apportati miglioramenti significativi alle API di riproduzione multimediale, tra cui una progettazione semplificata a processo singolo per l'audio in background, l'integrazione automatica con i controlli SMTC (System Media Transport Controls), la possibilità di sincronizzare più lettori multimediali, la possibilità di eseguire il rendering di fotogrammi video in una superficie Windows.UI.Composition e un'interfaccia semplice per creare e pianificare interruzioni multimediali nel contenuto. Per sfruttare questi miglioramenti, la procedura consigliata per riprodurre elementi multimediali consiste nell'usare la classe MediaPlayer invece di MediaElement per la riproduzione multimediale. Il controllo XAML leggero, MediaPlayerElement, è stato introdotto per consentire il rendering del contenuto multimediale in una pagina XAML. Molte delle API di stato e controllo di riproduzione fornite da MediaElement sono ora disponibili tramite il nuovo oggetto MediaPlaybackSession. MediaElement continua a funzionare per supportare la compatibilità con le versioni precedenti, ma non verranno aggiunte funzionalità aggiuntive a questa classe.

Questo articolo illustra le funzionalità di MediaPlayer che verranno usate da una tipica app per la riproduzione multimediale. Si noti che MediaPlayer usa la classe MediaSource come contenitore per tutti gli elementi multimediali. Questa classe consente di caricare e riprodurre supporti da molte origini diverse, inclusi file locali, flussi di memoria e origini di rete, usando la stessa interfaccia. Esistono anche classi di livello superiore che funzionano con MediaSource, come MediaPlaybackItem e MediaPlaybackList, che offrono funzionalità più avanzate come playlist e la possibilità di gestire origini multimediali con più tracce audio, video e metadati. Per altre informazioni su MediaSource e sulle API correlate, vedere Elementi multimediali, playlist e tracce.

Nota

Le edizioni Windows 10 N e Windows 10 KN non includono le funzionalità multimediali necessarie per l'uso di MediaPlayer per la riproduzione. Queste funzionalità possono essere installate manualmente. Per altre informazioni, vedere Media Feature Pack per le edizioni Windows 10 N e Windows 10 KN.

Riprodurre un file multimediale con MediaPlayer

La riproduzione multimediale di base con MediaPlayer è molto semplice da implementare. Creare prima di tutto una nuova istanza della classe MediaPlayer. L'app può avere più istanze di MediaPlayer attive contemporaneamente. Impostare quindi la proprietà Source del lettore su un oggetto che implementa IMediaPlaybackSource, ad esempio MediaSource, MediaPlaybackItem o MediaPlaybackList. In questo esempio viene creato un oggetto MediaSource da un file nella risorsa di archiviazione locale dell'app e quindi viene creato un oggetto MediaPlaybackItem dall'origine e quindi assegnato alla proprietà Source del lettore.

A differenza di MediaElement, MediaPlayer non inizia automaticamente la riproduzione per impostazione predefinita. Puoi avviare la riproduzione chiamando Play, impostando la proprietà putoPlay su true o aspettando che l'utente avvii la riproduzione con i controlli multimediali predefiniti.

mediaPlayer = new MediaPlayer();
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer.Play();

Al termine dell'app usando un MediaPlayer, si deve chiamare il metodo Close (proiettato per Dispose in C#) per pulire le risorse usate dal lettore.

mediaPlayer.Dispose();

Usare MediaPlayerElement per eseguire il rendering di video in XAML

Si possono riprodurre elementi multimediali in MediaPlayer senza visualizzarli in XAML, ma molte app di riproduzione multimediale dovranno eseguire il rendering degli elementi multimediali in una pagina XAML. A tale scopo, usare il controllo MediaPlayerElement leggero. Come MediaElement, MediaPlayerElement consente di specificare se devono essere visualizzati i controlli di trasporto predefiniti.

<MediaPlayerElement x:Name="_mediaPlayerElement" AreTransportControlsEnabled="False" HorizontalAlignment="Stretch"  Grid.Row="0"/>

Si può impostare l'istanza di MediaPlayer a cui è associato l'elemento chiamando SetMediaPlayer.

_mediaPlayerElement.SetMediaPlayer(mediaPlayer);

Si può anche impostare l'origine di riproduzione in MediaPlayerElement e l'elemento creerà automaticamente una nuova istanza MediaPlayer a cui puoi accedere usando la proprietà MediaPlayer.

Nota

Se si impostano le proprietà MediaPlayerElement, le proprietà corrispondenti verranno impostate sull'oggetto MediaPlayer sottostante. È possibile usare direttamente MediaPlayer sottostante anziché usare le proprietà MediaPlayerElement. Tenere presente che l'uso diretto di MediaPlayer in cui potrebbe essere usata una proprietà MediaPlayerElement equivalente può causare comportamenti imprevisti. Ciò è dovuto al fatto che MediaPlayerElement non è a conoscenza di tutto ciò che accade a MediaPlayer sottostante. Ad esempio, se imposti l'origine direttamente su MediaPlayer, la proprietà MediaPlayerElement Source non rifletterà la modifica. Per questo motivo, devi essere coerente con l'uso delle proprietà MediaPlayerElement o direttamente usando MediaPlayer sottostante.

_mediaPlayerElement.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer = _mediaPlayerElement.MediaPlayer;
mediaPlayer.Play();

Nota

Se si abilita il MediaPlaybackCommandManager di MediaPlayer impostando IsEnabled su falso, verrà interrotto il collegamento tra MediaPlayer e TransportControls forniti da MediaPlayerElement, in modo che i controlli di trasporto predefiniti non controllino più automaticamente la riproduzione del lettore. Invece, per controllare MediaPlayer, è necessario implementare controlli personalizzati.

MediaPlayer viene scollegato da MediaPlayerElement quando MediaPlayerElement viene eliminato definitivamente o quando viene impostato un nuovo MediaPlayer usando SetMediaPlayer. Quando si scollega, MediaPlayerElement considera l'oggetto MediaPlayer sottostante in modo diverso a seconda che sia stato creato da MediaPlayerElement o impostato tramite SetMediaPlayer.

Se MediaPlayer è stato creato da MediaPlayerElement, chiuderà correttamente MediaPlayer per te. Se MediaPlayer è stato impostato su MediaPlayerElement usando SetMediaPlayer, si è responsabili della corretta chiusura di MediaPlayer. In caso contrario, potrebbero verificarsi errori di riproduzione irreversibili in MediaPlayer. Il frammento di codice seguente mostra come scollegare e chiudere correttamente nel codice.

// Get a reference to the current media source.
IMediaPlaybackSource _mediaPlayerElement = _mediaPlayerElement.Source;

// Pause playback if able.
if (mediaPlayer.PlaybackSession.CanPause)
{
    mediaPlayer.Pause();
}

// Disconnect the MediaPlayer from its source. This can be done by setting 
// the MediaPlayerElement Source property to null or by directly setting the
// source to null on the underlying MediaPlayer.
_mediaPlayerElement.Source = null;

// Disconnect the MediaPlayer from MediaPlayerElement.
_mediaPlayerElement.SetMediaPlayer(null);

// Dispose of the MediaPlayer or Source if they're no longer needed.
if (source is MediaSource mediaSource)
{
    mediaSource.Dispose();
}

mediaPlayer.Dispose();

Attività comuni di MediaPlayer

Questa sezione illustra come usare alcune delle funzionalità di MediaPlayer.

Impostare la categoria audio

Impostare la proprietà AudioCategory di un MediaPlayer a uno dei valori dell'enumerazione MediaPlayerAudioCategory per informare il sistema del tipo di supporto riprodotto. I giochi devono classificare i flussi musicali come GameMedia in modo che la musica del gioco venga disattivata automaticamente se un'altra applicazione riproduce musica in background. Musica o applicazioni video devono classificare i flussi come Media o Movie in modo che abbiano la priorità sui flussi GameMedia.

mediaPlayer.AudioCategory = MediaPlayerAudioCategory.Media;

Output in un endpoint audio specifico

Per impostazione predefinita, l'output audio di un MediaPlayer viene instradato all'endpoint audio predefinito per il sistema, ma puoi specificare un endpoint audio specifico che MediaPlayer deve usare per l'output. Nell'esempio seguente MediaDevice.GetAudioRenderSelector restituisce una stringa che identifica in modo univoco la categoria di rendering audio dei dispositivi. Viene quindi chiamato il metodo DeviceInformation FindAllAsync per ottenere un elenco di tutti i dispositivi disponibili del tipo selezionato. È possibile determinare a livello di codice il dispositivo che si vuole usare o aggiungere i dispositivi restituiti a un controllo ComboBox per consentire all'utente di selezionare un dispositivo.

string audioSelector = MediaDevice.GetAudioRenderSelector();
var outputDevices = await DeviceInformation.FindAllAsync(audioSelector);
foreach (var device in outputDevices)
{
    var deviceItem = new ComboBoxItem();
    deviceItem.Content = device.Name;
    deviceItem.Tag = device;
    _audioDeviceComboBox.Items.Add(deviceItem);
}

Nell'evento SelectionChanged per la casella combinata dispositivi la proprietà AudioDevice Del dispositivo MediaPlayer is set to the selezionato, archiviato nella proprietà Tag di ComboBoxItem.

private void _audioDeviceComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    DeviceInformation selectedDevice = (DeviceInformation)((ComboBoxItem)_audioDeviceComboBox.SelectedItem).Tag;
    if (selectedDevice != null)
    {
        mediaPlayer.AudioDevice = selectedDevice;
    }
}

Sessione di riproduzione

Come descritto in precedenza in questo articolo, molte delle funzioni esposte dalla classe MediaElement sono state spostate nella classe MediaPlaybackSession. Sono incluse informazioni sullo stato di riproduzione del lettore, ad esempio la posizione di riproduzione corrente, la sospensione o la riproduzione del lettore e la velocità di riproduzione corrente. MediaPlaybackSession fornisce anche diversi eventi per notificare quando lo stato cambia, tra cui il buffer corrente e lo stato di download del contenuto riprodotto e le dimensioni naturali e le proporzioni del contenuto video attualmente in riproduzione.

Nell'esempio seguente viene illustrato come implementare un gestore di clic del pulsante che ignora 10 secondi in avanti nel contenuto. Innanzitutto, l'oggetto MediaPlaybackSession per il lettore viene recuperato con la proprietà PlaybackSession. Successivamente, la proprietà position è impostata sulla posizione di riproduzione corrente più 10 secondi.

private void _skipForwardButton_Click(object sender, RoutedEventArgs e)
{
    var session = mediaPlayer.PlaybackSession;
    session.Position = session.Position + TimeSpan.FromSeconds(10);
}

Nell'esempio seguente viene illustrato l'uso di un interruttore per alternare la normale velocità di riproduzione e la velocità 2X impostando la proprietà PlaybackRate della sessione.

private void _speedToggleButton_Checked(object sender, RoutedEventArgs e)
{
    mediaPlayer.PlaybackSession.PlaybackRate = 2.0;
}
private void _speedToggleButton_Unchecked(object sender, RoutedEventArgs e)
{
    mediaPlayer.PlaybackSession.PlaybackRate = 1.0;
}

A partire da Windows 10, versione 1803, si può impostare la rotazione con cui il video viene presentato in MediaPlayer in incrementi di 90 gradi.

mediaPlayer.PlaybackSession.PlaybackRotation = MediaRotation.Clockwise90Degrees;

Rilevare il buffer previsto e imprevisto

L'oggetto MediaPlaybackSession descritto nella sezione precedente fornisce due eventi per rilevare quando il file multimediale attualmente in riproduzione inizia e termina il buffering, BufferingStarted e BufferingEnded. In questo modo è possibile aggiornare l'interfaccia utente per mostrare all'utente che si sta verificando il buffering. Il buffering iniziale è previsto quando un file multimediale viene aperto per la prima volta o quando l'utente passa a un nuovo elemento in una playlist. Il buffering imprevisto può verificarsi quando la velocità di rete si degrada o se il sistema di gestione dei contenuti fornisce problemi tecnici. A partire da RS3, puoi usare l'evento BufferingStarted per determinare se l'evento di buffering è previsto o se è imprevisto e interrompe la riproduzione. È possibile usare queste informazioni come dati di telemetria per l'app o il servizio di distribuzione multimediale.

Registrare i gestori per gli eventi BufferingStarted e BufferingEnded per ricevere notifiche di stato di buffering.

mediaPlayer.PlaybackSession.BufferingStarted += MediaPlaybackSession_BufferingStarted;
mediaPlayer.PlaybackSession.BufferingEnded += MediaPlaybackSession_BufferingEnded;

Nel gestore dell'evento BufferingStarted eseguire il cast degli argomenti dell'evento passati all'evento a un oggetto MediaPlaybackSessionBufferingStartedEventArgs controllare la proprietà IsPlaybackInterruption. Se questo valore è true, il buffering che ha attivato l'evento è imprevisto e interrompe la riproduzione. In caso contrario, è previsto il buffering iniziale.

private void MediaPlaybackSession_BufferingStarted(MediaPlaybackSession sender, object args)
{
    MediaPlaybackSessionBufferingStartedEventArgs bufferingStartedEventArgs = args as MediaPlaybackSessionBufferingStartedEventArgs;
    if (bufferingStartedEventArgs != null && bufferingStartedEventArgs.IsPlaybackInterruption)
    {
        // update the playback quality telemetry report to indicate that
        // playback was interrupted
    }

    // update the UI to indicate that playback is buffering
}
private void MediaPlaybackSession_BufferingEnded(MediaPlaybackSession sender, object args)
{
    // update the UI to indicate that playback is no longer buffering
}

Avvicinamento delle dita e zoom video

MediaPlayer consente di specificare il rettangolo di origine all'interno del contenuto video di cui eseguire il rendering, consentendo di ingrandire il video. Il rettangolo specificato è relativo a un rettangolo normalizzato (0,0,1,1) dove 0,0 è la mano superiore sinistra del frame e 1,1 specifica la larghezza e l'altezza intere del fotogramma. Ad esempio, per impostare il rettangolo di zoom in modo che venga eseguito il rendering del quadrante superiore destro del video, è necessario specificare il rettangolo (.5,0,.5,.5). È importante controllare i valori per assicurarsi che il rettangolo di origine si trovi all'interno del rettangolo normalizzato (0,0,1,1). Se si tenta di impostare un valore all'esterno di questo intervallo, verrà generata un'eccezione.

Per implementare l'avvicinamento delle dita e lo zoom usando i movimenti multitocco, si devono innanzitutto specificare i movimenti che si vogliono supportare. In questo esempio vengono richiesti movimenti di ridimensionamento e traslazione. L'evento ManipulationDelta viene generato quando si verifica uno dei movimenti sottoscritti. L'evento DoubleTapped verrà usato per reimpostare lo zoom sul frame completo.

_mediaPlayerElement.ManipulationMode = ManipulationModes.Scale | ManipulationModes.TranslateX | ManipulationModes.TranslateY;
_mediaPlayerElement.ManipulationDelta += _mediaPlayerElement_ManipulationDelta;
_mediaPlayerElement.DoubleTapped += _mediaPlayerElement_DoubleTapped;

Dichiarare quindi un oggetto Rect che archivierà il rettangolo di origine dello zoom corrente.

Rect _sourceRect = new Rect(0, 0, 1, 1);

Il gestore ManipulationDelta regola la scala o la traslazione del rettangolo di zoom. Se il valore della scala differenziale non è 1, significa che l'utente ha eseguito un gesto di avvicinamento delle dita. Se il valore è maggiore di 1, il rettangolo di origine deve essere ridotto per ingrandire il contenuto. Se il valore è minore di 1, il rettangolo di origine deve essere reso più grande per eseguire lo zoom indietro. Prima di impostare i nuovi valori di scala, il rettangolo risultante viene controllato per assicurarsi che si trovi interamente entro i limiti (0,0,1,1).

Se il valore di scala è 1, il movimento di traduzione viene gestito. Il rettangolo viene semplicemente convertito in base al numero di pixel nel movimento diviso per la larghezza e l'altezza del controllo. Anche in questo caso, il rettangolo risultante viene controllato per assicurarsi che si trovi all'interno dei limiti (0,0,1,1).

Infine, NormalizedSourceRect di MediaPlaybackSession è impostato sul rettangolo appena regolato, specificando l'area all'interno del fotogramma video di cui deve essere eseguito il rendering.

private void _mediaPlayerElement_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{

    if (e.Delta.Scale != 1)
    {
        var halfWidth = _sourceRect.Width / 2;
        var halfHeight = _sourceRect.Height / 2;

        var centerX = _sourceRect.X + halfWidth;
        var centerY = _sourceRect.Y + halfHeight;

        var scale = e.Delta.Scale;
        var newHalfWidth = (_sourceRect.Width * e.Delta.Scale) / 2;
        var newHalfHeight = (_sourceRect.Height * e.Delta.Scale) / 2;

        if (centerX - newHalfWidth > 0 && centerX + newHalfWidth <= 1.0 &&
            centerY - newHalfHeight > 0 && centerY + newHalfHeight <= 1.0)
        {
            _sourceRect.X = centerX - newHalfWidth;
            _sourceRect.Y = centerY - newHalfHeight;
            _sourceRect.Width *= e.Delta.Scale;
            _sourceRect.Height *= e.Delta.Scale;
        }
    }
    else
    {
        var translateX = -1 * e.Delta.Translation.X / _mediaPlayerElement.ActualWidth;
        var translateY = -1 * e.Delta.Translation.Y / _mediaPlayerElement.ActualHeight;

        if (_sourceRect.X + translateX >= 0 && _sourceRect.X + _sourceRect.Width + translateX <= 1.0 &&
            _sourceRect.Y + translateY >= 0 && _sourceRect.Y + _sourceRect.Height + translateY <= 1.0)
        {
            _sourceRect.X += translateX;
            _sourceRect.Y += translateY;
        }
    }

    mediaPlayer.PlaybackSession.NormalizedSourceRect = _sourceRect;
}

Nel gestore eventi DoubleTapped, il rettangolo di origine viene impostato nuovamente su (0,0,1,1) in modo che venga eseguito il rendering dell'intero fotogramma video.

private void _mediaPlayerElement_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
    _sourceRect = new Rect(0, 0, 1, 1);
    mediaPlayer.PlaybackSession.NormalizedSourceRect = _sourceRect;
}

NOTA Questa sezione descrive l'input tramite tocco. Touchpad invia eventi puntatore e non invierà eventi di manipolazione.

Gestione della riduzione della riproduzione basata su criteri

In alcune circostanze, il sistema può ridurre la riproduzione di un elemento multimediale, ad esempio ridurre la risoluzione (constrizione), in base a un criterio anziché a un problema di prestazioni. Ad esempio, il video può essere danneggiato dal sistema se viene riprodotto usando un driver video non firmato. È possibile chiamare MediaPlaybackSession.GetOutputDegradationPolicyState per determinare se e perché si sta verificando questa riduzione basata su criteri e avvisare l'utente o registrare il motivo per scopi di telemetria.

L'esempio seguente mostra un'implementazione di un gestore per l'evento MediaPlayer.MediaOpened generato quando il lettore apre un nuovo elemento multimediale. GetOutputDegradationPolicyState viene chiamato su MediaPlayer passato al gestore. Il valore di VideoConstrictionReason indica il motivo del criterio per cui il video è vincolato. Se il valore non è Nessuno, in questo esempio viene registrato il motivo della riduzione delle prestazioni a scopo di telemetria. Questo esempio mostra anche l'impostazione della velocità in bit di AdaptiveMediaSource attualmente riprodotta sulla larghezza di banda più bassa per salvare l'utilizzo dei dati, poiché il video è ristretto e non verrà comunque visualizzato ad alta risoluzione. Per altre informazioni sull'uso di AdaptiveMediaSource, vedere Streaming adattivo.

private void MediaPlayer_MediaOpened(MediaPlayer sender, object args)
{
    MediaPlaybackSessionOutputDegradationPolicyState info = sender.PlaybackSession.GetOutputDegradationPolicyState();

    if (info.VideoConstrictionReason != MediaPlaybackSessionVideoConstrictionReason.None)
    {
        // Switch to lowest bitrate to save bandwidth
        adaptiveMediaSource.DesiredMaxBitrate = adaptiveMediaSource.AvailableBitrates[0];

        // Log the degradation reason or show a message to the user
        System.Diagnostics.Debug.WriteLine("Logging constriction reason: " + info.VideoConstrictionReason);
    }
}

Usare MediaPlayerSurface per eseguire il rendering del video in una superficie Windows.UI.Composition

A partire da Windows 10, versione 1607, puoi usare MediaPlayer per eseguire il rendering del video in un oggetto ICompositionSurface, che consente al lettore di interagire con le API nello spazio dei nomi Windows.UI.Composition. Il framework di composizione consente di usare grafica nel livello visivo tra XAML e le API grafiche DirectX di basso livello. Ciò consente scenari come il rendering di video in qualsiasi controllo XAML. Per altre informazioni sull'uso delle API di composizione, vedere Livello visivo.

Nell'esempio seguente viene illustrato come eseguire il rendering del contenuto del lettore video in un controllo Canvas. Le chiamate specifiche del lettore multimediale in questo esempio sono SetSurfaceSize e GetSurface. SetSurfaceSize indica al sistema le dimensioni del buffer da allocare per il rendering del contenuto. GetSurface accetta un compositore come argomento e recupera un'istanza della classe MediaPlayerSurface. Questa classe fornisce l'accesso a MediaPlayer e Compositor usati per creare la superficie ed espone la superficie stessa tramite la proprietà pompositionSurface .

Il resto del codice in questo esempio crea un oggetto SpriteVisual in cui viene eseguito il rendering del video e imposta le dimensioni sulle dimensioni dell'elemento canvas che visualizzerà l'oggetto visivo. Successivamente viene creato un oggetto CompositionBrush da MediaPlayerSurface e assegnato alla proprietà Brush dell'oggetto visivo. Successivamente viene creato un oggetto ContainerVisual e SpriteVisual viene inserito nella parte superiore della struttura ad albero visuale. Infine, SetElementChildVisual viene chiamato per assegnare l'oggetto visivo contenitore all'oggetto Canvas.

mediaPlayer.SetSurfaceSize(new Size(_compositionCanvas.ActualWidth, _compositionCanvas.ActualHeight));

var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
MediaPlayerSurface surface = mediaPlayer.GetSurface(compositor);

SpriteVisual spriteVisual = compositor.CreateSpriteVisual();
spriteVisual.Size =
    new System.Numerics.Vector2((float)_compositionCanvas.ActualWidth, (float)_compositionCanvas.ActualHeight);

CompositionBrush brush = compositor.CreateSurfaceBrush(surface.CompositionSurface);
spriteVisual.Brush = brush;

ContainerVisual container = compositor.CreateContainerVisual();
container.Children.InsertAtTop(spriteVisual);

ElementCompositionPreview.SetElementChildVisual(_compositionCanvas, container);

Usare MediaTimelineController per sincronizzare il contenuto tra più lettori.

Come descritto in precedenza in questo articolo, l'app può avere diversi oggetti MediaPlayer attivi alla volta. Per impostazione predefinita, ogni MediaPlayer creato opera in modo indipendente. Per alcuni scenari, ad esempio la sincronizzazione di una traccia di commento in un video, è possibile sincronizzare lo stato del lettore, la posizione di riproduzione e la velocità di riproduzione di più lettori. A partire da Windows 10 versione 1607, si può implementare questo comportamento usando la classe MediaTimelineController.

Implementare i controlli di riproduzione

L'esempio seguente illustra come usare un MediaTimelineController per controllare due istanze di MediaPlayer. Prima di tutto, ogni istanza di MediaPlayer viene creata un'istanza di e Source è impostata su un file multimediale. Viene quindi creato un nuovo MediaTimelineController. Per ogni MediaPlayer, MediaPlaybackCommandManager associato a ogni lettore è disabilitato impostando la proprietà IsEnabled su false. E la proprietà TimelineController viene quindi impostata sull'oggetto controller della sequenza temporale.

MediaTimelineController _mediaTimelineController;
mediaPlayer = new MediaPlayer();
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
_mediaPlayerElement.SetMediaPlayer(mediaPlayer);


_mediaPlayer2 = new MediaPlayer();
_mediaPlayer2.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video_2.mkv"));
_mediaPlayerElement2.SetMediaPlayer(_mediaPlayer2);

_mediaTimelineController = new MediaTimelineController();

mediaPlayer.CommandManager.IsEnabled = false;
mediaPlayer.TimelineController = _mediaTimelineController;

_mediaPlayer2.CommandManager.IsEnabled = false;
_mediaPlayer2.TimelineController = _mediaTimelineController;

PrecauzioneMediaPlaybackCommandManager fornisce l'integrazione automatica tra MediaPlayer e i controlli SMTC (System Media Transport Controls), ma questa integrazione automatica non può essere usata con i lettori multimediali controllati con un MediaTimelineController. È pertanto necessario disabilitare il gestore comandi per il lettore multimediale prima di impostare il controller della sequenza temporale del lettore. In caso contrario, verrà generata un'eccezione con il messaggio seguente: "Attaching Media Timeline Controller is blocked because of the current state of the object". Per altre informazioni sull'integrazione del lettore multimediale con i controlli SMTC, vedere Integrare con i controlli System Media Transport. Se si usa un MediaTimelineController , è comunque possibile controllare manualmente i controlli SMTC. Per ulteriori informazioni, vedere Controllo manuale dei controlli System Media Transport.

Dopo aver collegato MediaTimelineController a uno o più lettori multimediali, è possibile controllare lo stato di riproduzione usando i metodi esposti dal controller. Nell'esempio seguente viene chiamato Start per avviare la riproduzione di tutti i lettori multimediali associati all'inizio dell'oggetto multimediale.

private void PlayButton_Click(object sender, RoutedEventArgs e)
{
    _mediaTimelineController.Start();
}

In questo esempio viene illustrata la sospensione e la ripresa di tutti i lettori multimediali collegati.

private void PauseButton_Click(object sender, RoutedEventArgs e)
{
    if(_mediaTimelineController.State == MediaTimelineControllerState.Running)
    {
        _mediaTimelineController.Pause();
        _pauseButton.Content = "Resume";
    }
    else
    {
        _mediaTimelineController.Resume();
        _pauseButton.Content = "Pause";
    }
}

Per inoltrare rapidamente tutti i lettori multimediali connessi, impostare la velocità di riproduzione su un valore maggiore di 1.

private void FastForwardButton_Click(object sender, RoutedEventArgs e)
{
    _mediaTimelineController.ClockRate = 2.0;
}

Nell'esempio seguente viene illustrato come usare un controllo Slider per visualizzare la posizione di riproduzione corrente del controller della sequenza temporale rispetto alla durata del contenuto di uno dei lettori multimediali connessi. Prima di tutto, viene creato un nuovo MediaSource e viene registrato un gestore per OpenOperationCompleted dell'origine multimediale.

var mediaSource = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaSource.OpenOperationCompleted += MediaSource_OpenOperationCompleted;
mediaPlayer.Source = mediaSource;
_mediaPlayerElement.SetMediaPlayer(mediaPlayer);

Il gestore OpenOperationCompleted viene usato come opportunità per individuare la durata del contenuto dell'origine multimediale. Dopo aver determinato la durata, il valore massimo del controllo Slider viene impostato sul numero totale di secondi dell'elemento multimediale. Il valore viene impostato all'interno di una chiamata a RunAsync per assicurarsi che venga eseguito nel thread dell'interfaccia utente.

TimeSpan _duration;
private async void MediaSource_OpenOperationCompleted(MediaSource sender, MediaSourceOpenOperationCompletedEventArgs args)
{
    _duration = sender.Duration.GetValueOrDefault();

    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        _positionSlider.Minimum = 0;
        _positionSlider.Maximum = _duration.TotalSeconds;
        _positionSlider.StepFrequency = 1;
    }); 
}

Viene quindi registrato un gestore per l'evento PositionChanged del controller della sequenza temporale. Questo viene chiamato periodicamente dal sistema, circa 4 volte al secondo.

_mediaTimelineController.PositionChanged += _mediaTimelineController_PositionChanged;

Nel gestore per PositionChanged il valore del dispositivo di scorrimento viene aggiornato in modo da riflettere la posizione corrente del controller della sequenza temporale.

private async void _mediaTimelineController_PositionChanged(MediaTimelineController sender, object args)
{
    if (_duration != TimeSpan.Zero)
    {
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            _positionSlider.Value = sender.Position.TotalSeconds / (float)_duration.TotalSeconds;
        });
    }
}

Offset della posizione di riproduzione dalla posizione della sequenza temporale

In alcuni casi può essere necessario che la posizione di riproduzione di uno o più lettori multimediali associati a un controller della sequenza temporale vengano sfalsati dagli altri lettori. Si può farlo impostando la proprietà pimelineControllerPositionOffset dell'oggetto MediaPlayer che si vuole sfalsare. Nell'esempio seguente vengono utilizzate le durate del contenuto di due lettori multimediali per impostare i valori minimi e massimi di due controlli dispositivo di scorrimento su più e meno la lunghezza dell'elemento.

_timelineOffsetSlider1.Minimum = -1 * _duration.TotalSeconds;
_timelineOffsetSlider1.Maximum = _duration.TotalSeconds;
_timelineOffsetSlider1.StepFrequency = 1;

_timelineOffsetSlider2.Minimum = -1 * _duration2.TotalSeconds;
_timelineOffsetSlider2.Maximum = _duration2.TotalSeconds;
_timelineOffsetSlider2.StepFrequency = 1;

Nell'evento ValueChanged per ogni dispositivo a scorrimento, viene impostato TimelineControllerPositionOffset per ogni giocatore viene impostato sul valore corrispondente.

private void _timelineOffsetSlider1_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
    mediaPlayer.TimelineControllerPositionOffset = TimeSpan.FromSeconds(_timelineOffsetSlider1.Value);
}

private void _timelineOffsetSlider2_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
    _mediaPlayer2.TimelineControllerPositionOffset = TimeSpan.FromSeconds(_timelineOffsetSlider2.Value);
}

Si noti che se il valore di offset di un lettore esegue il mapping a una posizione di riproduzione negativa, il clip rimarrà sospeso fino a quando l'offset raggiunge lo zero e la riproduzione inizierà. Analogamente, se il valore di offset viene mappato a una posizione di riproduzione maggiore della durata dell'elemento multimediale, verrà visualizzato il fotogramma finale, come avviene quando un singolo lettore multimediale raggiunge la fine del contenuto.

Riprodurre video sferico con MediaPlayer

A partire da Windows 10, versione 1703, MediaPlayer supporta la proiezione equirettangolare per la riproduzione di video sferici. Il contenuto video sferico non è diverso dal normale video flat in quanto MediaPlayer eseguirà il rendering del video, purché la codifica video sia supportata. Per il video sferico che contiene un tag di metadati che specifica che il video usa la proiezione equirettangolare, MediaPlayer può eseguire il rendering del video usando un campo di visualizzazione e un orientamento di visualizzazione specificati. Ciò consente scenari come la riproduzione di video di realtà virtuale con uno schermo montato sulla testa o semplicemente consentendo all'utente di eseguire una panoramica all'interno di contenuto video sferico usando il mouse o l'input della tastiera.

Per riprodurre video sferici, seguire la procedura per riprodurre contenuto video descritto in precedenza in questo articolo. Il passaggio aggiuntivo consiste nel registrare un gestore per l'evento MediaPlayer.MediaOpened. Questo evento offre l'opportunità di abilitare e controllare i parametri di riproduzione video sferica.

mediaPlayer = new MediaPlayer();
mediaPlayer.MediaOpened += _mediaPlayer_MediaOpened;
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video_spherical.mp4"));
_mediaPlayerElement.SetMediaPlayer(mediaPlayer);
mediaPlayer.Play();

Nel gestore MediaOpened controllare prima di tutto il formato del fotogramma dell'elemento multimediale appena aperto controllando la proprietà playbackSession.SphericalVideoProjection.FrameFormat. Se questo valore è SphericaVideoFrameFormat.Equirectangular, il sistema può proiettare automaticamente il contenuto video. Impostare prima di tutto la proprietà PlaybackSession.SphericalVideoProjection.IsEnabled su true. È anche possibile modificare le proprietà, ad esempio l'orientamento della visualizzazione e il campo di visualizzazione che il lettore multimediale userà per proiettare il contenuto video. In questo esempio il campo della visualizzazione viene impostato su un valore wide di 120 gradi impostando la proprietà HorizontalFieldOfViewInDegrees.

Se il contenuto video è sferico, ma è in un formato diverso da equirettangolare, è possibile implementare un algoritmo di proiezione personalizzato usando la modalità server frame del lettore multimediale per ricevere ed elaborare singoli fotogrammi.

private void _mediaPlayer_MediaOpened(MediaPlayer sender, object args)
{
    if (sender.PlaybackSession.SphericalVideoProjection.FrameFormat == SphericalVideoFrameFormat.Equirectangular)
    {
        sender.PlaybackSession.SphericalVideoProjection.IsEnabled = true;
        sender.PlaybackSession.SphericalVideoProjection.HorizontalFieldOfViewInDegrees = 120;

    }
    else if (sender.PlaybackSession.SphericalVideoProjection.FrameFormat == SphericalVideoFrameFormat.Unsupported)
    {
        // If the spherical format is unsupported, you can use frame server mode to implement a custom projection
    }
}

Il codice di esempio seguente illustra come regolare l'orientamento della visualizzazione video sferica usando i tasti di direzione sinistro e destro.

protected override void OnKeyDown(KeyRoutedEventArgs e)
{
    if (mediaPlayer.PlaybackSession.SphericalVideoProjection.FrameFormat != SphericalVideoFrameFormat.Equirectangular)
    {
        return;
    }

    switch (e.Key)
    {
        case Windows.System.VirtualKey.Right:
            mediaPlayer.PlaybackSession.SphericalVideoProjection.ViewOrientation *= Quaternion.CreateFromYawPitchRoll(.1f, 0, 0);
            break;
        case Windows.System.VirtualKey.Left:
            mediaPlayer.PlaybackSession.SphericalVideoProjection.ViewOrientation *= Quaternion.CreateFromYawPitchRoll(-.1f, 0, 0);
            break;
    }
}

Se l'supporta playlist di video, si potrebbe voler identificare gli elementi di riproduzione che contengono video sferici nell'interfaccia utente. Le playlist multimediali sono descritte in dettaglio nell'articolo Elementi multimediali, playlist e tracce. Nell'esempio seguente viene illustrata la creazione di una nuova playlist, l'aggiunta di un elemento e la registrazione di un gestore per l'evento MediaPlaybackItem.VideoTracksChanged, che si verifica quando vengono risolte le tracce video per un elemento multimediale.

var playbackList = new MediaPlaybackList();
var item = new MediaPlaybackItem(MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/RIFTCOASTER HD_injected.mp4")));
item.VideoTracksChanged += Item_VideoTracksChanged;
playbackList.Items.Add(item);
mediaPlayer.Source = playbackList;

Nel gestore eventi VideoTracksChanged ottenere le proprietà di codifica per le tracce video aggiunte chiamando VideoTrack.GetEncodingProperties. Se la proprietà SphericalVideoFrameFormat delle proprietà di codifica è un valore diverso da SphericaVideoFrameFormat.None, la traccia video contiene video sferici ed è possibile aggiornare di conseguenza l'interfaccia utente se si sceglie.

private void Item_VideoTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    if (args.CollectionChange != CollectionChange.ItemInserted)
    {
        return;
    }
    foreach (var videoTrack in sender.VideoTracks)
    {
        if (videoTrack.GetEncodingProperties().SphericalVideoFrameFormat != SphericalVideoFrameFormat.None)
        {
            // Optionally indicate in the UI that this item contains spherical video
        }
    }
}

Usare MediaPlayer in modalità server frame

A partire da Windows 10 versione 1703, si può usare MediaPlayer in modalità server frame. In questa modalità, MediaPlayer non esegue automaticamente il rendering dei fotogrammi in un oggetto MediaPlayerElement associato. L'app copia invece il frame corrente da MediaPlayer a un oggetto che implementa IDirect3DSurface. Lo scenario principale che questa funzionalità abilita è l'uso di pixel shader per elaborare i fotogrammi video forniti da MediaPlayer. L'app è responsabile della visualizzazione di ogni fotogramma dopo l'elaborazione, ad esempio mostrando il frame in un controllo Immagine XAML.

Nell'esempio seguente viene inizializzato un nuovo MediaPlayer e viene caricato il contenuto video. Viene quindi registrato un gestore per VideoFrameAvailable. La modalità server frame è abilitata impostando la proprietà IsVideoFrameServerEnabled dell'oggetto MediaPlayer su true. Infine, la riproduzione multimediale viene avviata con una chiamata a Riproduci.

mediaPlayer = new MediaPlayer();
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer.VideoFrameAvailable += mediaPlayer_VideoFrameAvailable;
mediaPlayer.IsVideoFrameServerEnabled = true;
mediaPlayer.Play();

L'esempio seguente mostra un gestore per VideoFrameAvailable che usa Win2D per aggiungere un semplice effetto sfocatura a ogni fotogramma di un video e quindi visualizza i fotogrammi elaborati in un controllo Immagine XAML.

Ogni volta che viene chiamato il gestore VideoFrameAvailable, il metodo CopyFrameToVideoSurface viene usato per copiare il contenuto del fotogramma in un oggetto IDirect3DSurface. Si può anche usare CopyFrameToStereoscopicVideoSurfaces per copiare il contenuto 3D in due superfici, per elaborare separatamente il contenuto dell'occhio sinistro e dell'occhio destro. Per ottenere un oggetto che implementa IDirect3DSurface questo esempio crea un SoftwareBitmap e quindi usa tale oggetto per creare un oggetto CanvasBitmap Win2D, che implementa l'interfaccia necessaria. CanvasImageSource è un oggetto Win2D che può essere usato come origine per un controllo Image, quindi ne viene creato uno nuovo e impostato come origine per l'immagine in cui verrà visualizzato il contenuto. Viene quindi creato un oggetto CanvasDrawingSession. Viene usato da Win2D per eseguire il rendering dell'effetto sfocatura.

Dopo aver creato un'istanza di tutti gli oggetti necessari, viene chiamato CopyFrameToVideoSurface, che copia il fotogramma corrente da MediaPlayer in CanvasBitmap. Viene quindi creato un Elemento GaussianBlurEffect Win2D con CanvasBitmap impostato come origine dell'operazione. Infine, CanvasDrawingSession.DrawImage viene chiamato per disegnare l'immagine di origine, con l'effetto sfocatura applicato, in CanvasImageSource associato al controllo Image, causandone il disegno nell'interfaccia utente.

private async void mediaPlayer_VideoFrameAvailable(MediaPlayer sender, object args)
{
    CanvasDevice canvasDevice = CanvasDevice.GetSharedDevice();

    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        if(frameServerDest == null)
        {
            // FrameServerImage in this example is a XAML image control
            frameServerDest = new SoftwareBitmap(BitmapPixelFormat.Rgba8, (int)FrameServerImage.Width, (int)FrameServerImage.Height, BitmapAlphaMode.Ignore);
        }
        if(canvasImageSource == null)
        {
            canvasImageSource = new CanvasImageSource(canvasDevice, (int)FrameServerImage.Width, (int)FrameServerImage.Height, DisplayInformation.GetForCurrentView().LogicalDpi);//96); 
            FrameServerImage.Source = canvasImageSource;
        }

        using (CanvasBitmap inputBitmap = CanvasBitmap.CreateFromSoftwareBitmap(canvasDevice, frameServerDest))
        using (CanvasDrawingSession ds = canvasImageSource.CreateDrawingSession(Windows.UI.Colors.Black))
        {

            mediaPlayer.CopyFrameToVideoSurface(inputBitmap);

            var gaussianBlurEffect = new GaussianBlurEffect
            {
                Source = inputBitmap,
                BlurAmount = 5f,
                Optimization = EffectOptimization.Speed
            };

            ds.DrawImage(gaussianBlurEffect);

        }
    });
}

private void FrameServerSubtitlesButton_Click(object sender, RoutedEventArgs e)
{

    mediaPlayer = new MediaPlayer();
    var source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
    var item = new MediaPlaybackItem(source);

    item.TimedMetadataTracksChanged += Item_TimedMetadataTracksChanged;


    mediaPlayer.Source = item;
    mediaPlayer.VideoFrameAvailable += mediaPlayer_VideoFrameAvailable_Subtitle;
    mediaPlayer.IsVideoFrameServerEnabled = true;
    mediaPlayer.Play();

    mediaPlayer.IsMuted = true;

}

private void Item_TimedMetadataTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    if(sender.TimedMetadataTracks.Count > 0)
    {
        sender.TimedMetadataTracks.SetPresentationMode(0, TimedMetadataTrackPresentationMode.PlatformPresented);
    }
}

private async void mediaPlayer_VideoFrameAvailable_Subtitle(MediaPlayer sender, object args)
{
    CanvasDevice canvasDevice = CanvasDevice.GetSharedDevice();

    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        if (frameServerDest == null)
        {
            // FrameServerImage in this example is a XAML image control
            frameServerDest = new SoftwareBitmap(BitmapPixelFormat.Rgba8, (int)FrameServerImage.Width, (int)FrameServerImage.Height, BitmapAlphaMode.Ignore);
        }
        if (canvasImageSource == null)
        {
            canvasImageSource = new CanvasImageSource(canvasDevice, (int)FrameServerImage.Width, (int)FrameServerImage.Height, DisplayInformation.GetForCurrentView().LogicalDpi);//96); 
            FrameServerImage.Source = canvasImageSource;
        }

        using (CanvasBitmap inputBitmap = CanvasBitmap.CreateFromSoftwareBitmap(canvasDevice, frameServerDest))
        {
            using (CanvasDrawingSession ds = canvasImageSource.CreateDrawingSession(Windows.UI.Colors.Black))
            {

                mediaPlayer.CopyFrameToVideoSurface(inputBitmap);

                //Rect subtitleTargetRect = new Rect(0, 0, inputBitmap.Bounds.Width, inputBitmap.Bounds.Bottom * .1);
                Rect subtitleTargetRect = new Rect(0, 0, 100, 100);

                mediaPlayer.RenderSubtitlesToSurface(inputBitmap);//, subtitleTargetRect);

                //var gaussianBlurEffect = new GaussianBlurEffect
                //{
                //    Source = inputBitmap,
                //    BlurAmount = 5f,
                //    Optimization = EffectOptimization.Speed
                //};

                //ds.DrawImage(gaussianBlurEffect);

                ds.DrawImage(inputBitmap);
            }
        }
    });
}

Per altre informazioni su Win2D, vedere il repository GitHub Win2D. Per provare il codice di esempio illustrato in precedenza, è necessario aggiungere il pacchetto NuGet Win2D al progetto con le istruzioni seguenti.

Per aggiungere il pacchetto NuGet Win2D al progetto dell'effetto

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Gestisci pacchetti NuGet.
  2. Nella parte superiore della finestra, selezionare la scheda Browse.
  3. Nella casella di ricerca immettere Win2D.
  4. Selezionare Win2D.uwp e quindi selezionare Installa nel riquadro a destra.
  5. La finestra di dialogo Rivedi modifiche mostra il pacchetto da installare. Fare clic su OK.
  6. Accettare la licenza del pacchetto.

Rilevare e rispondere alle modifiche del livello audio dal sistema

A partire da Windows 10, versione 1803, l'app può rilevare quando il sistema abbassa o disattiva il livello audio di un MediaPlayer attualmente in riproduzione. Ad esempio, il sistema può abbassare o "duck", il livello di riproduzione audio quando sta suonando una sveglia. Il sistema disattiva l'app quando entra in background se l'app non ha dichiarato la funzionalità fackgroundMediaPlayback nel manifesto dell'app. La classe AudioStateMonitor consente di registrare per ricevere un evento quando il sistema modifica il volume di un flusso audio. Accedere alla proprietà AudioStateMonitor di u MediaPlayer e registrare un gestore per l'evento SoundLevelChanged per ricevere una notifica quando il livello audio per tale MediaPlayer viene modificato dal sistema.

mediaPlayer.AudioStateMonitor.SoundLevelChanged += AudioStateMonitor_SoundLevelChanged;

Quando si gestisce l'evento SoundLevelChanged, è possibile eseguire azioni diverse a seconda del tipo di contenuto riprodotto. Se si sta riproducendo musica, si potrebbe voler lasciare che la musica continui a suonare mentre il volume è anatrato. Se si sta riproducendo un podcast, tuttavia, probabilmente vuoi sospendere la riproduzione mentre l'audio è anatrato in modo che l'utente non perde nessuno dei contenuti.

Questo esempio dichiara una variabile per tenere traccia se il contenuto attualmente in riproduzione è un podcast, si presuppone che sia impostato sul valore appropriato quando si seleziona il contenuto per MediaPlayer. Creiamo anche una variabile di classe per tenere traccia quando sospendiamo la riproduzione a livello di codice quando cambia il livello audio.

bool isPodcast;
bool isPausedDueToAudioStateMonitor;

Nel gestore dell'evento SoundLevelChanged controllare la proprietà SoundLevel del mittente AudioStateMonitor per determinare il nuovo livello audio. Questo esempio verifica se il nuovo livello audio è pieno, ovvero il sistema ha smesso di disattivare o anatrare il volume o se il livello audio è stato abbassato ma sta riproducendo contenuti non podcast. Se uno di questi valori è true e il contenuto è stato sospeso in precedenza a livello di codice, la riproduzione viene ripresa. Se il nuovo livello audio viene disattivato o se il contenuto corrente è un podcast e il livello audio è basso, la riproduzione viene sospesa e la variabile è impostata per tenere traccia dell'avvio della pausa a livello di codice.

private void AudioStateMonitor_SoundLevelChanged(Windows.Media.Audio.AudioStateMonitor sender, object args)
{
    if ((sender.SoundLevel == SoundLevel.Full) || (sender.SoundLevel == SoundLevel.Low && !isPodcast))
    {
        if (isPausedDueToAudioStateMonitor)
        {
            mediaPlayer.Play();
            isPausedDueToAudioStateMonitor = false;
        }
    }
    else if ((sender.SoundLevel == SoundLevel.Muted) ||
         (sender.SoundLevel == SoundLevel.Low && isPodcast))
    {
        if (mediaPlayer.PlaybackSession.PlaybackState == MediaPlaybackState.Playing)
        {
            mediaPlayer.Pause();
            isPausedDueToAudioStateMonitor = true;
        }
    }

}

L'utente può decidere di sospendere o continuare la riproduzione, anche se l'audio è anatrato dal sistema. Questo esempio mostra i gestori eventi per un gioco e un pulsante di sospensione. Nel gestore di clic del pulsante pausa viene sospeso, se la riproduzione era già stata sospesa a livello di codice, aggiorniamo la variabile per indicare che l'utente ha sospeso il contenuto. Nel gestore di clic del pulsante di riproduzione, riprendiamo la riproduzione e cancellano la variabile di rilevamento.

private void PauseButton_User_Click(object sender, RoutedEventArgs e)
{
    if (isPausedDueToAudioStateMonitor)
    {
        isPausedDueToAudioStateMonitor = false;
    }
    else
    {
        mediaPlayer.Pause();
    }
}

public void PlayButton_User_Click()
{
    isPausedDueToAudioStateMonitor = false;
    mediaPlayer.Play();
}