diff --git a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GtkMediaPlayer.cs b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GtkMediaPlayer.cs index 151f84dbfd710f4428601ee558692cdf9ce4d616..a1ac28bb61c0d8430041067ad8ff2cc09ebe6e3b 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GtkMediaPlayer.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GtkMediaPlayer.cs @@ -29,7 +29,6 @@ public partial class GtkMediaPlayer : FrameworkElement private VideoView? _videoView; private Uri? _mediaPath; private bool _isEnding; - private bool _isLoopingEnabled; private double _playbackRate; private Rect _transportControlsBounds; private Windows.UI.Xaml.Media.Stretch _stretch = Windows.UI.Xaml.Media.Stretch.Uniform; @@ -519,11 +518,6 @@ public partial class GtkMediaPlayer : FrameworkElement } } - public void SetIsLoopingEnabled(bool value) - { - _isLoopingEnabled = value; - } - internal void UpdateVideoStretch(Windows.UI.Xaml.Media.Stretch stretch) { if (_videoView != null) diff --git a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GtkMediaPlayer.events.cs b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GtkMediaPlayer.events.cs index 34d190efdeb2ed474e952d9d01964a364aa5d711..5bded4e4b2e049168aa0c58bca6b7bec8019e02c 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GtkMediaPlayer.events.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GtkMediaPlayer.events.cs @@ -651,10 +651,6 @@ public partial class GtkMediaPlayer UpdateMedia(); OnSourceEnded?.Invoke(this, EventArgs.Empty); - if (_isLoopingEnabled) - { - _mediaPlayer.Play(); - } _videoView.Visible = true; _isEnding = false; }); diff --git a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.cs b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.cs index 92609d3c37d523103a269d02786d9dbbce31cdc2..0c9e82abff9b739ffef36a300f111144f77f15a3 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.cs @@ -44,6 +44,7 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension private int _playlistIndex; private TimeSpan _naturalDuration; private bool _isLoopingEnabled; + private bool _isLoopingAllEnabled; private double _playbackRate; public MediaPlayerExtension(object owner) @@ -177,9 +178,16 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension switch (_owner.Source) { - case MediaPlaybackList playlist when playlist.Items.Count > 0 && _playlistItems is not null: + case MediaPlaybackList playlist when playlist.Items.Count > 0: SetPlaylistItems(playlist); - _uri = _playlistItems[0]; + if (_playlistItems is not null) + { + _uri = _playlistItems[0]; + } + else + { + throw new InvalidOperationException("Playlist Items could not be set"); + } break; case MediaPlaybackItem item: @@ -190,6 +198,7 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension _uri = source.Uri; break; + default: throw new InvalidOperationException("Unsupported media source type"); } @@ -203,6 +212,30 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension _owner.PlaybackSession.PlaybackState = MediaPlaybackState.Paused; } + public void ReInitializeSource() + { + NaturalDuration = TimeSpan.Zero; + if (Position != TimeSpan.Zero) + { + Position = TimeSpan.Zero; + } + + if (_owner.Source == null) + { + return; + } + _owner.PlaybackSession.PlaybackState = MediaPlaybackState.Opening; + InitializePlayer(); + ApplyVideoSource(); + Events?.RaiseSourceChanged(); + + // Set the player back to the paused state, so that the + // transport controls can be shown properly. + // This may need to be changed when the initialization of libVLC + // can be taken into account, as well as the media status. + _owner.PlaybackSession.PlaybackState = MediaPlaybackState.Paused; + } + private void SetPlaylistItems(MediaPlaybackList playlist) { _playlistItems = playlist.Items @@ -281,10 +314,15 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension set { _isLoopingEnabled = value; - if (_player is not null) - { - _player.SetIsLoopingEnabled(value); - } + } + } + + public bool IsLoopingAllEnabled + { + get => _isLoopingAllEnabled; + set + { + _isLoopingAllEnabled = value; } } @@ -393,4 +431,26 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension _isPlayerPrepared = false; } } + + public void PreviousTrack() + { + // Play next item in playlist, if any + if (_playlistItems != null && _playlistIndex > 0) + { + _uri = _playlistItems[--_playlistIndex]; + ReInitializeSource(); + Play(); + } + } + + public void NextTrack() + { + // Play next item in playlist, if any + if (_playlistItems != null && _playlistIndex < _playlistItems.Count - 1) + { + _uri = _playlistItems[++_playlistIndex]; + ReInitializeSource(); + Play(); + } + } } diff --git a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.events.cs b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.events.cs index 3764504ffa36fb01667459cd19a87692e0445912..1e08cf9a039649563f45b7a51ebf01074794fc01 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.events.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.events.cs @@ -89,12 +89,31 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension { Events?.RaiseMediaEnded(); _owner.PlaybackSession.PlaybackState = MediaPlaybackState.None; - - // Play next item in playlist, if any - if (_playlistItems != null && _playlistIndex < _playlistItems.Count - 1) + if (IsLoopingEnabled && !IsLoopingAllEnabled) + { + ReInitializeSource(); + Play(); + } + else { - _uri = _playlistItems[++_playlistIndex]; - ApplyVideoSource(); + // Play first item in playlist, if any and repeat all + if (_playlistItems != null && _playlistIndex >= _playlistItems.Count - 1 && IsLoopingAllEnabled) + { + _playlistIndex = 0; + _uri = _playlistItems[_playlistIndex]; + ReInitializeSource(); + Play(); + } + else + { + // Play next item in playlist, if any + if (_playlistItems != null && _playlistIndex < _playlistItems.Count - 1) + { + _uri = _playlistItems[++_playlistIndex]; + ReInitializeSource(); + Play(); + } + } } } diff --git a/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/HtmlMediaPlayer.cs b/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/HtmlMediaPlayer.cs index 2c7820f39a053d103d36f13d9310264622f44ce3..bcc5208d02ac2d9cfc77f2cb1e249fc4084769e0 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/HtmlMediaPlayer.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/HtmlMediaPlayer.cs @@ -165,18 +165,13 @@ internal partial class HtmlMediaPlayer : Border { add { - if (_activeElement == null) - { - return; - } - _activeElement.RegisterHtmlEventHandler("timeupdate", value); + _htmlVideo.RegisterHtmlEventHandler("timeupdate", value); + _htmlAudio.RegisterHtmlEventHandler("timeupdate", value); } remove { - if (_activeElement != null) - { - _activeElement.UnregisterHtmlEventHandler("timeupdate", value); - } + _htmlVideo.UnregisterHtmlEventHandler("timeupdate", value); + _htmlAudio.UnregisterHtmlEventHandler("timeupdate", value); } } @@ -187,18 +182,13 @@ internal partial class HtmlMediaPlayer : Border { add { - if (_activeElement == null) - { - return; - } - _activeElement.RegisterHtmlEventHandler("loadedmetadata", value); + _htmlVideo.RegisterHtmlEventHandler("loadedmetadata", value); + _htmlAudio.RegisterHtmlEventHandler("loadedmetadata", value); } remove { - if (_activeElement != null) - { - _activeElement.UnregisterHtmlEventHandler("loadedmetadata", value); - } + _htmlVideo.UnregisterHtmlEventHandler("loadedmetadata", value); + _htmlAudio.UnregisterHtmlEventHandler("loadedmetadata", value); } } @@ -209,18 +199,13 @@ internal partial class HtmlMediaPlayer : Border { add { - if (_activeElement == null) - { - return; - } - _activeElement.RegisterHtmlEventHandler("ended", value); + _htmlVideo.RegisterHtmlEventHandler("ended", value); + _htmlAudio.RegisterHtmlEventHandler("ended", value); } remove { - if (_activeElement != null) - { - _activeElement.UnregisterHtmlEventHandler("ended", value); - } + _htmlVideo.UnregisterHtmlEventHandler("ended", value); + _htmlAudio.UnregisterHtmlEventHandler("ended", value); } } @@ -460,6 +445,7 @@ internal partial class HtmlMediaPlayer : Border } } + //player.TimeUpdated += player.OnHtmlTimeUpdated; } } @@ -650,25 +636,4 @@ internal partial class HtmlMediaPlayer : Border NativeMethods.SetPlaybackRate(IsAudio ? _htmlAudio.HtmlId : _htmlVideo.HtmlId, value); } } - - private bool _isLoopingEnabled; - public void SetIsLoopingEnabled(bool value) - { - _isLoopingEnabled = value; - if (_activeElement != null) - { - if (_isLoopingEnabled) - { - _activeElement.SetHtmlAttribute("loop", "loop"); - } - else - { - _activeElement.ClearHtmlAttribute("loop"); - } - if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Debug)) - { - this.Log().Debug($"{_activeElementName} loop {_isLoopingEnabled}: [{Source}]"); - } - } - } } diff --git a/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/MediaPlayerExtension.cs b/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/MediaPlayerExtension.cs index e41e29f5523f1a45792423693e57e70f7f1ec475..1075b78d88552e85782328cac75bd5ed8ccdeda5 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/MediaPlayerExtension.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/MediaPlayerExtension.cs @@ -31,6 +31,8 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension private bool _updatingPosition; private bool _isPlayRequested; private bool _isPlayerPrepared; + private bool _isLoopingEnabled; + private bool _isLoopingAllEnabled; private List? _playlistItems; private int _playlistIndex; private TimeSpan _naturalDuration; @@ -115,17 +117,21 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension } } - private bool _isLoopingEnabled; public bool IsLoopingEnabled { get => _isLoopingEnabled; set { _isLoopingEnabled = value; - if (_player is not null) - { - _player.SetIsLoopingEnabled(value); - } + } + } + + public bool IsLoopingAllEnabled + { + get => _isLoopingAllEnabled; + set + { + _isLoopingAllEnabled = value; } } @@ -245,13 +251,14 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension if (this.Log().IsEnabled(LogLevel.Debug)) { this.Log().Debug($"MediaPlayerExtension.OnStatusMediaChanged to state {_player?.PlayerState.ToString()}"); + this.Log().Debug($"MediaPlayerExtension owner PlaybackSession PlaybackState {_owner?.PlaybackSession?.PlaybackState.ToString()}"); } - if (_player?.PlayerState == HtmlMediaPlayerState.Paused && _owner.PlaybackSession.PlaybackState == MediaPlaybackState.Playing) + if (_player?.PlayerState == HtmlMediaPlayerState.Paused && _owner?.PlaybackSession.PlaybackState == MediaPlaybackState.Playing) { _owner.PlaybackSession.PlaybackState = MediaPlaybackState.Paused; } - else if (_player?.PlayerState == HtmlMediaPlayerState.Playing && _owner.PlaybackSession.PlaybackState == MediaPlaybackState.Paused) + else if (_player?.PlayerState == HtmlMediaPlayerState.Playing && _owner?.PlaybackSession.PlaybackState == MediaPlaybackState.Paused) { _owner.PlaybackSession.PlaybackState = MediaPlaybackState.Playing; } @@ -292,9 +299,16 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension switch (_owner.Source) { - case MediaPlaybackList playlist when playlist.Items.Count > 0 && _playlistItems is not null: + case MediaPlaybackList playlist when playlist.Items.Count > 0: SetPlaylistItems(playlist); - _uri = _playlistItems[0]; + if (_playlistItems is not null && _playlistItems.Any()) + { + _uri = _playlistItems[0]; + } + else + { + throw new InvalidOperationException("Playlist Items could not be set"); + } break; case MediaPlaybackItem item: @@ -320,6 +334,24 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension } } + public void ReInitializeSource() + { + NaturalDuration = TimeSpan.Zero; + if (Position != TimeSpan.Zero) + { + Position = TimeSpan.Zero; + } + + if (_owner.Source == null) + { + return; + } + _owner.PlaybackSession.PlaybackState = MediaPlaybackState.Opening; + InitializePlayer(); + ApplyVideoSource(); + Events?.RaiseSourceChanged(); + } + private void ApplyVideoSource() { if (this.Log().IsEnabled(LogLevel.Debug)) @@ -501,6 +533,23 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension } } + private void SetPrepared(HtmlMediaPlayer _player) + { + if (_owner.PlaybackSession.PlaybackState == MediaPlaybackState.Opening) + { + if (_isPlayRequested) + { + _player.Play(); + _owner.PlaybackSession.PlaybackState = MediaPlaybackState.Playing; + } + else + { + _owner.PlaybackSession.PlaybackState = MediaPlaybackState.Paused; + } + } + _isPlayerPrepared = true; + } + public void OnPrepared(object? sender, object what) { if (sender is HtmlMediaPlayer mp && _player is not null) @@ -527,21 +576,10 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension } catch { } } - - if (_owner.PlaybackSession.PlaybackState == MediaPlaybackState.Opening) + if (NaturalDuration > TimeSpan.Zero) { - if (_isPlayRequested) - { - _player.Play(); - _owner.PlaybackSession.PlaybackState = MediaPlaybackState.Playing; - } - else - { - _owner.PlaybackSession.PlaybackState = MediaPlaybackState.Paused; - } + SetPrepared(_player); } - - _isPlayerPrepared = true; } if (Events is not null) @@ -566,6 +604,7 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension OnMediaFailed(message: $"MediaPlayer Error: {(string)what}"); } + public void OnCompletion(object? sender, object what) { if (this.Log().IsEnabled(LogLevel.Debug)) @@ -575,15 +614,34 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension Events?.RaiseMediaEnded(); _owner.PlaybackSession.PlaybackState = MediaPlaybackState.None; - - // Play next item in playlist, if any - if (_playlistItems != null && _playlistIndex < _playlistItems.Count - 1) + if (IsLoopingEnabled && !IsLoopingAllEnabled) { - _uri = _playlistItems[++_playlistIndex]; - ApplyVideoSource(); + Play(); + } + else + { + // Play first item in playlist, if any and repeat all + if (_playlistItems != null && _playlistIndex >= _playlistItems.Count - 1 && IsLoopingAllEnabled) + { + _playlistIndex = 0; + _uri = _playlistItems[_playlistIndex]; + ReInitializeSource(); + Play(); + } + else + { + // Play next item in playlist, if any + if (_playlistItems != null && _playlistIndex < _playlistItems.Count - 1) + { + _uri = _playlistItems[++_playlistIndex]; + ReInitializeSource(); + Play(); + } + } } } + private void OnMediaFailed(global::System.Exception? ex = null, string? message = null) { Events?.RaiseMediaFailed(MediaPlayerError.Unknown, message ?? ex?.Message, ex); @@ -645,4 +703,28 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension { // No effect on WebAssembly. } + + public void PreviousTrack() + { + // Play prev item in playlist, if any + if (_playlistItems != null && _playlistIndex > 0) + { + Pause(); + _uri = _playlistItems[--_playlistIndex]; + ReInitializeSource(); + Play(); + } + } + + public void NextTrack() + { + // Play next item in playlist, if any + if (_playlistItems != null && _playlistIndex < _playlistItems.Count - 1) + { + Pause(); + _uri = _playlistItems[++_playlistIndex]; + ReInitializeSource(); + Play(); + } + } } diff --git a/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/WasmScripts/uno.ui.media.js b/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/WasmScripts/uno.ui.media.js index a381b12341b08faa4356fffbe92b68e13633cf4d..22f2a90f15d351f92ff85c6f388b0346d3a43fad 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/WasmScripts/uno.ui.media.js +++ b/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/WasmScripts/uno.ui.media.js @@ -13,13 +13,25 @@ var Uno; return document.getElementById(htmlId.toString()).videoHeight; } static getCurrentPosition(htmlId) { - return document.getElementById(htmlId.toString()).currentTime; + const element = document.getElementById(htmlId); + if (element !== null && element !== undefined) { + return element.currentTime; + } + else { + return 0; + } } static getPaused(htmlId) { - return document.getElementById(htmlId.toString()).paused; + const element = document.getElementById(htmlId); + if (element !== null && element !== undefined) { + return element.paused; + } } static setCurrentPosition(htmlId, currentTime) { - document.getElementById(htmlId.toString()).currentTime = currentTime; + const element = document.getElementById(htmlId); + if (element !== null && element !== undefined) { + element.currentTime = currentTime; + } } static setAttribute(htmlId, name, value) { document.getElementById(htmlId.toString()).setAttribute(name, value); diff --git a/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/ts/MediaElement.ts b/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/ts/MediaElement.ts index dd32dddf463491349c56a378cd2c603da6e1914c..a8f26ced00e5a3ff015b7278ace4803fbd4a41d6 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/ts/MediaElement.ts +++ b/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/ts/MediaElement.ts @@ -10,15 +10,26 @@ namespace Uno.UI.Media { } public static getCurrentPosition(htmlId: number): number { - return document.getElementById(htmlId.toString()).currentTime; + const element = document.getElementById(htmlId); + if (element !== null && element !== undefined) { + return element.currentTime; + } else { + return 0; + } } public static getPaused(htmlId: number): number { - return document.getElementById(htmlId.toString()).paused; + const element = document.getElementById(htmlId); + if (element !== null && element !== undefined) { + return element.paused; + } } public static setCurrentPosition(htmlId: number, currentTime: number) { - document.getElementById(htmlId.toString()).currentTime = currentTime; + const element = document.getElementById(htmlId); + if (element !== null && element !== undefined) { + element.currentTime = currentTime; + } } public static setAttribute(htmlId: number, name: string, value: string) { diff --git a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems index 087779811cf917dff47f03920f8ee6ef6170a9c4..0db210106438d7242f215b6cec623d699f17f672 100644 --- a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems +++ b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems @@ -3306,6 +3306,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -7213,6 +7217,9 @@ MediaPlayerElement_Full.xaml + + MediaPlayerElement_Playlist.xaml + MediaPlayerElement_Minimal.xaml diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/MediaPlayerElement/MediaPlayerElement_Playlist.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/MediaPlayerElement/MediaPlayerElement_Playlist.xaml new file mode 100644 index 0000000000000000000000000000000000000000..130f7fd6179355bcc6a4e84dad7f943933540176 --- /dev/null +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/MediaPlayerElement/MediaPlayerElement_Playlist.xaml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/MediaPlayerElement/MediaPlayerElement_Playlist.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/MediaPlayerElement/MediaPlayerElement_Playlist.xaml.cs new file mode 100644 index 0000000000000000000000000000000000000000..6b3214903832d458911ce0eb23a27f322e81e899 --- /dev/null +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/MediaPlayerElement/MediaPlayerElement_Playlist.xaml.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Uno.UI.Samples.Controls; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.Media.Core; +using Windows.Media.Playback; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; + +// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 + +namespace UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement +{ + [SampleControlInfo("MediaPlayerElement", "Playlist", ignoreInSnapshotTests: true, description: "Test the playlist and the Repeat")] + public sealed partial class MediaPlayerElement_Playlist : UserControl + { + public MediaPlayerElement_Playlist() + { + this.InitializeComponent(); + InitializePlaybackList(); + } + + private void InitializePlaybackList() + { + var mediaPlaybackList = new MediaPlaybackList(); + mediaPlaybackList.Items.Add(new MediaPlaybackItem(MediaSource.CreateFromUri(new Uri("https://uno-assets.platform.uno/tests/videos/Mobile_Development_in_VS_Code_with_Uno_Platform_orDotNetMAUI.mp4")))); + mediaPlaybackList.Items.Add(new MediaPlaybackItem(MediaSource.CreateFromUri(new Uri("https://uno-assets.platform.uno/tests/audio/Getting_Started_with_Uno_Platform_and_Visual_Studio_Code.mp3")))); + mediaPlaybackList.Items.Add(new MediaPlaybackItem(MediaSource.CreateFromUri(new Uri("https://uno-assets.platform.uno/tests/videos/Getting_Started_with_Uno_Platform_and_Visual_Studio_Code.mp4")))); + if (mpe.MediaPlayer == null) + { + mpe.SetMediaPlayer(new Windows.Media.Playback.MediaPlayer()); + } + mpe.MediaPlayer.Source = mediaPlaybackList; + } + } +} diff --git a/src/Uno.UI-Skia-only.slnf b/src/Uno.UI-Skia-only.slnf index 36c9701ddde881d2609cbd41063d5ecf248e8a80..25d3f84bcc76c16f62aa0c56132941e926298c28 100644 --- a/src/Uno.UI-Skia-only.slnf +++ b/src/Uno.UI-Skia-only.slnf @@ -9,7 +9,6 @@ "SamplesApp\\Benchmarks.Shared\\SamplesApp.Benchmarks.shproj", "SamplesApp\\SamplesApp.Shared\\SamplesApp.Shared.shproj", "SamplesApp\\SamplesApp.Skia.Gtk\\SamplesApp.Skia.Gtk.csproj", - "SamplesApp\\SamplesApp.Skia.Tizen\\SamplesApp.Skia.Tizen.csproj", "SamplesApp\\SamplesApp.Skia.Linux.FrameBuffer\\SamplesApp.Skia.Linux.FrameBuffer.csproj", "SamplesApp\\SamplesApp.Skia.Wpf\\SamplesApp.Skia.Wpf.csproj", "SamplesApp\\SamplesApp.Skia\\SamplesApp.Skia.csproj", diff --git a/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaTransportControls.cs b/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaTransportControls.cs index ec85f3d3650b7ce7b0849c8cb18856438c69ac08..13698932ce47fef9813d7621b1b072e679f3cb44 100644 --- a/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaTransportControls.cs +++ b/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaTransportControls.cs @@ -777,25 +777,71 @@ namespace Windows.UI.Xaml.Controls { return; } - +#if !(__ANDROID__ || __IOS__ || __MACOS__) + if (_mpe.MediaPlayer.IsLoopingEnabled + && !_mpe.MediaPlayer.IsLoopingAllEnabled + && _mpe.MediaPlayer.Source is MediaPlaybackList) + { + _mpe.MediaPlayer.IsLoopingAllEnabled = true; + } + else + { + _mpe.MediaPlayer.IsLoopingEnabled = !_mpe.MediaPlayer.IsLoopingEnabled; + _mpe.MediaPlayer.IsLoopingAllEnabled = false; + } +#else _mpe.MediaPlayer.IsLoopingEnabled = !_mpe.MediaPlayer.IsLoopingEnabled; +#endif UpdateRepeatStates(); } private void PreviousTrackButtonTapped(object sender, RoutedEventArgs e) { +#if !(__ANDROID__ || __IOS__ || __MACOS__) + + if (_mpe is not null + && _mpe.MediaPlayer.Source is MediaPlaybackList) + { + _mpe?.MediaPlayer?.PreviousTrack(); + } + else + { + if (_mediaPlayer is not null) + { + _mediaPlayer.PlaybackSession.Position = TimeSpan.Zero; + _mediaPlayer.PlaybackSession.Position = TimeSpan.Zero; + } + } +#else if (_mediaPlayer is not null) { _mediaPlayer.PlaybackSession.Position = TimeSpan.Zero; _mediaPlayer.PlaybackSession.Position = TimeSpan.Zero; } +#endif } private void NextTrackButtonTapped(object sender, RoutedEventArgs e) { +#if !(__ANDROID__ || __IOS__ || __MACOS__) + if (_mpe is not null + && _mpe.MediaPlayer.Source is MediaPlaybackList) + { + _mpe?.MediaPlayer?.NextTrack(); + } + else + { + if (_mediaPlayer is not null) + { + _mediaPlayer.PlaybackSession.Position = _mediaPlayer.PlaybackSession.NaturalDuration; + _mediaPlayer.PlaybackSession.Position = _mediaPlayer.PlaybackSession.NaturalDuration; + } + } +#else if (_mediaPlayer is not null) { _mediaPlayer.PlaybackSession.Position = _mediaPlayer.PlaybackSession.NaturalDuration; _mediaPlayer.PlaybackSession.Position = _mediaPlayer.PlaybackSession.NaturalDuration; } +#endif } private void ZoomButtonTapped(object sender, RoutedEventArgs e) { @@ -1154,16 +1200,31 @@ namespace Windows.UI.Xaml.Controls { return; } +#if !(__ANDROID__ || __IOS__ || __MACOS__) + var state = _mpe.MediaPlayer.IsLoopingAllEnabled + ? VisualState.RepeatStates.RepeatAllState + : _mpe.MediaPlayer.IsLoopingEnabled + ? VisualState.RepeatStates.RepeatOneState + : VisualState.RepeatStates.RepeatNoneState; + VisualStateManager.GoToState(this, state, useTransitions); + var uiaKey = _mpe.MediaPlayer.IsLoopingAllEnabled + ? UIAKeys.UIA_MEDIA_REPEAT_ALL + : _mpe.MediaPlayer.IsLoopingEnabled + ? UIAKeys.UIA_MEDIA_REPEAT_ONE + : UIAKeys.UIA_MEDIA_REPEAT_NONE; + SetAutomationNameAndTooltip(m_tpRepeatButton, uiaKey); +#else var state = _mpe.MediaPlayer.IsLoopingEnabled - ? VisualState.RepeatStates.RepeatAllState - : VisualState.RepeatStates.RepeatNoneState; + ? VisualState.RepeatStates.RepeatAllState + : VisualState.RepeatStates.RepeatNoneState; VisualStateManager.GoToState(this, state, useTransitions); var uiaKey = _mpe.MediaPlayer.IsLoopingEnabled - ? UIAKeys.UIA_MEDIA_REPEAT_ALL - : UIAKeys.UIA_MEDIA_REPEAT_NONE; + ? UIAKeys.UIA_MEDIA_REPEAT_ALL + : UIAKeys.UIA_MEDIA_REPEAT_NONE; SetAutomationNameAndTooltip(m_tpRepeatButton, uiaKey); +#endif } } diff --git a/src/Uno.UWP/Generated/3.0.0.0/Windows.Media.Playback/MediaPlaybackItem.cs b/src/Uno.UWP/Generated/3.0.0.0/Windows.Media.Playback/MediaPlaybackItem.cs index 294b6b0c3bb57c5926335cdff2d4897d1d0c5816..58b0f91afc20de79fb5610de4130f2a5e1adbf6f 100644 --- a/src/Uno.UWP/Generated/3.0.0.0/Windows.Media.Playback/MediaPlaybackItem.cs +++ b/src/Uno.UWP/Generated/3.0.0.0/Windows.Media.Playback/MediaPlaybackItem.cs @@ -17,10 +17,10 @@ namespace Windows.Media.Playback throw new global::System.NotImplementedException("The member MediaPlaybackAudioTrackList MediaPlaybackItem.AudioTracks is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=MediaPlaybackAudioTrackList%20MediaPlaybackItem.AudioTracks"); } } -#endif -#if false || false || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || false - [global::Uno.NotImplemented("IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__")] - public global::Windows.Media.Core.MediaSource Source + #endif + #if false || false || IS_UNIT_TESTS || false || false || __NETSTD_REFERENCE__ || false + [global::Uno.NotImplemented("IS_UNIT_TESTS", "__NETSTD_REFERENCE__")] + public global::Windows.Media.Core.MediaSource Source { get { @@ -146,7 +146,7 @@ namespace Windows.Media.Playback } #endif // Forced skipping of method Windows.Media.Playback.MediaPlaybackItem.MediaPlaybackItem(Windows.Media.Core.MediaSource, System.TimeSpan, System.TimeSpan) -#if false || false || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || false +#if false || false || IS_UNIT_TESTS || false || false || __NETSTD_REFERENCE__ || false [global::Uno.NotImplemented("IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__")] public MediaPlaybackItem(global::Windows.Media.Core.MediaSource source) { @@ -171,7 +171,7 @@ namespace Windows.Media.Playback // Forced skipping of method Windows.Media.Playback.MediaPlaybackItem.CanSkip.set #if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::Windows.Media.Playback.MediaItemDisplayProperties GetDisplayProperties() + public global::Windows.Media.Playback.MediaItemDisplayProperties GetDisplayProperties() { throw new global::System.NotImplementedException("The member MediaItemDisplayProperties MediaPlaybackItem.GetDisplayProperties() is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=MediaItemDisplayProperties%20MediaPlaybackItem.GetDisplayProperties%28%29"); } diff --git a/src/Uno.UWP/Generated/3.0.0.0/Windows.Media.Playback/MediaPlaybackList.cs b/src/Uno.UWP/Generated/3.0.0.0/Windows.Media.Playback/MediaPlaybackList.cs index db8d2deeb59e9f9706c6a8edfe0fbf796a41b30d..d3023ba99fe1c5df0f6392cba47f3db505116f4e 100644 --- a/src/Uno.UWP/Generated/3.0.0.0/Windows.Media.Playback/MediaPlaybackList.cs +++ b/src/Uno.UWP/Generated/3.0.0.0/Windows.Media.Playback/MediaPlaybackList.cs @@ -55,10 +55,10 @@ namespace Windows.Media.Playback throw new global::System.NotImplementedException("The member uint MediaPlaybackList.CurrentItemIndex is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=uint%20MediaPlaybackList.CurrentItemIndex"); } } -#endif -#if false || false || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || false - [global::Uno.NotImplemented("IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__")] - public global::Windows.Foundation.Collections.IObservableVector Items + #endif + #if false || false || IS_UNIT_TESTS || false || false || __NETSTD_REFERENCE__ || false + [global::Uno.NotImplemented("IS_UNIT_TESTS", "__WASM__", "__NETSTD_REFERENCE__")] + public global::Windows.Foundation.Collections.IObservableVector Items { get { diff --git a/src/Uno.UWP/Media/Playback/IMediaPlaybackList.cs b/src/Uno.UWP/Media/Playback/IMediaPlaybackList.cs index 41de1301f20fdf435d76c498d36b82d50bee8ef0..5433fd41c1b7d24a6b4dc263b6749e0a89d9524d 100644 --- a/src/Uno.UWP/Media/Playback/IMediaPlaybackList.cs +++ b/src/Uno.UWP/Media/Playback/IMediaPlaybackList.cs @@ -1,4 +1,4 @@ -#if __IOS__ || __ANDROID__ || __MACOS__ +#if __IOS__ || __ANDROID__ || __MACOS__ || __SKIA__ || __WASM__ namespace Windows.Media.Playback { diff --git a/src/Uno.UWP/Media/Playback/IMediaPlayerExtension.cs b/src/Uno.UWP/Media/Playback/IMediaPlayerExtension.cs index df0a49d855ff42fd71739e84343545ac203cefe2..1b0cc2b98623d97cf6b3e705b9845402055c07f2 100644 --- a/src/Uno.UWP/Media/Playback/IMediaPlayerExtension.cs +++ b/src/Uno.UWP/Media/Playback/IMediaPlayerExtension.cs @@ -29,6 +29,11 @@ namespace Uno.Media.Playback /// bool IsLoopingEnabled { get; set; } + /// + /// Gets or sets the looping all mode + /// + bool IsLoopingAllEnabled { get; set; } + /// /// Gets the current player state /// @@ -173,5 +178,15 @@ namespace Uno.Media.Playback /// Notifies the extension that an option has changed /// void OnOptionChanged(string name, object value); + + /// + /// Previous Track on a Playlist + /// + void PreviousTrack(); + + /// + /// Next Track on a Playlist + /// + void NextTrack(); } } diff --git a/src/Uno.UWP/Media/Playback/MediaPlaybackItem.cs b/src/Uno.UWP/Media/Playback/MediaPlaybackItem.cs index e59da8188a69b21a1906af7608e0f0eee4393121..bdc71fdd7bf3f216bbe25a10aee836ef9a8ee3ab 100644 --- a/src/Uno.UWP/Media/Playback/MediaPlaybackItem.cs +++ b/src/Uno.UWP/Media/Playback/MediaPlaybackItem.cs @@ -1,4 +1,4 @@ -#if __ANDROID__ || __IOS__ || __MACOS__ +#if __ANDROID__ || __IOS__ || __MACOS__ || __SKIA__ || __WASM__ using Windows.Media.Core; namespace Windows.Media.Playback diff --git a/src/Uno.UWP/Media/Playback/MediaPlaybackList.cs b/src/Uno.UWP/Media/Playback/MediaPlaybackList.cs index 22e1ea5bc494214d995966eb9da3d6affe9b0ec9..9c3c5741c3a785db8ebb23e724d578f07ef1d1d5 100644 --- a/src/Uno.UWP/Media/Playback/MediaPlaybackList.cs +++ b/src/Uno.UWP/Media/Playback/MediaPlaybackList.cs @@ -1,5 +1,6 @@ -#if __ANDROID__ || __IOS__ || __MACOS__ +#if __ANDROID__ || __IOS__ || __MACOS__ || __SKIA__ || __WASM__ using Windows.Foundation.Collections; +using Windows.Media.Playback; namespace Windows.Media.Playback { diff --git a/src/Uno.UWP/Media/Playback/MediaPlayer.others.cs b/src/Uno.UWP/Media/Playback/MediaPlayer.others.cs index 9265e8e774863286c3d576a88242b6dc7d21ec23..139ce444fc30317bd1f547aeb161b1327f22db5c 100644 --- a/src/Uno.UWP/Media/Playback/MediaPlayer.others.cs +++ b/src/Uno.UWP/Media/Playback/MediaPlayer.others.cs @@ -70,6 +70,17 @@ namespace Windows.Media.Playback } } + public bool IsLoopingAllEnabled + { + get => _extension?.IsLoopingAllEnabled ?? false; + set + { + if (_extension is not null) + { + _extension.IsLoopingAllEnabled = value; + } + } + } public MediaPlayerState CurrentState => _extension?.CurrentState ?? MediaPlayerState.Closed; @@ -201,6 +212,11 @@ namespace Windows.Media.Playback public void OnVolumeChanged() => _extension?.OnVolumeChanged(); + public void PreviousTrack() + => _extension?.PreviousTrack(); + public void NextTrack() + => _extension?.NextTrack(); + public event TypedEventHandler BufferingEnded; public event TypedEventHandler BufferingStarted; public event TypedEventHandler CurrentStateChanged;