UNPKG

playable

Version:

Video player based on HTML5Video

242 lines (207 loc) 6.31 kB
import { MediaPlayer, LogLevel } from 'dashjs/build/es5/index_mediaplayerOnly'; import { getNearestBufferSegmentInfo } from '../utils/video-data'; import { NativeEnvironmentSupport } from '../utils/environment-detection'; import { Error as PlayableError, MediaStreamType, MediaStreamDeliveryPriority, VideoEvent, } from '../constants'; import { IPlaybackAdapter } from '../modules/playback-engine/output/native/adapters/types'; import { IEventEmitter } from '../modules/event-emitter/types'; import { IParsedPlayableSource } from '../modules/playback-engine/types'; const INITIAL_BITRATE = 5000; const DashEvents = MediaPlayer.events; export default class DashAdapter implements IPlaybackAdapter { static isSupported() { return NativeEnvironmentSupport.MSE; } private eventEmitter: IEventEmitter; private dashPlayer: any; private mediaStream: IParsedPlayableSource; private videoElement: HTMLVideoElement; constructor(eventEmitter: IEventEmitter) { this.eventEmitter = eventEmitter; this.dashPlayer = null; this.mediaStream = null; this.videoElement = null; this._bindCallbacks(); } canPlay(mediaType: MediaStreamType) { return mediaType === MediaStreamType.DASH; } get mediaStreamDeliveryPriority() { return MediaStreamDeliveryPriority.ADAPTIVE_VIA_MSE; } get currentUrl() { return this.mediaStream.url; } get syncWithLiveTime(): any { // TODO: implement syncWithLiveTime for `dash` return undefined; } get isDynamicContent() { return false; } get isDynamicContentEnded() { // TODO: implement isDynamicContentEnded return false; } get isSyncWithLive() { return false; } get isSeekAvailable() { return true; } get debugInfo() { const currentStream = this.dashPlayer.getActiveStream(); let currentTime = 0; if (currentStream) { currentTime = this.dashPlayer.time(currentStream.getId()); } const bitrates = this.dashPlayer .getBitrateInfoListFor('video') .map((bitrateInfo: any) => bitrateInfo.bitrate); let currentBitrate = null; if (this.dashPlayer.getQualityFor('video') && bitrates) { currentBitrate = bitrates[this.dashPlayer.getQualityFor('video')]; } const overallBufferLength = this.dashPlayer.getBufferLength('video'); const currentTrack = this.dashPlayer.getCurrentTrackFor('video'); const nearestBufferSegInfo = getNearestBufferSegmentInfo( this.dashPlayer.getVideoElement().buffered, currentTime, ); const bwEstimate: number = this.dashPlayer.getAverageThroughput('video'); return { ...this.mediaStream, bwEstimate, deliveryPriority: this.mediaStreamDeliveryPriority, bitrates, currentBitrate, overallBufferLength, currentTrack, nearestBufferSegInfo, }; } private _bindCallbacks() { this._broadcastError = this._broadcastError.bind(this); } setMediaStreams(mediaStreams: IParsedPlayableSource[]) { if (mediaStreams.length === 1) { this.mediaStream = mediaStreams[0]; } else { throw new Error( `Can only handle a single DASH stream. Received ${mediaStreams.length} streams.`, ); } } private _logError(error: string, errorEvent: any) { this.eventEmitter.emitAsync(VideoEvent.ERROR, { errorType: error, streamType: MediaStreamType.DASH, streamProvider: 'dash.js', errorInstance: errorEvent, }); } private _broadcastError(errorEvent: any) { if (!errorEvent) { return; } if (errorEvent.error === 'download') { switch (errorEvent.event.id) { case 'manifest': this._logError(PlayableError.MANIFEST_LOAD, errorEvent); break; case 'content': this._logError(PlayableError.CONTENT_LOAD, errorEvent); break; case 'initialization': this._logError(PlayableError.LEVEL_LOAD, errorEvent); break; default: this._logError(PlayableError.UNKNOWN, errorEvent); } } else if (errorEvent.error === 'manifestError') { switch (errorEvent.event.id) { case 'codec': this._logError(PlayableError.MANIFEST_INCOMPATIBLE, errorEvent); break; case 'parse': this._logError(PlayableError.MANIFEST_PARSE, errorEvent); break; default: this._logError(PlayableError.UNKNOWN, errorEvent); } } else if (errorEvent.error === 'mediasource') { this._logError(PlayableError.MEDIA, errorEvent); } else { this._logError(PlayableError.UNKNOWN, errorEvent); } } attach(videoOutput: HTMLVideoElement) { if (!this.mediaStream) { return; } this.videoElement = videoOutput; this.dashPlayer = MediaPlayer().create(); this.dashPlayer.updateSettings({ debug: { logLevel: LogLevel.LOG_LEVEL_NONE, }, }); this.dashPlayer.on(DashEvents.ERROR, this._broadcastError); if (videoOutput.preload === 'none') { this._startDelayedInitPlayer(); } else { this._initPlayer(); } } private _delayedInitPlayer() { this._stopDelayedInitPlayer(); this._initPlayer(true); } private _startDelayedInitPlayer() { this.eventEmitter.on( VideoEvent.PLAY_REQUEST, this._delayedInitPlayer, this, ); } private _stopDelayedInitPlayer() { this.eventEmitter.off( VideoEvent.PLAY_REQUEST, this._delayedInitPlayer, this, ); } private _initPlayer(forceAutoplay?: boolean) { this.dashPlayer.initialize( this.videoElement, this.mediaStream.url, forceAutoplay || this.videoElement.autoplay, ); this.dashPlayer.updateSettings({ streaming: { abr: { initialBitrate: { video: INITIAL_BITRATE, }, }, liveCatchup: {}, }, }); //this.dashPlayer.setInitialBitrateFor('video', INITIAL_BITRATE); } detach() { this._stopDelayedInitPlayer(); if (!this.mediaStream) { return; } this.dashPlayer.reset(); this.dashPlayer.off(DashEvents.ERROR, this._broadcastError); this.dashPlayer = null; this.videoElement.removeAttribute('src'); this.videoElement = null; } }