Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Den här artikeln visar hur du spelar upp media i din Universella Windows-app med hjälp av klassen MediaPlayer . Med Windows 10 version 1607 gjordes betydande förbättringar av API:erna för medieuppspelning, inklusive en förenklad design med en process för bakgrundsljud, automatisk integrering med SMTC (System Media Transport Controls), möjligheten att synkronisera flera mediespelare, möjligheten att återge videoramar till en Windows.UI.Composition-yta och ett enkelt gränssnitt för att skapa och schemalägga mediebrytningar i ditt innehåll. För att dra nytta av dessa förbättringar rekommenderar vi att du använder MediaPlayer-klassen i stället för MediaElement för medieuppspelning. Den lätta XAML-kontrollen MediaPlayerElement har introducerats så att du kan återge medieinnehåll på en XAML-sida. Många av uppspelningskontroll- och status-API:erna som tillhandahålls av MediaElement är nu tillgängliga via det nya MediaPlaybackSession-objektet . MediaElement fortsätter att fungera för att stödja bakåtkompatibilitet, men inga ytterligare funktioner läggs till i den här klassen.
Den här artikeln beskriver de MediaPlayer-funktioner som en typisk app för medieuppspelning kommer att använda. Observera att MediaPlayer använder klassen MediaSource som container för alla medieobjekt. Med den här klassen kan du läsa in och spela upp media från många olika källor, inklusive lokala filer, minnesströmmar och nätverkskällor, som alla använder samma gränssnitt. Det finns också klasser på högre nivå som fungerar med MediaSource, till exempel MediaPlaybackItem och MediaPlaybackList, som tillhandahåller mer avancerade funktioner som spellistor och möjligheten att hantera mediekällor med flera ljud-, video- och metadataspår. Mer information om MediaSource och relaterade API:er finns i Medieobjekt, spellistor och spår.
Anmärkning
Windows 10 N- och Windows 10 KN-utgåvor innehåller inte de mediefunktioner som krävs för att använda MediaPlayer för uppspelning. Dessa funktioner kan installeras manuellt. Mer information finns i Media-funktionspaket för Windows 10 N- och Windows 10 KN-utgåvor.
Spela upp en mediefil med MediaPlayer
Grundläggande medieuppspelning med MediaPlayer är mycket enkelt att implementera. Skapa först en ny instans av klassen MediaPlayer . Din app kan ha flera MediaPlayer-instanser aktiva samtidigt. Ange nästa spelarens Source-egenskap till ett objekt som implementerar IMediaPlaybackSource, till exempel en MediaSource, en MediaPlaybackItem eller en MediaPlaybackList. I det här exemplet skapas en MediaSource från en fil i appens lokala lagring och sedan skapas en MediaPlaybackItem från källan och tilldelas sedan till spelarens källegenskap .
Till skillnad från MediaElement börjar MediaPlayer inte automatiskt uppspelningen som standard. Du kan starta uppspelningen genom att anropa Spela upp genom att ange egenskapen Spela upp automatiskt till true eller vänta på att användaren ska starta uppspelningen med de inbyggda mediekontrollerna.
mediaPlayer = new MediaPlayer();
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer.Play();
När appen är klar med en MediaPlayer-bör du anropa metoden Stäng (projiceras som Dispose i C#) för att rensa upp de resurser som spelaren använder.
mediaPlayer.Dispose();
Använda MediaPlayerElement för att återge video i XAML
Du kan spela upp media i en MediaPlayer utan att visa det i XAML, men många appar för medieuppspelning vill återge mediet på en XAML-sida. Det gör du genom att använda den lätta MediaPlayerElement-kontrollen . Precis som MediaElement kan du med MediaPlayerElement ange om de inbyggda transportkontrollerna ska visas.
<MediaPlayerElement x:Name="_mediaPlayerElement" AreTransportControlsEnabled="False" HorizontalAlignment="Stretch" Grid.Row="0"/>
Du kan ange den MediaPlayer-instans som elementet är bundet till genom att anropa SetMediaPlayer.
_mediaPlayerElement.SetMediaPlayer(mediaPlayer);
Du kan också ange uppspelningskällan på MediaPlayerElement och elementet skapar automatiskt en ny MediaPlayer-instans som du kan komma åt med egenskapen MediaPlayer .
Anmärkning
Om du anger MediaPlayerElement-egenskaper anges motsvarande egenskaper på dess underliggande MediaPlayer. Du kan välja att använda den underliggande MediaPlayer direkt i stället för att använda MediaPlayerElement-egenskaper. Tänk på att användning av MediaPlayer direkt där en motsvarande MediaPlayerElement-egenskap annars skulle kunna användas kan orsaka oväntat beteende. Detta beror på att MediaPlayerElement inte är medveten om allt som händer med dess underliggande MediaPlayer. Om du till exempel ställer in källan direkt på MediaPlayer återspeglar egenskapen MediaPlayerElementSource inte ändringen. Därför måste du vara konsekvent i att använda MediaPlayerElement-egenskaper eller direkt använda den underliggande MediaPlayer.
_mediaPlayerElement.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer = _mediaPlayerElement.MediaPlayer;
mediaPlayer.Play();
Anmärkning
Om du inaktiverar MediaPlaybackCommandManager- för MediaPlayer- genom att ange IsEnabled till false, Den bryter länken mellan MediaPlayerTransportControls som tillhandahålls av MediaPlayerElement, så att de inbyggda transportkontrollerna inte längre automatiskt styr uppspelningen av spelaren. I stället måste du implementera egna kontroller för att styra MediaPlayer-.
MediaPlayer kopplas från MediaPlayerElement när MediaPlayerElement förstörs eller när en ny MediaPlayer ställs in med SetMediaPlayer. Vid frånkopplad behandlar MediaPlayerElement den underliggande MediaPlayer på olika sätt beroende på om den har skapats av MediaPlayerElement eller angetts med SetMediaPlayer.
Om MediaPlayer skapades av MediaPlayerElement, kommer den att stänga MediaPlayer korrekt åt dig. Om MediaPlayer har ställts in på MediaPlayerElement med SetMediaPlayer ansvarar du för att MediaPlayer stängs korrekt. Om du inte gör det kan det leda till allvarliga uppspelningsfel i MediaPlayer. Följande kodfragment visar hur du kopplar från och stänger koden korrekt.
// 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();
Vanliga MediaPlayer-uppgifter
Det här avsnittet visar hur du använder några av funktionerna i MediaPlayer.
Ange ljudkategorin
Ange egenskapen AudioCategory för en MediaPlayer till ett av värdena i MediaPlayerAudioCategory enumeration för att låta systemet veta vilken typ av media du spelar upp. Spel bör kategorisera sina musikströmmar som GameMedia så att spelmusiken stängs av automatiskt om ett annat program spelar musik i bakgrunden. Musik- eller videoprogram bör kategorisera sina strömmar som media eller film så att de prioriteras framför GameMedia-strömmar .
mediaPlayer.AudioCategory = MediaPlayerAudioCategory.Media;
Utdata till en specifik ljudslutpunkt
Som standard dirigeras ljudutdata från en MediaPlayer till systemets standardslutpunkt för ljud, men du kan ange en specifik ljudslutpunkt som MediaPlayer ska använda för utdata. I exemplet nedan returnerar MediaDevice.GetAudioRenderSelector en sträng som unikt identifierar ljudåtergivningskategorin för enheter. Sedan anropas DeviceInformation-metodenFindAllAsync för att hämta en lista över alla tillgängliga enheter av den valda typen. Du kan programmatiskt avgöra vilken enhet du vill använda eller lägga till de returnerade enheterna i en Kombinationsruta så att användaren kan välja en enhet.
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);
}
I SelectionChanged-händelsen för enhetskombinationsrutan är egenskapen AudioDevice för MediaPlayer inställd på den valda enheten, som lagrades i egenskapen Tag för egenskapen ComboBoxItem.
private void _audioDeviceComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DeviceInformation selectedDevice = (DeviceInformation)((ComboBoxItem)_audioDeviceComboBox.SelectedItem).Tag;
if (selectedDevice != null)
{
mediaPlayer.AudioDevice = selectedDevice;
}
}
Uppspelningssession
Som tidigare beskrivits i den här artikeln har många av de funktioner som exponeras av klassen MediaElement flyttats till klassen MediaPlaybackSession . Detta innefattar information om spelarens uppspelningstillstånd, såsom den aktuella uppspelningspositionen, om spelaren är pausad eller spelar, samt den aktuella uppspelningshastigheten. MediaPlaybackSession innehåller också flera händelser som meddelar dig när tillståndet ändras, inklusive den aktuella buffrings- och nedladdningsstatusen för innehåll som spelas upp och den naturliga storleken och proportionen för det videoinnehåll som spelas upp för närvarande.
I följande exempel visas hur du implementerar en knappklickshanterare som hoppar över 10 sekunder framåt i innehållet. Först hämtas objektet MediaPlaybackSession för spelaren med hjälp av egenskapen PlaybackSession. Därefter är egenskapen Position inställd på den aktuella uppspelningspositionen plus 10 sekunder.
private void _skipForwardButton_Click(object sender, RoutedEventArgs e)
{
var session = mediaPlayer.PlaybackSession;
session.Position = session.Position + TimeSpan.FromSeconds(10);
}
I nästa exempel visas hur du använder en växlingsknapp för att växla mellan normal uppspelningshastighet och 2X-hastighet genom att ange egenskapen PlaybackRate för sessionen.
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;
}
Från och med Windows 10, version 1803, kan du ange den rotation som videon visas med i MediaPlayer i steg om 90 grader.
mediaPlayer.PlaybackSession.PlaybackRotation = MediaRotation.Clockwise90Degrees;
Identifiera förväntad och oväntad buffring
MediaPlaybackSession-objektet som beskrivs i föregående avsnitt innehåller två händelser för att identifiera när den mediefil som spelas upp börjar och slutar buffring, BufferingStarted och BufferingEnded. På så sätt kan du uppdatera användargränssnittet för att visa användaren att buffring sker. Inledande buffring förväntas när en mediefil öppnas först eller när användaren växlar till ett nytt objekt i en spellista. Oväntad buffring kan inträffa när nätverkshastigheten försämras eller om innehållshanteringssystemet som tillhandahåller innehållet upplever tekniska problem. Från och med RS3 kan du använda händelsen BufferingStarted för att avgöra om buffringshändelsen förväntas eller om den är oväntad och avbryter uppspelningen. Du kan använda den här informationen som telemetridata för din app eller medialeveranstjänst.
Registrera händelsehanterare för händelserna BufferingStarted och BufferingEnded för att ta emot aviseringar om buffringstillstånd.
mediaPlayer.PlaybackSession.BufferingStarted += MediaPlaybackSession_BufferingStarted;
mediaPlayer.PlaybackSession.BufferingEnded += MediaPlaybackSession_BufferingEnded;
I händelsehanteraren BufferingStarted, omvandla händelseargumenten som skickas till händelsen till ett MediaPlaybackSessionBufferingStartedEventArgs-objekt och kontrollera egenskapen IsPlaybackInterruption. Om det här värdet är sant är buffring som utlöste händelsen oväntad och avbryter uppspelningen. Annars förväntas första buffring.
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
}
Nyp och zooma video
Med MediaPlayer kan du ange källrektangeln i videoinnehåll som ska återges, vilket gör att du kan zooma in i video. Den rektangel som du anger är relativ till en normaliserad rektangel (0,0,1,1) där 0,0 är ramens övre vänstra hand och 1,1 anger ramens fulla bredd och höjd. Om du till exempel vill ange zoomrektangeln så att videons övre högra kvadrant återges anger du rektangeln (.5,0,.5,.5). Det är viktigt att du kontrollerar dina värden för att se till att källrektangeln ligger inom den (0,0,1,1) normaliserade rektangeln. Om du försöker ange ett värde utanför det här intervallet genereras ett undantag.
Om du vill implementera nypa och zooma med hjälp av multi-touch-gester måste du först ange vilka gester du vill stödja. I det här exemplet begärs skalnings- och översättningsgester. Händelsen ManipulationDelta utlöses när en av de registrerade gesterna inträffar. Händelsen DoubleTapped används för att återställa zoomningen till hela ramen.
_mediaPlayerElement.ManipulationMode = ManipulationModes.Scale | ManipulationModes.TranslateX | ManipulationModes.TranslateY;
_mediaPlayerElement.ManipulationDelta += _mediaPlayerElement_ManipulationDelta;
_mediaPlayerElement.DoubleTapped += _mediaPlayerElement_DoubleTapped;
Deklarera sedan ett Rect-objekt som lagrar den aktuella zoomkällans rektangel.
Rect _sourceRect = new Rect(0, 0, 1, 1);
ManipulationDelta-hanteraren justerar antingen skalan eller översättningen av zoomrektangeln. Om deltaskalningsvärdet inte är 1 innebär det att användaren utförde en nypgest. Om värdet är större än 1 bör källrektangeln göras mindre för att zooma in i innehållet. Om värdet är mindre än 1 bör källrektangeln göras större för att zooma ut. Innan du anger de nya skalningsvärdena kontrolleras den resulterande rektangeln så att den ligger helt inom gränserna (0,0,1,1).
Om skalningsvärdet är 1 hanteras översättningsgesten. Rektangeln översätts helt enkelt av antalet bildpunkter i gest dividerat med kontrollens bredd och höjd. Återigen kontrolleras den resulterande rektangeln för att se till att den ligger inom gränserna (0,0,1,1).
Slutligen är NormalizedSourceRect för MediaPlaybackSession inställd på den nyligen justerade rektangeln, vilket specificerar det område inom videoramen som ska återges.
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;
}
I DoubleTapped-händelsehanteraren anges källrektangeln tillbaka till (0,0,1,1) så att hela videoramen återges.
private void _mediaPlayerElement_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
_sourceRect = new Rect(0, 0, 1, 1);
mediaPlayer.PlaybackSession.NormalizedSourceRect = _sourceRect;
}
NOTE Det här avsnittet beskriver pekinmatning. Pekplatta skickar pekarhändelser och skickar inte manipulationshändelser.
Hantera policybaserad uppspelningsförsämring
I vissa fall kan systemet försämra uppspelningen av ett medieobjekt, till exempel att minska upplösningen (förträngning), baserat på en princip i stället för ett prestandaproblem. Video kan till exempel försämras av systemet om den spelas upp med en osignerad videodrivrutin. Du kan anropa MediaPlaybackSession.GetOutputDegradationPolicyState för att avgöra om och varför den här principbaserade försämringen inträffar och varna användaren eller registrera orsaken till telemetri.
I följande exempel visas en implementering av en hanterare för MediaPlayer.MediaOpened-händelsen som aktiveras när spelaren öppnar ett nytt medieobjekt. GetOutputDegradationPolicyState anropas på MediaPlayer som skickades till hanteraren. Värdet för VideoConstrictionReason anger principorsaken till att videon är begränsad. Om värdet inte är Ingen loggar det här exemplet försämringsorsaken i telemetrisyfte. Det här exemplet visar också hur bithastigheten för AdaptiveMediaSource för närvarande spelas upp till den lägsta bandbredden för att spara dataanvändning, eftersom videon är begränsad och inte visas med hög upplösning ändå. Mer information om hur du använder AdaptiveMediaSource finns i Anpassningsbar direktuppspelning.
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);
}
}
Använda MediaPlayerSurface för att återge video till en Windows.UI.Composition-yta
Från och med Windows 10 version 1607 kan du använda MediaPlayer för att återge video till en ICompositionSurface, vilket gör att spelaren kan samverka med API:erna i namnområdet Windows.UI.Composition . Med kompositionsramverket kan du arbeta med grafik i det visuella lagret mellan XAML och DirectX-grafik-API:er på låg nivå. Detta möjliggör scenarier som att återge video till valfri XAML-kontroll. Mer information om hur du använder API:er för sammansättning finns i Visual Layer.
I följande exempel illustreras hur du renderar videospelarinnehåll på en Canvas-kontroll. Mediespelarspecifika anrop i det här exemplet är SetSurfaceSize och GetSurface. SetSurfaceSize talar om för systemet storleken på bufferten som ska allokeras för rendering av innehåll. GetSurface tar en Compositor som argument och hämtar en instans av klassen MediaPlayerSurface . Den här klassen ger åtkomst till MediaPlayer och Compositor som används för att skapa ytan och exponerar själva ytan via egenskapen CompositionSurface .
Resten av koden i det här exemplet skapar en SpriteVisual- som videon renderas på och anger storleken till storleken på canvaselementet som visar det visuella. Därefter skapas en CompositionBrush- från MediaPlayerSurface- och tilldelas egenskapen Brush för det visuella objektet. Nästa ContainerVisual skapas och SpriteVisual infogas överst i det visuella trädet. Slutligen anropas SetElementChildVisual för att tilldela det visuella containerobjektet till 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);
Använd MediaTimelineController för att synkronisera innehåll mellan flera spelare.
Som vi beskrev tidigare i den här artikeln kan appen ha flera MediaPlayer-objekt aktiva åt gången. Som standard fungerar varje MediaPlayer som du skapar oberoende av varandra. För vissa scenarier, till exempel synkronisering av ett kommentarsspår till en video, kanske du vill synkronisera spelarens tillstånd, uppspelningsposition och uppspelningshastighet för flera spelare. Från och med Windows 10, version 1607, kan du implementera det här beteendet med hjälp av klassen MediaTimelineController .
Implementera uppspelningskontroller
I följande exempel visas hur du använder en MediaTimelineController för att styra två instanser av MediaPlayer. Först skapas varje instans av MediaPlayer och Source ställs in på en mediefil. Därefter skapas en ny MediaTimelineController . För varje MediaPlayer-inaktiveras MediaPlaybackCommandManager- som är associerad med varje spelare genom att ange egenskapen IsEnabled till false. Och sedan är egenskapen TimelineController inställd på tidslinjens kontrollantobjekt.
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;
FörsiktighetMediaPlaybackCommandManager tillhandahåller automatisk integrering mellan MediaPlayer och SMTC (System Media Transport Controls), men den här automatiska integreringen kan inte användas med mediespelare som styrs med en MediaTimelineController. Därför måste du inaktivera kommandohanteraren för mediespelaren innan du anger spelarens tidslinjekontrollant. Om du inte gör det, kommer ett undantag att utlösas med följande meddelande: "Att lägga till en mediatidslinjestyrning blockeras på grund av det aktuella tillståndet hos objektet." Mer information om integrering av mediespelaren med SMTC finns i Integrera med System Media Transport Controls. Om du använder en MediaTimelineController kan du fortfarande styra SMTC manuellt. Mer information finns i Manuell kontroll av System Media Transport Controls.
När du har kopplat en MediaTimelineController till en eller flera mediespelare kan du styra uppspelningstillståndet med hjälp av de metoder som exponeras av kontrollanten. I följande exempel anropas Start för att börja uppspelningen av alla associerade mediespelare från början av mediet.
private void PlayButton_Click(object sender, RoutedEventArgs e)
{
_mediaTimelineController.Start();
}
Det här exemplet visar hur du pausar och återupptar alla anslutna mediespelare.
private void PauseButton_Click(object sender, RoutedEventArgs e)
{
if(_mediaTimelineController.State == MediaTimelineControllerState.Running)
{
_mediaTimelineController.Pause();
_pauseButton.Content = "Resume";
}
else
{
_mediaTimelineController.Resume();
_pauseButton.Content = "Pause";
}
}
Om du vill snabbsnabbföra alla anslutna mediaspelare anger du uppspelningshastigheten till ett värde som är större än 1.
private void FastForwardButton_Click(object sender, RoutedEventArgs e)
{
_mediaTimelineController.ClockRate = 2.0;
}
I nästa exempel visas hur du använder en kontroll för Skjutreglage för att visa den aktuella uppspelningspositionen för tidslinjekontrollern i förhållande till varaktigheten av innehållet i en av de anslutna mediespelarna. Först skapas en ny MediaSource- och en hanterare för mediekällans OpenOperationCompleted- registreras.
var mediaSource = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaSource.OpenOperationCompleted += MediaSource_OpenOperationCompleted;
mediaPlayer.Source = mediaSource;
_mediaPlayerElement.SetMediaPlayer(mediaPlayer);
OpenOperationCompleted-hanteraren används som en möjlighet att identifiera varaktigheten för mediekällans innehåll. När varaktigheten har fastställts, sätts det maximala värdet för skjutreglaget på kontrollen till det totala antalet sekunder för medieobjektet. Värdet anges i ett anrop till RunAsync för att se till att det körs på användargränssnittstråden.
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;
});
}
Därefter registreras en hanterare för tidslinjekontrollantens PositionChanged händelse. Detta anropas regelbundet av systemet, ungefär 4 gånger per sekund.
_mediaTimelineController.PositionChanged += _mediaTimelineController_PositionChanged;
I hanteraren för PositionChanged uppdateras skjutreglagets värde så att det återspeglar tidslinjekontrollantens aktuella position.
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;
});
}
}
Förskjut uppspelningspositionen från tidslinjens position
I vissa fall kanske du vill att uppspelningspositionen för en eller flera mediespelare som är associerade med en tidslinjekontrollant ska vara förskjuten i förhållande till de andra spelarna. Du kan göra detta genom att ange egenskapen TimelineControllerPositionOffset för det MediaPlayer-objekt som du vill förskjuta. I följande exempel används varaktigheterna för innehållet i två mediespelare för att ange minimi- och maxvärdena för två skjutreglage till plus och minus objektets längd.
_timelineOffsetSlider1.Minimum = -1 * _duration.TotalSeconds;
_timelineOffsetSlider1.Maximum = _duration.TotalSeconds;
_timelineOffsetSlider1.StepFrequency = 1;
_timelineOffsetSlider2.Minimum = -1 * _duration2.TotalSeconds;
_timelineOffsetSlider2.Maximum = _duration2.TotalSeconds;
_timelineOffsetSlider2.StepFrequency = 1;
I händelsen ValueChanged för varje skjutreglage anges TimelineControllerPositionOffset för varje spelare till motsvarande värde.
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);
}
Observera att om förskjutningsvärdet för en spelare mappas till en negativ uppspelningsposition förblir klippet pausat tills förskjutningen når noll och sedan börjar uppspelningen. På samma sätt, om förskjutningsvärdet mappas till en uppspelningsposition som är större än medieobjektets varaktighet, visas den sista bildrutan, precis som när en enskild mediaspelare når slutet av sitt innehåll.
Spela upp sfärisk video med MediaPlayer
Från och med Windows 10 version 1703 stöder MediaPlayer equirectangular-projektion för sfärisk videouppspelning. Sfäriskt videoinnehåll skiljer sig inte från vanlig, platt video eftersom MediaPlayer renderar videon så länge videokodningen stöds. För sfärisk video som innehåller en metadatatagg som anger att videon använder equirectangular-projektion kan MediaPlayer återge videon med hjälp av ett angivet visningsfält och visningsorientering. Detta möjliggör scenarier som videouppspelning av virtuell verklighet med en huvudmonterad skärm eller helt enkelt så att användaren kan panorera runt i sfäriskt videoinnehåll med hjälp av mus- eller tangentbordsindata.
Om du vill spela upp sfärisk video använder du stegen för att spela upp videoinnehåll som beskrivs tidigare i den här artikeln. Ett ytterligare steg är att registrera en hanterare för MediaPlayer.MediaOpened-händelsen . Den här händelsen ger dig möjlighet att aktivera och styra de sfäriska videouppspelningsparametrarna.
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();
I MediaOpened-hanteraren kontrollerar du först bildruteformatet för det nyligen öppnade medieobjektet genom att kontrollera egenskapen PlaybackSession.SphericalVideoProjection.FrameFormat . Om det här värdet är SphericaVideoFrameFormat.Equirectangular kan systemet automatiskt projicera videoinnehållet. Ange egenskapen PlaybackSession.SphericalVideoProjection.IsEnabled först till true . Du kan också justera egenskaper som visningsorientering och visningsfält som mediespelaren ska använda för att projicera videoinnehållet. I det här exemplet anges visningsfältet till ett brett värde på 120 grader genom att ange egenskapen HorizontalFieldOfViewInDegrees .
Om videoinnehållet är sfäriskt, men är i ett annat format än equirectangular, kan du implementera din egen projektionsalgoritm med mediespelarens ramserverläge för att ta emot och bearbeta enskilda bildrutor.
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
}
}
Följande exempelkod visar hur du justerar den sfäriska videovisningsorienteringen med hjälp av vänster- och högerpiltangenterna.
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;
}
}
Om din app stöder spellistor med video kanske du vill identifiera uppspelningsobjekt som innehåller sfärisk video i användargränssnittet. Mediespellistor beskrivs i detalj i artikeln Mediaobjekt, spellistor och spår. I följande exempel visas hur du skapar en ny spellista, lägger till ett objekt och registrerar en hanterare för mediaPlaybackItem.VideoTracksChanged-händelsen , som inträffar när videospåren för ett medieobjekt matchas.
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;
I händelsehanteraren VideoTracksChanged hämtar du kodningsegenskaperna för eventuella tillagda videospår genom att anropa VideoTrack.GetEncodingProperties. Om egenskapen SphericalVideoFrameFormat för kodningsegenskaperna är ett annat värde än SphericaVideoFrameFormat.None, innehåller videospåret sfärisk video och du kan uppdatera användargränssnittet om du vill.
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
}
}
}
Använda MediaPlayer i ramserverläge
Från och med Windows 10 version 1703 kan du använda MediaPlayer i ramserverläge. I det här läget visar MediaPlayer inte automatiskt bilder på en associerad MediaPlayerElement. I stället kopierar appen den aktuella bildrutan från MediaPlayer till ett objekt som implementerar IDirect3DSurface. Det primära scenariot som den här funktionen aktiverar är att använda pixelskuggare för att bearbeta videoramar som tillhandahålls av MediaPlayer. Din app ansvarar för att visa varje bildruta efter bearbetning, till exempel genom att visa bildrutan i en XAML Image kontroll.
I följande exempel initieras en ny MediaPlayer och videoinnehåll läses in. Därefter registreras en hanterare för VideoFrameAvailable . Ramserverläget aktiveras genom att ställa in MediaPlayer-objektets egenskap IsVideoFrameServerEnabled till true. Slutligen startas medieuppspelningen genom ett anrop till Spela upp.
mediaPlayer = new MediaPlayer();
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer.VideoFrameAvailable += mediaPlayer_VideoFrameAvailable;
mediaPlayer.IsVideoFrameServerEnabled = true;
mediaPlayer.Play();
I nästa exempel visas en hanterare för VideoFrameAvailable som använder Win2D för att lägga till en enkel oskärpa i varje bildruta i en video och sedan visar de bearbetade bildrutorna i en XAML Image-kontroll.
När VideoFrameAvailable-hanteraren anropas används metoden CopyFrameToVideoSurface för att kopiera innehållet i ramen till en IDirect3DSurface. Du kan också använda CopyFrameToStereoscopicVideoSurfaces för att kopiera 3D-innehåll till två ytor, för att bearbeta innehållet i vänster öga och höger öga separat. Om du vill hämta ett objekt som implementerar IDirect3DSurface skapar det här exemplet en SoftwareBitmap och använder sedan objektet för att skapa en Win2D CanvasBitmap som implementerar det nödvändiga gränssnittet. En CanvasImageSource är ett Win2D-objekt som kan användas som källa för en bildkontroll , så en ny skapas och anges som källa för den bild där innehållet ska visas. Därefter skapas en CanvasDrawingSession. Detta används av Win2D för att återge oskärpaeffekten.
När alla nödvändiga objekt har instansierats anropas CopyFrameToVideoSurface , som kopierar den aktuella ramen från MediaPlayer till CanvasBitmap. Därefter skapas en Win2D GaussianBlurEffect med CanvasBitmap som källa för åtgärden. Slutligen anropas CanvasDrawingSession.DrawImage för att rita källbilden, med oskärpaeffekten tillämpad, i CanvasImageSource som har associerats med bildkontrollen , vilket gör att den ritas i användargränssnittet.
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);
}
}
});
}
Mer information om Win2D finns på Win2D GitHub-lagringsplatsen. Om du vill testa exempelkoden som visas ovan måste du lägga till Win2D NuGet-paketet i projektet med följande instruktioner.
Om du vill lägga till Win2D NuGet-paketet i ditt effektprojekt
- Högerklicka på projektet i Solution Explorer och välj Hantera NuGet-paket.
- Längst upp i fönstret väljer du fliken Bläddra.
- I sökrutan anger du Win2D.
- Välj Win2D.uwpoch välj sedan Installera i den högra rutan.
- Dialogrutan Granska ändringar visar paketet som ska installeras. Klicka på OK.
- Godkänn paketlicensen.
Identifiera och svara på ändringar på ljudnivå av systemet
Från och med Windows 10 version 1803 kan din app upptäcka när systemet sänker eller stänger av volymen för en MediaPlayer. Systemet kan till exempel sänka, eller "ducka", ljuduppspelningsnivån när ett larm ringer. Systemet stänger av ljudet för din app när den hamnar i bakgrunden om appen inte har deklarerat funktionen backgroundMediaPlayback i appmanifestet. Med klassen AudioStateMonitor kan du registrera dig för att ta emot en händelse när systemet ändrar volymen för en ljudström. Få åtkomst till egenskapen AudioStateMonitor för en MediaPlayer och registrera en hanterare för händelsen SoundLevelChanged som ska meddelas när ljudnivån för mediaplayer ändras av systemet.
mediaPlayer.AudioStateMonitor.SoundLevelChanged += AudioStateMonitor_SoundLevelChanged;
När du hanterar händelsen SoundLevelChanged kan du vidta olika åtgärder beroende på vilken typ av innehåll som spelas upp. Om du spelar musik för närvarande kanske du vill låta musiken fortsätta att spela medan volymen sänks. Om du spelar upp en podcast vill du förmodligen pausa uppspelningen medan ljudet duckas så att användaren inte missar något av innehållet.
I det här exemplet deklareras en variabel för att spåra om det innehåll som spelas upp för närvarande är en podcast. Det förutsätts att du anger det till lämpligt värde när du väljer innehållet för MediaPlayer. Vi skapar också en klassvariabel att spåra när vi pausar uppspelningen programmatiskt när ljudnivån ändras.
bool isPodcast;
bool isPausedDueToAudioStateMonitor;
I händelsehanteraren SoundLevelChanged kontrollerar du egenskapen SoundLevel för AudioStateMonitor-avsändaren för att fastställa den nya ljudnivån. Det här exemplet kontrollerar om den nya ljudnivån är full volym, vilket innebär att systemet har slutat stänga av eller ducka volymen, eller om ljudnivån har sänkts men spelar upp icke-podcastinnehåll. Om något av dessa är sant och innehållet tidigare pausats programmatiskt återupptas uppspelningen. Om den nya ljudnivån är avstängd eller om det aktuella innehållet är en podcast och ljudnivån är låg pausas uppspelningen och variabeln är inställd på att spåra att pausen initierades programmatiskt.
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;
}
}
}
Användaren kan besluta att de vill pausa eller fortsätta uppspelningen, även om ljudet duckas av systemet. I det här exemplet visas händelsehanterare för en uppspelning och en pausknapp. I pausknappens klickhanterare pausas, om uppspelningen redan hade pausats programmatiskt, uppdaterar vi variabeln för att indikera att användaren har pausat innehållet. I uppspelningsknappens klickhanterare återupptar vi uppspelningen och rensar vår spårningsvariabel.
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();
}
Relaterade ämnen
- Medieuppspelning
- Medieobjekt, spellistor och spår
- Integrera med systemets medietransportkontroller
- Skapa, schemalägga och hantera mediebrytningar
- Spela upp media i bakgrunden