diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 9892265b485..bbbdd0c5b85 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.9.4 + +* Reduces the position update interval from 500ms to 100ms. + ## 2.9.3 * Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index b7ba8340fa6..0f56ae0146d 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -585,10 +585,9 @@ class VideoPlayerController extends ValueNotifier { if (value.isPlaying) { await _videoPlayerPlatform.play(_textureId); - // Cancel previous timer. _timer?.cancel(); _timer = Timer.periodic( - const Duration(milliseconds: 500), + const Duration(milliseconds: 100), (Timer timer) async { if (_isDisposed) { return; diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 7c5e7980db4..a12cf336024 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.9.3 +version: 2.9.4 environment: sdk: ^3.4.0 diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index da8effde146..279490ad94b 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -757,6 +757,45 @@ void main() { }); group('caption', () { + test('works when position updates', () async { + final VideoPlayerController controller = + VideoPlayerController.networkUrl( + _localhostUri, + closedCaptionFile: _loadClosedCaption(), + ); + + await controller.initialize(); + await controller.play(); + + // Optionally record caption changes for later verification. + final Map recordedCaptions = {}; + + controller.addListener(() { + // Record the caption for the current position (in milliseconds). + final int ms = controller.value.position.inMilliseconds; + recordedCaptions[ms] = controller.value.caption.text; + }); + + const Duration updateInterval = Duration(milliseconds: 100); + const int totalDurationMs = 350; + + // Simulate continuous playback by incrementing in 50ms steps. + for (int ms = 0; ms <= totalDurationMs; ms += 50) { + fakeVideoPlayerPlatform._positions[controller.textureId] = + Duration(milliseconds: ms); + await Future.delayed(updateInterval); + } + + // Now, given your closed caption file and the 100ms update interval, + // you expect: + // • at 100ms: caption should be 'one' + // • at 250ms: no caption (i.e. '') + // • at 300ms: caption should be 'two' + expect(recordedCaptions[100], 'one'); + expect(recordedCaptions[250], ''); + expect(recordedCaptions[300], 'two'); + }); + test('works when seeking', () async { final VideoPlayerController controller = VideoPlayerController.networkUrl( @@ -995,6 +1034,41 @@ void main() { }); }); + test('updates position', () async { + final VideoPlayerController controller = VideoPlayerController.networkUrl( + _localhostUri, + videoPlayerOptions: VideoPlayerOptions(), + ); + + await controller.initialize(); + + const Duration updatesInterval = Duration(milliseconds: 100); + + final List positions = []; + final Completer intervalUpdateCompleter = Completer(); + + // Listen for position updates + controller.addListener(() { + positions.add(controller.value.position); + if (positions.length >= 3 && !intervalUpdateCompleter.isCompleted) { + intervalUpdateCompleter.complete(); + } + }); + await controller.play(); + for (int i = 0; i < 3; i++) { + await Future.delayed(updatesInterval); + fakeVideoPlayerPlatform._positions[controller.textureId] = + Duration(milliseconds: i * updatesInterval.inMilliseconds); + } + + // Wait for at least 3 position updates + await intervalUpdateCompleter.future; + + // Verify that the intervals between updates are approximately correct + expect(positions[1] - positions[0], greaterThanOrEqualTo(updatesInterval)); + expect(positions[2] - positions[1], greaterThanOrEqualTo(updatesInterval)); + }); + group('DurationRange', () { test('uses given values', () { const Duration start = Duration(seconds: 2);