From f0e0e555dd5c49f8d94540a3486bfec8c45f14d9 Mon Sep 17 00:00:00 2001 From: Hampton Maxwell Date: Fri, 12 Oct 2018 13:41:01 -0700 Subject: [PATCH] Add id prop & autoplay handling, support DASH & HLS via Shaka Player --- dom/RCTVideo.js | 82 +++++++++++++++++++++++++++--------------- dom/RCTVideoManager.js | 10 ++++++ package.json | 5 +-- 3 files changed, 66 insertions(+), 31 deletions(-) diff --git a/dom/RCTVideo.js b/dom/RCTVideo.js index b249721f..9831b61d 100644 --- a/dom/RCTVideo.js +++ b/dom/RCTVideo.js @@ -1,6 +1,7 @@ // @flow import { RCTEvent, RCTView, type RCTBridge } from "react-native-dom"; +import shaka from "shaka-player"; import resizeModes from "./resizeModes"; import type { VideoSource } from "./types"; @@ -25,6 +26,8 @@ class RCTVideo extends RCTView { this.eventDispatcher = bridge.getModuleByName("EventDispatcher"); + shaka.polyfill.installAll(); + this.onEnd = this.onEnd.bind(this); this.onLoad = this.onLoad.bind(this); this.onLoadStart = this.onLoadStart.bind(this); @@ -37,11 +40,11 @@ class RCTVideo extends RCTView { this.videoElement.addEventListener("loadstart", this.onLoadStart); this.videoElement.addEventListener("pause", this.onPause); this.videoElement.addEventListener("play", this.onPlay); + this.player = new shaka.Player(this.videoElement); this.muted = false; this.rate = 1.0; this.volume = 1.0; - this.videoElement.autoplay = true; this.childContainer.appendChild(this.videoElement); } @@ -71,36 +74,28 @@ class RCTVideo extends RCTView { } presentFullscreenPlayer() { - console.log("V PF"); this.videoElement.webkitRequestFullScreen(); } set controls(value: boolean) { - if (value) { - this.videoElement.controls = true; - this.videoElement.style.pointerEvents = "auto"; - } else { - this.videoElement.controls = false; - this.videoElement.style.pointerEvents = ""; - } + this.videoElement.controls = value; + this.videoElement.style.pointerEvents = value ? "auto" : ""; + } + + set id(value: string) { + this.videoElement.id = value; } set muted(value: boolean) { - if (value) { - this.videoElement.muted = true; - } else { - this.videoElement.muted = false; - } + this.videoElement.muted = true; } set paused(value: boolean) { - this.playPromise.then(() => { - if (value) { - this.videoElement.pause(); - } else { - this.playPromise = this.videoElement.play(); - } - }); + if (value) { + this.videoElement.pause(); + } else { + this.requestPlay(); + } this._paused = value; } @@ -118,11 +113,7 @@ class RCTVideo extends RCTView { } set repeat(value: boolean) { - if (value) { - this.videoElement.setAttribute("loop", "true"); - } else { - this.videoElement.removeAttribute("loop"); - } + this.videoElement.loop = value; } set resizeMode(value: number) { @@ -161,9 +152,19 @@ class RCTVideo extends RCTView { uri = URL.createObjectURL(blob); } - this.videoElement.setAttribute("src", uri); - if (!this._paused) { - this.playPromise = this.videoElement.play(); + if (!shaka.Player.isBrowserSupported()) { // primarily iOS WebKit + this.videoElement.setAttribute("src", uri); + if (!this._paused) { + this.requestPlay(); + } + } else { + this.player.load(uri) + .then(() => { + if (!this._paused) { + this.requestPlay(); + } + }) + .catch(this.onError); } } @@ -182,6 +183,10 @@ class RCTVideo extends RCTView { this.stopProgressTimer(); } + onError = error => { + console.warn("topVideoError", error); + } + onLoad = () => { // height & width are safe with audio, will be 0 const height = this.videoElement.videoHeight; @@ -223,6 +228,25 @@ class RCTVideo extends RCTView { this.sendEvent("topVideoProgress", payload); } + onRejectedAutoplay = () => { + this.sendEvent("topVideoRejectedAutoplay", null); + } + + requestPlay() { + const playPromise = this.videoElement.play(); + if (playPromise) { + playPromise + .then(() => {}) + .catch(e => { + /* This is likely one of: + * name: NotAllowedError - autoplay is not supported + * name: NotSupportedError - format is not supported + */ + this.onError({ code: e.name, message: e.message }); + }); + } + } + sendEvent(eventName, payload) { const event = new RCTVideoEvent(eventName, this.reactTag, 0, payload); this.eventDispatcher.sendEvent(event); diff --git a/dom/RCTVideoManager.js b/dom/RCTVideoManager.js index b5b3f3f3..2eb5309b 100644 --- a/dom/RCTVideoManager.js +++ b/dom/RCTVideoManager.js @@ -18,6 +18,7 @@ class RCTVideoManager extends RCTViewManager { return super .describeProps() .addBooleanProp("controls", this.setControls) + .addStringProp("id", this.setId) .addBooleanProp("muted", this.setMuted) .addBooleanProp("paused", this.setPaused) .addNumberProp("progressUpdateInterval", this.setProgressUpdateInterval) @@ -28,11 +29,16 @@ class RCTVideoManager extends RCTViewManager { .addObjectProp("src", this.setSource) .addNumberProp("volume", this.setVolume) .addDirectEvent("onVideoEnd") + .addDirectEvent("onVideoError") .addDirectEvent("onVideoLoad") .addDirectEvent("onVideoLoadStart") .addDirectEvent("onVideoProgress"); } + dismissFullscreenPlayer() { + // not currently working + } + presentFullscreenPlayer() { // not currently working } @@ -41,6 +47,10 @@ class RCTVideoManager extends RCTViewManager { view.controls = value; } + setId(view: RCTVideo, value: string) { + view.id = value; + } + setMuted(view: RCTVideo, value: boolean) { view.muted = value; } diff --git a/package.json b/package.json index 5fc9a51c..92366be3 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ }, "dependencies": { "keymirror": "0.1.1", - "prop-types": "^15.5.10" + "prop-types": "^15.5.10", + "shaka-player": "2.4.4" }, "scripts": { "test": "node_modules/.bin/eslint *.js" @@ -54,4 +55,4 @@ "react-native-video.podspec", "VideoResizeMode.js" ] -} \ No newline at end of file +}