Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions packages/video_player/lib/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
import 'dart:async';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';

final MethodChannel _channel = const MethodChannel('flutter.io/videoPlayer')
// This will clear all open videos on the platform when a full restart is
// performed.
// This will clear all open videos on the platform when a full restart is
// performed.
..invokeMethod<void>('init');

class DurationRange {
Expand Down Expand Up @@ -88,7 +89,9 @@ class VideoPlayerValue {
final Size size;

bool get initialized => duration != null;

bool get hasError => errorDescription != null;

double get aspectRatio => size != null ? size.width / size.height : 1.0;

VideoPlayerValue copyWith({
Expand Down Expand Up @@ -498,7 +501,11 @@ class _VideoPlayerState extends State<VideoPlayer> {

@override
Widget build(BuildContext context) {
return _textureId == null ? Container() : Texture(textureId: _textureId);
return _textureId == null
? Container()
: !kIsWeb
? Texture(textureId: _textureId)
: HtmlElementView(viewType: _textureId.toString());
}
}

Expand Down
188 changes: 188 additions & 0 deletions packages/video_player/lib/video_player_plugin.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import 'dart:async';
import 'dart:html';
import 'dart:ui' as ui;

import 'package:flutter/services.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';

class VideoPlayer {
VideoPlayer({this.uri, this.eventChannel, this.textureId});

final PluginEventChannel<Map<String, dynamic>> eventChannel;
final StreamController<Map<String, dynamic>> controller =
StreamController<Map<String, dynamic>>();

final Uri uri;
final int textureId;
VideoElement videoElement;
bool isInitialized = false;

Map<String, int> setupVideoPlayer(
PluginEventChannel<Map<String, dynamic>> eventChannel) {
eventChannel.controller = controller;
videoElement = VideoElement()
..src = uri.toString()
..autoplay = false
..controls = false
..style.border = 'none';

ui.platformViewRegistry.registerViewFactory(
textureId.toString(), (int viewId) => videoElement);

videoElement.onCanPlay.listen((dynamic _) {
if (!isInitialized) {
isInitialized = true;
sendInitialized();
}
});
videoElement.onError.listen((dynamic error) {
controller.addError(error);
});
videoElement.onEnded.listen((dynamic _) {
final Map<String, String> event = <String, String>{"event": "completed"};
controller.add(event);
});

final Map<String, int> reply = <String, int>{"textureId": textureId};
return reply;
}

void sendBufferingUpdate() {
// TODO: convert TimeRanges
final Map<String, dynamic> event = <String, dynamic>{
"event": "bufferingUpdate",
"values": videoElement.buffered,
};
controller.add(event);
}

void play() {
videoElement.play();
}

void pause() {
videoElement.pause();
}

void setLooping(bool value) {
videoElement.loop = value;
}

void setVolume(double value) {
videoElement.volume = value;
}

void seekTo(int location) {
videoElement.currentTime = location.toDouble() / 1000;
}

int getPosition() {
final int position = (videoElement.currentTime * 1000).round();
return position;
}

void sendInitialized() {
final Map<String, dynamic> event = <String, dynamic>{
'event': 'initialized',
'duration': videoElement.duration * 1000,
'width': videoElement.videoWidth,
'height': videoElement.videoHeight,
};
controller.add(event);
}

void dispose() {
videoElement.removeAttribute('src');
videoElement.load();
}
}

class VideoPlayerPlugin {
VideoPlayerPlugin(this._registrar);

static void registerWith(Registrar registrar) {
final MethodChannel channel = MethodChannel('flutter.io/videoPlayer',
const StandardMethodCodec(), registrar.messenger);
final VideoPlayerPlugin instance = VideoPlayerPlugin(registrar);
channel.setMethodCallHandler(instance.handleMethodCall);
}

Map<int, VideoPlayer> videoPlayers = <int, VideoPlayer>{};
Registrar _registrar;

int textureCounter = 1;

Future<dynamic> handleMethodCall(MethodCall call) async {
switch (call.method) {
case "init":
// TODO: gracefully handle multiple calls to init
// disposeAllPlayers();
break;
case "create":
final int textureId = textureCounter;
textureCounter++;

final PluginEventChannel<Map<String, dynamic>> eventChannel =
PluginEventChannel<Map<String, dynamic>>(
'flutter.io/videoPlayer/videoEvents$textureId',
const StandardMethodCodec(),
_registrar.messenger);

final VideoPlayer player = VideoPlayer(
uri: Uri.parse(call.arguments['uri']),
eventChannel: eventChannel,
textureId: textureId,
);

final Map<String, int> reply = player.setupVideoPlayer(eventChannel);

videoPlayers[textureId] = player;
return reply;

default:
final int textureId = call.arguments["textureId"];

final VideoPlayer player = videoPlayers[textureId];
if (player == null) {
throw Exception(
"No video player associated with texture id $textureId");
}

return _onMethodCall(call, textureId, player);
}
}

void disposeAllPlayers() {
videoPlayers.forEach((_, VideoPlayer videoPlayer) => videoPlayer.dispose());
videoPlayers.clear();
}

dynamic _onMethodCall(MethodCall call, int textureId, VideoPlayer player) {
switch (call.method) {
case "setLooping":
player.setLooping(call.arguments["looping"]);
return null;
case "setVolume":
player.setVolume(call.arguments["volume"]);
return null;
case "play":
player.play();
return null;
case "pause":
player.pause();
return null;
case "seekTo":
player.seekTo(call.arguments["location"]);
return null;
case "position":
player.sendBufferingUpdate();
return player.getPosition();
case "dispose":
player.dispose();
videoPlayers.remove(textureId);
return null;
default:
throw UnimplementedError();
}
}
}
15 changes: 12 additions & 3 deletions packages/video_player/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,23 @@ homepage: https://github.com/flutter/plugins/tree/master/packages/video_player

flutter:
plugin:
androidPackage: io.flutter.plugins.videoplayer
iosPrefix: FLT
pluginClass: VideoPlayerPlugin
platforms:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't land this before next stable release as the plugin won't compile with the stable tool.

web:
fileName: video_player_plugin.dart
pluginClass: VideoPlayerPlugin
ios:
classPefix: FLT
pluginClass: VideoPlayerPlugin
android:
package: io.flutter.plugins.videoplayer
pluginClass: VideoPlayerPlugin

dependencies:
meta: "^1.0.5"
flutter:
sdk: flutter
flutter_web_plugins:
sdk: flutter

dev_dependencies:
flutter_test:
Expand Down