mirror of
https://github.com/video-dev/hls.js.git
synced 2026-06-16 13:34:44 +00:00
Resolves #7474 * Use Widevine KEYID or parse Playready when level keys are present (Update keyUriToKeyIdMap set after KEY_LOADING) * Get KEYID from init segment 'tenc' when not found elsewhere - Fixes #7541 * Support license challenge resolution on "encrypted" event for all key systems
253 lines
7.0 KiB
JavaScript
253 lines
7.0 KiB
JavaScript
/* eslint-disable no-var, no-console */
|
|
|
|
// Browser environment state
|
|
var video;
|
|
var logString = '';
|
|
var hls;
|
|
|
|
function setupConsoleLogRedirection() {
|
|
var log = document.getElementById('log');
|
|
var inner = log.getElementsByClassName('inner')[0];
|
|
|
|
// append log message
|
|
function append(methodName, msg) {
|
|
var a =
|
|
new Date().toISOString().replace('T', ' ').replace('Z', '') + ': ' + msg;
|
|
if ((msg || '').slice(1, 7) === '[test]') {
|
|
var text = document.createTextNode(a);
|
|
var line = document.createElement('pre');
|
|
line.className = 'line line-' + methodName;
|
|
line.appendChild(text);
|
|
inner.appendChild(line);
|
|
}
|
|
|
|
// The empty log line at the beginning comes from a test in `enableLogs`.
|
|
self.logString = logString += a + '\n';
|
|
}
|
|
|
|
// overload global window console methods
|
|
var methods = ['log', 'debug', 'info', 'warn', 'error'];
|
|
methods.forEach(function (methodName) {
|
|
var original = self.console[methodName];
|
|
if (!original) {
|
|
return;
|
|
}
|
|
|
|
self.console[methodName] = function () {
|
|
append(
|
|
methodName,
|
|
Array.prototype.slice
|
|
.call(arguments)
|
|
.map(function (arg) {
|
|
try {
|
|
return JSON.stringify(arg);
|
|
} catch (err) {
|
|
return 'Unserializable (reason: ' + err.message + ')';
|
|
}
|
|
})
|
|
.join(' ')
|
|
);
|
|
|
|
return original.apply(this, arguments);
|
|
};
|
|
});
|
|
}
|
|
|
|
// Object.assign polyfill
|
|
function objectAssign(target) {
|
|
if (target === undefined || target === null) {
|
|
throw new TypeError('Cannot convert first argument to object');
|
|
}
|
|
|
|
var to = Object(target);
|
|
for (var i = 1; i < arguments.length; i++) {
|
|
var nextSource = arguments[i];
|
|
if (nextSource === undefined || nextSource === null) {
|
|
continue;
|
|
}
|
|
|
|
var keysArray = Object.keys(Object(nextSource));
|
|
for (
|
|
var nextIndex = 0, len = keysArray.length;
|
|
nextIndex < len;
|
|
nextIndex++
|
|
) {
|
|
var nextKey = keysArray[nextIndex];
|
|
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
|
|
if (desc && desc.enumerable) {
|
|
to[nextKey] = nextSource[nextKey];
|
|
}
|
|
}
|
|
}
|
|
return to;
|
|
}
|
|
|
|
function startStream(streamUrl, config, callback, autoplay) {
|
|
var Hls = self.Hls;
|
|
if (!Hls) {
|
|
throw new Error('Hls not installed');
|
|
}
|
|
if (!Hls.isSupported()) {
|
|
callback({ code: 'notSupported', logs: logString });
|
|
return;
|
|
}
|
|
if (hls) {
|
|
callback({ code: 'hlsjsAlreadyInitialised', logs: logString });
|
|
return;
|
|
}
|
|
self.video = video = document.getElementById('video');
|
|
try {
|
|
self.hls = hls = new Hls(
|
|
objectAssign(
|
|
{
|
|
// Override `debug` with process.env.DEBUG string -> boolean
|
|
debug: {
|
|
debug: function () {},
|
|
log: console.log.bind(console),
|
|
info: console.info.bind(console, '[info]'),
|
|
warn: console.warn.bind(console, '[warn]'),
|
|
error: console.error.bind(console, '[error]'),
|
|
},
|
|
// Increase TTFB timeout for functional test runs
|
|
fragLoadPolicy: {
|
|
default: {
|
|
maxTimeToFirstByteMs: 20000,
|
|
maxLoadTimeMs: 90000,
|
|
timeoutRetry: {
|
|
maxNumRetry: 2,
|
|
retryDelayMs: 0,
|
|
maxRetryDelayMs: 0,
|
|
},
|
|
errorRetry: {
|
|
maxNumRetry: 1,
|
|
retryDelayMs: 1000,
|
|
maxRetryDelayMs: 8000,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
config
|
|
)
|
|
);
|
|
console.log('[test] > userAgent:', navigator.userAgent);
|
|
if (autoplay !== false) {
|
|
// attempt to ready playback in case test start is treated as a user interaction
|
|
video.src = null;
|
|
video.load();
|
|
|
|
hls.on(Hls.Events.MEDIA_ATTACHED, function () {
|
|
console.log('[test] > Media attached. Calling video.play()');
|
|
var playPromise = video.play();
|
|
if (playPromise) {
|
|
playPromise
|
|
.catch(function (error) {
|
|
if (error.name === 'NotAllowedError') {
|
|
console.log('[test] > Attempting to play with video muted');
|
|
video.muted = true;
|
|
return video.play();
|
|
}
|
|
throw error;
|
|
})
|
|
.then(function () {
|
|
video.controls = true;
|
|
console.log(
|
|
'[test] > video.play() resolved' +
|
|
(video.muted ? ' (muted)' : '') +
|
|
' currentTime: ' +
|
|
video.currentTime
|
|
);
|
|
})
|
|
.catch(function (error) {
|
|
console.log(
|
|
'[test] > video.play()' +
|
|
(video.muted ? ' (muted)' : '') +
|
|
' failed with error: ' +
|
|
error.name +
|
|
' ' +
|
|
error.message
|
|
);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
hls.on(Hls.Events.ERROR, function (event, data) {
|
|
if (data.fatal) {
|
|
console.log('[test] > FAIL: hlsjs fatal error :' + data.details);
|
|
if (data.details === Hls.ErrorDetails.INTERNAL_EXCEPTION) {
|
|
console.log('[test] > exception in :' + data.event);
|
|
console.log(
|
|
data.error.stack
|
|
? JSON.stringify(data.error.stack)
|
|
: data.error.message
|
|
);
|
|
}
|
|
callback({ code: data.details, logs: logString });
|
|
}
|
|
});
|
|
video.onerror = function () {
|
|
console.log('[test] > FAIL: video error, code :' + video.error.code);
|
|
callback({ code: 'video_error_' + video.error.code, logs: logString });
|
|
};
|
|
hls.loadSource(streamUrl);
|
|
hls.attachMedia(video);
|
|
} catch (err) {
|
|
callback({ code: 'exception', logs: logString });
|
|
}
|
|
|
|
if (self === self.window) {
|
|
self.onerror = function (message, source, lineno, colno, error) {
|
|
console.error(
|
|
'[test] > ERROR: ' +
|
|
message +
|
|
'\n' +
|
|
source +
|
|
'\n ln:' +
|
|
lineno +
|
|
' cn: ' +
|
|
colno +
|
|
'\n' +
|
|
error
|
|
);
|
|
callback({ code: 'global exception', logs: logString });
|
|
};
|
|
}
|
|
}
|
|
|
|
function switchToLowestLevel(mode) {
|
|
console.log('[test] > switch to lowest level', mode);
|
|
switch (mode) {
|
|
case 'current':
|
|
hls.currentLevel = 0;
|
|
break;
|
|
case 'next':
|
|
hls.nextLevel = 0;
|
|
break;
|
|
case 'load':
|
|
default:
|
|
hls.loadLevel = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
function switchToHighestLevel(mode) {
|
|
var highestLevel = hls.levels.length - 1;
|
|
console.log('[test] > switch to highest level', highestLevel, mode);
|
|
switch (mode) {
|
|
case 'current':
|
|
hls.currentLevel = highestLevel;
|
|
break;
|
|
case 'next':
|
|
hls.nextLevel = highestLevel;
|
|
break;
|
|
case 'load':
|
|
default:
|
|
hls.loadLevel = highestLevel;
|
|
break;
|
|
}
|
|
}
|
|
|
|
self.setupConsoleLogRedirection = setupConsoleLogRedirection;
|
|
self.startStream = startStream;
|
|
self.switchToHighestLevel = switchToHighestLevel;
|
|
self.switchToLowestLevel = switchToLowestLevel;
|