Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
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 @@
## 0.11.0+1

* Added isDurationIndefinite to support indefinite streams

## 0.11.0

* Added option to set the video playback speed on the video controller.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ private void sendInitialized() {
Map<String, Object> event = new HashMap<>();
event.put("event", "initialized");
event.put("duration", exoPlayer.getDuration());
event.put("isDurationIndefinite", exoPlayer.isCurrentWindowDynamic());

if (exoPlayer.getVideoFormat() != null) {
Format videoFormat = exoPlayer.getVideoFormat();
Expand Down
7 changes: 5 additions & 2 deletions packages/video_player/video_player/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,16 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> {
void initState() {
super.initState();
_controller = VideoPlayerController.network(
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
// 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
'https://rtmp.api.rt.com/hls/rtdru.m3u8',
closedCaptionFile: _loadCaptions(),
videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true),
);

_controller.addListener(() {
setState(() {});
setState(() {
debugPrint('Indefinite = ${_controller.value.isDurationIndefinite}');
});
});
_controller.setLooping(true);
_controller.initialize();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';

import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';

Future<void> main() async {
final FlutterDriver driver = await FlutterDriver.connect();

tearDownAll(() async {
await driver.close();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@ - (void)updatePlayingState {
_displayLink.paused = !_isPlaying;
}

- (bool)isDurationIndefinite {
return CMTIME_IS_INDEFINITE([[_player currentItem] duration]);
}

- (void)sendInitialized {
if (_eventSink && !_isInitialized) {
CGSize size = [self.player currentItem].presentationSize;
Expand All @@ -313,14 +317,15 @@ - (void)sendInitialized {
return;
}
// The player may be initialized but still needs to determine the duration.
if ([self duration] == 0) {
if ([self duration] == 0 && ![self isDurationIndefinite]) {
return;
}

_isInitialized = true;
_eventSink(@{
@"event" : @"initialized",
@"duration" : @([self duration]),
@"isDurationIndefinite": @([self isDurationIndefinite]),
@"width" : @(width),
@"height" : @(height)
});
Expand Down
12 changes: 10 additions & 2 deletions packages/video_player/video_player/lib/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class VideoPlayerValue {
this.isPlaying = false,
this.isLooping = false,
this.isBuffering = false,
this.isDurationIndefinite = false,
this.volume = 1.0,
this.playbackSpeed = 1.0,
this.errorDescription,
Expand Down Expand Up @@ -75,6 +76,9 @@ class VideoPlayerValue {
/// True if the video is currently buffering.
final bool isBuffering;

/// True if the video has an undetermined duration, eg. a live stream.
final bool isDurationIndefinite;

/// The current volume of the playback.
final double volume;

Expand Down Expand Up @@ -122,6 +126,7 @@ class VideoPlayerValue {
bool isPlaying,
bool isLooping,
bool isBuffering,
bool isDurationIndefinite,
double volume,
double playbackSpeed,
String errorDescription,
Expand All @@ -135,6 +140,7 @@ class VideoPlayerValue {
isPlaying: isPlaying ?? this.isPlaying,
isLooping: isLooping ?? this.isLooping,
isBuffering: isBuffering ?? this.isBuffering,
isDurationIndefinite: isDurationIndefinite ?? this.isDurationIndefinite,
volume: volume ?? this.volume,
playbackSpeed: playbackSpeed ?? this.playbackSpeed,
errorDescription: errorDescription ?? this.errorDescription,
Expand All @@ -152,6 +158,7 @@ class VideoPlayerValue {
'isPlaying: $isPlaying, '
'isLooping: $isLooping, '
'isBuffering: $isBuffering, '
'isDurationIndefinite: $isDurationIndefinite, '
'volume: $volume, '
'playbackSpeed: $playbackSpeed, '
'errorDescription: $errorDescription)';
Expand Down Expand Up @@ -292,6 +299,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
case VideoEventType.initialized:
value = value.copyWith(
duration: event.duration,
isDurationIndefinite: event.isDurationIndefinite,
size: event.size,
);
initializingCompleter.complete(null);
Expand Down Expand Up @@ -819,12 +827,12 @@ class _VideoProgressIndicatorState extends State<VideoProgressIndicator> {
fit: StackFit.passthrough,
children: <Widget>[
LinearProgressIndicator(
value: maxBuffering / duration,
value: duration > 0 ? maxBuffering / duration : 0,
valueColor: AlwaysStoppedAnimation<Color>(colors.bufferedColor),
backgroundColor: colors.backgroundColor,
),
LinearProgressIndicator(
value: position / duration,
value: duration > 0 ? position / duration : 0,
valueColor: AlwaysStoppedAnimation<Color>(colors.playedColor),
backgroundColor: Colors.transparent,
),
Expand Down
11 changes: 6 additions & 5 deletions packages/video_player/video_player/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: Flutter plugin for displaying inline video with other Flutter
# 0.10.y+z is compatible with 1.0.0, if you land a breaking change bump
# the version to 2.0.0.
# See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0
version: 0.11.0
version: 0.11.0+1
homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player

flutter:
Expand All @@ -15,19 +15,20 @@ flutter:
pluginClass: VideoPlayerPlugin
ios:
pluginClass: FLTVideoPlayerPlugin
web:
default_package: video_player_web
# web:
# default_package: video_player_web

dependencies:
meta: ^1.0.5
video_player_platform_interface: ^2.2.0
video_player_platform_interface: # ^2.2.0
path: ../video_player_platform_interface

# The design on https://flutter.dev/go/federated-plugins was to leave
# this constraint as "any". We cannot do it right now as it fails pub publish
# validation, so we set a ^ constraint.
# TODO(amirh): Revisit this (either update this part in the design or the pub tool).
# https://github.com/flutter/flutter/issues/46264
video_player_web: '>=0.1.4 <2.0.0'
# video_player_web: '>=0.1.4 <2.0.0'

flutter:
sdk: flutter
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:video_player/video_player.dart';
import 'package:video_player_platform_interface/method_channel_video_player.dart';
import 'package:video_player_platform_interface/video_player_platform_interface.dart';

VideoEvent createVideoEvent({
bool isIndefiniteStream,
}) {
if (isIndefiniteStream) {
return (VideoEvent(
eventType: VideoEventType.initialized,
duration: Duration(seconds: 0),
isDurationIndefinite: true,
));
} else {
return (VideoEvent(
eventType: VideoEventType.unknown,
duration: Duration(seconds: 0),
isDurationIndefinite: false,
));
}
}

void main() {
TestWidgetsFlutterBinding.ensureInitialized();

group('Mock videoEvent', () {
final log = <MethodCall>[];

MethodChannelVideoPlayer methodChannelVideoPlayer;

StreamSubscription videoEventStreamSubscription;
VideoPlayerController _controller;

setUp(() async {
methodChannelVideoPlayer = MethodChannelVideoPlayer();

_controller = VideoPlayerController.network('https://127.0.0.1');

// Configure mock implementation for the EventChannel
MethodChannel(methodChannelVideoPlayer
.eventChannelFor(_controller.textureId)
.name)
.setMockMethodCallHandler((methodCall) async {
log.add(methodCall);
switch (methodCall.method) {
case 'listen':
await ServicesBinding.instance.defaultBinaryMessenger
.handlePlatformMessage(
methodChannelVideoPlayer
.eventChannelFor(_controller.textureId)
.name,
methodChannelVideoPlayer
.eventChannelFor(_controller.textureId)
.codec
.encodeSuccessEnvelope(
createVideoEvent(isIndefiniteStream: true)),
(_) {});
break;
case 'cancel':
break;
default:
return null;
}
});
});

tearDownAll(() async {
await videoEventStreamSubscription.cancel();
});

test('Indefinite stream', () {

expect(_controller.value.isDurationIndefinite, true);
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,15 @@ void main() {
'dash');
});

test('network with stream', () async {
final VideoPlayerController controller =
VideoPlayerController.network('https://127.0.0.1/indefinite');

await controller.initialize();

expect(controller.value.isDurationIndefinite, true);
});

test('init errors', () async {
final VideoPlayerController controller = VideoPlayerController.network(
'http://testing.com/invalid_url',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform {

@override
Stream<VideoEvent> videoEventsFor(int textureId) {
return _eventChannelFor(textureId)
return eventChannelFor(textureId)
.receiveBroadcastStream()
.map((dynamic event) {
final Map<dynamic, dynamic> map = event;
Expand All @@ -105,6 +105,7 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform {
return VideoEvent(
eventType: VideoEventType.initialized,
duration: Duration(milliseconds: map['duration']),
isDurationIndefinite: map['isDurationIndefinite'],
size: Size(map['width']?.toDouble() ?? 0.0,
map['height']?.toDouble() ?? 0.0),
);
Expand Down Expand Up @@ -141,7 +142,8 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform {
);
}

EventChannel _eventChannelFor(int textureId) {
/// Returns [EventChannel] for a specific texture id
EventChannel eventChannelFor(int textureId) {
return EventChannel('flutter.io/videoPlayer/videoEvents$textureId');
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ class VideoEvent {
VideoEvent({
@required this.eventType,
this.duration,
this.isDurationIndefinite,
this.size,
this.buffered,
});
Expand All @@ -229,6 +230,11 @@ class VideoEvent {
///
/// Only used if [eventType] is [VideoEventType.initialized].
final Duration duration;

/// Determines if the video has a defined duration
///
/// Only used if [eventType] is [VideoEventType.initialized].
final bool isDurationIndefinite;

/// Size of the video.
///
Expand All @@ -247,6 +253,7 @@ class VideoEvent {
runtimeType == other.runtimeType &&
eventType == other.eventType &&
duration == other.duration &&
isDurationIndefinite == other.isDurationIndefinite &&
size == other.size &&
listEquals(buffered, other.buffered);
}
Expand All @@ -255,6 +262,7 @@ class VideoEvent {
int get hashCode =>
eventType.hashCode ^
duration.hashCode ^
isDurationIndefinite.hashCode ^
size.hashCode ^
buffered.hashCode;
}
Expand Down