From 2d2d89a75a5e1a4fc3de1dc3687dda66eeea1b76 Mon Sep 17 00:00:00 2001 From: Juanu Haedo Date: Mon, 9 Sep 2019 16:47:59 -0300 Subject: [PATCH 1/2] Implemented SeekTo --- ...e.Xamarin.Forms.VideoPlayer.Android.csproj | 3 +- .../Renderers/VideoPlayerRenderer.cs | 33 +++++++++++++++++++ ...ctane.Xamarin.Forms.VideoPlayer.UWP.csproj | 1 + .../Renderers/VideoPlayerRenderer.cs | 4 +++ ...ctane.Xamarin.Forms.VideoPlayer.iOS.csproj | 3 +- .../Renderers/VideoPlayerRenderer.cs | 33 +++++++++++++++++++ .../Interfaces/IVideoPlayerRenderer.cs | 18 ++++++++-- .../Octane.Xamarin.Forms.VideoPlayer.csproj | 3 +- 8 files changed, 93 insertions(+), 5 deletions(-) diff --git a/src/Octane.Xamarin.Forms.VideoPlayer.Android/Octane.Xamarin.Forms.VideoPlayer.Android.csproj b/src/Octane.Xamarin.Forms.VideoPlayer.Android/Octane.Xamarin.Forms.VideoPlayer.Android.csproj index 1b268c9..64cb0ae 100644 --- a/src/Octane.Xamarin.Forms.VideoPlayer.Android/Octane.Xamarin.Forms.VideoPlayer.Android.csproj +++ b/src/Octane.Xamarin.Forms.VideoPlayer.Android/Octane.Xamarin.Forms.VideoPlayer.Android.csproj @@ -18,6 +18,7 @@ v8.1 + 0.1.1 true @@ -140,7 +141,7 @@ - {6f36403b-c460-4a84-9c7a-e90f689576bd} + {353C7A43-D286-4F96-A221-363157CF20BC} Octane.Xamarin.Forms.VideoPlayer diff --git a/src/Octane.Xamarin.Forms.VideoPlayer.Android/Renderers/VideoPlayerRenderer.cs b/src/Octane.Xamarin.Forms.VideoPlayer.Android/Renderers/VideoPlayerRenderer.cs index 2fa02c0..4cdf4eb 100644 --- a/src/Octane.Xamarin.Forms.VideoPlayer.Android/Renderers/VideoPlayerRenderer.cs +++ b/src/Octane.Xamarin.Forms.VideoPlayer.Android/Renderers/VideoPlayerRenderer.cs @@ -156,6 +156,39 @@ public virtual bool CanSeek(int time) || (time < 0 && Control.CanSeekBackward() && (Control.CurrentPosition + time) >= 0)); } + /// + /// Seeks to a specific position on the playback stream. + /// + /// The time in milliseconds. + public virtual void SeekTo(int time) + { + if (CanSeekTo(time)) + { + var control = Control; + + if (control != null) + { + var currentTime = control.CurrentPosition; + Log.Info($"SEEK: CurrentTime={currentTime}; NewTime={time}"); + control.SeekTo(time); + Element.SetValue(VideoPlayer.CurrentTimePropertyKey, + TimeSpan.FromMilliseconds(control.CurrentPosition)); + } + } + } + + /// + /// Determines if the video player instance can seek to a given position. + /// + /// The time in milliseconds. + /// + /// true if this instance can stop; otherwise, false. + public virtual bool CanSeekTo(int time) + { + var control = Control; + return control != null && time >= 0 && time <= control.CurrentPosition; + } + #endregion #region ViewRenderer Overrides diff --git a/src/Octane.Xamarin.Forms.VideoPlayer.UWP/Octane.Xamarin.Forms.VideoPlayer.UWP.csproj b/src/Octane.Xamarin.Forms.VideoPlayer.UWP/Octane.Xamarin.Forms.VideoPlayer.UWP.csproj index 90b8205..0df295a 100644 --- a/src/Octane.Xamarin.Forms.VideoPlayer.UWP/Octane.Xamarin.Forms.VideoPlayer.UWP.csproj +++ b/src/Octane.Xamarin.Forms.VideoPlayer.UWP/Octane.Xamarin.Forms.VideoPlayer.UWP.csproj @@ -16,6 +16,7 @@ 14 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 0.1.1 AnyCPU diff --git a/src/Octane.Xamarin.Forms.VideoPlayer.UWP/Renderers/VideoPlayerRenderer.cs b/src/Octane.Xamarin.Forms.VideoPlayer.UWP/Renderers/VideoPlayerRenderer.cs index c7aa279..cbf6eea 100644 --- a/src/Octane.Xamarin.Forms.VideoPlayer.UWP/Renderers/VideoPlayerRenderer.cs +++ b/src/Octane.Xamarin.Forms.VideoPlayer.UWP/Renderers/VideoPlayerRenderer.cs @@ -148,6 +148,10 @@ public virtual bool CanSeek(int time) || (time < 0 && control.CanSeek && (control.Position.Subtract(TimeSpan.FromSeconds(absoluteTime)) >= TimeSpan.Zero))); } + public virtual void SeekTo(int time) { } + + public virtual bool CanSeekTo(int time) => true; + #endregion #region ViewRenderer Overrides diff --git a/src/Octane.Xamarin.Forms.VideoPlayer.iOS/Octane.Xamarin.Forms.VideoPlayer.iOS.csproj b/src/Octane.Xamarin.Forms.VideoPlayer.iOS/Octane.Xamarin.Forms.VideoPlayer.iOS.csproj index 3916037..46cbdb7 100644 --- a/src/Octane.Xamarin.Forms.VideoPlayer.iOS/Octane.Xamarin.Forms.VideoPlayer.iOS.csproj +++ b/src/Octane.Xamarin.Forms.VideoPlayer.iOS/Octane.Xamarin.Forms.VideoPlayer.iOS.csproj @@ -15,6 +15,7 @@ Octane.Xamarin.Forms.VideoPlayer.iOS + 0.1.1 true @@ -67,7 +68,7 @@ - {6f36403b-c460-4a84-9c7a-e90f689576bd} + {353C7A43-D286-4F96-A221-363157CF20BC} Octane.Xamarin.Forms.VideoPlayer diff --git a/src/Octane.Xamarin.Forms.VideoPlayer.iOS/Renderers/VideoPlayerRenderer.cs b/src/Octane.Xamarin.Forms.VideoPlayer.iOS/Renderers/VideoPlayerRenderer.cs index c43f1bf..5a36eb8 100644 --- a/src/Octane.Xamarin.Forms.VideoPlayer.iOS/Renderers/VideoPlayerRenderer.cs +++ b/src/Octane.Xamarin.Forms.VideoPlayer.iOS/Renderers/VideoPlayerRenderer.cs @@ -160,6 +160,39 @@ public virtual bool CanSeek(int time) || player.CurrentItem.CanStepForward); } + /// + /// Seeks to a specific position on the playback stream. + /// + /// The time in milliseconds. + public virtual void SeekTo(int time) + { + if(CanSeekTo(time)) + { + var player = _playerControl?.Player; + + if (player != null) + { + var currentTime = player.CurrentTime.Seconds; + var timeScale = player.CurrentItem.Duration.TimeScale; + Log.Info($"SEEK: CurrentTime={currentTime}; TimeScale={timeScale}; NewTime={time}"); + player.Seek(CMTime.FromSeconds(time / 1000d, timeScale)); + } + } + } + + /// + /// Determines if the video player instance can seek to a given position. + /// + /// The time in milliseconds. + /// + /// true if this instance can stop; otherwise, false. + public virtual bool CanSeekTo(int time) + { + var player = _playerControl?.Player; + return player != null && (player.CurrentItem.CanStepBackward + || player.CurrentItem.CanStepForward) && (time >= 0 && time <= Element.CurrentTime.Milliseconds); + } + #endregion #region ViewRenderer Overrides diff --git a/src/Octane.Xamarin.Forms.VideoPlayer/Interfaces/IVideoPlayerRenderer.cs b/src/Octane.Xamarin.Forms.VideoPlayer/Interfaces/IVideoPlayerRenderer.cs index d0b84be..44668ac 100644 --- a/src/Octane.Xamarin.Forms.VideoPlayer/Interfaces/IVideoPlayerRenderer.cs +++ b/src/Octane.Xamarin.Forms.VideoPlayer/Interfaces/IVideoPlayerRenderer.cs @@ -54,6 +54,20 @@ internal interface IVideoPlayerRenderer /// true if this instance can stop; otherwise, false. bool CanSeek(int time); - #endregion - } + /// + /// Seeks to a specific position in milliseconds on the playback stream. + /// + /// The time in milliseconds . + void SeekTo(int time); + + /// + /// Determines if the video player instance can seek to a given position in milliseconds. + /// + /// The time in milliseconds. + /// + /// true if this instance can stop; otherwise, false. + bool CanSeekTo(int time); + + #endregion + } } diff --git a/src/Octane.Xamarin.Forms.VideoPlayer/Octane.Xamarin.Forms.VideoPlayer.csproj b/src/Octane.Xamarin.Forms.VideoPlayer/Octane.Xamarin.Forms.VideoPlayer.csproj index c216afe..043c8dd 100644 --- a/src/Octane.Xamarin.Forms.VideoPlayer/Octane.Xamarin.Forms.VideoPlayer.csproj +++ b/src/Octane.Xamarin.Forms.VideoPlayer/Octane.Xamarin.Forms.VideoPlayer.csproj @@ -6,7 +6,6 @@ Octane Software, LLC Xamarin.Forms Video Player - false Octane.Xam.VideoPlayer Copyright © 2019 https://github.com/adamfisher/Xamarin.Forms.VideoPlayer/blob/master/License.md @@ -14,6 +13,8 @@ https://github.com/adamfisher/Xamarin.Forms.VideoPlayer/raw/master/icons/VideoPlayer_128x128.png xamarin forms xamarin.forms ios android windows phone mobile video portable ui AVPlayer MediaPlayer AVPlayerViewController MediaElement pcl xam.pcl wp8 winphone UWP A Xamarin Forms control to render the native video player on every platform. + true + 0.1.1 From ce0033b2de84d03904cc521c5f4b62529369f704 Mon Sep 17 00:00:00 2001 From: Juanu Haedo Date: Mon, 9 Sep 2019 18:06:58 -0300 Subject: [PATCH 2/2] Implemente SeekTo feature --- .../Renderers/VideoPlayerRenderer.cs | 2 +- .../Renderers/VideoPlayerRenderer.cs | 2 +- .../Renderers/VideoPlayerRenderer.cs | 7 ++-- .../VideoPlayer.cs | 35 ++++++++++++++++++- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/Octane.Xamarin.Forms.VideoPlayer.Android/Renderers/VideoPlayerRenderer.cs b/src/Octane.Xamarin.Forms.VideoPlayer.Android/Renderers/VideoPlayerRenderer.cs index 4cdf4eb..ff16df2 100644 --- a/src/Octane.Xamarin.Forms.VideoPlayer.Android/Renderers/VideoPlayerRenderer.cs +++ b/src/Octane.Xamarin.Forms.VideoPlayer.Android/Renderers/VideoPlayerRenderer.cs @@ -74,7 +74,7 @@ public virtual bool CanPlay() { var control = Control; return control != null && (control.Status == MediaPlayerStatus.Prepared - || control.Status == MediaPlayerStatus.Paused); + || control.Status == MediaPlayerStatus.Paused || control.Status == MediaPlayerStatus.PlaybackCompleted); } /// diff --git a/src/Octane.Xamarin.Forms.VideoPlayer.UWP/Renderers/VideoPlayerRenderer.cs b/src/Octane.Xamarin.Forms.VideoPlayer.UWP/Renderers/VideoPlayerRenderer.cs index cbf6eea..08ee100 100644 --- a/src/Octane.Xamarin.Forms.VideoPlayer.UWP/Renderers/VideoPlayerRenderer.cs +++ b/src/Octane.Xamarin.Forms.VideoPlayer.UWP/Renderers/VideoPlayerRenderer.cs @@ -148,7 +148,7 @@ public virtual bool CanSeek(int time) || (time < 0 && control.CanSeek && (control.Position.Subtract(TimeSpan.FromSeconds(absoluteTime)) >= TimeSpan.Zero))); } - public virtual void SeekTo(int time) { } + public virtual void SeekTo(int time) { }// TODO: Implement for UWP public virtual bool CanSeekTo(int time) => true; diff --git a/src/Octane.Xamarin.Forms.VideoPlayer.iOS/Renderers/VideoPlayerRenderer.cs b/src/Octane.Xamarin.Forms.VideoPlayer.iOS/Renderers/VideoPlayerRenderer.cs index 5a36eb8..73f0571 100644 --- a/src/Octane.Xamarin.Forms.VideoPlayer.iOS/Renderers/VideoPlayerRenderer.cs +++ b/src/Octane.Xamarin.Forms.VideoPlayer.iOS/Renderers/VideoPlayerRenderer.cs @@ -174,8 +174,9 @@ public virtual void SeekTo(int time) { var currentTime = player.CurrentTime.Seconds; var timeScale = player.CurrentItem.Duration.TimeScale; - Log.Info($"SEEK: CurrentTime={currentTime}; TimeScale={timeScale}; NewTime={time}"); - player.Seek(CMTime.FromSeconds(time / 1000d, timeScale)); + var newTime = time / 1000d; + Log.Info($"SEEK: CurrentTime={currentTime}; TimeScale={timeScale}; NewTime={newTime}"); + player.Seek(CMTime.FromSeconds(newTime, timeScale), CMTime.Zero, CMTime.Zero); } } } @@ -190,7 +191,7 @@ public virtual bool CanSeekTo(int time) { var player = _playerControl?.Player; return player != null && (player.CurrentItem.CanStepBackward - || player.CurrentItem.CanStepForward) && (time >= 0 && time <= Element.CurrentTime.Milliseconds); + || player.CurrentItem.CanStepForward) && (time >= 0 && time <= (_playerControl.Player.CurrentItem.Duration.Seconds * 1000)); } #endregion diff --git a/src/Octane.Xamarin.Forms.VideoPlayer/VideoPlayer.cs b/src/Octane.Xamarin.Forms.VideoPlayer/VideoPlayer.cs index a4ac3ac..47ea4fd 100644 --- a/src/Octane.Xamarin.Forms.VideoPlayer/VideoPlayer.cs +++ b/src/Octane.Xamarin.Forms.VideoPlayer/VideoPlayer.cs @@ -464,10 +464,29 @@ public ICommand SeekCommand private set { SetValue(SeekCommandProperty, value); } } + /// + /// The seek command property. + /// + public static readonly BindableProperty SeekToCommandProperty = BindableProperty.Create(nameof(SeekToCommand), typeof(ICommand), typeof(VideoPlayer), null); + + /// + /// This command seeks to a specific position in milliseconds. + /// + /// + /// The seek to command. + /// + public ICommand SeekToCommand + { + get { return (ICommand)GetValue(SeekToCommandProperty); } + private set { SetValue(SeekToCommandProperty, value); } + } + + + #endregion #region Constructors - + /// /// Initializes a new instance of the class. /// @@ -488,6 +507,10 @@ public VideoPlayer() SeekCommand = new Command( (time) => NativeRenderer?.Seek(int.Parse(time)), (time) => NativeRenderer != null && NativeRenderer.CanSeek(int.Parse(time))); + + SeekToCommand = new Command( + (time) => NativeRenderer?.SeekTo(int.Parse(time)), + (time) => NativeRenderer != null && NativeRenderer.CanSeekTo(int.Parse(time))); } /// @@ -547,6 +570,16 @@ public void Seek(int time) SeekCommand.Execute(time.ToString()); } + /// + /// Seeks to a specific position in milliseconds in the playback stream. + /// It may be best to hook into the PlayerStateChanged event to listen for the prepared state before calling this method directly. + /// + /// The time in seconds to seek forward or backward. + public void SeekTo(int time) + { + SeekToCommand.Execute(time.ToString()); + } + #endregion } }