[video_player_web, video_player] Detect DOM playback/init errors and convert them to PlatformExceptions (#2483)
video_player_web:
* Add a `PlatformException` to the event Stream `onError`, instead of the Event coming straight from the DOM.
* Handle videoElement.play() rejection. Funnel that as a PlatformException to the event Stream as well.
* Update documentation about web-specific _quirks and features_.
video_player:
* Add documentation about the web platform.
diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md
index 1c5bc50..2c9c5dd 100644
--- a/packages/video_player/video_player/CHANGELOG.md
+++ b/packages/video_player/video_player/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.10.5+3
+
+* Add integration instructions for the `web` platform.
+
## 0.10.5+2
* Make sure the plugin is correctly initialized
diff --git a/packages/video_player/video_player/README.md b/packages/video_player/video_player/README.md
index eae8f6c..1b9eb80 100644
--- a/packages/video_player/video_player/README.md
+++ b/packages/video_player/video_player/README.md
@@ -40,15 +40,24 @@
The Flutter project template adds it, so it may already be there.
-### Supported Formats
+### Web
+
+This plugin compiles for the web platform since version `0.10.5`, in recent enough versions of Flutter (`>=1.12.13+hotfix.4`).
+
+> The Web platform does **not** suppport `dart:io`, so attempts to create a `VideoPlayerController.file` will throw an `UnimplementedError`.
+
+Different web browsers may have different video-playback capabilities (supported formats, autoplay...). Check [package:video_player_web](https://pub.dev/packages/video_player_web) for more web-specific information.
+
+## Supported Formats
- On iOS, the backing player is [AVPlayer](https://developer.apple.com/documentation/avfoundation/avplayer).
The supported formats vary depending on the version of iOS, [AVURLAsset](https://developer.apple.com/documentation/avfoundation/avurlasset) class
has [audiovisualTypes](https://developer.apple.com/documentation/avfoundation/avurlasset/1386800-audiovisualtypes?language=objc) that you can query for supported av formats.
- On Android, the backing player is [ExoPlayer](https://google.github.io/ExoPlayer/),
please refer [here](https://google.github.io/ExoPlayer/supported-formats.html) for list of supported formats.
+- On Web, available formats depend on your users' browsers (vendor and version). Check [package:video_player_web](https://pub.dev/packages/video_player_web) for more specific information.
-### Example
+## Example
```dart
import 'package:video_player/video_player.dart';
diff --git a/packages/video_player/video_player/example/.gitignore b/packages/video_player/video_player/example/.gitignore
new file mode 100644
index 0000000..d3e68fd
--- /dev/null
+++ b/packages/video_player/video_player/example/.gitignore
@@ -0,0 +1 @@
+lib/generated_plugin_registrant.dart
diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml
index 691acc1..724f066 100644
--- a/packages/video_player/video_player/pubspec.yaml
+++ b/packages/video_player/video_player/pubspec.yaml
@@ -1,7 +1,7 @@
name: video_player
description: Flutter plugin for displaying inline video with other Flutter
widgets on Android and iOS.
-version: 0.10.5+2
+version: 0.10.5+3
homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player
flutter:
diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md
index 951e8ce..4c3aaa7 100644
--- a/packages/video_player/video_player_web/CHANGELOG.md
+++ b/packages/video_player/video_player_web/CHANGELOG.md
@@ -1,6 +1,12 @@
+## 0.1.2
+
+* Add a `PlatformException` to the player's `eventController` when there's a `videoElement.onError`. Fixes https://github.com/flutter/flutter/issues/48884.
+* Handle DomExceptions on videoElement.play() and turn them into `PlatformException` as well, so we don't end up with unhandled Futures.
+* Update setup instructions in the README.
+
## 0.1.1+1
-- Add an android/ folder with no-op implementation to workaround https://github.com/flutter/flutter/issues/46898.
+* Add an android/ folder with no-op implementation to workaround https://github.com/flutter/flutter/issues/46898.
## 0.1.1
diff --git a/packages/video_player/video_player_web/README.md b/packages/video_player/video_player_web/README.md
index b8a441c..2e63d5e 100644
--- a/packages/video_player/video_player_web/README.md
+++ b/packages/video_player/video_player_web/README.md
@@ -4,26 +4,57 @@
## Usage
-To use this plugin in your Flutter Web app, simply add it as a dependency in
-your pubspec using a `git` dependency. This is only temporary: in the future
-we hope to make this package an "endorsed" implementation of `video_player`,
-so that it is automatically included in your Flutter Web app when you depend
-on `package:video_player`.
+This package is the endorsed implementation of `video_player` for the web platform since version `0.10.5`, so it gets automatically added to your application by depending on `video_player: ^0.10.5`.
+
+No further modifications to your `pubspec.yaml` should be required in a recent enough version of Flutter (`>=1.12.13+hotfix.4`):
```yaml
+...
dependencies:
- video_player: ^0.10.4
- video_player_web:
- git:
- url: git://github.com/flutter/plugins.git
- path: packages/video_player/video_player_web
+ ...
+ video_player: ^0.10.5
+ ...
```
-Once you have the `video_player_web` dependency in your pubspec, you should
-be able to use `package:video_player` as normal.
+Once you have the correct `video_player` dependency in your pubspec, you should
+be able to use `package:video_player` as normal, even from your web code.
+
+## dart:io
+
+The Web platform does **not** suppport `dart:io`, so attempts to create a `VideoPlayerController.file` will throw an `UnimplementedError`.
## Autoplay
Playing videos without prior interaction with the site might be prohibited
by the browser and lead to runtime errors. See also: https://goo.gl/xX8pDD.
+## Supported Formats
+
+**Different web browsers support different sets of video codecs.**
+
+### Video codecs?
+
+Check MDN's [**Web video codec guide**](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs) to learn more about the pros and cons of each video codec.
+
+### What codecs are supported?
+
+Visit [**caniuse.com: 'video format'**](https://caniuse.com/#search=video%20format) for a breakdown of which browsers support what codecs. You can customize charts there for the users of your particular website(s).
+
+Here's an abridged version of the data from caniuse, for a Global audience:
+
+#### MPEG-4/H.264
+[![Data on Global support for the MPEG-4/H.264 video format](https://caniuse.bitsofco.de/image/mpeg4.png)](https://caniuse.com/#feat=mpeg4)
+
+#### WebM
+[![Data on Global support for the WebM video format](https://caniuse.bitsofco.de/image/webm.png)](https://caniuse.com/#feat=webm)
+
+#### Ogg/Theora
+[![Data on Global support for the Ogg/Theora video format](https://caniuse.bitsofco.de/image/ogv.png)](https://caniuse.com/#feat=ogv)
+
+#### AV1
+[![Data on Global support for the AV1 video format](https://caniuse.bitsofco.de/image/av1.png)](https://caniuse.com/#feat=av1)
+
+#### HEVC/H.265
+[![Data on Global support for the HEVC/H.265 video format](https://caniuse.bitsofco.de/image/hevc.png)](https://caniuse.com/#feat=hevc)
+
+
[1]: ../video_player
\ No newline at end of file
diff --git a/packages/video_player/video_player_web/lib/video_player_web.dart b/packages/video_player/video_player_web/lib/video_player_web.dart
index 070e81f..039c3ce 100644
--- a/packages/video_player/video_player_web/lib/video_player_web.dart
+++ b/packages/video_player/video_player_web/lib/video_player_web.dart
@@ -3,9 +3,33 @@
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:video_player_platform_interface/video_player_platform_interface.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 = {
+ 1: 'MEDIA_ERR_ABORTED',
+ 2: 'MEDIA_ERR_NETWORK',
+ 3: 'MEDIA_ERR_DECODE',
+ 4: 'MEDIA_ERR_SRC_NOT_SUPPORTED',
+};
+
+// An error code value to description Map.
+// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code
+const Map<int, String> _kErrorValueToErrorDescription = {
+ 1: 'The user canceled the fetching of the video.',
+ 2: 'A network error occurred while fetching the video, despite having previously been available.',
+ 3: 'An error occurred while trying to decode the video, despite having previously been determined to be usable.',
+ 4: 'The video has been found to be unsuitable (missing or in a format not supported by your browser).',
+};
+
+// The default error message, when the error is an empty string
+// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/message
+const String _kDefaultErrorMessage =
+ 'No further diagnostic information can be determined or provided.';
+
/// The web implementation of [VideoPlayerPlatform].
///
/// This class implements the `package:video_player` functionality for the web.
@@ -144,9 +168,20 @@
sendInitialized();
}
});
- videoElement.onError.listen((dynamic error) {
- eventController.addError(error);
+
+ // The error event fires when some form of error occurs while attempting to load or perform the media.
+ videoElement.onError.listen((Event _) {
+ // The Event itself (_) doesn't contain info about the actual error.
+ // We need to look at the HTMLMediaElement.error.
+ // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error
+ MediaError error = videoElement.error;
+ eventController.addError(PlatformException(
+ code: _kErrorValueToErrorName[error.code],
+ message: error.message != '' ? error.message : _kDefaultErrorMessage,
+ details: _kErrorValueToErrorDescription[error.code],
+ ));
});
+
videoElement.onEnded.listen((dynamic _) {
eventController.add(VideoEvent(eventType: VideoEventType.completed));
});
@@ -159,8 +194,19 @@
));
}
- void play() {
- videoElement.play();
+ Future<void> play() {
+ return videoElement.play().catchError((e) {
+ // play() attempts to begin playback of the media. It returns
+ // a Promise which can get rejected in case of failure to begin
+ // playback for any reason, such as permission issues.
+ // The rejection handler is called with a DomException.
+ // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play
+ DomException exception = e;
+ eventController.addError(PlatformException(
+ code: exception.name,
+ message: exception.message,
+ ));
+ }, test: (e) => e is DomException);
}
void pause() {
diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml
index ceeea94..4358f82 100644
--- a/packages/video_player/video_player_web/pubspec.yaml
+++ b/packages/video_player/video_player_web/pubspec.yaml
@@ -1,7 +1,7 @@
name: video_player_web
description: Web platform implementation of video_player
homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_web
-version: 0.1.1+1
+version: 0.1.2
flutter:
plugin:
diff --git a/packages/video_player/video_player_web/test/video_player_web_test.dart b/packages/video_player/video_player_web/test/video_player_web_test.dart
index ae35c30..ef6dc02 100644
--- a/packages/video_player/video_player_web/test/video_player_web_test.dart
+++ b/packages/video_player/video_player_web/test/video_player_web_test.dart
@@ -5,6 +5,7 @@
import 'dart:async';
+import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:video_player/video_player.dart';
@@ -82,6 +83,24 @@
expect(VideoPlayerPlatform.instance.play(textureId), completes);
});
+ test('throws PlatformException when playing bad media', () async {
+ int videoPlayerId = await VideoPlayerPlatform.instance.create(
+ DataSource(
+ sourceType: DataSourceType.network,
+ uri:
+ 'https://flutter.github.io/assets-for-api-docs/assets/videos/_non_existent_video.mp4'),
+ );
+
+ Stream<VideoEvent> eventStream =
+ VideoPlayerPlatform.instance.videoEventsFor(videoPlayerId);
+
+ // Mute video to allow autoplay (See https://goo.gl/xX8pDD)
+ await VideoPlayerPlatform.instance.setVolume(videoPlayerId, 0);
+ await VideoPlayerPlatform.instance.play(videoPlayerId);
+
+ expect(eventStream, emitsError(isA<PlatformException>()));
+ });
+
test('can pause', () {
expect(VideoPlayerPlatform.instance.pause(textureId), completes);
});