UNPKG

labo-components

Version:
287 lines (255 loc) 7.21 kB
/* Implement the following: - https://www.w3.org/2010/05/video/mediaevents.html - http://ronallo.com/blog/html5-video-caption-cue-settings-tester/ - http://www.w3schools.com/tags/ref_av_dom.asp */ import PlayerAPI from '../PlayerAPI'; import IDUtil from '../../../util/IDUtil'; import SessionStorageHandler from "../../../util/SessionStorageHandler"; import FlexPlayerUtil from '../../../util/FlexPlayerUtil'; import FlexPlayerControls from '../FlexPlayerControls'; import { SESSION_PLAYER_VOLUME } from '../PlayerAPI'; class HTML5VideoPlayer extends React.Component { constructor(props) { super(props); this.state = { playerAPI: null }; this.controlsRef = React.createRef(); this.fullScreenWrapperRef = React.createRef(); this.alreadyShowing = true; this.alertTimerId = null; } componentDidMount() { const vid = document.getElementById('video-player'); if (this.props.eventCallbacks) { vid.onprogress = this.props.eventCallbacks.loadProgress.bind(this); vid.ontimeupdate = this.props.eventCallbacks.playProgress.bind( this ); vid.onplay = this.props.eventCallbacks.onPlay.bind(this); vid.onpause = this.props.eventCallbacks.onPause.bind(this); vid.onended = this.props.eventCallbacks.onFinish.bind(this); vid.onseeked = this.props.eventCallbacks.onSeek.bind(this); vid.onloadedmetadata = this.onReady.bind(this, vid); } //needed until React will support the controlsList attribute of the video tag vid.setAttribute('controlsList', 'nodownload'); } shouldComponentUpdate(nextProps, nextState) { if (nextState.playerAPI != null && this.state.playerAPI == null) { //rerender when the player is ready return true; } if (nextState.fullScreen != this.state.fullScreen) { //rerender when full screen is toggled return true; } if (nextProps.mediaObject.assetId == this.props.mediaObject.assetId) { //but only rerender when the media object changed if ( this.state.playerAPI && this.props.mediaObject.segments && (nextProps.segment && nextProps.segment.start != this.props.segment.start) ) { this.state.playerAPI.seek(nextProps.segment.start); } return false; } return true; } componentDidUpdate() { this.state.playerAPI.getApi().load(); } onReady(playerAPI) { if (this.state.playerAPI == null) { this.setState( { playerAPI: new HTML5VideoPlayerAPI(playerAPI) }, () => { this.onSourceLoaded(); } ); } else { this.onSourceLoaded(); } } onSourceLoaded() { if (this.state.playerAPI) { //then seek to the starting point const start = this.props.mediaObject.start ? this.props.mediaObject.start : 0; if (start > 0) { this.state.playerAPI.seek(start / 1000); } //skip to the on-air content if (this.props.segment) { this.props.segment && this.state.playerAPI.seek(this.props.segment.start); } else if ( FlexPlayerUtil.containsOffAirStartOffset(this.props.mediaObject) ) { this.state.playerAPI.seek(this.props.mediaObject.resourceStart); } //notify the owner if (this.props.onPlayerReady) { this.props.onPlayerReady(this.state.playerAPI); } } else { console.error('No player API? (too many renders?)'); } } toggleFullScreen(exitFullScreen) { const curMode = exitFullScreen === true ? 'full-screen' : this.fullScreenWrapperRef.current.className; this.fullScreenWrapperRef.current.className = curMode === 'default' ? 'full-screen' : 'default'; } toggleControls(e) { if (this.controlsRef.current) { if (!this.alreadyShowing) { this.controlsRef.current.setVisible(true); this.alreadyShowing = true; } if (this.alertTimerId == null) { this.alertTimerId = setTimeout(() => { this.alreadyShowing = false; if (this.controlsRef.current) { this.controlsRef.current.setVisible(false); } }, 2000); } else { clearTimeout(this.alertTimerId); this.alertTimerId = setTimeout(() => { this.alreadyShowing = false; if (this.controlsRef.current) { this.controlsRef.current.setVisible(false); } }, 2000); } } } renderCustomControls = (playerAPI, hideOffAirContent) => { if (playerAPI && hideOffAirContent) { return ( <FlexPlayerControls ref={this.controlsRef} api={playerAPI} mediaObject={this.props.mediaObject} duration={FlexPlayerUtil.onAirDuration( playerAPI.getDuration(), this.props.mediaObject )} toggleFullScreen={this.toggleFullScreen.bind(this)} /> ); } return null; }; render() { //only show the custom controls when absolutely necessary. They are not yet perfect const customControls = this.renderCustomControls( this.state.playerAPI, this.props.hideOffAirContent ); const nativeControls = customControls ? { controls: false } : { controls: true, controlsList: 'nodownload', muted: false }; return ( <div className={IDUtil.cssClassName('html5-video-player')}> <div id={'__htmlvid__' + this.props.mediaObject.assetId} ref={this.fullScreenWrapperRef} className="default" onMouseMove={this.toggleControls.bind(this)} > <video id="video-player" width="100%" crossOrigin={ this.props.useCredentials ? 'use-credentials' : null } {...nativeControls} > <source src={this.props.mediaObject.url}></source> Your browser does not support the video tag </video> {customControls} </div> </div> ); } } //TODO implement volume & mute functions as well //TODO make sure the getPosition, getDuration and isPaused also supports direct returns accross all other players (check Vimeo) class HTML5VideoPlayerAPI extends PlayerAPI { constructor(playerAPI) { super(playerAPI); this.lastVolume = this.playerAPI.volume; } /* ------------ Implemented API calls ------------- */ play() { const promise = this.playerAPI.play(); if (promise) { //Older browsers may not return a promise, according to the MDN website promise.catch(function(error) {}); } } pause() { this.playerAPI.pause(); } seek(secs) { if (secs != isNaN && secs != undefined) { this.playerAPI.currentTime = secs; } } getPosition(callback = null) { if (!callback) { return this.playerAPI.currentTime; } callback(this.playerAPI.currentTime); } getDuration(callback = null) { if (!callback) { return this.playerAPI.duration; } callback(this.playerAPI.duration); } isPaused(callback = null) { if (!callback) { return this.playerAPI.paused; } callback(this.playerAPI.paused); } setVolume(volume) { //value between 0-1 if (volume !== 0) { this.lastVolume = volume; } this.playerAPI.volume = volume; this.storeVolume(volume); } getVolume() { return this.playerAPI.volume; } getLastVolume() { return this.lastVolume; } toggleMute() { this.playerAPI.muted = !this.playerAPI.muted; if (this.isMuted()) { this.lastVolume = this.playerAPI.volume; } } isMuted() { return this.playerAPI.muted; } storeVolume(volume) { SessionStorageHandler.set(SESSION_PLAYER_VOLUME, volume); } } export default HTML5VideoPlayer;