@playkit-js/playkit-js
Version:
[](https://github.com/kaltura/playkit-js/actions/workflows/run_canary.yaml) [ • 4.66 kB
text/typescript
import { FakeEventTarget } from '../event/fake-event-target';
import getLogger from '../utils/logger';
import {CustomEventType} from '../event/event-type';
import { EventManager } from '../event/event-manager';
import { FakeEvent } from '../event/fake-event';
import {IMediaSourceAdapter} from '../types';
import {PKAbrConfigObject} from '../types';
const NOT_SUPPORTED: number = -1;
class DroppedFramesWatcher extends FakeEventTarget {
private _droppedFramesInterval: number | null = null;
private _lastDroppedFrames: number = 0;
private _lastDecodedFrames: number = 0;
private _lastTime: number = 0;
private _mediaSourceAdapter: IMediaSourceAdapter;
private _config: PKAbrConfigObject;
private _videoElement: HTMLVideoElement;
private _currentBitrate: number = 0;
private _eventManager: EventManager;
private static _logger: any = getLogger('droppedFramesWatcher');
constructor(mediaSourceAdapter: IMediaSourceAdapter, config: PKAbrConfigObject, videoElement: HTMLVideoElement) {
super();
this._eventManager = new EventManager();
this._mediaSourceAdapter = mediaSourceAdapter;
this._config = config;
this._videoElement = videoElement;
if (this._mediaSourceAdapter.capabilities.fpsControl) {
this._eventManager.listen(this._mediaSourceAdapter, CustomEventType.FPS_DROP, (event: FakeEvent) => this._triggerFPSDrop(event.payload.data));
return;
}
if (this._getDroppedAndDecodedFrames()[0] === NOT_SUPPORTED) {
DroppedFramesWatcher._logger.debug('Dropped frame watcher is not supported');
} else if (this._config.capLevelOnFPSDrop) {
DroppedFramesWatcher._logger.debug('Initialized capLevelOnFPSDrop = true');
this._init();
} else {
DroppedFramesWatcher._logger.debug('Not Initialized');
}
}
private _init(): void {
this._eventManager.listen(
this._mediaSourceAdapter,
CustomEventType.VIDEO_TRACK_CHANGED,
(event: FakeEvent) => (this._currentBitrate = event.payload.selectedVideoTrack.bandwidth)
);
this._droppedFramesInterval = setInterval(() => this._checkFPS(), this._config.fpsDroppedFramesInterval);
}
private _triggerFPSDrop(data: any): void {
this.dispatchEvent(new FakeEvent(CustomEventType.FPS_DROP, data));
}
private _getDroppedAndDecodedFrames(): [number, number] {
if (typeof this._videoElement.getVideoPlaybackQuality === 'function') {
const videoPlaybackQuality = this._videoElement.getVideoPlaybackQuality();
return [videoPlaybackQuality.droppedVideoFrames, videoPlaybackQuality.totalVideoFrames];
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
} else if (typeof this._videoElement.webkitDroppedFrameCount === 'number' && typeof this._videoElement.webkitDecodedFrameCount === 'number') {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return [this._videoElement.webkitDroppedFrameCount, this._videoElement.webkitDecodedFrameCount];
} else {
return [NOT_SUPPORTED, NOT_SUPPORTED];
}
}
private _checkFPS(): void {
const [droppedFrames, decodedFrames] = this._getDroppedAndDecodedFrames();
try {
const currentTime = performance.now();
if (decodedFrames) {
if (this._lastTime) {
const currentPeriod = currentTime - this._lastTime,
currentDropped = droppedFrames - this._lastDroppedFrames,
currentDecoded = decodedFrames - this._lastDecodedFrames,
droppedFPS = (1000 * currentDropped) / currentPeriod;
if (droppedFPS > 0) {
DroppedFramesWatcher._logger.debug('checkFPS : droppedFPS/decodedFPS:' + droppedFPS / ((1000 * currentDecoded) / currentPeriod));
if (currentDropped > this._config.fpsDroppedMonitoringThreshold * currentDecoded) {
this._mediaSourceAdapter.setMaxBitrate(this._currentBitrate - 1);
this._triggerFPSDrop({
currentDropped: currentDropped,
currentDecoded: currentDecoded,
totalDroppedFrames: droppedFPS
});
}
}
}
this._lastTime = currentTime;
this._lastDroppedFrames = droppedFrames;
this._lastDecodedFrames = decodedFrames;
}
} catch (error) {
DroppedFramesWatcher._logger.error('Error occur while trying to check dropFrames: ', error);
}
}
public destroy(): void {
if (this._droppedFramesInterval) {
clearInterval(this._droppedFramesInterval);
}
this._droppedFramesInterval = null;
this._eventManager.destroy();
}
}
export {DroppedFramesWatcher};