Skip to content

Commit

Permalink
[video_player_web, video_player] Detect DOM playback/init errors and …
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
ditman authored and WoodyGuo committed Feb 10, 2020
1 parent 9bedc42 commit 054192b
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 21 deletions.
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.10.5+3

* Add integration instructions for the `web` platform.

## 0.10.5+2

* Make sure the plugin is correctly initialized
Expand Down
13 changes: 11 additions & 2 deletions packages/video_player/video_player/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,24 @@ Ensure the following permission is present in your Android Manifest file, locate

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';
Expand Down
1 change: 1 addition & 0 deletions packages/video_player/video_player/example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib/generated_plugin_registrant.dart
2 changes: 1 addition & 1 deletion packages/video_player/video_player/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
8 changes: 7 additions & 1 deletion packages/video_player/video_player_web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
55 changes: 43 additions & 12 deletions packages/video_player/video_player_web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,57 @@ The web implementation of [`video_player`][1].

## 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
54 changes: 50 additions & 4 deletions packages/video_player/video_player_web/lib/video_player_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,33 @@ import 'dart:html';
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.
Expand Down Expand Up @@ -144,9 +168,20 @@ class _VideoPlayer {
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));
});
Expand All @@ -159,8 +194,19 @@ class _VideoPlayer {
));
}

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() {
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
@@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -82,6 +83,24 @@ void main() {
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);
});
Expand Down

0 comments on commit 054192b

Please sign in to comment.