UNPKG

playable

Version:

Video player based on HTML5Video

354 lines (286 loc) 8.2 kB
import { CrossOriginValue, INativeDebugInfo, IVideoOutput, PlayableMediaSource, PreloadType, } from '../../types'; import { IPlaybackAdapter, IPlaybackAdapterClass } from './adapters/types'; import { IEventEmitter } from '../../../event-emitter/types'; import { IPlayerConfig } from '../../../../core/config'; import StateEngine from './state-engine'; import NativeEventsBroadcaster from './native-events-broadcaster'; import { EngineState } from '../../../../constants'; import AdapterStrategy from './adapters-strategy'; import VideoEvent from '../../../../constants/events/video'; import { isAndroid, isIPad, isIPhone, isIPod, } from '../../../../utils/device-detection'; const preventDefault = (e: MouseEvent) => { e.preventDefault(); }; export default class NativeOutput implements IVideoOutput { static moduleName = 'nativeOutput'; static dependencies = ['eventEmitter', 'config', 'availablePlaybackAdapters']; private _video: HTMLVideoElement; private _availablePlaybackAdapters: IPlaybackAdapterClass[]; private _eventEmitter: IEventEmitter; private _stateEngine: StateEngine; private _nativeEventsBroadcaster: NativeEventsBroadcaster; private _adapterStrategy: AdapterStrategy; private _playPromise: Promise<any>; private _pauseRequested: boolean; constructor({ eventEmitter, config, availablePlaybackAdapters = [], }: { eventEmitter: IEventEmitter; config: IPlayerConfig; availablePlaybackAdapters: IPlaybackAdapterClass[]; }) { this._createVideoTag(config); this._eventEmitter = eventEmitter; this._availablePlaybackAdapters = availablePlaybackAdapters; this._stateEngine = new StateEngine(this._eventEmitter, this._video); this._nativeEventsBroadcaster = new NativeEventsBroadcaster( eventEmitter, this._video, ); this._adapterStrategy = new AdapterStrategy( this._eventEmitter, this._video, this._availablePlaybackAdapters, ); } private _createVideoTag({ videoElement, preventContextMenu, }: Partial<IPlayerConfig>) { if (videoElement && videoElement.tagName === 'VIDEO') { this._video = videoElement; } else { this._video = document.createElement('video'); } if (preventContextMenu) { this._video.addEventListener('contextmenu', preventDefault); } } play() { //Workaround for triggering functionality that requires user event pipe this._eventEmitter.emitAsync(VideoEvent.PLAY_REQUEST); this._pauseRequested = false; if (!this._playPromise) { this._playPromise = this._video.play(); if (this._playPromise !== undefined) { this._playPromise .then(() => { this._playPromise = null; if (this._pauseRequested) { this.pause(); } }) .catch((event: DOMException) => { this._eventEmitter.emitAsync(VideoEvent.PLAY_ABORTED, event); this._playPromise = null; }); } } } pause() { if (this._playPromise) { this._pauseRequested = true; } else { this._video.pause(); this._pauseRequested = false; } } setMute(mute: boolean) { this._video.muted = mute; //Workaround for problem with HTML5Video not firing volumechange if source changed right after volume/muted changed this._nativeEventsBroadcaster.checkVolumeChangeAfterLoadStart(); } setAutoplay(isAutoplay: boolean) { this._video.autoplay = isAutoplay; } setInline(isPlaysinline: boolean) { if (isPlaysinline) { this._video.setAttribute('playsinline', 'true'); } else { this._video.removeAttribute('playsinline'); } } setCrossOrigin(crossOrigin?: CrossOriginValue) { if (crossOrigin) { this._video.setAttribute('crossorigin', crossOrigin); } else { this._video.removeAttribute('crossorigin'); } } setCurrentTime(time: number) { this._video.currentTime = time; } setVolume(volume: number) { this._video.volume = volume; //Workaround for problem with HTML5Video not firing volumechange if source changed right after volume/muted changed this._nativeEventsBroadcaster.checkVolumeChangeAfterLoadStart(); } setLoop(isLoop: boolean) { this._video.loop = isLoop; } setPlaybackRate(rate: number) { this._video.playbackRate = rate; } setPreload(preload: PreloadType = PreloadType.AUTO) { this._video.preload = preload || PreloadType.AUTO; } setSrc(src?: PlayableMediaSource, callback?: Function) { this._stateEngine.clearTimestamps(); this._adapterStrategy.connectAdapter(src); this._stateEngine.setState(EngineState.SRC_SET); if (typeof callback === 'function') { callback(); } } syncWithLive() { if ( this.attachedAdapter && this.attachedAdapter.isDynamicContent && !this.attachedAdapter.isDynamicContentEnded && !this.isSyncWithLive ) { this.setCurrentTime(this.attachedAdapter.syncWithLiveTime); this.play(); } } getElement() { return this._video; } private _getViewDimensions() { return { width: this._video.offsetWidth, height: this._video.offsetHeight, }; } get volume() { return this._video.volume; } get currentTime() { return this._video.currentTime; } get duration() { return this._video.duration; } get autoplay() { return this._video.autoplay; } get crossOrigin() { return this._video.getAttribute('crossorigin') as CrossOriginValue; } get playbackRate() { return this._video.playbackRate; } get buffered() { return this._video.buffered; } get preload() { return this._video.preload as PreloadType; } get isPaused() { return this._video.paused; } get isMuted() { return Boolean(this._video.muted); } get isEnded() { return this._video.ended; } get isInline() { return Boolean(this._video.getAttribute('playsinline')); } get isAutoplay() { return this._video.autoplay; } get isLoop() { return this._video.loop; } get isMetadataLoaded() { return this._stateEngine.isMetadataLoaded; } get isDynamicContent() { if (!this.attachedAdapter) { return false; } return this.attachedAdapter.isDynamicContent; } get isDynamicContentEnded() { if (!this.attachedAdapter) { return false; } return this.attachedAdapter.isDynamicContentEnded; } get isSeekAvailable() { if (!this.attachedAdapter) { return false; } return this.attachedAdapter.isSeekAvailable; } get isSyncWithLive(): boolean { if (!this.attachedAdapter) { return false; } return this.attachedAdapter.isSyncWithLive; } get isPreloadActive(): boolean { if (isIPad() || isIPhone() || isIPod() || isAndroid()) { return false; } return this.preload !== 'none'; } get isAutoPlayActive() { if (isIPad() || isIPhone() || isIPod() || isAndroid()) { return false; } return this.isAutoplay; } get videoHeight() { return this._video.videoHeight; } get videoWidth() { return this._video.videoWidth; } get src() { return this.attachedAdapter && this.attachedAdapter.currentUrl; } get currentState() { return this._stateEngine.state; } get attachedAdapter(): IPlaybackAdapter { return this._adapterStrategy.attachedAdapter; } getDebugInfo(): INativeDebugInfo { const { duration, currentTime } = this; let adapterDebugInfo; if (this.attachedAdapter) { adapterDebugInfo = this.attachedAdapter.debugInfo; } return { ...adapterDebugInfo, duration, currentTime, loadingStateTimestamps: this._stateEngine.stateTimestamps, viewDimensions: this._getViewDimensions(), output: 'html5video', }; } destroy() { this._nativeEventsBroadcaster.destroy(); this._adapterStrategy.destroy(); this._video.removeEventListener('contextmenu', preventDefault); this._video.parentNode && this._video.parentNode.removeChild(this._video); this._video = null; } }