Skip to content

Commit

Permalink
[video_player_web] Improve handling of "Infinite" videos. (flutter#6101)
Browse files Browse the repository at this point in the history
  • Loading branch information
ditman authored Jul 20, 2022
1 parent 1e6b8fa commit 8a7222f
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 7 deletions.
4 changes: 4 additions & 0 deletions packages/video_player/video_player_web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.0.11

* Improves handling of videos with `Infinity` duration.

## 2.0.10

* Minor fixes for new analysis options.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:video_player_web/src/duration_utils.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

group('convertNumVideoDurationToPluginDuration', () {
testWidgets('Finite value converts to milliseconds',
(WidgetTester _) async {
final Duration? result = convertNumVideoDurationToPluginDuration(1.5);
final Duration? zero = convertNumVideoDurationToPluginDuration(0.0001);

expect(result, isNotNull);
expect(result!.inMilliseconds, equals(1500));
expect(zero, equals(Duration.zero));
});

testWidgets('Finite value rounds 3rd decimal value',
(WidgetTester _) async {
final Duration? result =
convertNumVideoDurationToPluginDuration(1.567899089087);
final Duration? another =
convertNumVideoDurationToPluginDuration(1.567199089087);

expect(result, isNotNull);
expect(result!.inMilliseconds, equals(1568));
expect(another!.inMilliseconds, equals(1567));
});

testWidgets('Infinite value returns magic constant',
(WidgetTester _) async {
final Duration? result =
convertNumVideoDurationToPluginDuration(double.infinity);

expect(result, isNotNull);
expect(result, equals(jsCompatibleTimeUnset));
expect(result!.inMilliseconds, equals(-9007199254740990));
});

testWidgets('NaN value returns null', (WidgetTester _) async {
final Duration? result =
convertNumVideoDurationToPluginDuration(double.nan);

expect(result, isNull);
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

@JS()
library integration_test_utils;

import 'dart:html';

import 'package:js/js.dart';

// Returns the URL to load an asset from this example app as a network source.
//
// TODO(stuartmorgan): Convert this to a local `HttpServer` that vends the
Expand All @@ -14,3 +21,36 @@ String getUrlForAssetAsNetworkSource(String assetKey) {
'$assetKey'
'?raw=true';
}

@JS()
@anonymous
class _Descriptor {
// May also contain "configurable" and "enumerable" bools.
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#description
external factory _Descriptor({
// bool configurable,
// bool enumerable,
bool writable,
Object value,
});
}

@JS('Object.defineProperty')
external void _defineProperty(
Object object,
String property,
_Descriptor description,
);

/// Forces a VideoElement to report "Infinity" duration.
///
/// Uses JS Object.defineProperty to set the value of a readonly property.
void setInfinityDuration(VideoElement element) {
_defineProperty(
element,
'duration',
_Descriptor(
writable: true,
value: double.infinity,
));
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import 'dart:html' as html;
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:video_player_platform_interface/video_player_platform_interface.dart';
import 'package:video_player_web/src/duration_utils.dart';
import 'package:video_player_web/src/video_player.dart';

import 'utils.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

Expand Down Expand Up @@ -190,6 +193,25 @@ void main() {
expect(events, hasLength(1));
expect(events[0].eventType, VideoEventType.initialized);
});

// Issue: https://github.com/flutter/flutter/issues/105649
testWidgets('supports `Infinity` duration', (WidgetTester _) async {
setInfinityDuration(video);
expect(video.duration.isInfinite, isTrue);

final Future<List<VideoEvent>> stream = timedStream
.where((VideoEvent event) =>
event.eventType == VideoEventType.initialized)
.toList();

video.dispatchEvent(html.Event('canplay'));

final List<VideoEvent> events = await stream;

expect(events, hasLength(1));
expect(events[0].eventType, VideoEventType.initialized);
expect(events[0].duration, equals(jsCompatibleTimeUnset));
});
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ environment:
dependencies:
flutter:
sdk: flutter
js: ^0.6.0
video_player_web:
path: ../

Expand Down
33 changes: 33 additions & 0 deletions packages/video_player/video_player_web/lib/src/duration_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/// The "length" of a video which doesn't have finite duration.
// See: https://github.com/flutter/flutter/issues/107882
const Duration jsCompatibleTimeUnset = Duration(
milliseconds: -9007199254740990, // Number.MIN_SAFE_INTEGER + 1. -(2^53 - 1)
);

/// Converts a `num` duration coming from a [VideoElement] into a [Duration] that
/// the plugin can use.
///
/// From the documentation, `videoDuration` is "a double-precision floating-point
/// value indicating the duration of the media in seconds.
/// If no media data is available, the value `NaN` is returned.
/// If the element's media doesn't have a known duration —such as for live media
/// streams— the value of duration is `+Infinity`."
///
/// If the `videoDuration` is finite, this method returns it as a `Duration`.
/// If the `videoDuration` is `Infinity`, the duration will be
/// `-9007199254740990` milliseconds. (See https://github.com/flutter/flutter/issues/107882)
/// If the `videoDuration` is `NaN`, this will return null.
Duration? convertNumVideoDurationToPluginDuration(num duration) {
if (duration.isFinite) {
return Duration(
milliseconds: (duration * 1000).round(),
);
} else if (duration.isInfinite) {
return jsCompatibleTimeUnset;
}
return null;
}
11 changes: 5 additions & 6 deletions packages/video_player/video_player_web/lib/src/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:video_player_platform_interface/video_player_platform_interface.dart';

import 'duration_utils.dart';

// An error code value to error name Map.
// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code
const Map<int, String> _kErrorValueToErrorName = <int, String>{
Expand Down Expand Up @@ -194,13 +196,10 @@ class VideoPlayer {

// Sends an [VideoEventType.initialized] [VideoEvent] with info about the wrapped video.
void _sendInitialized() {
final Duration? duration = !_videoElement.duration.isNaN
? Duration(
milliseconds: (_videoElement.duration * 1000).round(),
)
: null;
final Duration? duration =
convertNumVideoDurationToPluginDuration(_videoElement.duration);

final Size? size = !_videoElement.videoHeight.isNaN
final Size? size = _videoElement.videoHeight.isFinite
? Size(
_videoElement.videoWidth.toDouble(),
_videoElement.videoHeight.toDouble(),
Expand Down
2 changes: 1 addition & 1 deletion packages/video_player/video_player_web/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: video_player_web
description: Web platform implementation of video_player.
repository: https://github.com/flutter/plugins/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.10
version: 2.0.11

environment:
sdk: ">=2.12.0 <3.0.0"
Expand Down

0 comments on commit 8a7222f

Please sign in to comment.