Skip to content

Commit 0fa7230

Browse files
authored
[video_player_web, video_player] Detect DOM playback/init errors and convert them to PlatformExceptions (flutter#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.
1 parent ec69971 commit 0fa7230

File tree

9 files changed

+137
-21
lines changed

9 files changed

+137
-21
lines changed

packages/video_player/video_player/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.10.5+3
2+
3+
* Add integration instructions for the `web` platform.
4+
15
## 0.10.5+2
26

37
* Make sure the plugin is correctly initialized

packages/video_player/video_player/README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,24 @@ Ensure the following permission is present in your Android Manifest file, locate
4040

4141
The Flutter project template adds it, so it may already be there.
4242

43-
### Supported Formats
43+
### Web
44+
45+
This plugin compiles for the web platform since version `0.10.5`, in recent enough versions of Flutter (`>=1.12.13+hotfix.4`).
46+
47+
> The Web platform does **not** suppport `dart:io`, so attempts to create a `VideoPlayerController.file` will throw an `UnimplementedError`.
48+
49+
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.
50+
51+
## Supported Formats
4452

4553
- On iOS, the backing player is [AVPlayer](https://developer.apple.com/documentation/avfoundation/avplayer).
4654
The supported formats vary depending on the version of iOS, [AVURLAsset](https://developer.apple.com/documentation/avfoundation/avurlasset) class
4755
has [audiovisualTypes](https://developer.apple.com/documentation/avfoundation/avurlasset/1386800-audiovisualtypes?language=objc) that you can query for supported av formats.
4856
- On Android, the backing player is [ExoPlayer](https://google.github.io/ExoPlayer/),
4957
please refer [here](https://google.github.io/ExoPlayer/supported-formats.html) for list of supported formats.
58+
- 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.
5059

51-
### Example
60+
## Example
5261

5362
```dart
5463
import 'package:video_player/video_player.dart';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lib/generated_plugin_registrant.dart

packages/video_player/video_player/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: video_player
22
description: Flutter plugin for displaying inline video with other Flutter
33
widgets on Android and iOS.
4-
version: 0.10.5+2
4+
version: 0.10.5+3
55
homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player
66

77
flutter:

packages/video_player/video_player_web/CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
## 0.1.2
2+
3+
* Add a `PlatformException` to the player's `eventController` when there's a `videoElement.onError`. Fixes https://github.com/flutter/flutter/issues/48884.
4+
* Handle DomExceptions on videoElement.play() and turn them into `PlatformException` as well, so we don't end up with unhandled Futures.
5+
* Update setup instructions in the README.
6+
17
## 0.1.1+1
28

3-
- Add an android/ folder with no-op implementation to workaround https://github.com/flutter/flutter/issues/46898.
9+
* Add an android/ folder with no-op implementation to workaround https://github.com/flutter/flutter/issues/46898.
410

511
## 0.1.1
612

packages/video_player/video_player_web/README.md

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,57 @@ The web implementation of [`video_player`][1].
44

55
## Usage
66

7-
To use this plugin in your Flutter Web app, simply add it as a dependency in
8-
your pubspec using a `git` dependency. This is only temporary: in the future
9-
we hope to make this package an "endorsed" implementation of `video_player`,
10-
so that it is automatically included in your Flutter Web app when you depend
11-
on `package:video_player`.
7+
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`.
8+
9+
No further modifications to your `pubspec.yaml` should be required in a recent enough version of Flutter (`>=1.12.13+hotfix.4`):
1210

1311
```yaml
12+
...
1413
dependencies:
15-
video_player: ^0.10.4
16-
video_player_web:
17-
git:
18-
url: git://github.com/flutter/plugins.git
19-
path: packages/video_player/video_player_web
14+
...
15+
video_player: ^0.10.5
16+
...
2017
```
2118

22-
Once you have the `video_player_web` dependency in your pubspec, you should
23-
be able to use `package:video_player` as normal.
19+
Once you have the correct `video_player` dependency in your pubspec, you should
20+
be able to use `package:video_player` as normal, even from your web code.
21+
22+
## dart:io
23+
24+
The Web platform does **not** suppport `dart:io`, so attempts to create a `VideoPlayerController.file` will throw an `UnimplementedError`.
2425

2526
## Autoplay
2627
Playing videos without prior interaction with the site might be prohibited
2728
by the browser and lead to runtime errors. See also: https://goo.gl/xX8pDD.
2829

30+
## Supported Formats
31+
32+
**Different web browsers support different sets of video codecs.**
33+
34+
### Video codecs?
35+
36+
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.
37+
38+
### What codecs are supported?
39+
40+
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).
41+
42+
Here's an abridged version of the data from caniuse, for a Global audience:
43+
44+
#### MPEG-4/H.264
45+
[![Data on Global support for the MPEG-4/H.264 video format](https://caniuse.bitsofco.de/image/mpeg4.png)](https://caniuse.com/#feat=mpeg4)
46+
47+
#### WebM
48+
[![Data on Global support for the WebM video format](https://caniuse.bitsofco.de/image/webm.png)](https://caniuse.com/#feat=webm)
49+
50+
#### Ogg/Theora
51+
[![Data on Global support for the Ogg/Theora video format](https://caniuse.bitsofco.de/image/ogv.png)](https://caniuse.com/#feat=ogv)
52+
53+
#### AV1
54+
[![Data on Global support for the AV1 video format](https://caniuse.bitsofco.de/image/av1.png)](https://caniuse.com/#feat=av1)
55+
56+
#### HEVC/H.265
57+
[![Data on Global support for the HEVC/H.265 video format](https://caniuse.bitsofco.de/image/hevc.png)](https://caniuse.com/#feat=hevc)
58+
59+
2960
[1]: ../video_player

packages/video_player/video_player_web/lib/video_player_web.dart

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,33 @@ import 'dart:html';
33
import 'dart:ui' as ui;
44

55
import 'package:flutter/material.dart';
6+
import 'package:flutter/services.dart';
67
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
78
import 'package:video_player_platform_interface/video_player_platform_interface.dart';
89

10+
// An error code value to error name Map.
11+
// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code
12+
const Map<int, String> _kErrorValueToErrorName = {
13+
1: 'MEDIA_ERR_ABORTED',
14+
2: 'MEDIA_ERR_NETWORK',
15+
3: 'MEDIA_ERR_DECODE',
16+
4: 'MEDIA_ERR_SRC_NOT_SUPPORTED',
17+
};
18+
19+
// An error code value to description Map.
20+
// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code
21+
const Map<int, String> _kErrorValueToErrorDescription = {
22+
1: 'The user canceled the fetching of the video.',
23+
2: 'A network error occurred while fetching the video, despite having previously been available.',
24+
3: 'An error occurred while trying to decode the video, despite having previously been determined to be usable.',
25+
4: 'The video has been found to be unsuitable (missing or in a format not supported by your browser).',
26+
};
27+
28+
// The default error message, when the error is an empty string
29+
// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/message
30+
const String _kDefaultErrorMessage =
31+
'No further diagnostic information can be determined or provided.';
32+
933
/// The web implementation of [VideoPlayerPlatform].
1034
///
1135
/// This class implements the `package:video_player` functionality for the web.
@@ -144,9 +168,20 @@ class _VideoPlayer {
144168
sendInitialized();
145169
}
146170
});
147-
videoElement.onError.listen((dynamic error) {
148-
eventController.addError(error);
171+
172+
// The error event fires when some form of error occurs while attempting to load or perform the media.
173+
videoElement.onError.listen((Event _) {
174+
// The Event itself (_) doesn't contain info about the actual error.
175+
// We need to look at the HTMLMediaElement.error.
176+
// See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error
177+
MediaError error = videoElement.error;
178+
eventController.addError(PlatformException(
179+
code: _kErrorValueToErrorName[error.code],
180+
message: error.message != '' ? error.message : _kDefaultErrorMessage,
181+
details: _kErrorValueToErrorDescription[error.code],
182+
));
149183
});
184+
150185
videoElement.onEnded.listen((dynamic _) {
151186
eventController.add(VideoEvent(eventType: VideoEventType.completed));
152187
});
@@ -159,8 +194,19 @@ class _VideoPlayer {
159194
));
160195
}
161196

162-
void play() {
163-
videoElement.play();
197+
Future<void> play() {
198+
return videoElement.play().catchError((e) {
199+
// play() attempts to begin playback of the media. It returns
200+
// a Promise which can get rejected in case of failure to begin
201+
// playback for any reason, such as permission issues.
202+
// The rejection handler is called with a DomException.
203+
// See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play
204+
DomException exception = e;
205+
eventController.addError(PlatformException(
206+
code: exception.name,
207+
message: exception.message,
208+
));
209+
}, test: (e) => e is DomException);
164210
}
165211

166212
void pause() {

packages/video_player/video_player_web/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: video_player_web
22
description: Web platform implementation of video_player
33
homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_web
4-
version: 0.1.1+1
4+
version: 0.1.2
55

66
flutter:
77
plugin:

packages/video_player/video_player_web/test/video_player_web_test.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import 'dart:async';
77

8+
import 'package:flutter/services.dart';
89
import 'package:flutter/widgets.dart';
910
import 'package:flutter_test/flutter_test.dart';
1011
import 'package:video_player/video_player.dart';
@@ -82,6 +83,24 @@ void main() {
8283
expect(VideoPlayerPlatform.instance.play(textureId), completes);
8384
});
8485

86+
test('throws PlatformException when playing bad media', () async {
87+
int videoPlayerId = await VideoPlayerPlatform.instance.create(
88+
DataSource(
89+
sourceType: DataSourceType.network,
90+
uri:
91+
'https://flutter.github.io/assets-for-api-docs/assets/videos/_non_existent_video.mp4'),
92+
);
93+
94+
Stream<VideoEvent> eventStream =
95+
VideoPlayerPlatform.instance.videoEventsFor(videoPlayerId);
96+
97+
// Mute video to allow autoplay (See https://goo.gl/xX8pDD)
98+
await VideoPlayerPlatform.instance.setVolume(videoPlayerId, 0);
99+
await VideoPlayerPlatform.instance.play(videoPlayerId);
100+
101+
expect(eventStream, emitsError(isA<PlatformException>()));
102+
});
103+
85104
test('can pause', () {
86105
expect(VideoPlayerPlatform.instance.pause(textureId), completes);
87106
});

0 commit comments

Comments
 (0)