playable
Version:
Video player based on HTML5Video
354 lines (286 loc) • 8.2 kB
text/typescript
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;
}
}