[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);
     });