npaw-plugin-adapters
Version:
NPAW's Plugin Adapters
311 lines (277 loc) • 10 kB
JavaScript
import NativeAdsAdapter from "./ads/NativeAdsAdapter";
export default class BitmovinAdapter {
adsAdapters = {
nativeAdsAdapter: NativeAdsAdapter,
};
getVersion() {
return '7.0.11-bitmovin8-jsclass';
}
getPlayhead() {
let ret = this.player ? this.player.getCurrentTime() : null;
if (this.getVideo().getAdsAdapter() && this.getVideo().getAdsAdapter().flags.isStarted) {
ret = this.lastPlayhead || 0;
}
return ret;
}
getPlayrate() {
return this.player ? this.player.getPlaybackSpeed() : null;
}
getStopParams() {
let params = {};
if (this.getVideo().getPlayhead() > 0) {
params = { playhead: this.getVideo().getPlayhead() };
}
return params;
}
getDroppedFrames() {
return this.player ? this.player.getDroppedVideoFrames() : null;
}
getDuration() {
let ret = null;
if (this.player) {
this.lastDuration = this.player.getDuration() || this.lastDuration;
ret = this.lastDuration;
}
return ret;
}
getBitrate() {
let ret = null;
if (this.player) {
const videodata = this.player.getPlaybackVideoData();
const videoquality = this.player.getVideoQuality();
if (videoquality && videoquality.bitrate) {
ret = videoquality.bitrate;
} else if (videodata && videodata.bitrate) {
ret = videodata.bitrate;
}
}
return ret;
}
getRendition() {
let ret = null;
if (this.player) {
const videodata = this.player.getPlaybackVideoData();
const videoquality = this.player.getVideoQuality();
if (videoquality && videoquality.bitrate) {
ret = this.getNpawUtils().buildRenditionString(videoquality.width, videoquality.height, videoquality.bitrate);
} else if (videodata && videodata.bitrate) {
ret = this.getNpawUtils().buildRenditionString(videodata.width, videodata.height, videodata.bitrate);
}
}
return ret;
}
detectQualityChange(e) {
let ret;
if (e.targetQuality && e.targetQuality.bitrate) {
ret = this.getNpawUtils().buildRenditionString(e.targetQuality.width, e.targetQuality.height, e.targetQuality.bitrate);
}
if (ret && this.storeNewRendition) {
this.storeNewRendition(ret);
}
}
getTitle() {
return this.player ? this.player.getSource().title : null;
}
getIsLive() {
return this.player ? this.player.isLive() : null;
}
getResource() {
let ret = null;
if (this.player && this.player.getSource && this.player.getSource()) {
try {
if (this.player.getStreamType && this.player.getStreamType()) {
if (this.player.getSource()[this.player.getStreamType()]) {
return this.player.getSource()[this.player.getStreamType()];
}
}
} catch (e) { };
ret = this.player.getSource().progressive || this.player.getSource().hls;
}
try {
if (this.player && this.player.getManifest && this.player.getManifest()) {
if (this.player.getManifest().indexOf('MPD') > 0) {
ret = this.player.getSource().dash;
} else {
ret = this.player.getSource().hls;
}
}
} catch (err) {
this.log.notice("Can't get manifest");
}
return ret;
}
getPlayerVersion() {
return this.player ? this.player.version : null;
}
getPlayerName() {
return 'Bitmovin';
}
/**
* Verify that the Bitmovin player is fully initialized.
* The player must exist and expose player.exports.PlayerEvent.
* This method is used by higher layers to decide whether the adapter
* can safely emit analytics events.
*/
checkExistsPlayer() {
try {
return !!(this.player && this.player.exports && this.player.exports.PlayerEvent);
} catch (err) {
return false;
}
}
_checkAdsAdapter() {
const adsAdapterInst = this.getVideo().getAdsAdapter();
if (!adsAdapterInst) {
this.plugin.setAdsAdapter(this.getAdapterClass('nativeAdsAdapter'), this.getVideo().getVideoKey());
this.getVideo().getAdsAdapter().registerListeners();
} else if (adsAdapterInst && !adsAdapterInst.player) {
adsAdapterInst.setPlayer(this.player);
}
}
registerListeners() {
// Guard against race condition: wait until player.exports is available
if (!this.player || !this.player.exports || !this.player.exports.PlayerEvent) {
this._registerRetryCount = (this._registerRetryCount || 0) + 1;
if (this._registerRetryCount < 50) { // retry for ~5 seconds max
setTimeout(() => this.registerListeners(), 100);
} else if (this.log && typeof this.log.warn === 'function') {
this.log.warn('BitmovinAdapter', 'Player not ready after maximum retries. Skipping listener registration.');
}
return;
}
const events = this.player.exports.PlayerEvent;
this.references = {};
this.references[events.Play] = this.playListener.bind(this);
this.references[events.Paused] = this.pauseListener.bind(this);
this.references[events.Playing] = this.playingListener.bind(this);
this.references[events.Error] = this.errorListener.bind(this);
this.references[events.Seek] = this.seekingListener.bind(this);
this.references[events.TimeShift] = this.seekingListener.bind(this);
this.references[events.TimeShifted] = this.seekedListener.bind(this);
this.references[events.PlaybackFinished] = this.endedListener.bind(this);
this.references[events.TimeChanged] = this.timeupdateListener.bind(this);
this.references[events.SourceUnloaded] = this.endedListener.bind(this);
this.references[events.Destroy] = this.destroyListener.bind(this);
this.references[events.Ready] = this.readyListener.bind(this);
this.references[events.AdBreakStarted] = this._checkAdsAdapter.bind(this);
this.references[events.VideoPlaybackQualityChanged] = this.detectQualityChange.bind(this);
this.references[events.StallStarted] = this.stallStartedListener.bind(this);
this.references[events.StallEnded] = this.stallEndedListener.bind(this);
for (let key in this.references) {
this.player.on(key, this.references[key]);
}
this.plugin.setAdsAdapter(this.getAdapterClass('nativeAdsAdapter'), this.getVideo().getVideoKey());
this.getVideo().getAdsAdapter().registerListeners();
}
unregisterListeners() {
try {
if (this.getVideo()) {
this.getVideo().removeAdsAdapter();
}
if (this.player && this.player.off && this.references) {
for (let key in this.references) {
this.player.off(key, this.references[key])
}
delete this.references;
}
} catch (err) { }
}
readyListener(e) {
this._checkAdsAdapter();
}
playListener(e) {
this.firePlayerLog('playListener', {});
this.lastPlayhead = 0;
this.fireStart();
this.failedView = false;
}
timeupdateListener(e) {
const previousLastPlayhead = this.lastPlayhead;
if (!this.getVideo().getAdsAdapter() || !this.getVideo().getAdsAdapter().flags.isStarted) {
this.lastPlayhead = this.player.getCurrentTime();
}
if (this.getPlayhead() > 0.2 && (this.getIsLive() || this.getPlayhead() + 1 < this.getDuration())) {
this.fireStart();
this.failedView = false;
if (!this.flags.isJoined) {
this.fireJoin();
}
}
if (!this.player.isPaused() && this.flags.isSeeking) {
if (this.plugin && this.plugin.options && this.plugin.options.ignoreSeekInTimeUpdate) {
const currentPosition = this.player.getCurrentTime();
if (this.seekStartPosition !== null) {
const positionDifference = Math.abs(currentPosition - this.seekStartPosition);
const playheadDifference = Math.abs(this.lastPlayhead - previousLastPlayhead);
const threshold = 0.25;
if (positionDifference > threshold || playheadDifference > threshold) {
this.fireSeekEnd({}, 'timeupdateListener');
this.seekStartPosition = null;
} else {
// cancel seek to avoid automatic internal seeks to the same position
// performed by the player when in PiP mode and screen is turned off
this.cancelSeek();
}
}
}
else {
this.fireSeekEnd({}, 'timeupdateListener');
}
}
}
pauseListener(e) {
this.firePlayerLog('pauseListener', {});
if (e.issuer && e.issuer === 'ui-seek') {
this.fireSeekBegin({}, false, 'pauseListener');
} else {
this.firePause();
}
}
playingListener(e) {
this.firePlayerLog('playingListener', {});
this.fireStart();
this.failedView = false;
this.fireResume();
}
errorListener(e) {
this.firePlayerLog('errorListener', {});
if (this.failedView) return;
if (!this.flags.isStarted) this.failedView = true;
this.fireError(e.code, e.name);
this.fireStop(this.getStopParams());
}
seekingListener(e) {
this.firePlayerLog('seekingListener', {});
this.seekStartPosition = e['position'] ?? this.player.getCurrentTime();
this.fireSeekBegin({}, false, 'seekingListener');
}
seekedListener(e) {
this.firePlayerLog('seekedListener', {});
this.fireSeekEnd({}, 'seekedListener');
}
endedListener(e) {
this.firePlayerLog('endedListener', {});
this.fireStop(this.getStopParams());
}
stallStartedListener(e) {
this.firePlayerLog('stallStartedListener', {});
this.fireBufferBegin({}, false, 'stallStartedListener');
}
stallEndedListener(e) {
this.firePlayerLog('stallEndedListener', {});
if (this.chronos && this.chronos.buffer && this.chronos.buffer.getDeltaTime(false) < 50) {
this.cancelBuffer();
} else {
this.fireBufferEnd({}, 'stallEndedListener');
}
}
destroyListener(e) {
this.player = null;
this.tag = null;
this.fireStop();
this.getVideo().removeAdapter();
}
getVideoBufferLength() {
return this.player ? this.player.getVideoBufferLength() : null;
}
}