300 lines
12 KiB
JavaScript
300 lines
12 KiB
JavaScript
|
|
// Prevent DASH.js from automatically attaching to video sources by default.
|
|
// Whoever thought this is a good idea?!
|
|
if (typeof window !== 'undefined' && !window.dashjs) {
|
|
window.dashjs = {
|
|
skipAutoCreate: true,
|
|
isDefaultSubject: true
|
|
};
|
|
}
|
|
|
|
export default function (playerInstance, options) {
|
|
playerInstance.initialiseStreamers = () => {
|
|
playerInstance.detachStreamers();
|
|
switch (playerInstance.displayOptions.layoutControls.mediaType) {
|
|
case 'application/dash+xml': // MPEG-DASH
|
|
if (!playerInstance.dashScriptLoaded && (!window.dashjs || window.dashjs.isDefaultSubject)) {
|
|
playerInstance.dashScriptLoaded = true;
|
|
import(/* webpackChunkName: "dashjs" */ 'dashjs').then((it) => {
|
|
window.dashjs = it.default;
|
|
playerInstance.initialiseDash();
|
|
});
|
|
} else {
|
|
playerInstance.initialiseDash();
|
|
}
|
|
break;
|
|
case 'application/x-mpegurl': // HLS
|
|
const { displayOptions, domRef } = playerInstance;
|
|
const { player } = domRef;
|
|
const { hls } = displayOptions;
|
|
|
|
// Doesn't load hls.js if player can play it natively
|
|
if (player.canPlayType('application/x-mpegurl') && !hls.overrideNative) {
|
|
playerInstance.debugMessage('Native HLS support found, skipping hls.js');
|
|
break;
|
|
}
|
|
|
|
if (!playerInstance.hlsScriptLoaded && !window.Hls) {
|
|
playerInstance.hlsScriptLoaded = true;
|
|
import(/* webpackChunkName: "hlsjs" */ 'hls.js').then((it) => {
|
|
window.Hls = it.default;
|
|
playerInstance.initialiseHls();
|
|
});
|
|
} else {
|
|
playerInstance.initialiseHls();
|
|
}
|
|
break;
|
|
}
|
|
};
|
|
|
|
playerInstance.initialiseDash = () => {
|
|
if (typeof (window.MediaSource || window.WebKitMediaSource) === 'function') {
|
|
// If false we want to override the autoPlay, as it comes from postRoll
|
|
const playVideo = !playerInstance.autoplayAfterAd
|
|
? playerInstance.autoplayAfterAd
|
|
: playerInstance.displayOptions.layoutControls.autoPlay;
|
|
|
|
const defaultOptions = {
|
|
'debug': {
|
|
'logLevel': typeof FP_DEBUG !== 'undefined' && FP_DEBUG === true
|
|
? dashjs.Debug.LOG_LEVEL_DEBUG
|
|
: dashjs.Debug.LOG_LEVEL_FATAL
|
|
}
|
|
};
|
|
|
|
const dashPlayer = dashjs.MediaPlayer().create();
|
|
const options = playerInstance.displayOptions.modules.configureDash(defaultOptions);
|
|
|
|
dashPlayer.updateSettings(options);
|
|
|
|
playerInstance.displayOptions.modules.onBeforeInitDash(dashPlayer);
|
|
|
|
dashPlayer.initialize(playerInstance.domRef.player, playerInstance.originalSrc, playVideo);
|
|
|
|
dashPlayer.on('streamInitializing', () => {
|
|
playerInstance.toggleLoader(true);
|
|
});
|
|
|
|
dashPlayer.on('canPlay', () => {
|
|
playerInstance.toggleLoader(false);
|
|
});
|
|
|
|
dashPlayer.on('playbackPlaying', () => {
|
|
playerInstance.toggleLoader(false);
|
|
});
|
|
|
|
playerInstance.displayOptions.modules.onAfterInitDash(dashPlayer);
|
|
|
|
playerInstance.dashPlayer = dashPlayer;
|
|
} else {
|
|
playerInstance.nextSource();
|
|
console.log('[FP_WARNING] Media type not supported by this browser using DASH.js. (application/dash+xml)');
|
|
}
|
|
};
|
|
|
|
playerInstance.initialiseHls = () => {
|
|
if (typeof Hls !== 'undefined' && Hls.isSupported()) {
|
|
playerInstance.debugMessage('Initializing hls.js');
|
|
|
|
const defaultOptions = {
|
|
debug: typeof FP_DEBUG !== 'undefined' && FP_DEBUG === true,
|
|
startPosition: 0,
|
|
p2pConfig: {
|
|
logLevel: false,
|
|
},
|
|
enableWebVTT: false,
|
|
enableCEA708Captions: false,
|
|
};
|
|
|
|
const options = playerInstance.displayOptions.modules.configureHls(defaultOptions);
|
|
const hls = new Hls(options);
|
|
playerInstance.displayOptions.modules.onBeforeInitHls(hls);
|
|
|
|
hls.attachMedia(playerInstance.domRef.player);
|
|
hls.loadSource(playerInstance.originalSrc);
|
|
|
|
playerInstance.displayOptions.modules.onAfterInitHls(hls);
|
|
|
|
playerInstance.hlsPlayer = hls;
|
|
|
|
if (!playerInstance.firstPlayLaunched && playerInstance.displayOptions.layoutControls.autoPlay) {
|
|
playerInstance.domRef.player.play();
|
|
}
|
|
|
|
playerInstance.createHLSVideoSourceSwitch();
|
|
} else {
|
|
playerInstance.nextSource();
|
|
console.log('[FP_WARNING] Media type not supported by this browser using HLS.js. (application/x-mpegURL)');
|
|
}
|
|
};
|
|
|
|
playerInstance.createHLSVideoSourceSwitch = () => {
|
|
playerInstance.hlsPlayer.on(Hls.Events.MANIFEST_PARSED, function () {
|
|
try {
|
|
const levels = createHLSLevels();
|
|
const sortedLevels = sortLevels(levels);
|
|
playerInstance.videoSources = sortedLevels;
|
|
|
|
// <=2 because of the added auto function
|
|
if (sortedLevels.length <= 2) return;
|
|
|
|
const sourceChangeButton = playerInstance.domRef.wrapper.querySelector('.fluid_control_video_source');
|
|
|
|
toggleSourceChangeButtonVisibility(sortedLevels, sourceChangeButton);
|
|
|
|
const sourceChangeList = createSourceChangeList(sortedLevels);
|
|
attachSourceChangeList(sourceChangeButton, sourceChangeList);
|
|
|
|
// Set initial level based on persisted quality or default to auto
|
|
setInitialLevel(sortedLevels);
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
});
|
|
};
|
|
|
|
function createHLSLevels() {
|
|
const HLSLevels = playerInstance.hlsPlayer.levels
|
|
.map((level, index) => ({
|
|
id: index,
|
|
title: String(level.width),
|
|
isHD: level.videoRange === 'HDR',
|
|
bitrate: level.bitrate
|
|
}));
|
|
|
|
const autoLevel = {
|
|
id: -1,
|
|
title: 'auto',
|
|
isHD: false,
|
|
bitrate: 0
|
|
};
|
|
|
|
return [...HLSLevels, autoLevel];
|
|
}
|
|
|
|
function toggleSourceChangeButtonVisibility(levels, sourceChangeButton) {
|
|
if (levels.length > 1) {
|
|
sourceChangeButton.style.display = 'inline-block';
|
|
} else {
|
|
sourceChangeButton.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
function createSourceChangeList(levels) {
|
|
const sourceChangeList = document.createElement('div');
|
|
sourceChangeList.className = 'fluid_video_sources_list';
|
|
sourceChangeList.style.display = 'none';
|
|
|
|
levels.forEach(level => {
|
|
const sourceChangeDiv = createSourceChangeItem(level);
|
|
sourceChangeList.appendChild(sourceChangeDiv);
|
|
});
|
|
|
|
return sourceChangeList;
|
|
}
|
|
|
|
function createSourceChangeItem(level) {
|
|
const sourceSelectedClass = getSourceSelectedClass(level);
|
|
const hdIndicator = level.isHD ? `<sup style="color:${playerInstance.displayOptions.layoutControls.primaryColor}" class="fp_hd_source"></sup>` : '';
|
|
|
|
const sourceChangeDiv = document.createElement('div');
|
|
sourceChangeDiv.className = `fluid_video_source_list_item js-source_${level.title}`;
|
|
sourceChangeDiv.innerHTML = `<span class="source_button_icon ${sourceSelectedClass}"></span>${level.title}${hdIndicator}`;
|
|
|
|
sourceChangeDiv.addEventListener('click', event => onSourceChangeClick(event, level));
|
|
|
|
return sourceChangeDiv;
|
|
}
|
|
|
|
function getSourceSelectedClass(level) {
|
|
const matchingLevels = playerInstance.videoSources.filter(source => source.title === playerInstance.fluidStorage.fluidQuality);
|
|
|
|
// If there are multiple matching levels, use the first one
|
|
if (matchingLevels.length > 1) {
|
|
if (matchingLevels[0].id === level.id) {
|
|
return "source_selected";
|
|
}
|
|
} else if (matchingLevels.length === 1) {
|
|
return matchingLevels[0].id === level.id ? "source_selected" : "";
|
|
}
|
|
|
|
// Fallback to auto selection if no persistent level exists
|
|
if (!matchingLevels.length && level.title === 'auto') {
|
|
return "source_selected";
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
function onSourceChangeClick(event, selectedLevel) {
|
|
event.stopPropagation();
|
|
|
|
setPlayerDimensions();
|
|
|
|
const videoChangedTo = event.currentTarget;
|
|
clearSourceSelectedIcons();
|
|
|
|
videoChangedTo.firstChild.classList.add('source_selected');
|
|
|
|
playerInstance.videoSources.forEach(source => {
|
|
if (source.title === videoChangedTo.innerText.replace(/(\r\n\t|\n|\r\t)/gm, '')) {
|
|
playerInstance.hlsPlayer.currentLevel = selectedLevel.id;
|
|
playerInstance.fluidStorage.fluidQuality = selectedLevel.title;
|
|
}
|
|
});
|
|
|
|
playerInstance.openCloseVideoSourceSwitch();
|
|
}
|
|
|
|
// While changing source the player size can flash, we want to set the pixel dimensions then back to 100% afterwards
|
|
function setPlayerDimensions() {
|
|
playerInstance.domRef.player.style.width = `${playerInstance.domRef.player.clientWidth}px`;
|
|
playerInstance.domRef.player.style.height = `${playerInstance.domRef.player.clientHeight}px`;
|
|
}
|
|
|
|
function clearSourceSelectedIcons() {
|
|
const sourceIcons = playerInstance.domRef.wrapper.getElementsByClassName('source_button_icon');
|
|
Array.from(sourceIcons).forEach(icon => icon.classList.remove('source_selected'));
|
|
}
|
|
|
|
function attachSourceChangeList(sourceChangeButton, sourceChangeList) {
|
|
sourceChangeButton.appendChild(sourceChangeList);
|
|
sourceChangeButton.removeEventListener('click', playerInstance.openCloseVideoSourceSwitch);
|
|
sourceChangeButton.addEventListener('click', playerInstance.openCloseVideoSourceSwitch);
|
|
}
|
|
|
|
function setInitialLevel(levels) {
|
|
// Check if a persistency level exists and set the current level accordingly
|
|
const persistedLevel = levels.find(level => level.title === playerInstance.fluidStorage.fluidQuality);
|
|
|
|
if (persistedLevel) {
|
|
playerInstance.hlsPlayer.currentLevel = persistedLevel.id;
|
|
} else {
|
|
// Default to 'auto' if no persisted level is found
|
|
const autoLevel = levels.find(level => level.title === 'auto');
|
|
playerInstance.hlsPlayer.currentLevel = autoLevel.id;
|
|
}
|
|
}
|
|
|
|
function sortLevels(levels) {
|
|
return [...levels].sort((a, b) => {
|
|
// First sort by width in descending order
|
|
if (b.width !== a.width) {
|
|
return b.width - a.width;
|
|
}
|
|
// If width is the same, sort by bitrate in descending order
|
|
return b.bitrate - a.bitrate;
|
|
});
|
|
}
|
|
|
|
playerInstance.detachStreamers = () => {
|
|
if (playerInstance.dashPlayer) {
|
|
playerInstance.dashPlayer.reset();
|
|
playerInstance.dashPlayer = false;
|
|
} else if (playerInstance.hlsPlayer) {
|
|
playerInstance.hlsPlayer.detachMedia();
|
|
playerInstance.hlsPlayer = false;
|
|
}
|
|
};
|
|
}
|