UNPKG

playable

Version:

Video player based on HTML5Video

137 lines (112 loc) 3.47 kB
import { EngineState, VideoEvent, LiveState, UIEvent } from '../../constants'; import { IEventEmitter, IEventMap } from '../event-emitter/types'; import { ILiveStateEngineDependencies, IPlaybackEngine } from './types'; const SEEK_BY_UI_EVENTS = [ UIEvent.GO_FORWARD_WITH_KEYBOARD, UIEvent.GO_BACKWARD_WITH_KEYBOARD, UIEvent.PROGRESS_CHANGE, ]; class LiveStateEngine { static moduleName = 'liveStateEngine'; static dependencies = ['eventEmitter', 'engine']; private _eventEmitter: IEventEmitter; private _engine: IPlaybackEngine; private _state: LiveState; private _isSeekedByUIWhilePlaying: boolean; private _unbindEvents: () => void; constructor({ eventEmitter, engine }: ILiveStateEngineDependencies) { this._eventEmitter = eventEmitter; this._engine = engine; this._state = LiveState.NONE; this._isSeekedByUIWhilePlaying = null; this._bindEvents(); } get state(): LiveState { return this._state; } private _bindEvents() { this._unbindEvents = this._eventEmitter.bindEvents( [ [VideoEvent.STATE_CHANGED, this._processStateChange], ...SEEK_BY_UI_EVENTS.map( eventName => [eventName, this._processSeekByUI] as IEventMap, ), [VideoEvent.DYNAMIC_CONTENT_ENDED, this._onDynamicContentEnded], ], this, ); } private _processStateChange({ prevState, nextState, }: { prevState: EngineState; nextState: EngineState; }) { if (nextState === EngineState.SRC_SET) { this._setState(LiveState.NONE); return; } if (!this._engine.isDynamicContent || this._engine.isDynamicContentEnded) { return; } switch (nextState) { case EngineState.METADATA_LOADED: this._setState(LiveState.INITIAL); break; case EngineState.PLAY_REQUESTED: if (this._state === LiveState.INITIAL) { this._engine.syncWithLive(); } break; case EngineState.PLAYING: // NOTE: skip PLAYING event after events like `WAITING` and other not important events. if ( this._state === LiveState.INITIAL || this._state === LiveState.NOT_SYNC || this._isSeekedByUIWhilePlaying ) { this._setState( this._engine.isSyncWithLive ? LiveState.SYNC : LiveState.NOT_SYNC, ); this._isSeekedByUIWhilePlaying = false; } break; case EngineState.PAUSED: // NOTE: process `PAUSED` event only `PLAYING`, to be sure its not related with `WAITING` events if (prevState === EngineState.PLAYING) { this._setState(LiveState.NOT_SYNC); } break; default: break; } } private _processSeekByUI() { if ( this._engine.isDynamicContent && this._engine.getCurrentState() === EngineState.PLAYING ) { // NOTE: flag should be handled on `PLAYING` state in `_processStateChange` this._isSeekedByUIWhilePlaying = true; } } private _onDynamicContentEnded() { this._setState(LiveState.ENDED); } private _setState(state: LiveState) { if (this._state !== state) { const prevState = this._state; const nextState = state; this._state = state; this._eventEmitter.emitAsync(VideoEvent.LIVE_STATE_CHANGED, { prevState, nextState, }); } } destroy() { this._unbindEvents(); } } export default LiveStateEngine;