Skip to content

Commit cdcb1c9

Browse files
cbenhagenEgor
authored andcommitted
[video_player] Add web implementation using platform interface (flutter#2279)
* Add web implementation * Address review comments * Rename setupVideoPlayer() to initialize() * Send correct VideoEventType * Add autoplay note to README.
1 parent 6f9b1e4 commit cdcb1c9

File tree

7 files changed

+396
-0
lines changed

7 files changed

+396
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 0.1.0
2+
3+
* Initial release
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2017 The Chromium Authors. All rights reserved.
2+
//
3+
// Redistribution and use in source and binary forms, with or without
4+
// modification, are permitted provided that the following conditions are
5+
// met:
6+
//
7+
// * Redistributions of source code must retain the above copyright
8+
// notice, this list of conditions and the following disclaimer.
9+
// * Redistributions in binary form must reproduce the above
10+
// copyright notice, this list of conditions and the following disclaimer
11+
// in the documentation and/or other materials provided with the
12+
// distribution.
13+
// * Neither the name of Google Inc. nor the names of its
14+
// contributors may be used to endorse or promote products derived from
15+
// this software without specific prior written permission.
16+
//
17+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18+
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19+
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21+
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22+
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23+
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24+
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25+
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# video_player_web
2+
3+
The web implementation of [`video_player`][1].
4+
5+
## Usage
6+
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`.
12+
13+
```yaml
14+
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
20+
```
21+
22+
Once you have the `video_player_web` dependency in your pubspec, you should
23+
be able to use `package:video_player` as normal.
24+
25+
## Autoplay
26+
Playing videos without prior interaction with the site might be prohibited
27+
by the browser and lead to runtime errors. See also: https://goo.gl/xX8pDD.
28+
29+
[1]: ../video_player
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#
2+
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
3+
#
4+
Pod::Spec.new do |s|
5+
s.name = 'video_player_web'
6+
s.version = '0.0.1'
7+
s.summary = 'No-op implementation of video_player_web web plugin to avoid build issues on iOS'
8+
s.description = <<-DESC
9+
temp fake video_player_web plugin
10+
DESC
11+
s.homepage = 'https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_web'
12+
s.license = { :file => '../LICENSE' }
13+
s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' }
14+
s.source = { :path => '.' }
15+
s.source_files = 'Classes/**/*'
16+
s.public_header_files = 'Classes/**/*.h'
17+
s.dependency 'Flutter'
18+
19+
s.ios.deployment_target = '8.0'
20+
end
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import 'dart:async';
2+
import 'dart:html';
3+
import 'dart:ui' as ui;
4+
5+
import 'package:flutter/material.dart';
6+
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
7+
import 'package:video_player_platform_interface/video_player_platform_interface.dart';
8+
9+
/// The web implementation of [VideoPlayerPlatform].
10+
///
11+
/// This class implements the `package:video_player` functionality for the web.
12+
class VideoPlayerPlugin extends VideoPlayerPlatform {
13+
/// Registers this class as the default instance of [VideoPlayerPlatform].
14+
static void registerWith(Registrar registrar) {
15+
VideoPlayerPlatform.instance = VideoPlayerPlugin();
16+
}
17+
18+
Map<int, _VideoPlayer> _videoPlayers = <int, _VideoPlayer>{};
19+
20+
int _textureCounter = 1;
21+
22+
@override
23+
Future<void> init() async {
24+
return _disposeAllPlayers();
25+
}
26+
27+
@override
28+
Future<void> dispose(int textureId) async {
29+
_videoPlayers[textureId].dispose();
30+
_videoPlayers.remove(textureId);
31+
return null;
32+
}
33+
34+
void _disposeAllPlayers() {
35+
_videoPlayers.values
36+
.forEach((_VideoPlayer videoPlayer) => videoPlayer.dispose());
37+
_videoPlayers.clear();
38+
}
39+
40+
@override
41+
Future<int> create(DataSource dataSource) async {
42+
final int textureId = _textureCounter;
43+
_textureCounter++;
44+
45+
final _VideoPlayer player = _VideoPlayer(
46+
uri: Uri.parse(dataSource.uri),
47+
textureId: textureId,
48+
);
49+
50+
player.initialize();
51+
52+
_videoPlayers[textureId] = player;
53+
return textureId;
54+
}
55+
56+
@override
57+
Future<void> setLooping(int textureId, bool looping) async {
58+
return _videoPlayers[textureId].setLooping(looping);
59+
}
60+
61+
@override
62+
Future<void> play(int textureId) async {
63+
return _videoPlayers[textureId].play();
64+
}
65+
66+
@override
67+
Future<void> pause(int textureId) async {
68+
return _videoPlayers[textureId].pause();
69+
}
70+
71+
@override
72+
Future<void> setVolume(int textureId, double volume) async {
73+
return _videoPlayers[textureId].setVolume(volume);
74+
}
75+
76+
@override
77+
Future<void> seekTo(int textureId, Duration position) async {
78+
return _videoPlayers[textureId].seekTo(position);
79+
}
80+
81+
@override
82+
Future<Duration> getPosition(int textureId) async {
83+
_videoPlayers[textureId].sendBufferingUpdate();
84+
return _videoPlayers[textureId].getPosition();
85+
}
86+
87+
@override
88+
Stream<VideoEvent> videoEventsFor(int textureId) {
89+
return _videoPlayers[textureId].eventController.stream;
90+
}
91+
92+
@override
93+
Widget buildView(int textureId) {
94+
return HtmlElementView(viewType: 'videoPlayer-$textureId');
95+
}
96+
}
97+
98+
class _VideoPlayer {
99+
_VideoPlayer({this.uri, this.textureId});
100+
101+
final StreamController<VideoEvent> eventController =
102+
StreamController<VideoEvent>();
103+
104+
final Uri uri;
105+
final int textureId;
106+
VideoElement videoElement;
107+
bool isInitialized = false;
108+
109+
void initialize() {
110+
videoElement = VideoElement()
111+
..src = uri.toString()
112+
..autoplay = false
113+
..controls = false
114+
..style.border = 'none';
115+
116+
// TODO(hterkelsen): Use initialization parameters once they are available
117+
// ignore: undefined_prefixed_name
118+
ui.platformViewRegistry.registerViewFactory(
119+
'videoPlayer-$textureId', (int viewId) => videoElement);
120+
121+
videoElement.onCanPlay.listen((dynamic _) {
122+
if (!isInitialized) {
123+
isInitialized = true;
124+
sendInitialized();
125+
}
126+
});
127+
videoElement.onError.listen((dynamic error) {
128+
eventController.addError(error);
129+
});
130+
videoElement.onEnded.listen((dynamic _) {
131+
eventController.add(VideoEvent(eventType: VideoEventType.completed));
132+
});
133+
}
134+
135+
void sendBufferingUpdate() {
136+
eventController.add(VideoEvent(
137+
buffered: _toDurationRange(videoElement.buffered),
138+
eventType: VideoEventType.bufferingUpdate,
139+
));
140+
}
141+
142+
void play() {
143+
videoElement.play();
144+
}
145+
146+
void pause() {
147+
videoElement.pause();
148+
}
149+
150+
void setLooping(bool value) {
151+
videoElement.loop = value;
152+
}
153+
154+
void setVolume(double value) {
155+
videoElement.volume = value;
156+
}
157+
158+
void seekTo(Duration position) {
159+
videoElement.currentTime = position.inMilliseconds.toDouble() / 1000;
160+
}
161+
162+
Duration getPosition() {
163+
return Duration(milliseconds: (videoElement.currentTime * 1000).round());
164+
}
165+
166+
void sendInitialized() {
167+
eventController.add(
168+
VideoEvent(
169+
eventType: VideoEventType.initialized,
170+
duration: Duration(
171+
milliseconds: (videoElement.duration * 1000).round(),
172+
),
173+
size: Size(
174+
videoElement.videoWidth.toDouble() ?? 0.0,
175+
videoElement.videoHeight.toDouble() ?? 0.0,
176+
),
177+
),
178+
);
179+
}
180+
181+
void dispose() {
182+
videoElement.removeAttribute('src');
183+
videoElement.load();
184+
}
185+
186+
List<DurationRange> _toDurationRange(TimeRanges buffered) {
187+
final List<DurationRange> durationRange = <DurationRange>[];
188+
for (int i = 0; i < buffered.length; i++) {
189+
durationRange.add(DurationRange(
190+
Duration(milliseconds: (buffered.start(i) * 1000).round()),
191+
Duration(milliseconds: (buffered.end(i) * 1000).round()),
192+
));
193+
}
194+
return durationRange;
195+
}
196+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: video_player_web
2+
description: Web platform implementation of video_player
3+
author: Flutter Team <flutter-dev@googlegroups.com>
4+
homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_web
5+
version: 0.1.0
6+
7+
flutter:
8+
plugin:
9+
platforms:
10+
web:
11+
pluginClass: VideoPlayerPlugin
12+
fileName: video_player_web.dart
13+
14+
dependencies:
15+
flutter:
16+
sdk: flutter
17+
flutter_web_plugins:
18+
sdk: flutter
19+
meta: ^1.1.7
20+
video_player_platform_interface: ^1.0.0
21+
22+
dev_dependencies:
23+
flutter_test:
24+
sdk: flutter
25+
video_player:
26+
path: ../video_player
27+
28+
environment:
29+
sdk: ">=2.0.0-dev.28.0 <3.0.0"
30+
flutter: ">=1.5.0 <2.0.0"

0 commit comments

Comments
 (0)