Skip to content

[video_player] synchronize isPlaying state #3261

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Apr 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
21f27e9
Recreating PR from flutter/plugins
maRci002 Feb 22, 2023
2c1f317
Merge branch 'main' into track-playing-updates
maRci002 Feb 22, 2023
6149904
Remove dependecy_overrides from video_player package
maRci002 Feb 22, 2023
bcfedbb
remove dependency_overrides
maRci002 Feb 23, 2023
5d512f9
increase stream timeout to reflect comment in test
maRci002 Feb 24, 2023
1d1e42d
add android test
maRci002 Feb 28, 2023
0b84ea7
Merge branch 'main' into track-playing-updates
maRci002 Mar 2, 2023
d9480d8
readd dependency_overrides to pass tests
maRci002 Mar 2, 2023
deec841
update android changelog
maRci002 Mar 2, 2023
14ff92c
update changelog
maRci002 Mar 2, 2023
a7eabed
override video_player / video_player_platform_interface dependency fo…
maRci002 Mar 3, 2023
31c1978
apply requested changes
maRci002 Mar 3, 2023
dbd637d
Merge branch 'main' into track-playing-updates
maRci002 Mar 6, 2023
88d173a
Merge branch 'main' into track-playing-updates
maRci002 Mar 11, 2023
28583e0
remove missing_enum_constant_in_switch
maRci002 Mar 11, 2023
6cc94b6
format VideoPlayerTest.java android
maRci002 Mar 12, 2023
13d111f
Merge branch 'main' into track-playing-updates
maRci002 Mar 12, 2023
cf16cb3
add version changes to iOS / web
maRci002 Mar 12, 2023
017c7de
Merge branch 'main' into track-playing-updates
maRci002 Mar 15, 2023
ac13998
Merge remote-tracking branch 'upstream/main' into track-playing-updates
maRci002 Mar 16, 2023
0ecb828
Merge branch 'main' into track-playing-updates
maRci002 Mar 16, 2023
12507c2
remove dependency_overrides
maRci002 Mar 16, 2023
810b83e
upgrade video_player_platform_interface dependency versions to 6.1.0
maRci002 Mar 16, 2023
845ca8b
Merge branch 'main' into track-playing-updates
maRci002 Mar 16, 2023
e79f190
Merge branch 'main' of github.com:flutter/packages into track-playing…
bparrishMines Apr 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/video_player/video_player/AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,4 @@ Anton Borries <mail@antonborri.es>
Alex Li <google@alexv525.com>
Rahul Raj <64.rahulraj@gmail.com>
Koen Van Looveren <vanlooverenkoen.dev@gmail.com>
Márton Matuz <matuzmarci@gmail.com>
4 changes: 4 additions & 0 deletions packages/video_player/video_player/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -32,6 +32,7 @@ abstract class ClosedCaptionFile {
///
/// 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.
///
Expand Down Expand Up @@ -74,4 +75,22 @@ class Caption {
'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,
);
}
57 changes: 49 additions & 8 deletions packages/video_player/video_player/lib/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ VideoPlayerPlatform get _videoPlayerPlatform {

/// 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,
Expand All @@ -54,11 +55,11 @@ class VideoPlayerValue {
});

/// 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,
Expand Down Expand Up @@ -195,6 +196,44 @@ class VideoPlayerValue {
'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
Expand All @@ -221,7 +260,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
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.
Expand All @@ -241,7 +280,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
}) : _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.
///
Expand All @@ -256,7 +295,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
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.
///
Expand All @@ -272,7 +311,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
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.
Expand Down Expand Up @@ -372,7 +411,6 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
return;
}

// ignore: missing_enum_constant_in_switch
switch (event.eventType) {
case VideoEventType.initialized:
value = value.copyWith(
Expand Down Expand Up @@ -403,6 +441,9 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
case VideoEventType.bufferingEnd:
value = value.copyWith(isBuffering: false);
break;
case VideoEventType.isPlayingStateUpdate:
value = value.copyWith(isPlaying: event.isPlaying);
break;
case VideoEventType.unknown:
break;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/video_player/video_player/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.6.0
version: 2.6.1

environment:
sdk: ">=2.17.0 <4.0.0"
Expand All @@ -25,7 +25,7 @@ dependencies:
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:
Expand Down
71 changes: 48 additions & 23 deletions packages/video_player/video_player/test/video_player_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import 'package:video_player_platform_interface/video_player_platform_interface.

class FakeController extends ValueNotifier<VideoPlayerValue>
implements VideoPlayerController {
FakeController() : super(VideoPlayerValue(duration: Duration.zero));
FakeController() : super(const VideoPlayerValue(duration: Duration.zero));

FakeController.value(super.value);

Expand Down Expand Up @@ -164,7 +164,8 @@ void main() {
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 =
Expand All @@ -184,7 +185,7 @@ void main() {
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);
Expand Down Expand Up @@ -803,6 +804,30 @@ void main() {
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',
Expand Down Expand Up @@ -865,7 +890,7 @@ void main() {

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));
Expand All @@ -886,7 +911,7 @@ void main() {

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));
Expand Down Expand Up @@ -956,26 +981,26 @@ void main() {

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');

Expand All @@ -985,45 +1010,45 @@ void main() {

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);
});
Expand Down
1 change: 1 addition & 0 deletions packages/video_player/video_player_android/AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy <sanekyy@gmail.com>
Anton Borries <mail@antonborri.es>
Alex Li <google@alexv525.com>
Rahul Raj <64.rahulraj@gmail.com>
Márton Matuz <matuzmarci@gmail.com>
3 changes: 2 additions & 1 deletion packages/video_player/video_player_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 2.4.4

* Synchronizes `VideoPlayerValue.isPlaying` with `ExoPlayer`.
* Updates minimum Flutter version to 3.3.

## 2.4.3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,16 @@ public void onPlayerError(final PlaybackException error) {
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);
}
}
});
}

Expand Down
Loading