From 9f0a50761dc96c05dbd0c50a338d8a59ba7653fa Mon Sep 17 00:00:00 2001 From: agneszitte Date: Fri, 2 Jun 2023 14:36:14 -0400 Subject: [PATCH] fix: Properly display PosterSource for MediaPlayerElement on all platforms --- .../GTKMediaPlayer.cs | 6 ++--- .../GTKMediaPlayer.events.cs | 11 +------- .../MediaPlayerExtension.cs | 3 ++- .../MediaPlayerExtension.events.cs | 7 +++++ .../MediaPlayerExtension.cs | 10 ++++++- .../UITests.Shared/UITests.Shared.projitems | 7 +++++ .../MediaPlayerElement_Mp3_Extension.xaml | 10 +++++++ .../MediaPlayerElement_Mp3_Extension.xaml.cs | 27 +++++++++++++++++++ .../MediaPlayerElement/MediaPlayerElement.cs | 18 ++++++++----- .../Media/Playback/IMediaPlayerExtension.cs | 5 ++++ .../Media/Playback/MediaPlayer.Android.cs | 23 ++++++++++------ .../Media/Playback/MediaPlayer.iOSmacOS.cs | 12 ++++++--- .../Media/Playback/MediaPlayer.others.cs | 2 ++ 13 files changed, 108 insertions(+), 33 deletions(-) create mode 100644 src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/MediaPlayerElement/MediaPlayerElement_Mp3_Extension.xaml create mode 100644 src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/MediaPlayerElement/MediaPlayerElement_Mp3_Extension.xaml.cs diff --git a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GTKMediaPlayer.cs b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GTKMediaPlayer.cs index e1c1fdb7a3e2..c9d69ae34c2b 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GTKMediaPlayer.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GTKMediaPlayer.cs @@ -33,8 +33,6 @@ public partial class GtkMediaPlayer : FrameworkElement private double _playbackRate; private Rect _transportControlsBounds; private Windows.UI.Xaml.Media.Stretch _stretch = Windows.UI.Xaml.Media.Stretch.Uniform; - private readonly ImmutableArray audioTagAllowedFormats = ImmutableArray.Create(".MP3", ".WAV"); - private readonly ImmutableArray videoTagAllowedFormats = ImmutableArray.Create(".MP4", ".WEBM", ".OGG"); private readonly MediaPlayerPresenter _owner; public GtkMediaPlayer(MediaPlayerPresenter owner) @@ -77,10 +75,10 @@ public string Source public double VideoRatio { get; set; } public bool IsVideo - => videoTagAllowedFormats.Contains(Path.GetExtension(Source), StringComparer.OrdinalIgnoreCase); + => _mediaPlayer?.Media?.Tracks?.Any(x => x.TrackType == TrackType.Video) == true; public bool IsAudio - => audioTagAllowedFormats.Contains(Path.GetExtension(Source), StringComparer.OrdinalIgnoreCase); + => _mediaPlayer?.Media?.Tracks?.Any(x => x.TrackType == TrackType.Video) == true; public void Play() { 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 150c316d7394..e34bfab3a22a 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GTKMediaPlayer.events.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GTKMediaPlayer.events.cs @@ -327,6 +327,7 @@ private void UpdateMedia() media.Parse(MediaParseOptions.ParseNetwork); _mediaPlayer.Media = media; AddMediaEvents(); + Duration = (double)(_videoView?.MediaPlayer?.Media?.Duration / 1000 ?? 0); OnSourceLoaded?.Invoke(this, EventArgs.Empty); UpdateVideoStretch(); @@ -431,11 +432,6 @@ private void AddMediaEvents() media.MetaChanged += OnStaticMetaChanged; media.StateChanged += OnStaticStateChanged; media.ParsedChanged += OnStaticParsedChanged; - - Duration = (double)(_videoView?.MediaPlayer?.Media?.Duration / 1000 ?? 0); - OnSourceLoaded?.Invoke(this, EventArgs.Empty); - - UpdateVideoStretch(); } else { @@ -587,11 +583,6 @@ private void OnMediaPlayerTimeChange(object? sender, MediaPlayerTimeChangedEvent } } - private void OnMediaPlayerTimeChangeIsMediaParse(object? sender, MediaPlayerTimeChangedEventArgs el) - { - AddMediaEvents(); - } - private void OnEndReached() { if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) diff --git a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.cs b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.cs index 5244e46ed4e6..e2bae02eb238 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.cs @@ -193,7 +193,6 @@ public void InitializeSource() throw new InvalidOperationException("Unsupported media source type"); } ApplyVideoSource(); - Events?.RaiseMediaOpened(); Events?.RaiseSourceChanged(); // Set the player back to the paused state, so that the @@ -337,6 +336,8 @@ public TimeSpan TimelineControllerPositionOffset public double AudioBalance { get; set; } + public bool? IsVideo { get; set; } + public void SetTransportControlsBounds(Rect bounds) { _player?.SetTransportControlsBounds(bounds); 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 bde4b46a5213..684d4586f728 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.events.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.events.cs @@ -37,6 +37,8 @@ public void OnPrepared(object? sender, object what) { if (_player is not null) { + IsVideo = _player.IsVideo; + if (mp.IsVideo && Events is not null) { Events?.RaiseVideoRatioChanged(global::System.Math.Max(1, (double)mp.VideoRatio)); @@ -66,6 +68,11 @@ public void OnPrepared(object? sender, object what) } } } + + if (Events is not null) + { + Events?.RaiseMediaOpened(); + } } public void OnError(object? sender, object what) diff --git a/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/MediaPlayerExtension.cs b/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/MediaPlayerExtension.cs index a885d6e12557..0eeb6cc5baff 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/MediaPlayerExtension.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/MediaPlayerExtension.cs @@ -147,6 +147,8 @@ public bool CanPause public bool CanSeek => true; + public bool? IsVideo { get; set; } + public MediaPlayerAudioDeviceType AudioDeviceType { get; set; } public MediaPlayerAudioCategory AudioCategory { get; set; } @@ -301,7 +303,6 @@ public void InitializeSource() } ApplyVideoSource(); - Events?.RaiseMediaOpened(); Events?.RaiseSourceChanged(); } catch (global::System.Exception ex) @@ -480,6 +481,8 @@ public void OnPrepared(object? sender, object what) NaturalDuration = TimeSpan.FromSeconds(_player.Duration); + IsVideo = _player.IsVideo; + if (mp.IsVideo && Events is not null) { try @@ -509,6 +512,11 @@ public void OnPrepared(object? sender, object what) _isPlayerPrepared = true; } + + if (Events is not null) + { + Events?.RaiseMediaOpened(); + } } void OnError(object? sender, object what) diff --git a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems index 43acdbe8ba55..52c1197b527d 100644 --- a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems +++ b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems @@ -3294,6 +3294,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -7150,6 +7154,9 @@ MediaPlayerElement_Mkv_Extension.xaml + + MediaPlayerElement_Mp3_Extension.xaml + MediaPlayerElement_Mov_Extension.xaml diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/MediaPlayerElement/MediaPlayerElement_Mp3_Extension.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/MediaPlayerElement/MediaPlayerElement_Mp3_Extension.xaml new file mode 100644 index 000000000000..d6106b88e696 --- /dev/null +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/MediaPlayerElement/MediaPlayerElement_Mp3_Extension.xaml @@ -0,0 +1,10 @@ + + + + + diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/MediaPlayerElement/MediaPlayerElement_Mp3_Extension.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/MediaPlayerElement/MediaPlayerElement_Mp3_Extension.xaml.cs new file mode 100644 index 000000000000..70a3ce5e01b8 --- /dev/null +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/MediaPlayerElement/MediaPlayerElement_Mp3_Extension.xaml.cs @@ -0,0 +1,27 @@ +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.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; + +namespace UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement +{ + [SampleControlInfo("MediaPlayerElement", "Using .mp3 (Audio only)", description: "MediaPlayerElement test using .mp3 (Audio only) with PosterSource")] + public sealed partial class MediaPlayerElement_Mp3_Extension : Page + { + public MediaPlayerElement_Mp3_Extension() + { + this.InitializeComponent(); + } + } +} diff --git a/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerElement.cs b/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerElement.cs index 0523e03c0b14..23d4fc3884f0 100644 --- a/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerElement.cs +++ b/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerElement.cs @@ -249,7 +249,11 @@ private void OnMediaOpened(Windows.Media.Playback.MediaPlayer session, object ar { _ = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { - TogglePosterImage(false); + // Always show the poster source for audio content. + if (session.IsVideo) + { + TogglePosterImage(false); + } }); } @@ -326,11 +330,15 @@ private protected override void OnLoaded() && AutoPlay) { MediaPlayer.Play(); - TogglePosterImage(false); } } } + // The PosterSource is displayed in the following situations: + // - When a valid source is not set.For example, Source is not set, Source was set to Null, or the source is invalid (as is the case when a MediaFailed event fires). + // - While media is loading. For example, a valid source is set, but the MediaOpened event has not fired yet. + // - When media is streaming to another device. + // - When the media is audio only. private void TogglePosterImage(bool showPoster) { if (PosterSource != null) @@ -365,10 +373,8 @@ protected override void OnApplyTemplate() _mediaPlayerPresenter?.ApplyStretch(); } - if (!IsLoaded && MediaPlayer.PlaybackSession.PlaybackState == MediaPlaybackState.None) - { - TogglePosterImage(true); - } + // For video content, show the poster source until it is ready to be displayed. + TogglePosterImage(true); if (!_isTransportControlsBound) { diff --git a/src/Uno.UWP/Media/Playback/IMediaPlayerExtension.cs b/src/Uno.UWP/Media/Playback/IMediaPlayerExtension.cs index 135362539c75..df0a49d855ff 100644 --- a/src/Uno.UWP/Media/Playback/IMediaPlayerExtension.cs +++ b/src/Uno.UWP/Media/Playback/IMediaPlayerExtension.cs @@ -89,6 +89,11 @@ public interface IMediaPlayerExtension : IDisposable /// TimeSpan Position { get; set; } + /// + /// Determines if the current media content is video + /// + bool? IsVideo { get; } + /// /// Sets the transport controls bounds so that video can be displayed around controls /// diff --git a/src/Uno.UWP/Media/Playback/MediaPlayer.Android.cs b/src/Uno.UWP/Media/Playback/MediaPlayer.Android.cs index b0bb239116e9..314f91989652 100644 --- a/src/Uno.UWP/Media/Playback/MediaPlayer.Android.cs +++ b/src/Uno.UWP/Media/Playback/MediaPlayer.Android.cs @@ -155,8 +155,6 @@ protected virtual void InitializeSource() SetVideoSource(uri); _player.PrepareAsync(); - - MediaOpened?.Invoke(this, null); } catch (global::System.Exception ex) { @@ -270,13 +268,17 @@ private void OnPlaying() public void OnPrepared(AndroidMediaPlayer mp) { - PlaybackSession.NaturalDuration = TimeSpan.FromMilliseconds(_player.Duration); + if (mp is not null) + { + PlaybackSession.NaturalDuration = TimeSpan.FromMilliseconds(_player.Duration); - VideoRatioChanged?.Invoke(this, (double)mp.VideoWidth / global::System.Math.Max(mp.VideoHeight, 1)); + VideoRatioChanged?.Invoke(this, (double)mp.VideoWidth / global::System.Math.Max(mp.VideoHeight, 1)); - if (PlaybackSession.PlaybackState == MediaPlaybackState.Opening) - { - UpdateVideoStretch(_currentStretch); + IsVideo = mp.GetTrackInfo()?.Any(x => x.TrackType == MediaTrackType.Video) == true; + + if (PlaybackSession.PlaybackState == MediaPlaybackState.Opening) + { + UpdateVideoStretch(_currentStretch); if (_isPlayRequested) { @@ -293,7 +295,10 @@ public void OnPrepared(AndroidMediaPlayer mp) } } - _isPlayerPrepared = true; + _isPlayerPrepared = true; + + MediaOpened?.Invoke(this, null); + } } public bool OnError(AndroidMediaPlayer mp, MediaError what, int extra) @@ -383,6 +388,8 @@ public virtual TimeSpan Position } } + public bool IsVideo { get; set; } + internal void UpdateVideoStretch(VideoStretch stretch) { _currentStretch = stretch; diff --git a/src/Uno.UWP/Media/Playback/MediaPlayer.iOSmacOS.cs b/src/Uno.UWP/Media/Playback/MediaPlayer.iOSmacOS.cs index c11ce852eb22..86ca15b84870 100644 --- a/src/Uno.UWP/Media/Playback/MediaPlayer.iOSmacOS.cs +++ b/src/Uno.UWP/Media/Playback/MediaPlayer.iOSmacOS.cs @@ -246,9 +246,6 @@ protected virtual void InitializeSource() // Adapt pitch to prevent "metallic echo" when changing playback rate _player.CurrentItem.AudioTimePitchAlgorithm = AVAudioTimePitchAlgorithm.TimeDomain; - - MediaOpened?.Invoke(this, null); - } catch (Exception ex) { @@ -358,6 +355,8 @@ private void OnStatusChanged() { if (_player?.CurrentItem != null) { + IsVideo = _player.CurrentItem.Tracks?.Any(x => x.AssetTrack.FormatDescriptions.Any(x => x.MediaType == CMMediaType.Video)) == true; + if (_player.CurrentItem.Status == AVPlayerItemStatus.Failed || _player.Status == AVPlayerStatus.Failed) { OnMediaFailed(); @@ -377,6 +376,11 @@ private void OnStatusChanged() _player.Play(); } } + + if (_player.Status == AVPlayerStatus.ReadyToPlay) + { + MediaOpened?.Invoke(this, null); + } } } @@ -473,6 +477,8 @@ public TimeSpan Position } } + public bool IsVideo { get; set; } + private void OnSeekCompleted(bool finished) { if (finished) diff --git a/src/Uno.UWP/Media/Playback/MediaPlayer.others.cs b/src/Uno.UWP/Media/Playback/MediaPlayer.others.cs index 5e34265b5479..9265e8e77486 100644 --- a/src/Uno.UWP/Media/Playback/MediaPlayer.others.cs +++ b/src/Uno.UWP/Media/Playback/MediaPlayer.others.cs @@ -160,6 +160,8 @@ public TimeSpan Position } } + public bool IsVideo => _extension?.IsVideo ?? false; + public void SetUriSource(global::System.Uri value) => _extension?.SetUriSource(value);