From 67e21c17c6e777af2c86196d1bed5ce8fa10bf15 Mon Sep 17 00:00:00 2001 From: Youssef Victor Date: Tue, 21 Nov 2023 09:47:53 +0200 Subject: [PATCH] fix: Fix measuring MPE --- .../GtkMediaPlayer.cs | 26 ++-- .../GtkMediaPlayer.events.cs | 2 +- .../MediaPlayerExtension.cs | 2 +- .../MediaPlayerExtension.events.cs | 4 +- .../MediaPlayerPresenterExtension.cs | 4 + .../MediaPlayerExtension.cs | 2 +- .../IMediaPlayerPresenterExtension.cs | 4 + .../MediaPlayerElement/MediaPlayerElement.cs | 7 +- .../MediaPlayerPresenter.Others.cs | 3 + .../MediaPlayerPresenter.cs | 134 ++++++++++++++---- .../Playback/IMediaPlayerEventsExtension.cs | 4 +- .../Media/Playback/MediaPlayer.Android.cs | 2 +- .../Playback/MediaPlayer.Events.others.cs | 9 +- src/Uno.UWP/Media/Playback/MediaPlayer.cs | 2 +- .../Media/Playback/MediaPlayer.iOSmacOS.cs | 2 +- 15 files changed, 147 insertions(+), 60 deletions(-) diff --git a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GtkMediaPlayer.cs b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GtkMediaPlayer.cs index 2e3870ab8422..b2021d8ccd25 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GtkMediaPlayer.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GtkMediaPlayer.cs @@ -32,7 +32,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 double _videoRatio; private readonly MediaPlayerPresenter _owner; private (double playerHeight, double playerWidth, uint videoHeight, uint videoWidth) _lastSetDimensions; @@ -61,6 +60,9 @@ public GtkMediaPlayer(MediaPlayerPresenter owner) //_libvlc?.Dispose(); } + internal uint NaturalVideoHeight => _lastSetDimensions.videoHeight; + internal uint NaturalVideoWidth => _lastSetDimensions.videoWidth; + public string Source { get => (string)GetValue(SourceProperty); @@ -73,19 +75,7 @@ public string Source public double Duration { get; set; } - public double VideoRatio - { - get => _videoRatio; - set - { - if (_videoRatio != value) - { - _videoRatio = value; - - OnVideoRatioChanged?.Invoke(this, EventArgs.Empty); - } - } - } + public double VideoRatio { get; set; } public bool IsVideo => _mediaPlayer?.Media?.Tracks?.Any(x => x.TrackType == TrackType.Video) == true; @@ -357,6 +347,10 @@ private void UpdateVideoStretch(bool forceVideoViewVisibility = false) } VideoRatio = (double)videoHeight / (double)videoWidth; + if (videoHeight != _lastSetDimensions.videoHeight || videoWidth != _lastSetDimensions.videoWidth) + { + OnNaturalVideoDimensionChanged?.Invoke(); + } var currentSize = new Size(ActualWidth, ActualHeight); @@ -468,10 +462,6 @@ private void UpdateVideoSizeAllocate(double playerHeight, double playerWidth, ui var topInsetFill = (playerHeight - newHeight) / 2; var leftInsetFill = 0; - var newHeightFill = (int)(videoHeight * playerRatio); - var newWidthFill = (int)(videoWidth * playerRatio); - double correctVideoRate = (VideoRatio / playerRatio); - if (_videoView is not null) { Point pagePositionFill = this.TransformToVisual(root).TransformPoint(new Point(leftInsetFill, topInsetFill)); 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 fd5a048d14e0..47a6472ff6fe 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GtkMediaPlayer.events.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/GtkMediaPlayer.events.cs @@ -29,7 +29,7 @@ public partial class GtkMediaPlayer public event EventHandler? OnMetadataLoaded; public event EventHandler? OnTimeUpdate; public event EventHandler? OnSourceLoaded; - public event EventHandler? OnVideoRatioChanged; + public event Action? OnNaturalVideoDimensionChanged; private bool _updateVideoSizeOnFirstTimeStamp = true; private bool _isParsedLocalFile; diff --git a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.cs b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.cs index 0c9e82abff9b..90c19b655d81 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.cs @@ -102,7 +102,7 @@ private void InitializePlayer() _player.OnSourceLoaded += OnPrepared; _player.OnSourceEnded += OnCompletion; _player.OnTimeUpdate += OnTimeUpdate; - _player.OnVideoRatioChanged += OnVideoRatioChanged; + _player.OnNaturalVideoDimensionChanged += OnNaturalVideoDimensionChanged; _owner.PlaybackSession.PlaybackStateChanged -= OnStatusChanged; _owner.PlaybackSession.PlaybackStateChanged += OnStatusChanged; 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 1e08cf9a0396..381f01916263 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.events.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerExtension.events.cs @@ -133,14 +133,14 @@ public void OnVolumeChanged() _player?.SetVolume(volume); } - private void OnVideoRatioChanged(object? sender, object? e) + private void OnNaturalVideoDimensionChanged() { if (_player is not null && _player.IsVideo && Events is not null) { IsVideo = _player.IsVideo; - Events?.RaiseVideoRatioChanged(Math.Max(1, (double)_player.VideoRatio)); + Events?.RaiseNaturalVideoDimensionChanged(); } } diff --git a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerPresenterExtension.cs b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerPresenterExtension.cs index d71b625d13be..612185c7d861 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerPresenterExtension.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.Skia.Gtk/MediaPlayerPresenterExtension.cs @@ -30,6 +30,10 @@ public class MediaPlayerPresenterExtension : IMediaPlayerPresenterExtension { private MediaPlayerPresenter? _owner; private GtkMediaPlayer _player; + + public uint NaturalVideoHeight => _player.NaturalVideoHeight; + public uint NaturalVideoWidth => _player.NaturalVideoWidth; + public MediaPlayerPresenterExtension(object owner) { if (owner is not MediaPlayerPresenter presenter) diff --git a/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/MediaPlayerExtension.cs b/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/MediaPlayerExtension.cs index ad15e7511807..c4ba1350b3fe 100644 --- a/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/MediaPlayerExtension.cs +++ b/src/AddIns/Uno.UI.MediaPlayer.WebAssembly/MediaPlayerExtension.cs @@ -572,7 +572,7 @@ public void OnPrepared(object? sender, object what) this.Log().Debug($"OnPrepared: {mp.VideoWidth}x{mp.VideoHeight}"); } - Events.RaiseVideoRatioChanged((double)mp.VideoWidth / global::System.Math.Max(mp.VideoHeight, 1)); + Events.RaiseNaturalVideoDimensionChanged(); } catch { } } diff --git a/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/IMediaPlayerPresenterExtension.cs b/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/IMediaPlayerPresenterExtension.cs index 668493d33e01..530070098d34 100644 --- a/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/IMediaPlayerPresenterExtension.cs +++ b/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/IMediaPlayerPresenterExtension.cs @@ -15,5 +15,9 @@ public interface IMediaPlayerPresenterExtension void RequestCompactOverlay(); void ExitCompactOverlay(); + + uint NaturalVideoHeight { get; } + + uint NaturalVideoWidth { get; } } } diff --git a/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerElement.cs b/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerElement.cs index bae2b90c920b..83067960c986 100644 --- a/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerElement.cs +++ b/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerElement.cs @@ -220,7 +220,7 @@ private static void OnMediaPlayerChanged(DependencyObject sender, DependencyProp { oldMediaPlayer.MediaFailed -= mpe.OnMediaFailed; oldMediaPlayer.MediaOpened -= mpe.OnMediaOpened; - oldMediaPlayer.VideoRatioChanged -= mpe.OnVideoRatioChanged; + oldMediaPlayer.NaturalVideoDimensionChanged -= mpe.OnNaturalVideoDimensionChanged; oldMediaPlayer.Dispose(); } @@ -229,14 +229,14 @@ private static void OnMediaPlayerChanged(DependencyObject sender, DependencyProp newMediaPlayer.Source = mpe.Source; newMediaPlayer.MediaFailed += mpe.OnMediaFailed; newMediaPlayer.MediaOpened += mpe.OnMediaOpened; - newMediaPlayer.VideoRatioChanged -= mpe.OnVideoRatioChanged; + newMediaPlayer.NaturalVideoDimensionChanged -= mpe.OnNaturalVideoDimensionChanged; mpe.TransportControls?.SetMediaPlayer(newMediaPlayer); mpe._isTransportControlsBound = true; } }; } - private void OnVideoRatioChanged(Windows.Media.Playback.MediaPlayer sender, double args) + private void OnNaturalVideoDimensionChanged(MediaPlayer sender, object args) { _ = Dispatcher.RunAsync( CoreDispatcherPriority.Normal, @@ -362,6 +362,7 @@ protected override void OnApplyTemplate() _layoutRoot = this.GetTemplateChild(LayoutRootName) as Grid; _posterImage = this.GetTemplateChild(PosterImageName) as Image; _mediaPlayerPresenter = this.GetTemplateChild(MediaPlayerPresenterName) as MediaPlayerPresenter; + _mediaPlayerPresenter?.SetOwner(this); _transportControlsPresenter = this.GetTemplateChild(TransportControlsPresenterName) as ContentPresenter; _transportControlsPresenter.Content = TransportControls; diff --git a/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerPresenter.Others.cs b/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerPresenter.Others.cs index 6563bf7caf16..e1900fa9691c 100644 --- a/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerPresenter.Others.cs +++ b/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerPresenter.Others.cs @@ -30,6 +30,9 @@ partial void InitializePartial() } } + internal uint NaturalVideoHeight => _extension?.NaturalVideoHeight ?? 0; + internal uint NaturalVideoWidth => _extension?.NaturalVideoWidth ?? 0; + partial void OnMediaPlayerChangedPartial(MediaPlayer mediaPlayer) => _extension?.MediaPlayerChanged(); diff --git a/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerPresenter.cs b/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerPresenter.cs index 65de4db38cb7..c62049ffbcad 100644 --- a/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerPresenter.cs +++ b/src/Uno.UI/UI/Xaml/Controls/MediaPlayerElement/MediaPlayerPresenter.cs @@ -9,7 +9,36 @@ namespace Windows.UI.Xaml.Controls { public partial class MediaPlayerPresenter : Border { - private double _currentRatio = 1; + private WeakReference wrOwner; + + internal void SetOwner(MediaPlayerElement owner) + { + wrOwner = new WeakReference(owner); + } + + private float GetScaledOtherDimension( + float scaledOneDimension, + uint naturalOneDimension, + uint naturalOtherDimension) + { + // + // naturalOneDimension is mapped to scaledOneDimension. Map naturalOtherDimension to scaledOtherDimension using the + // same scale factor. + // + // scaledOther / naturalOther = scaledOne / naturalOne + // scaledOther = naturalOther * scaledOne / naturalOne + // + + if (naturalOneDimension == 0) + { + return 0.0f; + } + else + { + return (float)naturalOtherDimension * scaledOneDimension / (float)naturalOneDimension; + } + } + #region MediaPlayer Property @@ -36,14 +65,14 @@ private static void OnMediaPlayerChanged(DependencyObject sender, DependencyProp } if (args.OldValue is Windows.Media.Playback.MediaPlayer oldPlayer) { - oldPlayer.VideoRatioChanged -= presenter.OnVideoRatioChanged; + oldPlayer.NaturalVideoDimensionChanged -= presenter.OnNaturalVideoDimensionChanged; oldPlayer.MediaFailed -= presenter.OnMediaFailed; oldPlayer.SourceChanged -= presenter.OnSourceChanged; } if (args.NewValue is Windows.Media.Playback.MediaPlayer newPlayer) { - newPlayer.VideoRatioChanged += presenter.OnVideoRatioChanged; + newPlayer.NaturalVideoDimensionChanged += presenter.OnNaturalVideoDimensionChanged; newPlayer.MediaFailed += presenter.OnMediaFailed; newPlayer.SourceChanged += presenter.OnSourceChanged; @@ -118,19 +147,14 @@ private protected override void OnUnloaded() base.OnUnloaded(); } - private void OnVideoRatioChanged(Windows.Media.Playback.MediaPlayer sender, double args) + private void OnNaturalVideoDimensionChanged(MediaPlayer sender, object args) { - if (args > 0) // The VideoRect may initially be empty, ignore because a 0 ratio will lead to infinite dims being returned on measure, resulting in an exception - { - _currentRatio = args; - } - _ = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { Visibility = Visibility.Visible; }); - InvalidateArrange(); + InvalidateMeasure(); } private void OnMediaFailed(Windows.Media.Playback.MediaPlayer sender, MediaPlayerFailedEventArgs args) @@ -148,30 +172,90 @@ private void OnSourceChanged(Windows.Media.Playback.MediaPlayer sender, object a }); } + private FrameworkElement GetLayoutOwner() + { + if (wrOwner?.TryGetTarget(out var owner) == true && owner is not null && !IsFullWindow) + { + return owner; + } + + return this; + } + protected override Size MeasureOverride(Size availableSize) { - if (double.IsNaN(Width) && double.IsNaN(Height)) + var layoutOwner = GetLayoutOwner(); + var explicitWidth = layoutOwner.Width; + var explicitHeight = layoutOwner.Height; + if (!double.IsNaN(explicitWidth) && !double.IsNaN(explicitHeight)) { - availableSize.Width = availableSize.Width; - if (_currentRatio != 0) - { - availableSize.Height = availableSize.Width / _currentRatio; - } + return new Size(explicitWidth, explicitHeight); } - else if (double.IsNaN(Width)) + + // + // When determining the layout size: + // + // 1. If the explicit size is provided, then use that. + // + // 2. If the explicit size is only provided in one dimension, then use the explicit size in that dimension + // and infer the other from the natural size of the media. + // + // Note that if the media is not ready yet, then we use 0x0. + // + // 3. If neither dimension is provided and the stretch is None, then use the natural size of the media. + // + // 4. If neither dimension is provided, the stretch isn't None, and the available size is finite, then use + // the available size. + // + // 5. If neither dimension is provided, the stretch isn't None, and the available size is infinite in one + // dimension, then fill the available area in one dimension and infer the other from the natural size + // of the media. + // + // 6. If neither dimension is provided, the stretch isn't None, and the available size is infinite in both + // dimensions, use the natural size of the media. + // + if (!double.IsNaN(explicitWidth)) { - availableSize.Width = Height * _currentRatio; - availableSize.Height = Height; + return new Size( + explicitWidth, + GetScaledOtherDimension((float)explicitWidth, NaturalVideoWidth, NaturalVideoHeight)); } - else if (double.IsNaN(Height)) + else if (!double.IsNaN(explicitHeight)) { - availableSize.Width = Width; - availableSize.Height = Width / _currentRatio; + return new Size( + GetScaledOtherDimension((float)explicitHeight, NaturalVideoHeight, NaturalVideoWidth), + explicitHeight); } + else if (Stretch == Stretch.None) + { + return new Size(NaturalVideoWidth, NaturalVideoHeight); + } + else + { + bool isFiniteWidth = !double.IsInfinity(availableSize.Width); + bool isFiniteHeight = !double.IsInfinity(availableSize.Height); - base.MeasureOverride(availableSize); - - return availableSize; + if (isFiniteWidth && isFiniteHeight) + { + return availableSize; + } + else if (isFiniteWidth) + { + return new Size( + availableSize.Width, + GetScaledOtherDimension((float)availableSize.Width, NaturalVideoWidth, NaturalVideoHeight)); + } + else if (isFiniteHeight) + { + return new Size( + GetScaledOtherDimension((float)availableSize.Height, NaturalVideoHeight, NaturalVideoWidth), + availableSize.Height); + } + else + { + return new Size(NaturalVideoWidth, NaturalVideoHeight); + } + } } } } diff --git a/src/Uno.UWP/Media/Playback/IMediaPlayerEventsExtension.cs b/src/Uno.UWP/Media/Playback/IMediaPlayerEventsExtension.cs index 9964f509e4c4..45daf9efbb35 100644 --- a/src/Uno.UWP/Media/Playback/IMediaPlayerEventsExtension.cs +++ b/src/Uno.UWP/Media/Playback/IMediaPlayerEventsExtension.cs @@ -46,9 +46,9 @@ public interface IMediaPlayerEventsExtension void RaiseSeekCompleted(); /// - /// Raises the event + /// Raises the event /// - void RaiseVideoRatioChanged(double videoRatio); + void RaiseNaturalVideoDimensionChanged(); /// /// Raises the event diff --git a/src/Uno.UWP/Media/Playback/MediaPlayer.Android.cs b/src/Uno.UWP/Media/Playback/MediaPlayer.Android.cs index 57ecae688115..17f9a74117e3 100644 --- a/src/Uno.UWP/Media/Playback/MediaPlayer.Android.cs +++ b/src/Uno.UWP/Media/Playback/MediaPlayer.Android.cs @@ -272,7 +272,7 @@ public void OnPrepared(AndroidMediaPlayer mp) { PlaybackSession.NaturalDuration = TimeSpan.FromMilliseconds(_player.Duration); - VideoRatioChanged?.Invoke(this, (double)mp.VideoWidth / global::System.Math.Max(mp.VideoHeight, 1)); + NaturalVideoDimensionChanged?.Invoke(this, null); IsVideo = mp.GetTrackInfo()?.Any(x => x.TrackType == MediaTrackType.Video) == true; diff --git a/src/Uno.UWP/Media/Playback/MediaPlayer.Events.others.cs b/src/Uno.UWP/Media/Playback/MediaPlayer.Events.others.cs index f280cbc9876f..b1d78a23130d 100644 --- a/src/Uno.UWP/Media/Playback/MediaPlayer.Events.others.cs +++ b/src/Uno.UWP/Media/Playback/MediaPlayer.Events.others.cs @@ -52,8 +52,8 @@ void RaiseSubtitleFrameChanged() void RaiseVideoFrameAvailable() => VideoFrameAvailable?.Invoke(this, new object()); - void RaiseVideoRatioChanged(double videoRatio) - => VideoRatioChanged?.Invoke(this, videoRatio); + void RaiseNaturalVideoDimensionChanged() + => NaturalVideoDimensionChanged?.Invoke(this, null); void RaiseVolumeChanged() => VolumeChanged?.Invoke(this, null); @@ -112,8 +112,9 @@ void IMediaPlayerEventsExtension.RaiseSubtitleFrameChanged() void IMediaPlayerEventsExtension.RaiseVideoFrameAvailable() => _owner.RaiseVideoFrameAvailable(); - void IMediaPlayerEventsExtension.RaiseVideoRatioChanged(double videoRatio) - => _owner.RaiseVideoRatioChanged(videoRatio); + void IMediaPlayerEventsExtension.RaiseNaturalVideoDimensionChanged() + => _owner.RaiseNaturalVideoDimensionChanged(); + void IMediaPlayerEventsExtension.RaiseVolumeChanged() => _owner.RaiseVolumeChanged(); diff --git a/src/Uno.UWP/Media/Playback/MediaPlayer.cs b/src/Uno.UWP/Media/Playback/MediaPlayer.cs index eec156106493..3fb7981c02bd 100644 --- a/src/Uno.UWP/Media/Playback/MediaPlayer.cs +++ b/src/Uno.UWP/Media/Playback/MediaPlayer.cs @@ -85,7 +85,7 @@ public double Volume public event TypedEventHandler SeekCompleted; - public event TypedEventHandler VideoRatioChanged; + public event TypedEventHandler NaturalVideoDimensionChanged; #endregion diff --git a/src/Uno.UWP/Media/Playback/MediaPlayer.iOSmacOS.cs b/src/Uno.UWP/Media/Playback/MediaPlayer.iOSmacOS.cs index 2c544a8135f2..588c7c3b5419 100644 --- a/src/Uno.UWP/Media/Playback/MediaPlayer.iOSmacOS.cs +++ b/src/Uno.UWP/Media/Playback/MediaPlayer.iOSmacOS.cs @@ -388,7 +388,7 @@ private void OnVideoRectChanged() { if (_videoLayer?.VideoRect != null) { - VideoRatioChanged?.Invoke(this, _videoLayer.VideoRect.Width / Math.Max(_videoLayer.VideoRect.Height, 1)); + NaturalVideoDimensionChanged?.Invoke(this, null); } }