From 77bdd3a43a5788885528f075557d5571a846e4a2 Mon Sep 17 00:00:00 2001 From: FlutterSu <46503636+FlutterSu@users.noreply.github.com> Date: Wed, 20 Jan 2021 20:47:21 +0300 Subject: [PATCH] Features absolute position from hls (#223) * Features absolute position from hls * Flutter format absolute position Co-authored-by: Egor --- .../jhomlala/better_player/BetterPlayer.java | 11 +++++++++ .../better_player/BetterPlayerPlugin.java | 4 ++++ ios/Classes/FLTBetterPlayerPlugin.m | 10 ++++++++ .../method_channel_video_player.dart | 11 +++++++++ lib/src/video_player/video_player.dart | 24 +++++++++++++++++-- .../video_player_platform_interface.dart | 5 ++++ 6 files changed, 63 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/jhomlala/better_player/BetterPlayer.java b/android/src/main/java/com/jhomlala/better_player/BetterPlayer.java index b34ebbe8b..b2e7cefa5 100644 --- a/android/src/main/java/com/jhomlala/better_player/BetterPlayer.java +++ b/android/src/main/java/com/jhomlala/better_player/BetterPlayer.java @@ -29,6 +29,7 @@ import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player.EventListener; import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; @@ -550,6 +551,16 @@ long getPosition() { return exoPlayer.getCurrentPosition(); } + long getAbsolutePosition() { + Timeline timeline = exoPlayer.getCurrentTimeline(); + if (!timeline.isEmpty()) { + long windowStartTimeMs = timeline.getWindow(0, new Timeline.Window()).windowStartTimeMs; + long pos = exoPlayer.getCurrentPosition(); + return (windowStartTimeMs + pos); + } + return exoPlayer.getCurrentPosition(); + } + @SuppressWarnings("SuspiciousNameCombination") private void sendInitialized() { if (isInitialized) { diff --git a/android/src/main/java/com/jhomlala/better_player/BetterPlayerPlugin.java b/android/src/main/java/com/jhomlala/better_player/BetterPlayerPlugin.java index acbf4e573..3961a5121 100644 --- a/android/src/main/java/com/jhomlala/better_player/BetterPlayerPlugin.java +++ b/android/src/main/java/com/jhomlala/better_player/BetterPlayerPlugin.java @@ -76,6 +76,7 @@ public class BetterPlayerPlugin implements FlutterPlugin, ActivityAware, MethodC private static final String PAUSE_METHOD = "pause"; private static final String SEEK_TO_METHOD = "seekTo"; private static final String POSITION_METHOD = "position"; + private static final String ABSOLUTE_POSITION_METHOD = "absolutePosition"; private static final String SET_SPEED_METHOD = "setSpeed"; private static final String SET_TRACK_PARAMETERS_METHOD = "setTrackParameters"; private static final String DISPOSE_METHOD = "dispose"; @@ -232,6 +233,9 @@ private void onMethodCall(MethodCall call, Result result, long textureId, Better result.success(player.getPosition()); player.sendBufferingUpdate(); break; + case ABSOLUTE_POSITION_METHOD: + result.success(player.getAbsolutePosition()); + break; case SET_SPEED_METHOD: player.setSpeed(call.argument(SPEED_PARAMETER)); result.success(null); diff --git a/ios/Classes/FLTBetterPlayerPlugin.m b/ios/Classes/FLTBetterPlayerPlugin.m index 273eccae1..871450810 100644 --- a/ios/Classes/FLTBetterPlayerPlugin.m +++ b/ios/Classes/FLTBetterPlayerPlugin.m @@ -18,6 +18,10 @@ int64_t FLTCMTimeToMillis(CMTime time) { return time.value * 1000 / time.timescale; } +int64_t FLTNSTimeIntervalToMillis(NSTimeInterval interval) { + return (int64_t)(interval * 1000.0); +} + @interface FLTFrameUpdater : NSObject @property(nonatomic) int64_t textureId; @property(nonatomic, weak, readonly) NSObject* registry; @@ -477,6 +481,10 @@ - (int64_t)position { return FLTCMTimeToMillis([_player currentTime]); } +- (int64_t)absolutePosition { + return FLTNSTimeIntervalToMillis([[[_player currentItem] currentDate] timeIntervalSince1970]); +} + - (int64_t)duration { CMTime time; if (@available(iOS 13, *)) { @@ -1065,6 +1073,8 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { result(nil); } else if ([@"position" isEqualToString:call.method]) { result(@([player position])); + } else if ([@"absolutePosition" isEqualToString:call.method]) { + result(@([player absolutePosition])); } else if ([@"seekTo" isEqualToString:call.method]) { [player seekTo:[argsMap[@"location"] intValue]]; result(nil); diff --git a/lib/src/video_player/method_channel_video_player.dart b/lib/src/video_player/method_channel_video_player.dart index 034ff4d22..18c7076f2 100644 --- a/lib/src/video_player/method_channel_video_player.dart +++ b/lib/src/video_player/method_channel_video_player.dart @@ -187,6 +187,17 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { } @override + Future getAbsolutePosition(int textureId) async { + final int milliseconds = await _channel.invokeMethod( + 'absolutePosition', + {'textureId': textureId}, + ); + + if (milliseconds <= 0) return null; + + return DateTime.fromMillisecondsSinceEpoch(milliseconds); + } + Future enablePictureInPicture(int textureId, double top, double left, double width, double height) async { return _channel.invokeMethod( diff --git a/lib/src/video_player/video_player.dart b/lib/src/video_player/video_player.dart index 00c070e3c..6bbff4f56 100644 --- a/lib/src/video_player/video_player.dart +++ b/lib/src/video_player/video_player.dart @@ -34,6 +34,7 @@ class VideoPlayerValue { @required this.duration, this.size, this.position = const Duration(), + this.absolutePosition, this.caption = const Caption(), this.buffered = const [], this.isPlaying = false, @@ -61,6 +62,11 @@ class VideoPlayerValue { /// The current playback position. final Duration position; + /// The current absolute playback position. + /// + /// Is null when is not available. + final DateTime absolutePosition; + /// The [Caption] that should be displayed based on the current [position]. /// /// This field will never be null. If there is no caption for the current @@ -124,6 +130,7 @@ class VideoPlayerValue { Duration duration, Size size, Duration position, + DateTime absolutePosition, Caption caption, List buffered, bool isPlaying, @@ -138,6 +145,7 @@ class VideoPlayerValue { duration: duration ?? this.duration, size: size ?? this.size, position: position ?? this.position, + absolutePosition: absolutePosition ?? this.absolutePosition, caption: caption ?? this.caption, buffered: buffered ?? this.buffered, isPlaying: isPlaying ?? this.isPlaying, @@ -157,6 +165,7 @@ class VideoPlayerValue { 'duration: $duration, ' 'size: $size, ' 'position: $position, ' + 'absolutePosition: $absolutePosition, ' 'caption: $caption, ' 'buffered: [${buffered.join(', ')}], ' 'isPlaying: $isPlaying, ' @@ -464,11 +473,12 @@ class VideoPlayerController extends ValueNotifier { return; } final Duration newPosition = await position; + final DateTime newAbsolutePosition = await absolutePosition; // ignore: invariant_booleans if (_isDisposed) { return; } - _updatePosition(newPosition); + _updatePosition(newPosition, absolutePosition: newAbsolutePosition); }, ); } else { @@ -498,6 +508,15 @@ class VideoPlayerController extends ValueNotifier { return _videoPlayerPlatform.getPosition(_textureId); } + /// The absolute position in the current video stream + /// (i.e. EXT-X-PROGRAM-DATE-TIME in HLS). + Future get absolutePosition async { + if (!value.initialized && _isDisposed) { + return null; + } + return _videoPlayerPlatform.getAbsolutePosition(_textureId); + } + /// Sets the video's current timestamp to be at [moment]. The next /// time the video is played it will resume from the given [moment]. /// @@ -577,8 +596,9 @@ class VideoPlayerController extends ValueNotifier { return const Caption(); } - void _updatePosition(Duration position) { + void _updatePosition(Duration position, {DateTime absolutePosition}) { value = value.copyWith(position: position); + value = value.copyWith(absolutePosition: absolutePosition); value = value.copyWith(caption: _getCaptionAt(position)); } diff --git a/lib/src/video_player/video_player_platform_interface.dart b/lib/src/video_player/video_player_platform_interface.dart index ba8df771a..a7e6096d0 100644 --- a/lib/src/video_player/video_player_platform_interface.dart +++ b/lib/src/video_player/video_player_platform_interface.dart @@ -127,6 +127,11 @@ abstract class VideoPlayerPlatform { throw UnimplementedError('getPosition() has not been implemented.'); } + /// Gets the video position as [DateTime]. + Future getAbsolutePosition(int textureId) { + throw UnimplementedError('getAbsolutePosition() has not been implemented.'); + } + ///Enables PiP mode. Future enablePictureInPicture( int textureId, double top, double left, double width, double height) {