未验证 提交 57a2a55c 编写于 作者: A Agnès ZITTE 提交者: GitHub

Merge pull request #12421 from unoplatform/dev/agzi/I12332

fix: Properly display PosterSource for MediaPlayerElement
......@@ -33,8 +33,7 @@ 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<string> audioTagAllowedFormats = ImmutableArray.Create(".MP3", ".WAV");
private readonly ImmutableArray<string> videoTagAllowedFormats = ImmutableArray.Create(".MP4", ".WEBM", ".OGG");
private double _videoRatio;
private readonly MediaPlayerPresenter _owner;
public GtkMediaPlayer(MediaPlayerPresenter owner)
......@@ -74,13 +73,25 @@ public partial class GtkMediaPlayer : FrameworkElement
public double Duration { get; set; }
public double VideoRatio { get; set; }
public double VideoRatio
{
get => _videoRatio;
set
{
if (_videoRatio != value)
{
_videoRatio = value;
OnVideoRatioChanged?.Invoke(this, EventArgs.Empty);
}
}
}
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()
{
......@@ -324,27 +335,46 @@ public partial class GtkMediaPlayer : FrameworkElement
&& _mediaPlayer?.Media?.Mrl is not null
&& _videoContainer is not null)
{
var currentSize = new Size(ActualWidth, ActualHeight);
if (currentSize.Height <= 0 || currentSize.Width <= 0)
await _mediaPlayer.Media.Parse(MediaParseOptions.ParseNetwork);
if (TryGetVideoDetails(out var videoWidth, out var videoHeight, out var videoSettings))
{
if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
// From: https://github.com/videolan/libvlcsharp/blob/bca0a53fe921e6f1f745e4e3ac83a7bd3b2e4a9d/src/LibVLCSharp/Shared/MediaPlayerElement/AspectRatioManager.cs#L188
videoWidth = videoWidth * videoSettings.Value.SarNum / videoSettings.Value.SarDen;
// Update video ratio first, so that the cover can be
// removed properly without the mediaplayerpresenter to be
// visible.
if (videoWidth == 0 || videoHeight == 0)
{
this.Log().Debug($"Skipping layout update for empty layout slot");
if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
this.Log().Debug($"Source video size is not valid ({videoWidth}x{videoHeight})");
}
return;
}
return;
}
VideoRatio = (double)videoHeight / (double)videoWidth;
var playerHeight = (double)currentSize.Height - _transportControlsBounds.Height;
var playerWidth = (double)currentSize.Width;
var currentSize = new Size(ActualWidth, ActualHeight);
await _mediaPlayer.Media.Parse(MediaParseOptions.ParseNetwork);
// If the control is not visible, or not sized properly
// we cannot display the video window.
if (currentSize.Height <= 0 || currentSize.Width <= 0)
{
if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
this.Log().Debug($"Skipping layout update for empty layout slot");
}
return;
}
var playerHeight = (double)currentSize.Height - _transportControlsBounds.Height;
var playerWidth = (double)currentSize.Width;
if (TryGetVideoDetails(out var videoWidth, out var videoHeight, out var videoSettings))
{
// From: https://github.com/videolan/libvlcsharp/blob/bca0a53fe921e6f1f745e4e3ac83a7bd3b2e4a9d/src/LibVLCSharp/Shared/MediaPlayerElement/AspectRatioManager.cs#L188
videoWidth = videoWidth * videoSettings.Value.SarNum / videoSettings.Value.SarDen;
UpdateVideoSizeAllocate(playerHeight, playerWidth, videoHeight.Value, videoWidth.Value);
if (forceVideoViewVisibility)
......
......@@ -27,6 +27,7 @@ public partial class GtkMediaPlayer
public event EventHandler<object>? OnMetadataLoaded;
public event EventHandler<object>? OnTimeUpdate;
public event EventHandler<object>? OnSourceLoaded;
public event EventHandler<object?>? OnVideoRatioChanged;
private bool _updateVideoSizeOnFirstTimeStamp = true;
......@@ -327,6 +328,7 @@ public partial class GtkMediaPlayer
media.Parse(MediaParseOptions.ParseNetwork);
_mediaPlayer.Media = media;
AddMediaEvents();
Duration = (double)(_videoView?.MediaPlayer?.Media?.Duration / 1000 ?? 0);
OnSourceLoaded?.Invoke(this, EventArgs.Empty);
UpdateVideoStretch();
......@@ -431,11 +433,6 @@ public partial class GtkMediaPlayer
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 +584,6 @@ public partial class GtkMediaPlayer
}
}
private void OnMediaPlayerTimeChangeIsMediaParse(object? sender, MediaPlayerTimeChangedEventArgs el)
{
AddMediaEvents();
}
private void OnEndReached()
{
if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
......
......@@ -101,6 +101,7 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension
_player.OnSourceLoaded += OnPrepared;
_player.OnSourceEnded += OnCompletion;
_player.OnTimeUpdate += OnTimeUpdate;
_player.OnVideoRatioChanged += OnVideoRatioChanged;
_owner.PlaybackSession.PlaybackStateChanged -= OnStatusChanged;
_owner.PlaybackSession.PlaybackStateChanged += OnStatusChanged;
......@@ -193,7 +194,6 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension
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 +337,8 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension
public double AudioBalance { get; set; }
public bool? IsVideo { get; set; }
public void SetTransportControlsBounds(Rect bounds)
{
_player?.SetTransportControlsBounds(bounds);
......
......@@ -37,10 +37,7 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension
{
if (_player is not null)
{
if (mp.IsVideo && Events is not null)
{
Events?.RaiseVideoRatioChanged(global::System.Math.Max(1, (double)mp.VideoRatio));
}
IsVideo = _player.IsVideo;
if (_owner.PlaybackSession.PlaybackState == MediaPlaybackState.Opening)
{
......@@ -66,6 +63,16 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension
}
}
}
if (Events is not null)
{
if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
this.Log().Debug($"Raising MediaOpened");
}
Events?.RaiseMediaOpened();
}
}
public void OnError(object? sender, object what)
......@@ -107,6 +114,17 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension
_player?.SetVolume(volume);
}
private void OnVideoRatioChanged(object? sender, object? e)
{
if (_player is not null
&& _player.IsVideo
&& Events is not null)
{
IsVideo = _player.IsVideo;
Events?.RaiseVideoRatioChanged(Math.Max(1, (double)_player.VideoRatio));
}
}
private void OnTimeUpdate(object? sender, object o)
{
try
......
......@@ -147,6 +147,8 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension
public bool CanSeek
=> true;
public bool? IsVideo { get; set; }
public MediaPlayerAudioDeviceType AudioDeviceType { get; set; }
public MediaPlayerAudioCategory AudioCategory { get; set; }
......@@ -301,7 +303,6 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension
}
ApplyVideoSource();
Events?.RaiseMediaOpened();
Events?.RaiseSourceChanged();
}
catch (global::System.Exception ex)
......@@ -480,6 +481,8 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension
NaturalDuration = TimeSpan.FromSeconds(_player.Duration);
IsVideo = _player.IsVideo;
if (mp.IsVideo && Events is not null)
{
try
......@@ -509,6 +512,11 @@ public partial class MediaPlayerExtension : IMediaPlayerExtension
_isPlayerPrepared = true;
}
if (Events is not null)
{
Events?.RaiseMediaOpened();
}
}
void OnError(object? sender, object what)
......
......@@ -3294,6 +3294,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\MediaPlayerElement\MediaPlayerElement_Mp3_Extension.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\MediaPlayerElement\MediaPlayerElement_Mov_Extension.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
......@@ -7150,6 +7154,9 @@
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\MediaPlayerElement\MediaPlayerElement_Mkv_Extension.xaml.cs">
<DependentUpon>MediaPlayerElement_Mkv_Extension.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\MediaPlayerElement\MediaPlayerElement_Mp3_Extension.xaml.cs">
<DependentUpon>MediaPlayerElement_Mp3_Extension.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\MediaPlayerElement\MediaPlayerElement_Mov_Extension.xaml.cs">
<DependentUpon>MediaPlayerElement_Mov_Extension.xaml</DependentUpon>
</Compile>
......
<Page x:Class="UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Mp3_Extension"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<MediaPlayerElement Source="https://uno-assets.platform.uno/tests/audio/Getting_Started_with_Uno_Platform_and_Visual_Studio_Code.mp3"
PosterSource="https://uno-assets.platform.uno/tests/thumbnails/Getting_Started_with_Uno_Platform_and_Visual_Studio_Code.png"
AreTransportControlsEnabled="True"
AutoPlay="True" />
</Page>
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();
}
}
}
......@@ -63,7 +63,7 @@ namespace Windows.UI.Xaml.Controls
if (source == null)
{
mpe.TogglePosterImage(true);
mpe.ShowPosterImage(true);
}
}
}
......@@ -91,7 +91,7 @@ namespace Windows.UI.Xaml.Controls
{
if (mpe.MediaPlayer == null || mpe.MediaPlayer.PlaybackSession.PlaybackState == MediaPlaybackState.None)
{
mpe.TogglePosterImage(true);
mpe.ShowPosterImage(true);
}
});
}
......@@ -222,7 +222,8 @@ namespace Windows.UI.Xaml.Controls
if (args.OldValue is Windows.Media.Playback.MediaPlayer oldMediaPlayer)
{
oldMediaPlayer.MediaFailed -= mpe.OnMediaFailed;
oldMediaPlayer.MediaFailed -= mpe.OnMediaOpened;
oldMediaPlayer.MediaOpened -= mpe.OnMediaOpened;
oldMediaPlayer.VideoRatioChanged -= mpe.OnVideoRatioChanged;
oldMediaPlayer.Dispose();
}
......@@ -231,26 +232,32 @@ namespace Windows.UI.Xaml.Controls
newMediaPlayer.Source = mpe.Source;
newMediaPlayer.MediaFailed += mpe.OnMediaFailed;
newMediaPlayer.MediaOpened += mpe.OnMediaOpened;
newMediaPlayer.VideoRatioChanged -= mpe.OnVideoRatioChanged;
mpe.TransportControls?.SetMediaPlayer(newMediaPlayer);
mpe._isTransportControlsBound = true;
}
};
}
private void OnMediaFailed(Windows.Media.Playback.MediaPlayer session, object args)
private void OnVideoRatioChanged(Windows.Media.Playback.MediaPlayer sender, double args)
{
_ = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
TogglePosterImage(true);
});
_ = Dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() => ShowPosterImage(!sender.IsVideo));
}
private void OnMediaOpened(Windows.Media.Playback.MediaPlayer session, object args)
private void OnMediaFailed(Windows.Media.Playback.MediaPlayer sender, object args)
{
_ = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
TogglePosterImage(false);
});
_ = Dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() => ShowPosterImage(true));
}
private void OnMediaOpened(Windows.Media.Playback.MediaPlayer sender, object args)
{
_ = Dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() => ShowPosterImage(!sender.IsVideo));
}
#endregion
......@@ -326,12 +333,16 @@ namespace Windows.UI.Xaml.Controls
&& AutoPlay)
{
MediaPlayer.Play();
TogglePosterImage(false);
}
}
}
private void TogglePosterImage(bool showPoster)
// 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 ShowPosterImage(bool showPoster)
{
if (PosterSource != null)
{
......@@ -365,10 +376,8 @@ namespace Windows.UI.Xaml.Controls
_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.
ShowPosterImage(true);
if (!_isTransportControlsBound)
{
......
......@@ -89,6 +89,11 @@ namespace Uno.Media.Playback
/// </summary>
TimeSpan Position { get; set; }
/// <summary>
/// Determines if the current media content is video
/// </summary>
bool? IsVideo { get; }
/// <summary>
/// Sets the transport controls bounds so that video can be displayed around controls
/// </summary>
......
......@@ -155,8 +155,6 @@ namespace Windows.Media.Playback
SetVideoSource(uri);
_player.PrepareAsync();
MediaOpened?.Invoke(this, null);
}
catch (global::System.Exception ex)
{
......@@ -270,30 +268,37 @@ namespace Windows.Media.Playback
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 (_isPlayRequested)
{
_player.Start();
PlaybackSession.PlaybackState = MediaPlaybackState.Playing;
}
else
if (PlaybackSession.PlaybackState == MediaPlaybackState.Opening)
{
// To display first image of media when setting a new source. Otherwise, last image of previous source remains visible
_player.Start();
_player.Pause();
_player.SeekTo(0);
PlaybackSession.PlaybackState = MediaPlaybackState.Paused;
UpdateVideoStretch(_currentStretch);
if (_isPlayRequested)
{
_player.Start();
PlaybackSession.PlaybackState = MediaPlaybackState.Playing;
}
else
{
// To display first image of media when setting a new source. Otherwise, last image of previous source remains visible
_player.Start();
_player.Pause();
_player.SeekTo(0);
PlaybackSession.PlaybackState = MediaPlaybackState.Paused;
}
}
}
_isPlayerPrepared = true;
_isPlayerPrepared = true;
MediaOpened?.Invoke(this, null);
}
}
public bool OnError(AndroidMediaPlayer mp, MediaError what, int extra)
......@@ -383,6 +388,8 @@ namespace Windows.Media.Playback
}
}
public bool IsVideo { get; set; }
internal void UpdateVideoStretch(VideoStretch stretch)
{
_currentStretch = stretch;
......
......@@ -246,9 +246,6 @@ namespace Windows.Media.Playback
// 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 @@ namespace Windows.Media.Playback
{
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 @@ namespace Windows.Media.Playback
_player.Play();
}
}
if (_player.Status == AVPlayerStatus.ReadyToPlay)
{
MediaOpened?.Invoke(this, null);
}
}
}
......@@ -473,6 +477,8 @@ namespace Windows.Media.Playback
}
}
public bool IsVideo { get; set; }
private void OnSeekCompleted(bool finished)
{
if (finished)
......
......@@ -160,6 +160,8 @@ namespace Windows.Media.Playback
}
}
public bool IsVideo => _extension?.IsVideo ?? false;
public void SetUriSource(global::System.Uri value)
=> _extension?.SetUriSource(value);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册