paella-core
Version:
Multistream HTML video player
138 lines (117 loc) • 4.89 kB
JavaScript
import Hls from 'hls.js';
import Events, { triggerEvent } from '../core/Events';
import { HlsVideo, getHlsSupport, HlsSupport, defaultHlsConfig } from './es.upv.paella.hlsVideoFormat';
import VideoPlugin from 'paella-core/js/core/VideoPlugin';
import VideoQualityItem from 'paella-core/js/core/VideoQualityItem';
import PaellaCoreVideoFormats from './PaellaCoreVideoFormats';
const hlsSupport = getHlsSupport();
const loadHls = (player, streamData, video, config, cors) => {
if (cors.withCredentials) {
config.xhrSetup = function(xhr,url) {
xhr.withCredentials = cors.withCredentials;
for (const header in cors.requestHeaders) {
const value = cors.requestHeaders[header];
xhr.setRequestHeader(header, value);
}
}
}
const hls = new Hls(config);
const hlsStream = streamData?.sources?.hlsLive?.length>0 &&
streamData.sources.hlsLive[0];
const initialQualityLevel = config.initialQualityLevel !== undefined ? config.initialQualityLevel : 1;
return [hls, new Promise((resolve,reject) => {
let autoQualitySet = false;
hls.on(Hls.Events.LEVEL_SWITCHED, (evt, data) => {
this.player.log.debug(`HLS: quality level switched to ${data.level}`);
if (!autoQualitySet) {
hls.currentLevel = -1;
autoQualitySet = true;
}
triggerEvent(player, Events.VIDEO_QUALITY_CHANGED, {});
});
hls.on(Hls.Events.ERROR, (event,data) => {
if (data.fatal) {
switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
if (data.details === Hls.ErrorDetails.MANIFEST_LOAD_ERROR) {
reject(Error("hlsVideoFormatPlugin: unrecoverable error in HLS player. The video is not available"));
}
else {
player.log.warn("hlsVideoFormatPlugin: Fatal network error. Try to recover");
hls.startLoad();
}
break;
case Hls.ErrorTypes.MEDIA_ERROR:
player.log.warn("hlsVideoFormatPlugin: Fatal media error encountered. Try to recover");
hls.recoverMediaError()
break;
default:
hls.destroy();
reject(Error("hlsVideoFormat: Fatal error. Can not recover"));
}
}
});
hls.on(Hls.Events.MANIFEST_PARSED, () => {
if (!config.autoStartLoad) {
hls.autoStartLoad();
}
});
const rand = Math.floor(Math.random() * 100000000000);
const url = hlsStream.src + (config.enableCache ?
(/\?/.test(hlsStream.src) ? `&cache=${rand}` : `?cache=${rand}`)
: "");
hls.loadSource(url);
hls.attachMedia(video);
hls._videoEventListener = () => {
resolve();
};
video.addEventListener("canplay", hls._videoEventListener);
})];
}
export class HlsLiveVideo extends HlsVideo {
async loadStreamData(streamData) {
if (hlsSupport === HlsSupport.NATIVE) {
// We delegate the load to HlsVideo, which in turn will delegate it to MP4Video'.
streamData.sources.hls = streamData.sources.hlsLive;
return super.loadStreamData(streamData);
}
else {
this.player.log.debug("Loading HLS stream");
const [hls, promise] = loadHls(this.player, streamData, this.video, this._config, this._cors);
this._hls = hls;
await promise;
this._autoQuality = new VideoQualityItem({
label: "auto",
shortLabel: "auto",
index: -1,
width: 1,
height: 1,
isAuto: true
});
// Initialize current quality
this._currentQuality = this._autoQuality;
// Initialize current audio track
const tracks = await this.getAudioTracks();
this._currentAudioTrack = tracks.find(track => track.selected);
this.saveDisabledProperties(this.video);
}
}
}
export default class HlsLiveVideoFormat extends VideoPlugin {
getPluginModuleInstance() {
return PaellaCoreVideoFormats.Get();
}
get name() {
return super.name || "es.upv.paella.hlsLiveVideoFormat";
}
get streamType() {
return "hlsLive";
}
isCompatible(streamData) {
const { hlsLive } = streamData.sources;
return hlsLive && hlsSupport;
}
async getVideoInstance(playerContainer, isMainAudio) {
return new HlsLiveVideo(this.player, playerContainer, this.config, isMainAudio);
}
}