playable
Version:
Video player based on HTML5Video
226 lines (194 loc) • 5.7 kB
text/typescript
import { VideoEvent, EngineState } from '../../constants';
import { IEventEmitter } from '../event-emitter/types';
import { IPlaybackEngine } from '../playback-engine/types';
import {
IReportReasons,
ITimeoutMap,
IReportTypes,
IReportType,
} from './types';
import playerAPI from '../../core/player-api-decorator';
export const REPORT_REASONS: IReportReasons = {
LONG_INITIAL_VIDEO_PARTS_LOADING: 'long-initial-video-parts-loading',
LONG_METADATA_LOADING: 'long-metadata-loading',
LONG_SEEK_PROCESSING: 'long-seek-processing',
BUFFER_EMPTY_FOR_CURRENT_SEGMENT: 'buffer-empty-for-current-segment',
LONG_PLAY_REQUESTED_PROCESSING: 'long-play-requested-processing',
};
export const DELAYED_REPORT_TYPES: IReportTypes = {
INITIAL_VIDEO_PARTS_LOADING: {
id: '_initialVideoPartsLoading',
timeoutTime: 5000,
},
METADATA_LOADING: {
id: '_metadataLoading',
timeoutTime: 5000,
},
RUNTIME_LOADING: {
id: '_runtimeLoading',
timeoutTime: 5000,
},
};
export default class AnomalyBloodhound {
static moduleName = 'anomalyBloodhound';
static dependencies = ['eventEmitter', 'engine'];
private _engine: IPlaybackEngine;
private _eventEmitter: IEventEmitter;
private _timeoutMap: ITimeoutMap;
private _unbindEvents: () => void;
private _callback: (anomalyData: any) => void;
constructor({
engine,
eventEmitter,
}: {
engine: IPlaybackEngine;
eventEmitter: IEventEmitter;
}) {
this._engine = engine;
this._eventEmitter = eventEmitter;
this._timeoutMap = Object.create(null);
this._bindEvents();
}
private _bindEvents() {
this._unbindEvents = this._eventEmitter.bindEvents(
[[VideoEvent.STATE_CHANGED, this._processStateChange]],
this,
);
}
private _processStateChange({
prevState,
nextState,
}: {
prevState: EngineState;
nextState: EngineState;
}) {
switch (nextState) {
case EngineState.LOAD_STARTED:
if (this._engine.isAutoPlayActive || this._engine.isPreloadActive) {
this.startDelayedReport(
DELAYED_REPORT_TYPES.METADATA_LOADING,
REPORT_REASONS.LONG_METADATA_LOADING,
);
}
break;
case EngineState.METADATA_LOADED:
if (this.isDelayedReportExist(DELAYED_REPORT_TYPES.METADATA_LOADING)) {
this.stopDelayedReport(DELAYED_REPORT_TYPES.METADATA_LOADING);
if (this._engine.getPreload() !== 'metadata') {
this.startDelayedReport(
DELAYED_REPORT_TYPES.INITIAL_VIDEO_PARTS_LOADING,
REPORT_REASONS.LONG_INITIAL_VIDEO_PARTS_LOADING,
);
}
}
break;
case EngineState.READY_TO_PLAY:
if (
this.isDelayedReportExist(
DELAYED_REPORT_TYPES.INITIAL_VIDEO_PARTS_LOADING,
)
) {
this.stopDelayedReport(
DELAYED_REPORT_TYPES.INITIAL_VIDEO_PARTS_LOADING,
);
}
if (this.isDelayedReportExist(DELAYED_REPORT_TYPES.RUNTIME_LOADING)) {
this.stopDelayedReport(DELAYED_REPORT_TYPES.RUNTIME_LOADING);
}
break;
case EngineState.SEEK_IN_PROGRESS:
if (prevState === EngineState.PAUSED) {
this.startDelayedReport(
DELAYED_REPORT_TYPES.RUNTIME_LOADING,
REPORT_REASONS.LONG_SEEK_PROCESSING,
);
}
break;
case EngineState.WAITING:
switch (prevState) {
case EngineState.PLAYING: {
this.reportDebugInfo({
reason: REPORT_REASONS.BUFFER_EMPTY_FOR_CURRENT_SEGMENT,
});
break;
}
case EngineState.PLAY_REQUESTED:
if (
!this.isDelayedReportExist(DELAYED_REPORT_TYPES.RUNTIME_LOADING)
) {
this.startDelayedReport(
DELAYED_REPORT_TYPES.RUNTIME_LOADING,
REPORT_REASONS.LONG_PLAY_REQUESTED_PROCESSING,
);
}
break;
/* ignore coverage */
default:
break;
}
break;
case EngineState.PLAYING:
if (this.isDelayedReportExist(DELAYED_REPORT_TYPES.RUNTIME_LOADING)) {
this.stopDelayedReport(DELAYED_REPORT_TYPES.RUNTIME_LOADING);
}
break;
default:
break;
}
}
isDelayedReportExist(type: IReportType) {
return Boolean(this._timeoutMap[type.id]);
}
startDelayedReport(type: IReportType, reason: string) {
if (this.isDelayedReportExist(type)) {
this.stopDelayedReport(type);
}
const startTS = Date.now();
this._timeoutMap[type.id] = window.setTimeout(() => {
const endTS = Date.now();
delete this._timeoutMap;
this.reportDebugInfo({
reason,
startTS,
endTS,
});
}, type.timeoutTime);
}
stopDelayedReport(type: IReportType) {
window.clearTimeout(this._timeoutMap[type.id]);
delete this._timeoutMap[type.id];
}
stopAllDelayedReports() {
Object.keys(this._timeoutMap).forEach(key => {
window.clearTimeout(this._timeoutMap[key]);
delete this._timeoutMap[key];
});
}
setAnomalyCallback(callback: (anomalyData: any) => void) {
if (callback) {
this._callback = callback;
}
}
reportDebugInfo({
reason,
startTS,
endTS,
}: {
reason: string;
startTS?: number;
endTS?: number;
}) {
this._callback &&
this._callback({
reason,
startTS,
endTS,
...this._engine.getDebugInfo(),
});
}
destroy() {
this.stopAllDelayedReports();
this._unbindEvents();
}
}