playable
Version:
Video player based on HTML5Video
299 lines • 12.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var hls_light_js_1 = (0, tslib_1.__importDefault)(require("hls.js/dist/hls.light.js"));
var video_data_1 = require("../utils/video-data");
var environment_detection_1 = require("../utils/environment-detection");
var device_detection_1 = require("../utils/device-detection");
var constants_1 = require("../constants");
var LIVE_SYNC_DURATION = 4;
var LIVE_SYNC_DURATION_DELTA = 5;
var DEFAULT_HLS_CONFIG = {
abrEwmaDefaultEstimate: 5000 * 1000,
liveSyncDuration: LIVE_SYNC_DURATION,
nudgeMaxRetry: 40, // can be removed with hls v1.5.0 https://github.com/video-dev/hls.js/issues/5904#issuecomment-1762365377
};
var NETWORK_ERROR_RECOVER_TIMEOUT = 1000;
var MEDIA_ERROR_RECOVER_TIMEOUT = 1000;
var HlsAdapter = /** @class */ (function () {
function HlsAdapter(eventEmitter) {
this.eventEmitter = eventEmitter;
this.hls = null;
this.videoElement = null;
this.mediaStream = null;
this._isDynamicContent = false;
this._isDynamicContentEnded = null;
this._bindCallbacks();
}
HlsAdapter.isSupported = function () {
return environment_detection_1.NativeEnvironmentSupport.MSE && hls_light_js_1.default.isSupported();
};
HlsAdapter.prototype._bindCallbacks = function () {
this._attachOnPlay = this._attachOnPlay.bind(this);
this._broadcastError = this._broadcastError.bind(this);
this._onEndOfStream = this._onEndOfStream.bind(this);
this._onLevelUpdated = this._onLevelUpdated.bind(this);
};
Object.defineProperty(HlsAdapter.prototype, "currentUrl", {
get: function () {
return this.mediaStream.url;
},
enumerable: false,
configurable: true
});
Object.defineProperty(HlsAdapter.prototype, "syncWithLiveTime", {
get: function () {
if (!this.isDynamicContent) {
return;
}
return (this.hls.liveSyncPosition ||
this.videoElement.duration - LIVE_SYNC_DURATION);
},
enumerable: false,
configurable: true
});
Object.defineProperty(HlsAdapter.prototype, "isDynamicContent", {
get: function () {
return this._isDynamicContent;
},
enumerable: false,
configurable: true
});
Object.defineProperty(HlsAdapter.prototype, "isDynamicContentEnded", {
get: function () {
return this._isDynamicContentEnded;
},
enumerable: false,
configurable: true
});
Object.defineProperty(HlsAdapter.prototype, "isSyncWithLive", {
get: function () {
if (!this.isDynamicContent || this.isDynamicContentEnded) {
return false;
}
return (this.videoElement.currentTime >
this.syncWithLiveTime - LIVE_SYNC_DURATION_DELTA);
},
enumerable: false,
configurable: true
});
Object.defineProperty(HlsAdapter.prototype, "isSeekAvailable", {
get: function () {
if (this.isDynamicContent && this.hls.levels) {
var level = this.hls.levels[this.hls.firstLevel];
if (!level.details) {
return false;
}
var type = level.details.type || '';
return type.trim() === 'EVENT';
}
return true;
},
enumerable: false,
configurable: true
});
Object.defineProperty(HlsAdapter.prototype, "mediaStreamDeliveryPriority", {
get: function () {
return (0, device_detection_1.isDesktopSafari)() || (0, device_detection_1.isAndroid)()
? constants_1.MediaStreamDeliveryPriority.FORCED
: constants_1.MediaStreamDeliveryPriority.ADAPTIVE_VIA_MSE;
},
enumerable: false,
configurable: true
});
Object.defineProperty(HlsAdapter.prototype, "debugInfo", {
get: function () {
var bitrates;
var currentTime = 0;
var currentBitrate = null;
var nearestBufferSegInfo = null;
var overallBufferLength = null;
var bwEstimate = 0;
var _a = this.hls, streamController = _a.streamController, levelController = _a.levelController;
if (levelController) {
bitrates = levelController.levels.map(function (level) { return level.bitrate; });
if (bitrates) {
currentBitrate = bitrates[levelController.level];
}
}
if (streamController) {
currentTime = streamController.lastCurrentTime;
if (streamController.mediaBuffer) {
overallBufferLength = (0, video_data_1.geOverallBufferLength)(streamController.mediaBuffer.buffered);
nearestBufferSegInfo = (0, video_data_1.getNearestBufferSegmentInfo)(streamController.mediaBuffer.buffered, currentTime);
}
if (streamController.stats) {
bwEstimate = streamController.stats.bwEstimate;
}
}
return (0, tslib_1.__assign)((0, tslib_1.__assign)({}, this.mediaStream), { bwEstimate: bwEstimate, deliveryPriority: this.mediaStreamDeliveryPriority, bitrates: bitrates, currentBitrate: currentBitrate, overallBufferLength: overallBufferLength, nearestBufferSegInfo: nearestBufferSegInfo });
},
enumerable: false,
configurable: true
});
HlsAdapter.prototype.canPlay = function (mediaType) {
return mediaType === constants_1.MediaStreamType.HLS;
};
HlsAdapter.prototype.setMediaStreams = function (mediaStreams) {
if (mediaStreams.length === 1) {
this.mediaStream = mediaStreams[0];
}
else {
throw new Error("Can only handle a single HLS stream. Received ".concat(mediaStreams.length, " streams."));
}
};
HlsAdapter.prototype._logError = function (error, errorEvent) {
this.eventEmitter.emitAsync(constants_1.VideoEvent.ERROR, {
errorType: error,
streamType: constants_1.MediaStreamType.HLS,
streamProvider: 'hls.js',
errorInstance: errorEvent,
});
};
HlsAdapter.prototype._broadcastError = function (_error, data) {
// TODO: Investigate why this callback is called after hls is destroyed
if (!this.hls) {
return;
}
var ErrorTypes = hls_light_js_1.default.ErrorTypes, ErrorDetails = hls_light_js_1.default.ErrorDetails;
if (data.type === ErrorTypes.NETWORK_ERROR) {
switch (data.details) {
case ErrorDetails.MANIFEST_LOAD_ERROR:
this._logError(constants_1.Error.MANIFEST_LOAD, data);
break;
case ErrorDetails.MANIFEST_LOAD_TIMEOUT:
this._logError(constants_1.Error.MANIFEST_LOAD, data);
break;
case ErrorDetails.MANIFEST_PARSING_ERROR:
this._logError(constants_1.Error.MANIFEST_PARSE, data);
break;
case ErrorDetails.LEVEL_LOAD_ERROR:
this._logError(constants_1.Error.LEVEL_LOAD, data);
break;
case ErrorDetails.LEVEL_LOAD_TIMEOUT:
this._logError(constants_1.Error.LEVEL_LOAD, data);
break;
case ErrorDetails.AUDIO_TRACK_LOAD_ERROR:
this._logError(constants_1.Error.CONTENT_LOAD, data);
break;
case ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT:
this._logError(constants_1.Error.CONTENT_LOAD, data);
break;
case ErrorDetails.FRAG_LOAD_ERROR:
this._logError(constants_1.Error.CONTENT_LOAD, data);
break;
case ErrorDetails.FRAG_LOAD_TIMEOUT:
this._logError(constants_1.Error.CONTENT_LOAD, data);
break;
default:
this._logError(constants_1.Error.UNKNOWN, data);
break;
}
if (data.fatal) {
this._tryRecoverNetworkError();
}
}
else if (data.type === ErrorTypes.MEDIA_ERROR) {
// NOTE: when error is BUFFER_STALLED_ERROR
// video play successfully without recovering
// while recover breaks video playback
if (data.fatal && data.details !== ErrorDetails.BUFFER_STALLED_ERROR) {
this._tryRecoverMediaError();
}
switch (data.details) {
case ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR:
this._logError(constants_1.Error.MANIFEST_INCOMPATIBLE, data);
break;
case ErrorDetails.FRAG_PARSING_ERROR:
this._logError(constants_1.Error.CONTENT_PARSE, data);
break;
default:
this._logError(constants_1.Error.MEDIA, data);
break;
}
}
else {
this._logError(constants_1.Error.UNKNOWN, data);
}
};
HlsAdapter.prototype._tryRecoverMediaError = function () {
var _this = this;
if (!this._mediaRecoverTimeout) {
this.hls.recoverMediaError();
this._mediaRecoverTimeout = window.setTimeout(function () {
_this._mediaRecoverTimeout = null;
}, MEDIA_ERROR_RECOVER_TIMEOUT);
}
};
HlsAdapter.prototype._tryRecoverNetworkError = function () {
var _this = this;
if (!this._networkRecoverTimeout) {
this.hls.startLoad();
this._networkRecoverTimeout = window.setTimeout(function () {
_this._networkRecoverTimeout = null;
}, NETWORK_ERROR_RECOVER_TIMEOUT);
}
};
HlsAdapter.prototype._attachOnPlay = function () {
if (!this.videoElement) {
return;
}
this.hls.startLoad();
this.videoElement.removeEventListener('play', this._attachOnPlay);
};
HlsAdapter.prototype._onLevelUpdated = function (_eventName, _a) {
var details = _a.details;
this._isDynamicContent = details.live;
this._isDynamicContentEnded = details.live ? false : null;
this.hls.off(hls_light_js_1.default.Events.LEVEL_UPDATED, this._onLevelUpdated);
};
HlsAdapter.prototype._onEndOfStream = function () {
if (this._isDynamicContent) {
this._isDynamicContentEnded = true;
this.eventEmitter.emitAsync(constants_1.VideoEvent.DYNAMIC_CONTENT_ENDED);
}
};
HlsAdapter.prototype.attach = function (videoElement) {
if (!this.mediaStream) {
return;
}
var config = (0, tslib_1.__assign)({}, HlsAdapter.DEFAULT_HLS_CONFIG);
this.videoElement = videoElement;
if (this.videoElement.preload === 'none') {
config.autoStartLoad = false;
this.videoElement.addEventListener('play', this._attachOnPlay);
}
this.hls = new hls_light_js_1.default(config);
this.hls.on(hls_light_js_1.default.Events.ERROR, this._broadcastError);
this.hls.on(hls_light_js_1.default.Events.LEVEL_UPDATED, this._onLevelUpdated);
this.hls.on(hls_light_js_1.default.Events.BUFFER_EOS, this._onEndOfStream);
this.hls.loadSource(this.mediaStream.url);
this.hls.attachMedia(this.videoElement);
this._isAttached = true;
};
HlsAdapter.prototype.detach = function () {
if (!this._isAttached) {
return;
}
if (this._networkRecoverTimeout) {
window.clearTimeout(this._networkRecoverTimeout);
this._networkRecoverTimeout = null;
}
if (this._mediaRecoverTimeout) {
window.clearTimeout(this._mediaRecoverTimeout);
this._mediaRecoverTimeout = null;
}
this.hls.off(hls_light_js_1.default.Events.ERROR, this._broadcastError);
this.hls.off(hls_light_js_1.default.Events.BUFFER_EOS, this._onEndOfStream);
this.hls.off(hls_light_js_1.default.Events.LEVEL_UPDATED, this._onLevelUpdated);
this.hls.destroy();
this.hls = null;
this.videoElement.removeEventListener('play', this._attachOnPlay);
this.videoElement.removeAttribute('src');
this.videoElement = null;
};
HlsAdapter.DEFAULT_HLS_CONFIG = DEFAULT_HLS_CONFIG;
return HlsAdapter;
}());
exports.default = HlsAdapter;
//# sourceMappingURL=hls.js.map