[video_player] synchronize isPlaying state (#3261)
[video_player] synchronize isPlaying state
diff --git a/packages/video_player/video_player/AUTHORS b/packages/video_player/video_player/AUTHORS
index 02a9c69..e57e00b 100644
--- a/packages/video_player/video_player/AUTHORS
+++ b/packages/video_player/video_player/AUTHORS
@@ -65,3 +65,4 @@
Alex Li <google@alexv525.com>
Rahul Raj <64.rahulraj@gmail.com>
Koen Van Looveren <vanlooverenkoen.dev@gmail.com>
+Márton Matuz <matuzmarci@gmail.com>
diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md
index 67c9fc4..368af5b 100644
--- a/packages/video_player/video_player/CHANGELOG.md
+++ b/packages/video_player/video_player/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.6.1
+
+* Synchronizes `VideoPlayerValue.isPlaying` with underlying video player.
+
## 2.6.0
* Adds option to configure HTTP headers via `VideoPlayerController` to fix access to M3U8 files on Android.
diff --git a/packages/video_player/video_player/lib/src/closed_caption_file.dart b/packages/video_player/video_player/lib/src/closed_caption_file.dart
index 324ffc4..91b4468 100644
--- a/packages/video_player/video_player/lib/src/closed_caption_file.dart
+++ b/packages/video_player/video_player/lib/src/closed_caption_file.dart
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'package:flutter/foundation.dart' show objectRuntimeType;
+import 'package:flutter/foundation.dart' show immutable, objectRuntimeType;
import 'sub_rip.dart';
import 'web_vtt.dart';
@@ -32,6 +32,7 @@
///
/// A typical closed captioning file will include several [Caption]s, each
/// linked to a start and end time.
+@immutable
class Caption {
/// Creates a new [Caption] object.
///
@@ -74,4 +75,22 @@
'end: $end, '
'text: $text)';
}
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is Caption &&
+ runtimeType == other.runtimeType &&
+ number == other.number &&
+ start == other.start &&
+ end == other.end &&
+ text == other.text;
+
+ @override
+ int get hashCode => Object.hash(
+ number,
+ start,
+ end,
+ text,
+ );
}
diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart
index 6a7661f..b2105e9 100644
--- a/packages/video_player/video_player/lib/video_player.dart
+++ b/packages/video_player/video_player/lib/video_player.dart
@@ -33,10 +33,11 @@
/// The duration, current position, buffering state, error state and settings
/// of a [VideoPlayerController].
+@immutable
class VideoPlayerValue {
/// Constructs a video with the given values. Only [duration] is required. The
/// rest will initialize with default values when unset.
- VideoPlayerValue({
+ const VideoPlayerValue({
required this.duration,
this.size = Size.zero,
this.position = Duration.zero,
@@ -54,11 +55,11 @@
});
/// Returns an instance for a video that hasn't been loaded.
- VideoPlayerValue.uninitialized()
+ const VideoPlayerValue.uninitialized()
: this(duration: Duration.zero, isInitialized: false);
/// Returns an instance with the given [errorDescription].
- VideoPlayerValue.erroneous(String errorDescription)
+ const VideoPlayerValue.erroneous(String errorDescription)
: this(
duration: Duration.zero,
isInitialized: false,
@@ -195,6 +196,44 @@
'playbackSpeed: $playbackSpeed, '
'errorDescription: $errorDescription)';
}
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is VideoPlayerValue &&
+ runtimeType == other.runtimeType &&
+ duration == other.duration &&
+ position == other.position &&
+ caption == other.caption &&
+ captionOffset == other.captionOffset &&
+ listEquals(buffered, other.buffered) &&
+ isPlaying == other.isPlaying &&
+ isLooping == other.isLooping &&
+ isBuffering == other.isBuffering &&
+ volume == other.volume &&
+ playbackSpeed == other.playbackSpeed &&
+ errorDescription == other.errorDescription &&
+ size == other.size &&
+ rotationCorrection == other.rotationCorrection &&
+ isInitialized == other.isInitialized;
+
+ @override
+ int get hashCode => Object.hash(
+ duration,
+ position,
+ caption,
+ captionOffset,
+ buffered,
+ isPlaying,
+ isLooping,
+ isBuffering,
+ volume,
+ playbackSpeed,
+ errorDescription,
+ size,
+ rotationCorrection,
+ isInitialized,
+ );
}
/// Controls a platform video player, and provides updates when the state is
@@ -221,7 +260,7 @@
dataSourceType = DataSourceType.asset,
formatHint = null,
httpHeaders = const <String, String>{},
- super(VideoPlayerValue(duration: Duration.zero));
+ super(const VideoPlayerValue(duration: Duration.zero));
/// Constructs a [VideoPlayerController] playing a video from obtained from
/// the network.
@@ -241,7 +280,7 @@
}) : _closedCaptionFileFuture = closedCaptionFile,
dataSourceType = DataSourceType.network,
package = null,
- super(VideoPlayerValue(duration: Duration.zero));
+ super(const VideoPlayerValue(duration: Duration.zero));
/// Constructs a [VideoPlayerController] playing a video from a file.
///
@@ -256,7 +295,7 @@
dataSourceType = DataSourceType.file,
package = null,
formatHint = null,
- super(VideoPlayerValue(duration: Duration.zero));
+ super(const VideoPlayerValue(duration: Duration.zero));
/// Constructs a [VideoPlayerController] playing a video from a contentUri.
///
@@ -272,7 +311,7 @@
package = null,
formatHint = null,
httpHeaders = const <String, String>{},
- super(VideoPlayerValue(duration: Duration.zero));
+ super(const VideoPlayerValue(duration: Duration.zero));
/// The URI to the video file. This will be in different formats depending on
/// the [DataSourceType] of the original video.
@@ -372,7 +411,6 @@
return;
}
- // ignore: missing_enum_constant_in_switch
switch (event.eventType) {
case VideoEventType.initialized:
value = value.copyWith(
@@ -403,6 +441,9 @@
case VideoEventType.bufferingEnd:
value = value.copyWith(isBuffering: false);
break;
+ case VideoEventType.isPlayingStateUpdate:
+ value = value.copyWith(isPlaying: event.isPlaying);
+ break;
case VideoEventType.unknown:
break;
}
diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml
index ffe90c5..91d0a2c 100644
--- a/packages/video_player/video_player/pubspec.yaml
+++ b/packages/video_player/video_player/pubspec.yaml
@@ -3,7 +3,7 @@
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.6.0
+version: 2.6.1
environment:
sdk: ">=2.17.0 <4.0.0"
@@ -25,7 +25,7 @@
html: ^0.15.0
video_player_android: ^2.3.5
video_player_avfoundation: ^2.2.17
- video_player_platform_interface: ">=5.1.1 <7.0.0"
+ video_player_platform_interface: ">=6.1.0 <7.0.0"
video_player_web: ^2.0.0
dev_dependencies:
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 a24ac84..ef4f0bf 100644
--- a/packages/video_player/video_player/test/video_player_test.dart
+++ b/packages/video_player/video_player/test/video_player_test.dart
@@ -16,7 +16,7 @@
class FakeController extends ValueNotifier<VideoPlayerValue>
implements VideoPlayerController {
- FakeController() : super(VideoPlayerValue(duration: Duration.zero));
+ FakeController() : super(const VideoPlayerValue(duration: Duration.zero));
FakeController.value(super.value);
@@ -164,7 +164,8 @@
testWidgets('non-zero rotationCorrection value is used',
(WidgetTester tester) async {
final FakeController controller = FakeController.value(
- VideoPlayerValue(duration: Duration.zero, rotationCorrection: 180));
+ const VideoPlayerValue(
+ duration: Duration.zero, rotationCorrection: 180));
controller.textureId = 1;
await tester.pumpWidget(VideoPlayer(controller));
final Transform actualRotationCorrection =
@@ -184,7 +185,7 @@
testWidgets('no transform when rotationCorrection is zero',
(WidgetTester tester) async {
final FakeController controller =
- FakeController.value(VideoPlayerValue(duration: Duration.zero));
+ FakeController.value(const VideoPlayerValue(duration: Duration.zero));
controller.textureId = 1;
await tester.pumpWidget(VideoPlayer(controller));
expect(find.byType(Transform), findsNothing);
@@ -803,6 +804,30 @@
expect(controller.value.position, nonzeroDuration);
});
+ testWidgets('playback status', (WidgetTester tester) async {
+ final VideoPlayerController controller = VideoPlayerController.network(
+ 'https://127.0.0.1',
+ );
+ await controller.initialize();
+ expect(controller.value.isPlaying, isFalse);
+ final StreamController<VideoEvent> fakeVideoEventStream =
+ fakeVideoPlayerPlatform.streams[controller.textureId]!;
+
+ fakeVideoEventStream.add(VideoEvent(
+ eventType: VideoEventType.isPlayingStateUpdate,
+ isPlaying: true,
+ ));
+ await tester.pumpAndSettle();
+ expect(controller.value.isPlaying, isTrue);
+
+ fakeVideoEventStream.add(VideoEvent(
+ eventType: VideoEventType.isPlayingStateUpdate,
+ isPlaying: false,
+ ));
+ await tester.pumpAndSettle();
+ expect(controller.value.isPlaying, isFalse);
+ });
+
testWidgets('buffering status', (WidgetTester tester) async {
final VideoPlayerController controller = VideoPlayerController.network(
'https://127.0.0.1',
@@ -865,7 +890,7 @@
group('VideoPlayerValue', () {
test('uninitialized()', () {
- final VideoPlayerValue uninitialized = VideoPlayerValue.uninitialized();
+ const VideoPlayerValue uninitialized = VideoPlayerValue.uninitialized();
expect(uninitialized.duration, equals(Duration.zero));
expect(uninitialized.position, equals(Duration.zero));
@@ -886,7 +911,7 @@
test('erroneous()', () {
const String errorMessage = 'foo';
- final VideoPlayerValue error = VideoPlayerValue.erroneous(errorMessage);
+ const VideoPlayerValue error = VideoPlayerValue.erroneous(errorMessage);
expect(error.duration, equals(Duration.zero));
expect(error.position, equals(Duration.zero));
@@ -956,26 +981,26 @@
group('copyWith()', () {
test('exact copy', () {
- final VideoPlayerValue original = VideoPlayerValue.uninitialized();
+ const VideoPlayerValue original = VideoPlayerValue.uninitialized();
final VideoPlayerValue exactCopy = original.copyWith();
expect(exactCopy.toString(), original.toString());
});
test('errorDescription is not persisted when copy with null', () {
- final VideoPlayerValue original = VideoPlayerValue.erroneous('error');
+ const VideoPlayerValue original = VideoPlayerValue.erroneous('error');
final VideoPlayerValue copy = original.copyWith(errorDescription: null);
expect(copy.errorDescription, null);
});
test('errorDescription is changed when copy with another error', () {
- final VideoPlayerValue original = VideoPlayerValue.erroneous('error');
+ const VideoPlayerValue original = VideoPlayerValue.erroneous('error');
final VideoPlayerValue copy =
original.copyWith(errorDescription: 'new error');
expect(copy.errorDescription, 'new error');
});
test('errorDescription is changed when copy with error', () {
- final VideoPlayerValue original = VideoPlayerValue.uninitialized();
+ const VideoPlayerValue original = VideoPlayerValue.uninitialized();
final VideoPlayerValue copy =
original.copyWith(errorDescription: 'new error');
@@ -985,45 +1010,45 @@
group('aspectRatio', () {
test('640x480 -> 4:3', () {
- final VideoPlayerValue value = VideoPlayerValue(
+ const VideoPlayerValue value = VideoPlayerValue(
isInitialized: true,
- size: const Size(640, 480),
- duration: const Duration(seconds: 1),
+ size: Size(640, 480),
+ duration: Duration(seconds: 1),
);
expect(value.aspectRatio, 4 / 3);
});
test('no size -> 1.0', () {
- final VideoPlayerValue value = VideoPlayerValue(
+ const VideoPlayerValue value = VideoPlayerValue(
isInitialized: true,
- duration: const Duration(seconds: 1),
+ duration: Duration(seconds: 1),
);
expect(value.aspectRatio, 1.0);
});
test('height = 0 -> 1.0', () {
- final VideoPlayerValue value = VideoPlayerValue(
+ const VideoPlayerValue value = VideoPlayerValue(
isInitialized: true,
- size: const Size(640, 0),
- duration: const Duration(seconds: 1),
+ size: Size(640, 0),
+ duration: Duration(seconds: 1),
);
expect(value.aspectRatio, 1.0);
});
test('width = 0 -> 1.0', () {
- final VideoPlayerValue value = VideoPlayerValue(
+ const VideoPlayerValue value = VideoPlayerValue(
isInitialized: true,
- size: const Size(0, 480),
- duration: const Duration(seconds: 1),
+ size: Size(0, 480),
+ duration: Duration(seconds: 1),
);
expect(value.aspectRatio, 1.0);
});
test('negative aspect ratio -> 1.0', () {
- final VideoPlayerValue value = VideoPlayerValue(
+ const VideoPlayerValue value = VideoPlayerValue(
isInitialized: true,
- size: const Size(640, -480),
- duration: const Duration(seconds: 1),
+ size: Size(640, -480),
+ duration: Duration(seconds: 1),
);
expect(value.aspectRatio, 1.0);
});
diff --git a/packages/video_player/video_player_android/AUTHORS b/packages/video_player/video_player_android/AUTHORS
index 493a0b4..fc16c35 100644
--- a/packages/video_player/video_player_android/AUTHORS
+++ b/packages/video_player/video_player_android/AUTHORS
@@ -64,3 +64,4 @@
Anton Borries <mail@antonborri.es>
Alex Li <google@alexv525.com>
Rahul Raj <64.rahulraj@gmail.com>
+Márton Matuz <matuzmarci@gmail.com>
diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md
index fcb4f98..7dbfe5d 100644
--- a/packages/video_player/video_player_android/CHANGELOG.md
+++ b/packages/video_player/video_player_android/CHANGELOG.md
@@ -1,5 +1,6 @@
-## NEXT
+## 2.4.4
+* Synchronizes `VideoPlayerValue.isPlaying` with `ExoPlayer`.
* Updates minimum Flutter version to 3.3.
## 2.4.3
diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java
index 2aed171..4701c79 100644
--- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java
+++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java
@@ -231,6 +231,16 @@
eventSink.error("VideoError", "Video player had error " + error, null);
}
}
+
+ @Override
+ public void onIsPlayingChanged(boolean isPlaying) {
+ if (eventSink != null) {
+ Map<String, Object> event = new HashMap<>();
+ event.put("event", "isPlayingStateUpdate");
+ event.put("isPlaying", isPlaying);
+ eventSink.success(event);
+ }
+ }
});
}
diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java
index 5a646d8..2ba6ee0 100644
--- a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java
+++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java
@@ -5,10 +5,13 @@
package io.flutter.plugins.videoplayer;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -25,6 +28,7 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
@@ -234,4 +238,44 @@
assertEquals(event.get("height"), 200);
assertEquals(event.get("rotationCorrection"), 180);
}
+
+ @Test
+ public void onIsPlayingChangedSendsExpectedEvent() {
+ VideoPlayer videoPlayer =
+ new VideoPlayer(
+ fakeExoPlayer,
+ fakeEventChannel,
+ fakeSurfaceTextureEntry,
+ fakeVideoPlayerOptions,
+ fakeEventSink,
+ httpDataSourceFactorySpy);
+
+ doAnswer(
+ (Answer<Void>)
+ invocation -> {
+ Map<String, Object> event = new HashMap<>();
+ event.put("event", "isPlayingStateUpdate");
+ event.put("isPlaying", (Boolean) invocation.getArguments()[0]);
+ fakeEventSink.success(event);
+ return null;
+ })
+ .when(fakeExoPlayer)
+ .setPlayWhenReady(anyBoolean());
+
+ videoPlayer.play();
+
+ verify(fakeEventSink).success(eventCaptor.capture());
+ HashMap<String, Object> event1 = eventCaptor.getValue();
+
+ assertEquals(event1.get("event"), "isPlayingStateUpdate");
+ assertEquals(event1.get("isPlaying"), true);
+
+ videoPlayer.pause();
+
+ verify(fakeEventSink, times(2)).success(eventCaptor.capture());
+ HashMap<String, Object> event2 = eventCaptor.getValue();
+
+ assertEquals(event2.get("event"), "isPlayingStateUpdate");
+ assertEquals(event2.get("isPlaying"), false);
+ }
}
diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart
index 55f08fe..cea87ba 100644
--- a/packages/video_player/video_player_android/example/lib/mini_controller.dart
+++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart
@@ -8,6 +8,7 @@
import 'dart:async';
import 'dart:io';
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:video_player_platform_interface/video_player_platform_interface.dart';
@@ -24,10 +25,11 @@
/// The duration, current position, buffering state, error state and settings
/// of a [MiniController].
+@immutable
class VideoPlayerValue {
/// Constructs a video with the given values. Only [duration] is required. The
/// rest will initialize with default values when unset.
- VideoPlayerValue({
+ const VideoPlayerValue({
required this.duration,
this.size = Size.zero,
this.position = Duration.zero,
@@ -40,11 +42,11 @@
});
/// Returns an instance for a video that hasn't been loaded.
- VideoPlayerValue.uninitialized()
+ const VideoPlayerValue.uninitialized()
: this(duration: Duration.zero, isInitialized: false);
/// Returns an instance with the given [errorDescription].
- VideoPlayerValue.erroneous(String errorDescription)
+ const VideoPlayerValue.erroneous(String errorDescription)
: this(
duration: Duration.zero,
isInitialized: false,
@@ -127,6 +129,34 @@
errorDescription: errorDescription ?? this.errorDescription,
);
}
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is VideoPlayerValue &&
+ runtimeType == other.runtimeType &&
+ duration == other.duration &&
+ position == other.position &&
+ listEquals(buffered, other.buffered) &&
+ isPlaying == other.isPlaying &&
+ isBuffering == other.isBuffering &&
+ playbackSpeed == other.playbackSpeed &&
+ errorDescription == other.errorDescription &&
+ size == other.size &&
+ isInitialized == other.isInitialized;
+
+ @override
+ int get hashCode => Object.hash(
+ duration,
+ position,
+ buffered,
+ isPlaying,
+ isBuffering,
+ playbackSpeed,
+ errorDescription,
+ size,
+ isInitialized,
+ );
}
/// A very minimal version of `VideoPlayerController` for running the example
@@ -139,21 +169,21 @@
/// package and null otherwise.
MiniController.asset(this.dataSource, {this.package})
: dataSourceType = DataSourceType.asset,
- super(VideoPlayerValue(duration: Duration.zero));
+ super(const VideoPlayerValue(duration: Duration.zero));
/// Constructs a [MiniController] playing a video from obtained from
/// the network.
MiniController.network(this.dataSource)
: dataSourceType = DataSourceType.network,
package = null,
- super(VideoPlayerValue(duration: Duration.zero));
+ super(const VideoPlayerValue(duration: Duration.zero));
/// Constructs a [MiniController] playing a video from obtained from a file.
MiniController.file(File file)
: dataSource = Uri.file(file.absolute.path).toString(),
dataSourceType = DataSourceType.file,
package = null,
- super(VideoPlayerValue(duration: Duration.zero));
+ super(const VideoPlayerValue(duration: Duration.zero));
/// The URI to the video file. This will be in different formats depending on
/// the [DataSourceType] of the original video.
@@ -219,7 +249,6 @@
final Completer<void> initializingCompleter = Completer<void>();
void eventListener(VideoEvent event) {
- // ignore: missing_enum_constant_in_switch
switch (event.eventType) {
case VideoEventType.initialized:
value = value.copyWith(
@@ -244,6 +273,9 @@
case VideoEventType.bufferingEnd:
value = value.copyWith(isBuffering: false);
break;
+ case VideoEventType.isPlayingStateUpdate:
+ value = value.copyWith(isPlaying: event.isPlaying);
+ break;
case VideoEventType.unknown:
break;
}
diff --git a/packages/video_player/video_player_android/example/pubspec.yaml b/packages/video_player/video_player_android/example/pubspec.yaml
index 2b4bb09..e11d753 100644
--- a/packages/video_player/video_player_android/example/pubspec.yaml
+++ b/packages/video_player/video_player_android/example/pubspec.yaml
@@ -16,7 +16,7 @@
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
- video_player_platform_interface: ">=5.1.1 <7.0.0"
+ video_player_platform_interface: ">=6.1.0 <7.0.0"
dev_dependencies:
flutter_driver:
diff --git a/packages/video_player/video_player_android/lib/src/android_video_player.dart b/packages/video_player/video_player_android/lib/src/android_video_player.dart
index c8a2b7a..3d34fc8 100644
--- a/packages/video_player/video_player_android/lib/src/android_video_player.dart
+++ b/packages/video_player/video_player_android/lib/src/android_video_player.dart
@@ -148,6 +148,11 @@
return VideoEvent(eventType: VideoEventType.bufferingStart);
case 'bufferingEnd':
return VideoEvent(eventType: VideoEventType.bufferingEnd);
+ case 'isPlayingStateUpdate':
+ return VideoEvent(
+ eventType: VideoEventType.isPlayingStateUpdate,
+ isPlaying: map['isPlaying'] as bool,
+ );
default:
return VideoEvent(eventType: VideoEventType.unknown);
}
diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml
index b847a6d..a77b646 100644
--- a/packages/video_player/video_player_android/pubspec.yaml
+++ b/packages/video_player/video_player_android/pubspec.yaml
@@ -2,7 +2,7 @@
description: Android implementation of the video_player plugin.
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
-version: 2.4.3
+version: 2.4.4
environment:
sdk: ">=2.18.0 <4.0.0"
@@ -20,7 +20,7 @@
dependencies:
flutter:
sdk: flutter
- video_player_platform_interface: ">=5.1.1 <7.0.0"
+ video_player_platform_interface: ">=6.1.0 <7.0.0"
dev_dependencies:
flutter_test:
diff --git a/packages/video_player/video_player_android/test/android_video_player_test.dart b/packages/video_player/video_player_android/test/android_video_player_test.dart
index 00feb27..d30d1d4 100644
--- a/packages/video_player/video_player_android/test/android_video_player_test.dart
+++ b/packages/video_player/video_player_android/test/android_video_player_test.dart
@@ -246,10 +246,11 @@
});
test('videoEventsFor', () async {
+ const String mockChannel = 'flutter.io/videoPlayer/videoEvents123';
_ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.setMockMessageHandler(
- 'flutter.io/videoPlayer/videoEvents123',
+ mockChannel,
(ByteData? message) async {
final MethodCall methodCall =
const StandardMethodCodec().decodeMethodCall(message);
@@ -257,7 +258,7 @@
await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.handlePlatformMessage(
- 'flutter.io/videoPlayer/videoEvents123',
+ mockChannel,
const StandardMethodCodec()
.encodeSuccessEnvelope(<String, dynamic>{
'event': 'initialized',
@@ -270,7 +271,7 @@
await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.handlePlatformMessage(
- 'flutter.io/videoPlayer/videoEvents123',
+ mockChannel,
const StandardMethodCodec()
.encodeSuccessEnvelope(<String, dynamic>{
'event': 'initialized',
@@ -284,7 +285,7 @@
await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.handlePlatformMessage(
- 'flutter.io/videoPlayer/videoEvents123',
+ mockChannel,
const StandardMethodCodec()
.encodeSuccessEnvelope(<String, dynamic>{
'event': 'completed',
@@ -294,7 +295,7 @@
await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.handlePlatformMessage(
- 'flutter.io/videoPlayer/videoEvents123',
+ mockChannel,
const StandardMethodCodec()
.encodeSuccessEnvelope(<String, dynamic>{
'event': 'bufferingUpdate',
@@ -308,7 +309,7 @@
await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.handlePlatformMessage(
- 'flutter.io/videoPlayer/videoEvents123',
+ mockChannel,
const StandardMethodCodec()
.encodeSuccessEnvelope(<String, dynamic>{
'event': 'bufferingStart',
@@ -318,13 +319,35 @@
await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.handlePlatformMessage(
- 'flutter.io/videoPlayer/videoEvents123',
+ mockChannel,
const StandardMethodCodec()
.encodeSuccessEnvelope(<String, dynamic>{
'event': 'bufferingEnd',
}),
(ByteData? data) {});
+ await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
+ .defaultBinaryMessenger
+ .handlePlatformMessage(
+ mockChannel,
+ const StandardMethodCodec()
+ .encodeSuccessEnvelope(<String, dynamic>{
+ 'event': 'isPlayingStateUpdate',
+ 'isPlaying': true,
+ }),
+ (ByteData? data) {});
+
+ await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
+ .defaultBinaryMessenger
+ .handlePlatformMessage(
+ mockChannel,
+ const StandardMethodCodec()
+ .encodeSuccessEnvelope(<String, dynamic>{
+ 'event': 'isPlayingStateUpdate',
+ 'isPlaying': false,
+ }),
+ (ByteData? data) {});
+
return const StandardMethodCodec().encodeSuccessEnvelope(null);
} else if (methodCall.method == 'cancel') {
return const StandardMethodCodec().encodeSuccessEnvelope(null);
@@ -363,6 +386,14 @@
]),
VideoEvent(eventType: VideoEventType.bufferingStart),
VideoEvent(eventType: VideoEventType.bufferingEnd),
+ VideoEvent(
+ eventType: VideoEventType.isPlayingStateUpdate,
+ isPlaying: true,
+ ),
+ VideoEvent(
+ eventType: VideoEventType.isPlayingStateUpdate,
+ isPlaying: false,
+ ),
]));
});
});
diff --git a/packages/video_player/video_player_avfoundation/AUTHORS b/packages/video_player/video_player_avfoundation/AUTHORS
index 493a0b4..fc16c35 100644
--- a/packages/video_player/video_player_avfoundation/AUTHORS
+++ b/packages/video_player/video_player_avfoundation/AUTHORS
@@ -64,3 +64,4 @@
Anton Borries <mail@antonborri.es>
Alex Li <google@alexv525.com>
Rahul Raj <64.rahulraj@gmail.com>
+Márton Matuz <matuzmarci@gmail.com>
diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md
index b2308dd..ef73b92 100644
--- a/packages/video_player/video_player_avfoundation/CHANGELOG.md
+++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.4.3
+
+* Synchronizes `VideoPlayerValue.isPlaying` with `AVPlayer`.
+
## 2.4.2
* Makes seekTo async and only complete when AVPlayer.seekTo completes.
diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart
index 55f08fe..cea87ba 100644
--- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart
+++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart
@@ -8,6 +8,7 @@
import 'dart:async';
import 'dart:io';
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:video_player_platform_interface/video_player_platform_interface.dart';
@@ -24,10 +25,11 @@
/// The duration, current position, buffering state, error state and settings
/// of a [MiniController].
+@immutable
class VideoPlayerValue {
/// Constructs a video with the given values. Only [duration] is required. The
/// rest will initialize with default values when unset.
- VideoPlayerValue({
+ const VideoPlayerValue({
required this.duration,
this.size = Size.zero,
this.position = Duration.zero,
@@ -40,11 +42,11 @@
});
/// Returns an instance for a video that hasn't been loaded.
- VideoPlayerValue.uninitialized()
+ const VideoPlayerValue.uninitialized()
: this(duration: Duration.zero, isInitialized: false);
/// Returns an instance with the given [errorDescription].
- VideoPlayerValue.erroneous(String errorDescription)
+ const VideoPlayerValue.erroneous(String errorDescription)
: this(
duration: Duration.zero,
isInitialized: false,
@@ -127,6 +129,34 @@
errorDescription: errorDescription ?? this.errorDescription,
);
}
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is VideoPlayerValue &&
+ runtimeType == other.runtimeType &&
+ duration == other.duration &&
+ position == other.position &&
+ listEquals(buffered, other.buffered) &&
+ isPlaying == other.isPlaying &&
+ isBuffering == other.isBuffering &&
+ playbackSpeed == other.playbackSpeed &&
+ errorDescription == other.errorDescription &&
+ size == other.size &&
+ isInitialized == other.isInitialized;
+
+ @override
+ int get hashCode => Object.hash(
+ duration,
+ position,
+ buffered,
+ isPlaying,
+ isBuffering,
+ playbackSpeed,
+ errorDescription,
+ size,
+ isInitialized,
+ );
}
/// A very minimal version of `VideoPlayerController` for running the example
@@ -139,21 +169,21 @@
/// package and null otherwise.
MiniController.asset(this.dataSource, {this.package})
: dataSourceType = DataSourceType.asset,
- super(VideoPlayerValue(duration: Duration.zero));
+ super(const VideoPlayerValue(duration: Duration.zero));
/// Constructs a [MiniController] playing a video from obtained from
/// the network.
MiniController.network(this.dataSource)
: dataSourceType = DataSourceType.network,
package = null,
- super(VideoPlayerValue(duration: Duration.zero));
+ super(const VideoPlayerValue(duration: Duration.zero));
/// Constructs a [MiniController] playing a video from obtained from a file.
MiniController.file(File file)
: dataSource = Uri.file(file.absolute.path).toString(),
dataSourceType = DataSourceType.file,
package = null,
- super(VideoPlayerValue(duration: Duration.zero));
+ super(const VideoPlayerValue(duration: Duration.zero));
/// The URI to the video file. This will be in different formats depending on
/// the [DataSourceType] of the original video.
@@ -219,7 +249,6 @@
final Completer<void> initializingCompleter = Completer<void>();
void eventListener(VideoEvent event) {
- // ignore: missing_enum_constant_in_switch
switch (event.eventType) {
case VideoEventType.initialized:
value = value.copyWith(
@@ -244,6 +273,9 @@
case VideoEventType.bufferingEnd:
value = value.copyWith(isBuffering: false);
break;
+ case VideoEventType.isPlayingStateUpdate:
+ value = value.copyWith(isPlaying: event.isPlaying);
+ break;
case VideoEventType.unknown:
break;
}
diff --git a/packages/video_player/video_player_avfoundation/example/pubspec.yaml b/packages/video_player/video_player_avfoundation/example/pubspec.yaml
index f8b9ca6..bffa17d 100644
--- a/packages/video_player/video_player_avfoundation/example/pubspec.yaml
+++ b/packages/video_player/video_player_avfoundation/example/pubspec.yaml
@@ -16,7 +16,7 @@
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
- video_player_platform_interface: ">=4.2.0 <7.0.0"
+ video_player_platform_interface: ">=6.1.0 <7.0.0"
dev_dependencies:
flutter_driver:
diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m
index ce0a121..586d655 100644
--- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m
+++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m
@@ -62,6 +62,7 @@
static void *playbackLikelyToKeepUpContext = &playbackLikelyToKeepUpContext;
static void *playbackBufferEmptyContext = &playbackBufferEmptyContext;
static void *playbackBufferFullContext = &playbackBufferFullContext;
+static void *rateContext = &rateContext;
@implementation FLTVideoPlayer
- (instancetype)initWithAsset:(NSString *)asset frameUpdater:(FLTFrameUpdater *)frameUpdater {
@@ -69,7 +70,7 @@
return [self initWithURL:[NSURL fileURLWithPath:path] frameUpdater:frameUpdater httpHeaders:@{}];
}
-- (void)addObservers:(AVPlayerItem *)item {
+- (void)addObserversForItem:(AVPlayerItem *)item player:(AVPlayer *)player {
[item addObserver:self
forKeyPath:@"loadedTimeRanges"
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
@@ -99,6 +100,13 @@
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:playbackBufferFullContext];
+ // Add observer to AVPlayer instead of AVPlayerItem since the AVPlayerItem does not have a "rate"
+ // property
+ [player addObserver:self
+ forKeyPath:@"rate"
+ options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
+ context:rateContext];
+
// Add an observer that will respond to itemDidPlayToEndTime
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(itemDidPlayToEndTime:)
@@ -252,7 +260,7 @@
[self createVideoOutputAndDisplayLink:frameUpdater];
- [self addObservers:item];
+ [self addObserversForItem:item player:_player];
[asset loadValuesAsynchronouslyForKeys:@[ @"tracks" ] completionHandler:assetCompletionHandler];
@@ -317,6 +325,14 @@
if (_eventSink != nil) {
_eventSink(@{@"event" : @"bufferingEnd"});
}
+ } else if (context == rateContext) {
+ // Important: Make sure to cast the object to AVPlayer when observing the rate property,
+ // as it is not available in AVPlayerItem.
+ AVPlayer *player = (AVPlayer *)object;
+ if (_eventSink != nil) {
+ _eventSink(
+ @{@"event" : @"isPlayingStateUpdate", @"isPlaying" : player.rate > 0 ? @YES : @NO});
+ }
}
}
diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart
index b5ebedd..868c598 100644
--- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart
+++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart
@@ -146,6 +146,11 @@
return VideoEvent(eventType: VideoEventType.bufferingStart);
case 'bufferingEnd':
return VideoEvent(eventType: VideoEventType.bufferingEnd);
+ case 'isPlayingStateUpdate':
+ return VideoEvent(
+ eventType: VideoEventType.isPlayingStateUpdate,
+ isPlaying: map['isPlaying'] as bool,
+ );
default:
return VideoEvent(eventType: VideoEventType.unknown);
}
diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml
index 9f5566c..6cb0565 100644
--- a/packages/video_player/video_player_avfoundation/pubspec.yaml
+++ b/packages/video_player/video_player_avfoundation/pubspec.yaml
@@ -2,7 +2,7 @@
description: iOS implementation of the video_player plugin.
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_avfoundation
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
-version: 2.4.2
+version: 2.4.3
environment:
sdk: ">=2.18.0 <4.0.0"
@@ -19,7 +19,7 @@
dependencies:
flutter:
sdk: flutter
- video_player_platform_interface: ">=4.2.0 <7.0.0"
+ video_player_platform_interface: ">=6.1.0 <7.0.0"
dev_dependencies:
flutter_test:
diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart
index a0f37e2..1b25da8 100644
--- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart
+++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart
@@ -234,10 +234,11 @@
});
test('videoEventsFor', () async {
+ const String mockChannel = 'flutter.io/videoPlayer/videoEvents123';
_ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.setMockMessageHandler(
- 'flutter.io/videoPlayer/videoEvents123',
+ mockChannel,
(ByteData? message) async {
final MethodCall methodCall =
const StandardMethodCodec().decodeMethodCall(message);
@@ -245,7 +246,7 @@
await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.handlePlatformMessage(
- 'flutter.io/videoPlayer/videoEvents123',
+ mockChannel,
const StandardMethodCodec()
.encodeSuccessEnvelope(<String, dynamic>{
'event': 'initialized',
@@ -258,7 +259,7 @@
await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.handlePlatformMessage(
- 'flutter.io/videoPlayer/videoEvents123',
+ mockChannel,
const StandardMethodCodec()
.encodeSuccessEnvelope(<String, dynamic>{
'event': 'completed',
@@ -268,7 +269,7 @@
await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.handlePlatformMessage(
- 'flutter.io/videoPlayer/videoEvents123',
+ mockChannel,
const StandardMethodCodec()
.encodeSuccessEnvelope(<String, dynamic>{
'event': 'bufferingUpdate',
@@ -282,7 +283,7 @@
await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.handlePlatformMessage(
- 'flutter.io/videoPlayer/videoEvents123',
+ mockChannel,
const StandardMethodCodec()
.encodeSuccessEnvelope(<String, dynamic>{
'event': 'bufferingStart',
@@ -292,13 +293,35 @@
await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
.defaultBinaryMessenger
.handlePlatformMessage(
- 'flutter.io/videoPlayer/videoEvents123',
+ mockChannel,
const StandardMethodCodec()
.encodeSuccessEnvelope(<String, dynamic>{
'event': 'bufferingEnd',
}),
(ByteData? data) {});
+ await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
+ .defaultBinaryMessenger
+ .handlePlatformMessage(
+ mockChannel,
+ const StandardMethodCodec()
+ .encodeSuccessEnvelope(<String, dynamic>{
+ 'event': 'isPlayingStateUpdate',
+ 'isPlaying': true,
+ }),
+ (ByteData? data) {});
+
+ await _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
+ .defaultBinaryMessenger
+ .handlePlatformMessage(
+ mockChannel,
+ const StandardMethodCodec()
+ .encodeSuccessEnvelope(<String, dynamic>{
+ 'event': 'isPlayingStateUpdate',
+ 'isPlaying': false,
+ }),
+ (ByteData? data) {});
+
return const StandardMethodCodec().encodeSuccessEnvelope(null);
} else if (methodCall.method == 'cancel') {
return const StandardMethodCodec().encodeSuccessEnvelope(null);
@@ -330,6 +353,14 @@
]),
VideoEvent(eventType: VideoEventType.bufferingStart),
VideoEvent(eventType: VideoEventType.bufferingEnd),
+ VideoEvent(
+ eventType: VideoEventType.isPlayingStateUpdate,
+ isPlaying: true,
+ ),
+ VideoEvent(
+ eventType: VideoEventType.isPlayingStateUpdate,
+ isPlaying: false,
+ ),
]));
});
});
diff --git a/packages/video_player/video_player_web/AUTHORS b/packages/video_player/video_player_web/AUTHORS
index 493a0b4..fc16c35 100644
--- a/packages/video_player/video_player_web/AUTHORS
+++ b/packages/video_player/video_player_web/AUTHORS
@@ -64,3 +64,4 @@
Anton Borries <mail@antonborri.es>
Alex Li <google@alexv525.com>
Rahul Raj <64.rahulraj@gmail.com>
+Márton Matuz <matuzmarci@gmail.com>
diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md
index 160c154..19f5bb9 100644
--- a/packages/video_player/video_player_web/CHANGELOG.md
+++ b/packages/video_player/video_player_web/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.0.16
+
+* Synchronizes `VideoPlayerValue.isPlaying` with `VideoElement`.
+
## 2.0.15
* Clarifies explanation of endorsement in README.
diff --git a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart
index 5053ea6..7d74223 100644
--- a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart
+++ b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart
@@ -169,13 +169,47 @@
expect(VideoPlayerPlatform.instance.setMixWithOthers(false), completes);
});
+ testWidgets(
+ 'double call to play will emit a single isPlayingStateUpdate event',
+ (WidgetTester tester) async {
+ final int videoPlayerId = await textureId;
+ final Stream<VideoEvent> eventStream =
+ VideoPlayerPlatform.instance.videoEventsFor(videoPlayerId);
+
+ final Future<List<VideoEvent>> stream = eventStream.timeout(
+ const Duration(seconds: 2),
+ onTimeout: (EventSink<VideoEvent> sink) {
+ sink.close();
+ },
+ ).toList();
+
+ await VideoPlayerPlatform.instance.setVolume(videoPlayerId, 0);
+ await VideoPlayerPlatform.instance.play(videoPlayerId);
+ await VideoPlayerPlatform.instance.play(videoPlayerId);
+
+ // Let the video play, until we stop seeing events for two seconds
+ final List<VideoEvent> events = await stream;
+
+ await VideoPlayerPlatform.instance.pause(videoPlayerId);
+
+ expect(
+ events.where((VideoEvent e) =>
+ e.eventType == VideoEventType.isPlayingStateUpdate),
+ equals(<VideoEvent>[
+ VideoEvent(
+ eventType: VideoEventType.isPlayingStateUpdate,
+ isPlaying: true,
+ )
+ ]));
+ });
+
testWidgets('video playback lifecycle', (WidgetTester tester) async {
final int videoPlayerId = await textureId;
final Stream<VideoEvent> eventStream =
VideoPlayerPlatform.instance.videoEventsFor(videoPlayerId);
final Future<List<VideoEvent>> stream = eventStream.timeout(
- const Duration(seconds: 1),
+ const Duration(seconds: 2),
onTimeout: (EventSink<VideoEvent> sink) {
sink.close();
},
@@ -184,23 +218,25 @@
await VideoPlayerPlatform.instance.setVolume(videoPlayerId, 0);
await VideoPlayerPlatform.instance.play(videoPlayerId);
- // Let the video play, until we stop seeing events for a second
+ // Let the video play, until we stop seeing events for two seconds
final List<VideoEvent> events = await stream;
await VideoPlayerPlatform.instance.pause(videoPlayerId);
// The expected list of event types should look like this:
- // 1. bufferingStart,
- // 2. bufferingUpdate (videoElement.onWaiting),
- // 3. initialized (videoElement.onCanPlay),
- // 4. bufferingEnd (videoElement.onCanPlayThrough),
+ // 1. isPlayingStateUpdate (videoElement.onPlaying)
+ // 2. bufferingStart,
+ // 3. bufferingUpdate (videoElement.onWaiting),
+ // 4. initialized (videoElement.onCanPlay),
+ // 5. bufferingEnd (videoElement.onCanPlayThrough),
expect(
events.map((VideoEvent e) => e.eventType),
equals(<VideoEventType>[
+ VideoEventType.isPlayingStateUpdate,
VideoEventType.bufferingStart,
VideoEventType.bufferingUpdate,
VideoEventType.initialized,
- VideoEventType.bufferingEnd
+ VideoEventType.bufferingEnd,
]));
});
});
diff --git a/packages/video_player/video_player_web/example/pubspec.yaml b/packages/video_player/video_player_web/example/pubspec.yaml
index 3114335..4cb1d4d 100644
--- a/packages/video_player/video_player_web/example/pubspec.yaml
+++ b/packages/video_player/video_player_web/example/pubspec.yaml
@@ -9,7 +9,7 @@
flutter:
sdk: flutter
js: ^0.6.0
- video_player_platform_interface: ">=4.2.0 <7.0.0"
+ video_player_platform_interface: ">=6.1.0 <7.0.0"
video_player_web:
path: ../
diff --git a/packages/video_player/video_player_web/lib/src/video_player.dart b/packages/video_player/video_player_web/lib/src/video_player.dart
index 02ead1f..bc0021d 100644
--- a/packages/video_player/video_player_web/lib/src/video_player.dart
+++ b/packages/video_player/video_player_web/lib/src/video_player.dart
@@ -102,6 +102,20 @@
));
});
+ _videoElement.onPlay.listen((dynamic _) {
+ _eventController.add(VideoEvent(
+ eventType: VideoEventType.isPlayingStateUpdate,
+ isPlaying: true,
+ ));
+ });
+
+ _videoElement.onPause.listen((dynamic _) {
+ _eventController.add(VideoEvent(
+ eventType: VideoEventType.isPlayingStateUpdate,
+ isPlaying: false,
+ ));
+ });
+
_videoElement.onEnded.listen((dynamic _) {
setBuffering(false);
_eventController.add(VideoEvent(eventType: VideoEventType.completed));
diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml
index 07d9b84..70434ef 100644
--- a/packages/video_player/video_player_web/pubspec.yaml
+++ b/packages/video_player/video_player_web/pubspec.yaml
@@ -2,7 +2,7 @@
description: Web platform implementation of video_player.
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_web
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
-version: 2.0.15
+version: 2.0.16
environment:
sdk: ">=2.17.0 <4.0.0"
@@ -21,7 +21,7 @@
sdk: flutter
flutter_web_plugins:
sdk: flutter
- video_player_platform_interface: ">=4.2.0 <7.0.0"
+ video_player_platform_interface: ">=6.1.0 <7.0.0"
dev_dependencies:
flutter_test: