hls.js 1.5.8-0.canary.10151 → 1.5.8-0.canary.10154
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hls.js +764 -517
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +585 -351
- package/dist/hls.light.js.map +1 -1
- package/dist/hls.light.min.js +1 -1
- package/dist/hls.light.min.js.map +1 -1
- package/dist/hls.light.mjs +579 -351
- package/dist/hls.light.mjs.map +1 -1
- package/dist/hls.min.js +1 -1
- package/dist/hls.min.js.map +1 -1
- package/dist/hls.mjs +788 -547
- package/dist/hls.mjs.map +1 -1
- package/dist/hls.worker.js +1 -1
- package/dist/hls.worker.js.map +1 -1
- package/package.json +2 -2
- package/src/controller/eme-controller.ts +1 -1
- package/src/controller/id3-track-controller.ts +4 -3
- package/src/demux/audio/aacdemuxer.ts +2 -2
- package/src/demux/audio/ac3-demuxer.ts +4 -3
- package/src/demux/audio/base-audio-demuxer.ts +8 -6
- package/src/demux/audio/mp3demuxer.ts +4 -3
- package/src/utils/imsc1-ttml-parser.ts +1 -1
- package/src/utils/keysystem-util.ts +1 -6
- package/src/utils/mp4-tools.ts +1 -1
- package/src/utils/utf8-utils.ts +18 -0
- package/src/utils/webvtt-parser.ts +1 -1
- package/src/demux/id3.ts +0 -411
package/dist/hls.mjs
CHANGED
@@ -512,7 +512,7 @@ function enableLogs(debugConfig, context, id) {
|
|
512
512
|
// Some browsers don't allow to use bind on console object anyway
|
513
513
|
// fallback to default if needed
|
514
514
|
try {
|
515
|
-
newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.8-0.canary.
|
515
|
+
newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.8-0.canary.10154"}`);
|
516
516
|
} catch (e) {
|
517
517
|
/* log fn threw an exception. All logger methods are no-ops. */
|
518
518
|
return createLogger();
|
@@ -1022,6 +1022,22 @@ function base64Decode(base64encodedStr) {
|
|
1022
1022
|
return Uint8Array.from(atob(base64encodedStr), c => c.charCodeAt(0));
|
1023
1023
|
}
|
1024
1024
|
|
1025
|
+
// breaking up those two types in order to clarify what is happening in the decoding path.
|
1026
|
+
|
1027
|
+
// http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197
|
1028
|
+
// http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
|
1029
|
+
/* utf.js - UTF-8 <=> UTF-16 convertion
|
1030
|
+
*
|
1031
|
+
* Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
|
1032
|
+
* Version: 1.0
|
1033
|
+
* LastModified: Dec 25 1999
|
1034
|
+
* This library is free. You can redistribute it and/or modify it.
|
1035
|
+
*/
|
1036
|
+
|
1037
|
+
function strToUtf8array(str) {
|
1038
|
+
return Uint8Array.from(unescape(encodeURIComponent(str)), c => c.charCodeAt(0));
|
1039
|
+
}
|
1040
|
+
|
1025
1041
|
function getKeyIdBytes(str) {
|
1026
1042
|
const keyIdbytes = strToUtf8array(str).subarray(0, 16);
|
1027
1043
|
const paddedkeyIdbytes = new Uint8Array(16);
|
@@ -1059,9 +1075,6 @@ function convertDataUriToArrayBytes(uri) {
|
|
1059
1075
|
}
|
1060
1076
|
return keydata;
|
1061
1077
|
}
|
1062
|
-
function strToUtf8array(str) {
|
1063
|
-
return Uint8Array.from(unescape(encodeURIComponent(str)), c => c.charCodeAt(0));
|
1064
|
-
}
|
1065
1078
|
|
1066
1079
|
var DecrypterAesMode = {
|
1067
1080
|
cbc: 0,
|
@@ -1207,421 +1220,152 @@ function sliceUint8(array, start, end) {
|
|
1207
1220
|
return Uint8Array.prototype.slice ? array.slice(start, end) : new Uint8Array(Array.prototype.slice.call(array, start, end));
|
1208
1221
|
}
|
1209
1222
|
|
1210
|
-
//
|
1211
|
-
|
1212
|
-
|
1213
|
-
*
|
1214
|
-
*
|
1215
|
-
*
|
1223
|
+
// http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197
|
1224
|
+
// http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
|
1225
|
+
/* utf.js - UTF-8 <=> UTF-16 convertion
|
1226
|
+
*
|
1227
|
+
* Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
|
1228
|
+
* Version: 1.0
|
1229
|
+
* LastModified: Dec 25 1999
|
1230
|
+
* This library is free. You can redistribute it and/or modify it.
|
1216
1231
|
*/
|
1217
|
-
const isHeader$2 = (data, offset) => {
|
1218
|
-
/*
|
1219
|
-
* http://id3.org/id3v2.3.0
|
1220
|
-
* [0] = 'I'
|
1221
|
-
* [1] = 'D'
|
1222
|
-
* [2] = '3'
|
1223
|
-
* [3,4] = {Version}
|
1224
|
-
* [5] = {Flags}
|
1225
|
-
* [6-9] = {ID3 Size}
|
1226
|
-
*
|
1227
|
-
* An ID3v2 tag can be detected with the following pattern:
|
1228
|
-
* $49 44 33 yy yy xx zz zz zz zz
|
1229
|
-
* Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80
|
1230
|
-
*/
|
1231
|
-
if (offset + 10 <= data.length) {
|
1232
|
-
// look for 'ID3' identifier
|
1233
|
-
if (data[offset] === 0x49 && data[offset + 1] === 0x44 && data[offset + 2] === 0x33) {
|
1234
|
-
// check version is within range
|
1235
|
-
if (data[offset + 3] < 0xff && data[offset + 4] < 0xff) {
|
1236
|
-
// check size is within range
|
1237
|
-
if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) {
|
1238
|
-
return true;
|
1239
|
-
}
|
1240
|
-
}
|
1241
|
-
}
|
1242
|
-
}
|
1243
|
-
return false;
|
1244
|
-
};
|
1245
|
-
|
1246
1232
|
/**
|
1247
|
-
*
|
1248
|
-
*
|
1249
|
-
* @param
|
1233
|
+
* Converts a UTF-8 array to a string.
|
1234
|
+
*
|
1235
|
+
* @param array - The UTF-8 array to convert
|
1236
|
+
*
|
1237
|
+
* @returns The string
|
1238
|
+
*
|
1239
|
+
* @group Utils
|
1240
|
+
*
|
1241
|
+
* @beta
|
1250
1242
|
*/
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
if (data[offset + 3] < 0xff && data[offset + 4] < 0xff) {
|
1260
|
-
// check size is within range
|
1261
|
-
if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) {
|
1262
|
-
return true;
|
1263
|
-
}
|
1264
|
-
}
|
1243
|
+
function utf8ArrayToStr(array, exitOnNull = false) {
|
1244
|
+
if (typeof TextDecoder !== 'undefined') {
|
1245
|
+
const decoder = new TextDecoder('utf-8');
|
1246
|
+
const decoded = decoder.decode(array);
|
1247
|
+
if (exitOnNull) {
|
1248
|
+
// grab up to the first null
|
1249
|
+
const idx = decoded.indexOf('\0');
|
1250
|
+
return idx !== -1 ? decoded.substring(0, idx) : decoded;
|
1265
1251
|
}
|
1252
|
+
// remove any null characters
|
1253
|
+
return decoded.replace(/\0/g, '');
|
1266
1254
|
}
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1255
|
+
const len = array.length;
|
1256
|
+
let c;
|
1257
|
+
let char2;
|
1258
|
+
let char3;
|
1259
|
+
let out = '';
|
1260
|
+
let i = 0;
|
1261
|
+
while (i < len) {
|
1262
|
+
c = array[i++];
|
1263
|
+
if (c === 0x00 && exitOnNull) {
|
1264
|
+
return out;
|
1265
|
+
} else if (c === 0x00 || c === 0x03) {
|
1266
|
+
// If the character is 3 (END_OF_TEXT) or 0 (NULL) then skip it
|
1267
|
+
continue;
|
1268
|
+
}
|
1269
|
+
switch (c >> 4) {
|
1270
|
+
case 0:
|
1271
|
+
case 1:
|
1272
|
+
case 2:
|
1273
|
+
case 3:
|
1274
|
+
case 4:
|
1275
|
+
case 5:
|
1276
|
+
case 6:
|
1277
|
+
case 7:
|
1278
|
+
// 0xxxxxxx
|
1279
|
+
out += String.fromCharCode(c);
|
1280
|
+
break;
|
1281
|
+
case 12:
|
1282
|
+
case 13:
|
1283
|
+
// 110x xxxx 10xx xxxx
|
1284
|
+
char2 = array[i++];
|
1285
|
+
out += String.fromCharCode((c & 0x1f) << 6 | char2 & 0x3f);
|
1286
|
+
break;
|
1287
|
+
case 14:
|
1288
|
+
// 1110 xxxx 10xx xxxx 10xx xxxx
|
1289
|
+
char2 = array[i++];
|
1290
|
+
char3 = array[i++];
|
1291
|
+
out += String.fromCharCode((c & 0x0f) << 12 | (char2 & 0x3f) << 6 | (char3 & 0x3f) << 0);
|
1292
|
+
break;
|
1288
1293
|
}
|
1289
|
-
offset += length;
|
1290
|
-
}
|
1291
|
-
if (length > 0) {
|
1292
|
-
return data.subarray(front, front + length);
|
1293
1294
|
}
|
1294
|
-
return
|
1295
|
-
}
|
1296
|
-
const readSize = (data, offset) => {
|
1297
|
-
let size = 0;
|
1298
|
-
size = (data[offset] & 0x7f) << 21;
|
1299
|
-
size |= (data[offset + 1] & 0x7f) << 14;
|
1300
|
-
size |= (data[offset + 2] & 0x7f) << 7;
|
1301
|
-
size |= data[offset + 3] & 0x7f;
|
1302
|
-
return size;
|
1303
|
-
};
|
1304
|
-
const canParse$2 = (data, offset) => {
|
1305
|
-
return isHeader$2(data, offset) && readSize(data, offset + 6) + 10 <= data.length - offset;
|
1306
|
-
};
|
1295
|
+
return out;
|
1296
|
+
}
|
1307
1297
|
|
1308
1298
|
/**
|
1309
|
-
*
|
1310
|
-
* @param data - Block of data containing one or more ID3 tags
|
1299
|
+
* hex dump helper class
|
1311
1300
|
*/
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1301
|
+
|
1302
|
+
const Hex = {
|
1303
|
+
hexDump: function (array) {
|
1304
|
+
let str = '';
|
1305
|
+
for (let i = 0; i < array.length; i++) {
|
1306
|
+
let h = array[i].toString(16);
|
1307
|
+
if (h.length < 2) {
|
1308
|
+
h = '0' + h;
|
1309
|
+
}
|
1310
|
+
str += h;
|
1318
1311
|
}
|
1312
|
+
return str;
|
1319
1313
|
}
|
1320
|
-
return undefined;
|
1321
1314
|
};
|
1322
1315
|
|
1323
|
-
|
1324
|
-
|
1325
|
-
*/
|
1326
|
-
const isTimeStampFrame = frame => {
|
1327
|
-
return frame && frame.key === 'PRIV' && frame.info === 'com.apple.streaming.transportStreamTimestamp';
|
1328
|
-
};
|
1329
|
-
const getFrameData = data => {
|
1330
|
-
/*
|
1331
|
-
Frame ID $xx xx xx xx (four characters)
|
1332
|
-
Size $xx xx xx xx
|
1333
|
-
Flags $xx xx
|
1334
|
-
*/
|
1335
|
-
const type = String.fromCharCode(data[0], data[1], data[2], data[3]);
|
1336
|
-
const size = readSize(data, 4);
|
1316
|
+
const UINT32_MAX$1 = Math.pow(2, 32) - 1;
|
1317
|
+
const push = [].push;
|
1337
1318
|
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1319
|
+
// We are using fixed track IDs for driving the MP4 remuxer
|
1320
|
+
// instead of following the TS PIDs.
|
1321
|
+
// There is no reason not to do this and some browsers/SourceBuffer-demuxers
|
1322
|
+
// may not like if there are TrackID "switches"
|
1323
|
+
// See https://github.com/video-dev/hls.js/issues/1331
|
1324
|
+
// Here we are mapping our internal track types to constant MP4 track IDs
|
1325
|
+
// With MSE currently one can only have one track of each, and we are muxing
|
1326
|
+
// whatever video/audio rendition in them.
|
1327
|
+
const RemuxerTrackIdConfig = {
|
1328
|
+
video: 1,
|
1329
|
+
audio: 2,
|
1330
|
+
id3: 3,
|
1331
|
+
text: 4
|
1345
1332
|
};
|
1333
|
+
function bin2str(data) {
|
1334
|
+
return String.fromCharCode.apply(null, data);
|
1335
|
+
}
|
1336
|
+
function readUint16(buffer, offset) {
|
1337
|
+
const val = buffer[offset] << 8 | buffer[offset + 1];
|
1338
|
+
return val < 0 ? 65536 + val : val;
|
1339
|
+
}
|
1340
|
+
function readUint32(buffer, offset) {
|
1341
|
+
const val = readSint32(buffer, offset);
|
1342
|
+
return val < 0 ? 4294967296 + val : val;
|
1343
|
+
}
|
1344
|
+
function readUint64(buffer, offset) {
|
1345
|
+
let result = readUint32(buffer, offset);
|
1346
|
+
result *= Math.pow(2, 32);
|
1347
|
+
result += readUint32(buffer, offset + 4);
|
1348
|
+
return result;
|
1349
|
+
}
|
1350
|
+
function readSint32(buffer, offset) {
|
1351
|
+
return buffer[offset] << 24 | buffer[offset + 1] << 16 | buffer[offset + 2] << 8 | buffer[offset + 3];
|
1352
|
+
}
|
1353
|
+
function writeUint32(buffer, offset, value) {
|
1354
|
+
buffer[offset] = value >> 24;
|
1355
|
+
buffer[offset + 1] = value >> 16 & 0xff;
|
1356
|
+
buffer[offset + 2] = value >> 8 & 0xff;
|
1357
|
+
buffer[offset + 3] = value & 0xff;
|
1358
|
+
}
|
1346
1359
|
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
1351
|
-
const
|
1352
|
-
|
1353
|
-
|
1354
|
-
while (isHeader$2(id3Data, offset)) {
|
1355
|
-
const size = readSize(id3Data, offset + 6);
|
1356
|
-
// skip past ID3 header
|
1357
|
-
offset += 10;
|
1358
|
-
const end = offset + size;
|
1359
|
-
// loop through frames in the ID3 tag
|
1360
|
-
while (offset + 8 < end) {
|
1361
|
-
const frameData = getFrameData(id3Data.subarray(offset));
|
1362
|
-
const frame = decodeFrame(frameData);
|
1363
|
-
if (frame) {
|
1364
|
-
frames.push(frame);
|
1365
|
-
}
|
1366
|
-
|
1367
|
-
// skip frame header and frame data
|
1368
|
-
offset += frameData.size + 10;
|
1369
|
-
}
|
1370
|
-
if (isFooter(id3Data, offset)) {
|
1371
|
-
offset += 10;
|
1360
|
+
// Find "moof" box
|
1361
|
+
function hasMoofData(data) {
|
1362
|
+
const end = data.byteLength;
|
1363
|
+
for (let i = 0; i < end;) {
|
1364
|
+
const size = readUint32(data, i);
|
1365
|
+
if (size > 8 && data[i + 4] === 0x6d && data[i + 5] === 0x6f && data[i + 6] === 0x6f && data[i + 7] === 0x66) {
|
1366
|
+
return true;
|
1372
1367
|
}
|
1373
|
-
|
1374
|
-
return frames;
|
1375
|
-
};
|
1376
|
-
const decodeFrame = frame => {
|
1377
|
-
if (frame.type === 'PRIV') {
|
1378
|
-
return decodePrivFrame(frame);
|
1379
|
-
} else if (frame.type[0] === 'W') {
|
1380
|
-
return decodeURLFrame(frame);
|
1381
|
-
}
|
1382
|
-
return decodeTextFrame(frame);
|
1383
|
-
};
|
1384
|
-
const decodePrivFrame = frame => {
|
1385
|
-
/*
|
1386
|
-
Format: <text string>\0<binary data>
|
1387
|
-
*/
|
1388
|
-
if (frame.size < 2) {
|
1389
|
-
return undefined;
|
1390
|
-
}
|
1391
|
-
const owner = utf8ArrayToStr(frame.data, true);
|
1392
|
-
const privateData = new Uint8Array(frame.data.subarray(owner.length + 1));
|
1393
|
-
return {
|
1394
|
-
key: frame.type,
|
1395
|
-
info: owner,
|
1396
|
-
data: privateData.buffer
|
1397
|
-
};
|
1398
|
-
};
|
1399
|
-
const decodeTextFrame = frame => {
|
1400
|
-
if (frame.size < 2) {
|
1401
|
-
return undefined;
|
1402
|
-
}
|
1403
|
-
if (frame.type === 'TXXX') {
|
1404
|
-
/*
|
1405
|
-
Format:
|
1406
|
-
[0] = {Text Encoding}
|
1407
|
-
[1-?] = {Description}\0{Value}
|
1408
|
-
*/
|
1409
|
-
let index = 1;
|
1410
|
-
const description = utf8ArrayToStr(frame.data.subarray(index), true);
|
1411
|
-
index += description.length + 1;
|
1412
|
-
const value = utf8ArrayToStr(frame.data.subarray(index));
|
1413
|
-
return {
|
1414
|
-
key: frame.type,
|
1415
|
-
info: description,
|
1416
|
-
data: value
|
1417
|
-
};
|
1418
|
-
}
|
1419
|
-
/*
|
1420
|
-
Format:
|
1421
|
-
[0] = {Text Encoding}
|
1422
|
-
[1-?] = {Value}
|
1423
|
-
*/
|
1424
|
-
const text = utf8ArrayToStr(frame.data.subarray(1));
|
1425
|
-
return {
|
1426
|
-
key: frame.type,
|
1427
|
-
data: text
|
1428
|
-
};
|
1429
|
-
};
|
1430
|
-
const decodeURLFrame = frame => {
|
1431
|
-
if (frame.type === 'WXXX') {
|
1432
|
-
/*
|
1433
|
-
Format:
|
1434
|
-
[0] = {Text Encoding}
|
1435
|
-
[1-?] = {Description}\0{URL}
|
1436
|
-
*/
|
1437
|
-
if (frame.size < 2) {
|
1438
|
-
return undefined;
|
1439
|
-
}
|
1440
|
-
let index = 1;
|
1441
|
-
const description = utf8ArrayToStr(frame.data.subarray(index), true);
|
1442
|
-
index += description.length + 1;
|
1443
|
-
const value = utf8ArrayToStr(frame.data.subarray(index));
|
1444
|
-
return {
|
1445
|
-
key: frame.type,
|
1446
|
-
info: description,
|
1447
|
-
data: value
|
1448
|
-
};
|
1449
|
-
}
|
1450
|
-
/*
|
1451
|
-
Format:
|
1452
|
-
[0-?] = {URL}
|
1453
|
-
*/
|
1454
|
-
const url = utf8ArrayToStr(frame.data);
|
1455
|
-
return {
|
1456
|
-
key: frame.type,
|
1457
|
-
data: url
|
1458
|
-
};
|
1459
|
-
};
|
1460
|
-
const readTimeStamp = timeStampFrame => {
|
1461
|
-
if (timeStampFrame.data.byteLength === 8) {
|
1462
|
-
const data = new Uint8Array(timeStampFrame.data);
|
1463
|
-
// timestamp is 33 bit expressed as a big-endian eight-octet number,
|
1464
|
-
// with the upper 31 bits set to zero.
|
1465
|
-
const pts33Bit = data[3] & 0x1;
|
1466
|
-
let timestamp = (data[4] << 23) + (data[5] << 15) + (data[6] << 7) + data[7];
|
1467
|
-
timestamp /= 45;
|
1468
|
-
if (pts33Bit) {
|
1469
|
-
timestamp += 47721858.84;
|
1470
|
-
} // 2^32 / 90
|
1471
|
-
|
1472
|
-
return Math.round(timestamp);
|
1473
|
-
}
|
1474
|
-
return undefined;
|
1475
|
-
};
|
1476
|
-
|
1477
|
-
// http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197
|
1478
|
-
// http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
|
1479
|
-
/* utf.js - UTF-8 <=> UTF-16 convertion
|
1480
|
-
*
|
1481
|
-
* Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
|
1482
|
-
* Version: 1.0
|
1483
|
-
* LastModified: Dec 25 1999
|
1484
|
-
* This library is free. You can redistribute it and/or modify it.
|
1485
|
-
*/
|
1486
|
-
const utf8ArrayToStr = (array, exitOnNull = false) => {
|
1487
|
-
const decoder = getTextDecoder();
|
1488
|
-
if (decoder) {
|
1489
|
-
const decoded = decoder.decode(array);
|
1490
|
-
if (exitOnNull) {
|
1491
|
-
// grab up to the first null
|
1492
|
-
const idx = decoded.indexOf('\0');
|
1493
|
-
return idx !== -1 ? decoded.substring(0, idx) : decoded;
|
1494
|
-
}
|
1495
|
-
|
1496
|
-
// remove any null characters
|
1497
|
-
return decoded.replace(/\0/g, '');
|
1498
|
-
}
|
1499
|
-
const len = array.length;
|
1500
|
-
let c;
|
1501
|
-
let char2;
|
1502
|
-
let char3;
|
1503
|
-
let out = '';
|
1504
|
-
let i = 0;
|
1505
|
-
while (i < len) {
|
1506
|
-
c = array[i++];
|
1507
|
-
if (c === 0x00 && exitOnNull) {
|
1508
|
-
return out;
|
1509
|
-
} else if (c === 0x00 || c === 0x03) {
|
1510
|
-
// If the character is 3 (END_OF_TEXT) or 0 (NULL) then skip it
|
1511
|
-
continue;
|
1512
|
-
}
|
1513
|
-
switch (c >> 4) {
|
1514
|
-
case 0:
|
1515
|
-
case 1:
|
1516
|
-
case 2:
|
1517
|
-
case 3:
|
1518
|
-
case 4:
|
1519
|
-
case 5:
|
1520
|
-
case 6:
|
1521
|
-
case 7:
|
1522
|
-
// 0xxxxxxx
|
1523
|
-
out += String.fromCharCode(c);
|
1524
|
-
break;
|
1525
|
-
case 12:
|
1526
|
-
case 13:
|
1527
|
-
// 110x xxxx 10xx xxxx
|
1528
|
-
char2 = array[i++];
|
1529
|
-
out += String.fromCharCode((c & 0x1f) << 6 | char2 & 0x3f);
|
1530
|
-
break;
|
1531
|
-
case 14:
|
1532
|
-
// 1110 xxxx 10xx xxxx 10xx xxxx
|
1533
|
-
char2 = array[i++];
|
1534
|
-
char3 = array[i++];
|
1535
|
-
out += String.fromCharCode((c & 0x0f) << 12 | (char2 & 0x3f) << 6 | (char3 & 0x3f) << 0);
|
1536
|
-
break;
|
1537
|
-
}
|
1538
|
-
}
|
1539
|
-
return out;
|
1540
|
-
};
|
1541
|
-
let decoder;
|
1542
|
-
function getTextDecoder() {
|
1543
|
-
// On Play Station 4, TextDecoder is defined but partially implemented.
|
1544
|
-
// Manual decoding option is preferable
|
1545
|
-
if (navigator.userAgent.includes('PlayStation 4')) {
|
1546
|
-
return;
|
1547
|
-
}
|
1548
|
-
if (!decoder && typeof self.TextDecoder !== 'undefined') {
|
1549
|
-
decoder = new self.TextDecoder('utf-8');
|
1550
|
-
}
|
1551
|
-
return decoder;
|
1552
|
-
}
|
1553
|
-
|
1554
|
-
/**
|
1555
|
-
* hex dump helper class
|
1556
|
-
*/
|
1557
|
-
|
1558
|
-
const Hex = {
|
1559
|
-
hexDump: function (array) {
|
1560
|
-
let str = '';
|
1561
|
-
for (let i = 0; i < array.length; i++) {
|
1562
|
-
let h = array[i].toString(16);
|
1563
|
-
if (h.length < 2) {
|
1564
|
-
h = '0' + h;
|
1565
|
-
}
|
1566
|
-
str += h;
|
1567
|
-
}
|
1568
|
-
return str;
|
1569
|
-
}
|
1570
|
-
};
|
1571
|
-
|
1572
|
-
const UINT32_MAX$1 = Math.pow(2, 32) - 1;
|
1573
|
-
const push = [].push;
|
1574
|
-
|
1575
|
-
// We are using fixed track IDs for driving the MP4 remuxer
|
1576
|
-
// instead of following the TS PIDs.
|
1577
|
-
// There is no reason not to do this and some browsers/SourceBuffer-demuxers
|
1578
|
-
// may not like if there are TrackID "switches"
|
1579
|
-
// See https://github.com/video-dev/hls.js/issues/1331
|
1580
|
-
// Here we are mapping our internal track types to constant MP4 track IDs
|
1581
|
-
// With MSE currently one can only have one track of each, and we are muxing
|
1582
|
-
// whatever video/audio rendition in them.
|
1583
|
-
const RemuxerTrackIdConfig = {
|
1584
|
-
video: 1,
|
1585
|
-
audio: 2,
|
1586
|
-
id3: 3,
|
1587
|
-
text: 4
|
1588
|
-
};
|
1589
|
-
function bin2str(data) {
|
1590
|
-
return String.fromCharCode.apply(null, data);
|
1591
|
-
}
|
1592
|
-
function readUint16(buffer, offset) {
|
1593
|
-
const val = buffer[offset] << 8 | buffer[offset + 1];
|
1594
|
-
return val < 0 ? 65536 + val : val;
|
1595
|
-
}
|
1596
|
-
function readUint32(buffer, offset) {
|
1597
|
-
const val = readSint32(buffer, offset);
|
1598
|
-
return val < 0 ? 4294967296 + val : val;
|
1599
|
-
}
|
1600
|
-
function readUint64(buffer, offset) {
|
1601
|
-
let result = readUint32(buffer, offset);
|
1602
|
-
result *= Math.pow(2, 32);
|
1603
|
-
result += readUint32(buffer, offset + 4);
|
1604
|
-
return result;
|
1605
|
-
}
|
1606
|
-
function readSint32(buffer, offset) {
|
1607
|
-
return buffer[offset] << 24 | buffer[offset + 1] << 16 | buffer[offset + 2] << 8 | buffer[offset + 3];
|
1608
|
-
}
|
1609
|
-
function writeUint32(buffer, offset, value) {
|
1610
|
-
buffer[offset] = value >> 24;
|
1611
|
-
buffer[offset + 1] = value >> 16 & 0xff;
|
1612
|
-
buffer[offset + 2] = value >> 8 & 0xff;
|
1613
|
-
buffer[offset + 3] = value & 0xff;
|
1614
|
-
}
|
1615
|
-
|
1616
|
-
// Find "moof" box
|
1617
|
-
function hasMoofData(data) {
|
1618
|
-
const end = data.byteLength;
|
1619
|
-
for (let i = 0; i < end;) {
|
1620
|
-
const size = readUint32(data, i);
|
1621
|
-
if (size > 8 && data[i + 4] === 0x6d && data[i + 5] === 0x6f && data[i + 6] === 0x6f && data[i + 7] === 0x66) {
|
1622
|
-
return true;
|
1623
|
-
}
|
1624
|
-
i = size > 1 ? i + size : end;
|
1368
|
+
i = size > 1 ? i + size : end;
|
1625
1369
|
}
|
1626
1370
|
return false;
|
1627
1371
|
}
|
@@ -4405,42 +4149,545 @@ function getFirstCueIndexAfterTime(cues, time) {
|
|
4405
4149
|
return mid;
|
4406
4150
|
}
|
4407
4151
|
}
|
4408
|
-
// At this point, left and right have swapped.
|
4409
|
-
// No direct match was found, left or right element must be the closest. Check which one has the smallest diff.
|
4410
|
-
return cues[left].startTime - time < time - cues[right].startTime ? left : right;
|
4152
|
+
// At this point, left and right have swapped.
|
4153
|
+
// No direct match was found, left or right element must be the closest. Check which one has the smallest diff.
|
4154
|
+
return cues[left].startTime - time < time - cues[right].startTime ? left : right;
|
4155
|
+
}
|
4156
|
+
function getCuesInRange(cues, start, end) {
|
4157
|
+
const cuesFound = [];
|
4158
|
+
const firstCueInRange = getFirstCueIndexAfterTime(cues, start);
|
4159
|
+
if (firstCueInRange > -1) {
|
4160
|
+
for (let i = firstCueInRange, len = cues.length; i < len; i++) {
|
4161
|
+
const cue = cues[i];
|
4162
|
+
if (cue.startTime >= start && cue.endTime <= end) {
|
4163
|
+
cuesFound.push(cue);
|
4164
|
+
} else if (cue.startTime > end) {
|
4165
|
+
return cuesFound;
|
4166
|
+
}
|
4167
|
+
}
|
4168
|
+
}
|
4169
|
+
return cuesFound;
|
4170
|
+
}
|
4171
|
+
function filterSubtitleTracks(textTrackList) {
|
4172
|
+
const tracks = [];
|
4173
|
+
for (let i = 0; i < textTrackList.length; i++) {
|
4174
|
+
const track = textTrackList[i];
|
4175
|
+
// Edge adds a track without a label; we don't want to use it
|
4176
|
+
if ((track.kind === 'subtitles' || track.kind === 'captions') && track.label) {
|
4177
|
+
tracks.push(textTrackList[i]);
|
4178
|
+
}
|
4179
|
+
}
|
4180
|
+
return tracks;
|
4181
|
+
}
|
4182
|
+
|
4183
|
+
var MetadataSchema = {
|
4184
|
+
audioId3: "org.id3",
|
4185
|
+
dateRange: "com.apple.quicktime.HLS",
|
4186
|
+
emsg: "https://aomedia.org/emsg/ID3"
|
4187
|
+
};
|
4188
|
+
|
4189
|
+
/**
|
4190
|
+
* Decode an ID3 PRIV frame.
|
4191
|
+
*
|
4192
|
+
* @param frame - the ID3 PRIV frame
|
4193
|
+
*
|
4194
|
+
* @returns The decoded ID3 PRIV frame
|
4195
|
+
*
|
4196
|
+
* @internal
|
4197
|
+
*
|
4198
|
+
* @group ID3
|
4199
|
+
*/
|
4200
|
+
function decodeId3PrivFrame(frame) {
|
4201
|
+
/*
|
4202
|
+
Format: <text string>\0<binary data>
|
4203
|
+
*/
|
4204
|
+
if (frame.size < 2) {
|
4205
|
+
return undefined;
|
4206
|
+
}
|
4207
|
+
const owner = utf8ArrayToStr(frame.data, true);
|
4208
|
+
const privateData = new Uint8Array(frame.data.subarray(owner.length + 1));
|
4209
|
+
return {
|
4210
|
+
key: frame.type,
|
4211
|
+
info: owner,
|
4212
|
+
data: privateData.buffer
|
4213
|
+
};
|
4214
|
+
}
|
4215
|
+
|
4216
|
+
/**
|
4217
|
+
* Decodes an ID3 text frame
|
4218
|
+
*
|
4219
|
+
* @param frame - the ID3 text frame
|
4220
|
+
*
|
4221
|
+
* @returns The decoded ID3 text frame
|
4222
|
+
*
|
4223
|
+
* @internal
|
4224
|
+
*
|
4225
|
+
* @group ID3
|
4226
|
+
*/
|
4227
|
+
function decodeId3TextFrame(frame) {
|
4228
|
+
if (frame.size < 2) {
|
4229
|
+
return undefined;
|
4230
|
+
}
|
4231
|
+
if (frame.type === 'TXXX') {
|
4232
|
+
/*
|
4233
|
+
Format:
|
4234
|
+
[0] = {Text Encoding}
|
4235
|
+
[1-?] = {Description}\0{Value}
|
4236
|
+
*/
|
4237
|
+
let index = 1;
|
4238
|
+
const description = utf8ArrayToStr(frame.data.subarray(index), true);
|
4239
|
+
index += description.length + 1;
|
4240
|
+
const value = utf8ArrayToStr(frame.data.subarray(index));
|
4241
|
+
return {
|
4242
|
+
key: frame.type,
|
4243
|
+
info: description,
|
4244
|
+
data: value
|
4245
|
+
};
|
4246
|
+
}
|
4247
|
+
/*
|
4248
|
+
Format:
|
4249
|
+
[0] = {Text Encoding}
|
4250
|
+
[1-?] = {Value}
|
4251
|
+
*/
|
4252
|
+
const text = utf8ArrayToStr(frame.data.subarray(1));
|
4253
|
+
return {
|
4254
|
+
key: frame.type,
|
4255
|
+
info: '',
|
4256
|
+
data: text
|
4257
|
+
};
|
4258
|
+
}
|
4259
|
+
|
4260
|
+
/**
|
4261
|
+
* Decode a URL frame
|
4262
|
+
*
|
4263
|
+
* @param frame - the ID3 URL frame
|
4264
|
+
*
|
4265
|
+
* @returns The decoded ID3 URL frame
|
4266
|
+
*
|
4267
|
+
* @internal
|
4268
|
+
*
|
4269
|
+
* @group ID3
|
4270
|
+
*/
|
4271
|
+
function decodeId3UrlFrame(frame) {
|
4272
|
+
if (frame.type === 'WXXX') {
|
4273
|
+
/*
|
4274
|
+
Format:
|
4275
|
+
[0] = {Text Encoding}
|
4276
|
+
[1-?] = {Description}\0{URL}
|
4277
|
+
*/
|
4278
|
+
if (frame.size < 2) {
|
4279
|
+
return undefined;
|
4280
|
+
}
|
4281
|
+
let index = 1;
|
4282
|
+
const description = utf8ArrayToStr(frame.data.subarray(index), true);
|
4283
|
+
index += description.length + 1;
|
4284
|
+
const value = utf8ArrayToStr(frame.data.subarray(index));
|
4285
|
+
return {
|
4286
|
+
key: frame.type,
|
4287
|
+
info: description,
|
4288
|
+
data: value
|
4289
|
+
};
|
4290
|
+
}
|
4291
|
+
/*
|
4292
|
+
Format:
|
4293
|
+
[0-?] = {URL}
|
4294
|
+
*/
|
4295
|
+
const url = utf8ArrayToStr(frame.data);
|
4296
|
+
return {
|
4297
|
+
key: frame.type,
|
4298
|
+
info: '',
|
4299
|
+
data: url
|
4300
|
+
};
|
4301
|
+
}
|
4302
|
+
|
4303
|
+
function toUint8(data, offset = 0, length = Infinity) {
|
4304
|
+
return view(data, offset, length, Uint8Array);
|
4305
|
+
}
|
4306
|
+
function view(data, offset, length, Type) {
|
4307
|
+
const buffer = unsafeGetArrayBuffer(data);
|
4308
|
+
let bytesPerElement = 1;
|
4309
|
+
if ('BYTES_PER_ELEMENT' in Type) {
|
4310
|
+
bytesPerElement = Type.BYTES_PER_ELEMENT;
|
4311
|
+
}
|
4312
|
+
// Absolute end of the |data| view within |buffer|.
|
4313
|
+
const dataOffset = isArrayBufferView(data) ? data.byteOffset : 0;
|
4314
|
+
const dataEnd = (dataOffset + data.byteLength) / bytesPerElement;
|
4315
|
+
// Absolute start of the result within |buffer|.
|
4316
|
+
const rawStart = (dataOffset + offset) / bytesPerElement;
|
4317
|
+
const start = Math.floor(Math.max(0, Math.min(rawStart, dataEnd)));
|
4318
|
+
// Absolute end of the result within |buffer|.
|
4319
|
+
const end = Math.floor(Math.min(start + Math.max(length, 0), dataEnd));
|
4320
|
+
return new Type(buffer, start, end - start);
|
4321
|
+
}
|
4322
|
+
function unsafeGetArrayBuffer(view) {
|
4323
|
+
if (view instanceof ArrayBuffer) {
|
4324
|
+
return view;
|
4325
|
+
} else {
|
4326
|
+
return view.buffer;
|
4327
|
+
}
|
4328
|
+
}
|
4329
|
+
function isArrayBufferView(obj) {
|
4330
|
+
return obj && obj.buffer instanceof ArrayBuffer && obj.byteLength !== undefined && obj.byteOffset !== undefined;
|
4331
|
+
}
|
4332
|
+
|
4333
|
+
function toArrayBuffer(view) {
|
4334
|
+
if (view instanceof ArrayBuffer) {
|
4335
|
+
return view;
|
4336
|
+
} else {
|
4337
|
+
if (view.byteOffset == 0 && view.byteLength == view.buffer.byteLength) {
|
4338
|
+
// This is a TypedArray over the whole buffer.
|
4339
|
+
return view.buffer;
|
4340
|
+
}
|
4341
|
+
// This is a 'view' on the buffer. Create a new buffer that only contains
|
4342
|
+
// the data. Note that since this isn't an ArrayBuffer, the 'new' call
|
4343
|
+
// will allocate a new buffer to hold the copy.
|
4344
|
+
return new Uint8Array(view).buffer;
|
4345
|
+
}
|
4346
|
+
}
|
4347
|
+
|
4348
|
+
/**
|
4349
|
+
* Encodes binary data to base64
|
4350
|
+
*
|
4351
|
+
* @param binary - The binary data to encode
|
4352
|
+
* @returns The base64 encoded string
|
4353
|
+
*
|
4354
|
+
* @group Utils
|
4355
|
+
*
|
4356
|
+
* @beta
|
4357
|
+
*/
|
4358
|
+
function base64encode(binary) {
|
4359
|
+
return btoa(String.fromCharCode(...binary));
|
4360
|
+
}
|
4361
|
+
|
4362
|
+
/**
|
4363
|
+
* This implements the rounding procedure described in step 2 of the "Serializing a Decimal" specification.
|
4364
|
+
* This rounding style is known as "even rounding", "banker's rounding", or "commercial rounding".
|
4365
|
+
*
|
4366
|
+
* @param value - The value to round
|
4367
|
+
* @param precision - The number of decimal places to round to
|
4368
|
+
* @returns The rounded value
|
4369
|
+
*
|
4370
|
+
* @group Utils
|
4371
|
+
*
|
4372
|
+
* @beta
|
4373
|
+
*/
|
4374
|
+
function roundToEven(value, precision) {
|
4375
|
+
if (value < 0) {
|
4376
|
+
return -roundToEven(-value, precision);
|
4377
|
+
}
|
4378
|
+
const decimalShift = Math.pow(10, precision);
|
4379
|
+
const isEquidistant = Math.abs(value * decimalShift % 1 - 0.5) < Number.EPSILON;
|
4380
|
+
if (isEquidistant) {
|
4381
|
+
// If the tail of the decimal place is 'equidistant' we round to the nearest even value
|
4382
|
+
const flooredValue = Math.floor(value * decimalShift);
|
4383
|
+
return (flooredValue % 2 === 0 ? flooredValue : flooredValue + 1) / decimalShift;
|
4384
|
+
} else {
|
4385
|
+
// Otherwise, proceed as normal
|
4386
|
+
return Math.round(value * decimalShift) / decimalShift;
|
4387
|
+
}
|
4388
|
+
}
|
4389
|
+
|
4390
|
+
/**
|
4391
|
+
* Constructs a relative path from a URL.
|
4392
|
+
*
|
4393
|
+
* @param url - The destination URL
|
4394
|
+
* @param base - The base URL
|
4395
|
+
* @returns The relative path
|
4396
|
+
*
|
4397
|
+
* @group Utils
|
4398
|
+
*
|
4399
|
+
* @beta
|
4400
|
+
*/
|
4401
|
+
function urlToRelativePath(url, base) {
|
4402
|
+
const to = new URL(url);
|
4403
|
+
const from = new URL(base);
|
4404
|
+
if (to.origin !== from.origin) {
|
4405
|
+
return url;
|
4406
|
+
}
|
4407
|
+
const toPath = to.pathname.split('/').slice(1);
|
4408
|
+
const fromPath = from.pathname.split('/').slice(1, -1);
|
4409
|
+
// remove common parents
|
4410
|
+
while (toPath[0] === fromPath[0]) {
|
4411
|
+
toPath.shift();
|
4412
|
+
fromPath.shift();
|
4413
|
+
}
|
4414
|
+
// add back paths
|
4415
|
+
while (fromPath.length) {
|
4416
|
+
fromPath.shift();
|
4417
|
+
toPath.unshift('..');
|
4418
|
+
}
|
4419
|
+
return toPath.join('/');
|
4420
|
+
}
|
4421
|
+
|
4422
|
+
/**
|
4423
|
+
* Generate a random v4 UUID
|
4424
|
+
*
|
4425
|
+
* @returns A random v4 UUID
|
4426
|
+
*
|
4427
|
+
* @group Utils
|
4428
|
+
*
|
4429
|
+
* @beta
|
4430
|
+
*/
|
4431
|
+
function uuid() {
|
4432
|
+
try {
|
4433
|
+
return crypto.randomUUID();
|
4434
|
+
} catch (error) {
|
4435
|
+
try {
|
4436
|
+
const url = URL.createObjectURL(new Blob());
|
4437
|
+
const uuid = url.toString();
|
4438
|
+
URL.revokeObjectURL(url);
|
4439
|
+
return uuid.slice(uuid.lastIndexOf('/') + 1);
|
4440
|
+
} catch (error) {
|
4441
|
+
let dt = new Date().getTime();
|
4442
|
+
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
4443
|
+
const r = (dt + Math.random() * 16) % 16 | 0;
|
4444
|
+
dt = Math.floor(dt / 16);
|
4445
|
+
return (c == 'x' ? r : r & 0x3 | 0x8).toString(16);
|
4446
|
+
});
|
4447
|
+
return uuid;
|
4448
|
+
}
|
4449
|
+
}
|
4450
|
+
}
|
4451
|
+
|
4452
|
+
function decodeId3ImageFrame(frame) {
|
4453
|
+
const metadataFrame = {
|
4454
|
+
key: frame.type,
|
4455
|
+
description: '',
|
4456
|
+
data: '',
|
4457
|
+
mimeType: null,
|
4458
|
+
pictureType: null
|
4459
|
+
};
|
4460
|
+
const utf8Encoding = 0x03;
|
4461
|
+
if (frame.size < 2) {
|
4462
|
+
return undefined;
|
4463
|
+
}
|
4464
|
+
if (frame.data[0] !== utf8Encoding) {
|
4465
|
+
console.log('Ignore frame with unrecognized character ' + 'encoding');
|
4466
|
+
return undefined;
|
4467
|
+
}
|
4468
|
+
const mimeTypeEndIndex = frame.data.subarray(1).indexOf(0);
|
4469
|
+
if (mimeTypeEndIndex === -1) {
|
4470
|
+
return undefined;
|
4471
|
+
}
|
4472
|
+
const mimeType = utf8ArrayToStr(toUint8(frame.data, 1, mimeTypeEndIndex));
|
4473
|
+
const pictureType = frame.data[2 + mimeTypeEndIndex];
|
4474
|
+
const descriptionEndIndex = frame.data.subarray(3 + mimeTypeEndIndex).indexOf(0);
|
4475
|
+
if (descriptionEndIndex === -1) {
|
4476
|
+
return undefined;
|
4477
|
+
}
|
4478
|
+
const description = utf8ArrayToStr(toUint8(frame.data, 3 + mimeTypeEndIndex, descriptionEndIndex));
|
4479
|
+
let data;
|
4480
|
+
if (mimeType === '-->') {
|
4481
|
+
data = utf8ArrayToStr(toUint8(frame.data, 4 + mimeTypeEndIndex + descriptionEndIndex));
|
4482
|
+
} else {
|
4483
|
+
data = toArrayBuffer(frame.data.subarray(4 + mimeTypeEndIndex + descriptionEndIndex));
|
4484
|
+
}
|
4485
|
+
metadataFrame.mimeType = mimeType;
|
4486
|
+
metadataFrame.pictureType = pictureType;
|
4487
|
+
metadataFrame.description = description;
|
4488
|
+
metadataFrame.data = data;
|
4489
|
+
return metadataFrame;
|
4490
|
+
}
|
4491
|
+
|
4492
|
+
/**
|
4493
|
+
* Decode an ID3 frame.
|
4494
|
+
*
|
4495
|
+
* @param frame - the ID3 frame
|
4496
|
+
*
|
4497
|
+
* @returns The decoded ID3 frame
|
4498
|
+
*
|
4499
|
+
* @internal
|
4500
|
+
*
|
4501
|
+
* @group ID3
|
4502
|
+
*/
|
4503
|
+
function decodeId3Frame(frame) {
|
4504
|
+
if (frame.type === 'PRIV') {
|
4505
|
+
return decodeId3PrivFrame(frame);
|
4506
|
+
} else if (frame.type[0] === 'W') {
|
4507
|
+
return decodeId3UrlFrame(frame);
|
4508
|
+
} else if (frame.type === 'APIC') {
|
4509
|
+
return decodeId3ImageFrame(frame);
|
4510
|
+
}
|
4511
|
+
return decodeId3TextFrame(frame);
|
4512
|
+
}
|
4513
|
+
|
4514
|
+
/**
|
4515
|
+
* Read ID3 size
|
4516
|
+
*
|
4517
|
+
* @param data - The data to read from
|
4518
|
+
* @param offset - The offset at which to start reading
|
4519
|
+
*
|
4520
|
+
* @returns The size
|
4521
|
+
*
|
4522
|
+
* @internal
|
4523
|
+
*
|
4524
|
+
* @group ID3
|
4525
|
+
*/
|
4526
|
+
function readId3Size(data, offset) {
|
4527
|
+
let size = 0;
|
4528
|
+
size = (data[offset] & 0x7f) << 21;
|
4529
|
+
size |= (data[offset + 1] & 0x7f) << 14;
|
4530
|
+
size |= (data[offset + 2] & 0x7f) << 7;
|
4531
|
+
size |= data[offset + 3] & 0x7f;
|
4532
|
+
return size;
|
4533
|
+
}
|
4534
|
+
|
4535
|
+
/**
|
4536
|
+
* Returns the data of an ID3 frame.
|
4537
|
+
*
|
4538
|
+
* @param data - The data to read from
|
4539
|
+
*
|
4540
|
+
* @returns The data of the ID3 frame
|
4541
|
+
*
|
4542
|
+
* @internal
|
4543
|
+
*
|
4544
|
+
* @group ID3
|
4545
|
+
*/
|
4546
|
+
function getId3FrameData(data) {
|
4547
|
+
/*
|
4548
|
+
Frame ID $xx xx xx xx (four characters)
|
4549
|
+
Size $xx xx xx xx
|
4550
|
+
Flags $xx xx
|
4551
|
+
*/
|
4552
|
+
const type = String.fromCharCode(data[0], data[1], data[2], data[3]);
|
4553
|
+
const size = readId3Size(data, 4);
|
4554
|
+
// skip frame id, size, and flags
|
4555
|
+
const offset = 10;
|
4556
|
+
return {
|
4557
|
+
type,
|
4558
|
+
size,
|
4559
|
+
data: data.subarray(offset, offset + size)
|
4560
|
+
};
|
4561
|
+
}
|
4562
|
+
|
4563
|
+
/**
|
4564
|
+
* Returns true if an ID3 footer can be found at offset in data
|
4565
|
+
*
|
4566
|
+
* @param data - The data to search in
|
4567
|
+
* @param offset - The offset at which to start searching
|
4568
|
+
*
|
4569
|
+
* @returns `true` if an ID3 footer is found
|
4570
|
+
*
|
4571
|
+
* @internal
|
4572
|
+
*
|
4573
|
+
* @group ID3
|
4574
|
+
*/
|
4575
|
+
function isId3Footer(data, offset) {
|
4576
|
+
/*
|
4577
|
+
* The footer is a copy of the header, but with a different identifier
|
4578
|
+
*/
|
4579
|
+
if (offset + 10 <= data.length) {
|
4580
|
+
// look for '3DI' identifier
|
4581
|
+
if (data[offset] === 0x33 && data[offset + 1] === 0x44 && data[offset + 2] === 0x49) {
|
4582
|
+
// check version is within range
|
4583
|
+
if (data[offset + 3] < 0xff && data[offset + 4] < 0xff) {
|
4584
|
+
// check size is within range
|
4585
|
+
if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) {
|
4586
|
+
return true;
|
4587
|
+
}
|
4588
|
+
}
|
4589
|
+
}
|
4590
|
+
}
|
4591
|
+
return false;
|
4592
|
+
}
|
4593
|
+
|
4594
|
+
/**
|
4595
|
+
* Returns true if an ID3 header can be found at offset in data
|
4596
|
+
*
|
4597
|
+
* @param data - The data to search in
|
4598
|
+
* @param offset - The offset at which to start searching
|
4599
|
+
*
|
4600
|
+
* @returns `true` if an ID3 header is found
|
4601
|
+
*
|
4602
|
+
* @internal
|
4603
|
+
*
|
4604
|
+
* @group ID3
|
4605
|
+
*/
|
4606
|
+
function isId3Header(data, offset) {
|
4607
|
+
/*
|
4608
|
+
* http://id3.org/id3v2.3.0
|
4609
|
+
* [0] = 'I'
|
4610
|
+
* [1] = 'D'
|
4611
|
+
* [2] = '3'
|
4612
|
+
* [3,4] = {Version}
|
4613
|
+
* [5] = {Flags}
|
4614
|
+
* [6-9] = {ID3 Size}
|
4615
|
+
*
|
4616
|
+
* An ID3v2 tag can be detected with the following pattern:
|
4617
|
+
* $49 44 33 yy yy xx zz zz zz zz
|
4618
|
+
* Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80
|
4619
|
+
*/
|
4620
|
+
if (offset + 10 <= data.length) {
|
4621
|
+
// look for 'ID3' identifier
|
4622
|
+
if (data[offset] === 0x49 && data[offset + 1] === 0x44 && data[offset + 2] === 0x33) {
|
4623
|
+
// check version is within range
|
4624
|
+
if (data[offset + 3] < 0xff && data[offset + 4] < 0xff) {
|
4625
|
+
// check size is within range
|
4626
|
+
if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) {
|
4627
|
+
return true;
|
4628
|
+
}
|
4629
|
+
}
|
4630
|
+
}
|
4631
|
+
}
|
4632
|
+
return false;
|
4411
4633
|
}
|
4412
|
-
|
4413
|
-
|
4414
|
-
|
4415
|
-
|
4416
|
-
|
4417
|
-
|
4418
|
-
|
4419
|
-
|
4420
|
-
|
4421
|
-
|
4634
|
+
|
4635
|
+
const HEADER_FOOTER_SIZE = 10;
|
4636
|
+
const FRAME_SIZE = 10;
|
4637
|
+
/**
|
4638
|
+
* Returns an array of ID3 frames found in all the ID3 tags in the id3Data
|
4639
|
+
*
|
4640
|
+
* @param id3Data - The ID3 data containing one or more ID3 tags
|
4641
|
+
*
|
4642
|
+
* @returns Array of ID3 frame objects
|
4643
|
+
*
|
4644
|
+
* @group ID3
|
4645
|
+
*
|
4646
|
+
* @beta
|
4647
|
+
*/
|
4648
|
+
function getId3Frames(id3Data) {
|
4649
|
+
let offset = 0;
|
4650
|
+
const frames = [];
|
4651
|
+
while (isId3Header(id3Data, offset)) {
|
4652
|
+
const size = readId3Size(id3Data, offset + 6);
|
4653
|
+
if (id3Data[offset + 5] >> 6 & 1) {
|
4654
|
+
// skip extended header
|
4655
|
+
offset += HEADER_FOOTER_SIZE;
|
4656
|
+
}
|
4657
|
+
// skip past ID3 header
|
4658
|
+
offset += HEADER_FOOTER_SIZE;
|
4659
|
+
const end = offset + size;
|
4660
|
+
// loop through frames in the ID3 tag
|
4661
|
+
while (offset + FRAME_SIZE < end) {
|
4662
|
+
const frameData = getId3FrameData(id3Data.subarray(offset));
|
4663
|
+
const frame = decodeId3Frame(frameData);
|
4664
|
+
if (frame) {
|
4665
|
+
frames.push(frame);
|
4422
4666
|
}
|
4667
|
+
// skip frame header and frame data
|
4668
|
+
offset += frameData.size + HEADER_FOOTER_SIZE;
|
4423
4669
|
}
|
4424
|
-
|
4425
|
-
|
4426
|
-
}
|
4427
|
-
function filterSubtitleTracks(textTrackList) {
|
4428
|
-
const tracks = [];
|
4429
|
-
for (let i = 0; i < textTrackList.length; i++) {
|
4430
|
-
const track = textTrackList[i];
|
4431
|
-
// Edge adds a track without a label; we don't want to use it
|
4432
|
-
if ((track.kind === 'subtitles' || track.kind === 'captions') && track.label) {
|
4433
|
-
tracks.push(textTrackList[i]);
|
4670
|
+
if (isId3Footer(id3Data, offset)) {
|
4671
|
+
offset += HEADER_FOOTER_SIZE;
|
4434
4672
|
}
|
4435
4673
|
}
|
4436
|
-
return
|
4674
|
+
return frames;
|
4437
4675
|
}
|
4438
4676
|
|
4439
|
-
|
4440
|
-
|
4441
|
-
|
4442
|
-
|
4443
|
-
|
4677
|
+
/**
|
4678
|
+
* Returns true if the ID3 frame is an Elementary Stream timestamp frame
|
4679
|
+
*
|
4680
|
+
* @param frame - the ID3 frame
|
4681
|
+
*
|
4682
|
+
* @returns `true` if the ID3 frame is an Elementary Stream timestamp frame
|
4683
|
+
*
|
4684
|
+
* @internal
|
4685
|
+
*
|
4686
|
+
* @group ID3
|
4687
|
+
*/
|
4688
|
+
function isId3TimestampFrame(frame) {
|
4689
|
+
return frame && frame.key === 'PRIV' && frame.info === 'com.apple.streaming.transportStreamTimestamp';
|
4690
|
+
}
|
4444
4691
|
|
4445
4692
|
const MIN_CUE_DURATION = 0.25;
|
4446
4693
|
function getCueClass() {
|
@@ -4586,7 +4833,7 @@ class ID3TrackController {
|
|
4586
4833
|
if (type === MetadataSchema.emsg && !enableEmsgMetadataCues || !enableID3MetadataCues) {
|
4587
4834
|
continue;
|
4588
4835
|
}
|
4589
|
-
const frames =
|
4836
|
+
const frames = getId3Frames(samples[i].data);
|
4590
4837
|
if (frames) {
|
4591
4838
|
const startTime = samples[i].pts;
|
4592
4839
|
let endTime = startTime + samples[i].duration;
|
@@ -4600,7 +4847,7 @@ class ID3TrackController {
|
|
4600
4847
|
for (let j = 0; j < frames.length; j++) {
|
4601
4848
|
const frame = frames[j];
|
4602
4849
|
// Safari doesn't put the timestamp frame in the TextTrack
|
4603
|
-
if (!
|
4850
|
+
if (!isId3TimestampFrame(frame)) {
|
4604
4851
|
// add a bounds to any unbounded cues
|
4605
4852
|
this.updateId3CueEnds(startTime, type);
|
4606
4853
|
const cue = createCueWithDataFields(Cue, startTime, endTime, frame, type);
|
@@ -10581,6 +10828,104 @@ function dummyTrack(type = '', inputTimeScale = 90000) {
|
|
10581
10828
|
};
|
10582
10829
|
}
|
10583
10830
|
|
10831
|
+
/**
|
10832
|
+
* Returns any adjacent ID3 tags found in data starting at offset, as one block of data
|
10833
|
+
*
|
10834
|
+
* @param data - The data to search in
|
10835
|
+
* @param offset - The offset at which to start searching
|
10836
|
+
*
|
10837
|
+
* @returns The block of data containing any ID3 tags found
|
10838
|
+
* or `undefined` if no header is found at the starting offset
|
10839
|
+
*
|
10840
|
+
* @internal
|
10841
|
+
*
|
10842
|
+
* @group ID3
|
10843
|
+
*/
|
10844
|
+
function getId3Data(data, offset) {
|
10845
|
+
const front = offset;
|
10846
|
+
let length = 0;
|
10847
|
+
while (isId3Header(data, offset)) {
|
10848
|
+
// ID3 header is 10 bytes
|
10849
|
+
length += 10;
|
10850
|
+
const size = readId3Size(data, offset + 6);
|
10851
|
+
length += size;
|
10852
|
+
if (isId3Footer(data, offset + 10)) {
|
10853
|
+
// ID3 footer is 10 bytes
|
10854
|
+
length += 10;
|
10855
|
+
}
|
10856
|
+
offset += length;
|
10857
|
+
}
|
10858
|
+
if (length > 0) {
|
10859
|
+
return data.subarray(front, front + length);
|
10860
|
+
}
|
10861
|
+
return undefined;
|
10862
|
+
}
|
10863
|
+
|
10864
|
+
/**
|
10865
|
+
* Read a 33 bit timestamp from an ID3 frame.
|
10866
|
+
*
|
10867
|
+
* @param timeStampFrame - the ID3 frame
|
10868
|
+
*
|
10869
|
+
* @returns The timestamp
|
10870
|
+
*
|
10871
|
+
* @internal
|
10872
|
+
*
|
10873
|
+
* @group ID3
|
10874
|
+
*/
|
10875
|
+
function readId3Timestamp(timeStampFrame) {
|
10876
|
+
if (timeStampFrame.data.byteLength === 8) {
|
10877
|
+
const data = new Uint8Array(timeStampFrame.data);
|
10878
|
+
// timestamp is 33 bit expressed as a big-endian eight-octet number,
|
10879
|
+
// with the upper 31 bits set to zero.
|
10880
|
+
const pts33Bit = data[3] & 0x1;
|
10881
|
+
let timestamp = (data[4] << 23) + (data[5] << 15) + (data[6] << 7) + data[7];
|
10882
|
+
timestamp /= 45;
|
10883
|
+
if (pts33Bit) {
|
10884
|
+
timestamp += 47721858.84;
|
10885
|
+
} // 2^32 / 90
|
10886
|
+
return Math.round(timestamp);
|
10887
|
+
}
|
10888
|
+
return undefined;
|
10889
|
+
}
|
10890
|
+
|
10891
|
+
/**
|
10892
|
+
* Searches for the Elementary Stream timestamp found in the ID3 data chunk
|
10893
|
+
*
|
10894
|
+
* @param data - Block of data containing one or more ID3 tags
|
10895
|
+
*
|
10896
|
+
* @returns The timestamp
|
10897
|
+
*
|
10898
|
+
* @group ID3
|
10899
|
+
*
|
10900
|
+
* @beta
|
10901
|
+
*/
|
10902
|
+
function getId3Timestamp(data) {
|
10903
|
+
const frames = getId3Frames(data);
|
10904
|
+
for (let i = 0; i < frames.length; i++) {
|
10905
|
+
const frame = frames[i];
|
10906
|
+
if (isId3TimestampFrame(frame)) {
|
10907
|
+
return readId3Timestamp(frame);
|
10908
|
+
}
|
10909
|
+
}
|
10910
|
+
return undefined;
|
10911
|
+
}
|
10912
|
+
|
10913
|
+
/**
|
10914
|
+
* Checks if the given data contains an ID3 tag.
|
10915
|
+
*
|
10916
|
+
* @param data - The data to check
|
10917
|
+
* @param offset - The offset at which to start checking
|
10918
|
+
*
|
10919
|
+
* @returns `true` if an ID3 tag is found
|
10920
|
+
*
|
10921
|
+
* @group ID3
|
10922
|
+
*
|
10923
|
+
* @beta
|
10924
|
+
*/
|
10925
|
+
function canParseId3(data, offset) {
|
10926
|
+
return isId3Header(data, offset) && readId3Size(data, offset + 6) + 10 <= data.length - offset;
|
10927
|
+
}
|
10928
|
+
|
10584
10929
|
class BaseAudioDemuxer {
|
10585
10930
|
constructor() {
|
10586
10931
|
this._audioTrack = void 0;
|
@@ -10622,12 +10967,12 @@ class BaseAudioDemuxer {
|
|
10622
10967
|
data = appendUint8Array(this.cachedData, data);
|
10623
10968
|
this.cachedData = null;
|
10624
10969
|
}
|
10625
|
-
let id3Data =
|
10970
|
+
let id3Data = getId3Data(data, 0);
|
10626
10971
|
let offset = id3Data ? id3Data.length : 0;
|
10627
10972
|
let lastDataIndex;
|
10628
10973
|
const track = this._audioTrack;
|
10629
10974
|
const id3Track = this._id3Track;
|
10630
|
-
const timestamp = id3Data ?
|
10975
|
+
const timestamp = id3Data ? getId3Timestamp(id3Data) : undefined;
|
10631
10976
|
const length = data.length;
|
10632
10977
|
if (this.basePTS === null || this.frameIndex === 0 && isFiniteNumber(timestamp)) {
|
10633
10978
|
this.basePTS = initPTSFn(timestamp, timeOffset, this.initPTS);
|
@@ -10658,9 +11003,9 @@ class BaseAudioDemuxer {
|
|
10658
11003
|
} else {
|
10659
11004
|
offset = length;
|
10660
11005
|
}
|
10661
|
-
} else if (
|
10662
|
-
// after a
|
10663
|
-
id3Data =
|
11006
|
+
} else if (canParseId3(data, offset)) {
|
11007
|
+
// after a canParse, a call to getId3Data *should* always returns some data
|
11008
|
+
id3Data = getId3Data(data, offset);
|
10664
11009
|
id3Track.samples.push({
|
10665
11010
|
pts: this.lastPTS,
|
10666
11011
|
dts: this.lastPTS,
|
@@ -11154,7 +11499,7 @@ class AACDemuxer extends BaseAudioDemuxer {
|
|
11154
11499
|
// Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1
|
11155
11500
|
// Layer bits (position 14 and 15) in header should be always 0 for ADTS
|
11156
11501
|
// More info https://wiki.multimedia.cx/index.php?title=ADTS
|
11157
|
-
const id3Data =
|
11502
|
+
const id3Data = getId3Data(data, 0);
|
11158
11503
|
let offset = (id3Data == null ? void 0 : id3Data.length) || 0;
|
11159
11504
|
if (probe(data, offset)) {
|
11160
11505
|
return false;
|
@@ -11372,14 +11717,14 @@ class AC3Demuxer extends BaseAudioDemuxer {
|
|
11372
11717
|
if (!data) {
|
11373
11718
|
return false;
|
11374
11719
|
}
|
11375
|
-
const id3Data =
|
11720
|
+
const id3Data = getId3Data(data, 0);
|
11376
11721
|
if (!id3Data) {
|
11377
11722
|
return false;
|
11378
11723
|
}
|
11379
11724
|
|
11380
11725
|
// look for the ac-3 sync bytes
|
11381
11726
|
const offset = id3Data.length;
|
11382
|
-
if (data[offset] === 0x0b && data[offset + 1] === 0x77 &&
|
11727
|
+
if (data[offset] === 0x0b && data[offset + 1] === 0x77 && getId3Timestamp(id3Data) !== undefined &&
|
11383
11728
|
// check the bsid to confirm ac-3
|
11384
11729
|
getAudioBSID(data, offset) < 16) {
|
11385
11730
|
return true;
|
@@ -13636,11 +13981,11 @@ class MP3Demuxer extends BaseAudioDemuxer {
|
|
13636
13981
|
// Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1
|
13637
13982
|
// Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III)
|
13638
13983
|
// More info http://www.mp3-tech.org/programmer/frame_header.html
|
13639
|
-
const id3Data =
|
13984
|
+
const id3Data = getId3Data(data, 0);
|
13640
13985
|
let offset = (id3Data == null ? void 0 : id3Data.length) || 0;
|
13641
13986
|
|
13642
13987
|
// Check for ac-3|ec-3 sync bytes and return false if present
|
13643
|
-
if (id3Data && data[offset] === 0x0b && data[offset + 1] === 0x77 &&
|
13988
|
+
if (id3Data && data[offset] === 0x0b && data[offset + 1] === 0x77 && getId3Timestamp(id3Data) !== undefined &&
|
13644
13989
|
// check the bsid to confirm ac-3 or ec-3 (not mp3)
|
13645
13990
|
getAudioBSID(data, offset) <= 16) {
|
13646
13991
|
return false;
|
@@ -23946,20 +24291,6 @@ function serializeBoolean(value) {
|
|
23946
24291
|
return value ? '?1' : '?0';
|
23947
24292
|
}
|
23948
24293
|
|
23949
|
-
/**
|
23950
|
-
* Encodes binary data to base64
|
23951
|
-
*
|
23952
|
-
* @param binary - The binary data to encode
|
23953
|
-
* @returns The base64 encoded string
|
23954
|
-
*
|
23955
|
-
* @group Utils
|
23956
|
-
*
|
23957
|
-
* @beta
|
23958
|
-
*/
|
23959
|
-
function base64encode(binary) {
|
23960
|
-
return btoa(String.fromCharCode(...binary));
|
23961
|
-
}
|
23962
|
-
|
23963
24294
|
const BYTES = 'Byte Sequence';
|
23964
24295
|
|
23965
24296
|
// 4.1.8. Serializing a Byte Sequence
|
@@ -24036,34 +24367,6 @@ function serializeDate(value) {
|
|
24036
24367
|
return `@${serializeInteger(value.getTime() / 1000)}`;
|
24037
24368
|
}
|
24038
24369
|
|
24039
|
-
/**
|
24040
|
-
* This implements the rounding procedure described in step 2 of the "Serializing a Decimal" specification.
|
24041
|
-
* This rounding style is known as "even rounding", "banker's rounding", or "commercial rounding".
|
24042
|
-
*
|
24043
|
-
* @param value - The value to round
|
24044
|
-
* @param precision - The number of decimal places to round to
|
24045
|
-
* @returns The rounded value
|
24046
|
-
*
|
24047
|
-
* @group Utils
|
24048
|
-
*
|
24049
|
-
* @beta
|
24050
|
-
*/
|
24051
|
-
function roundToEven(value, precision) {
|
24052
|
-
if (value < 0) {
|
24053
|
-
return -roundToEven(-value, precision);
|
24054
|
-
}
|
24055
|
-
const decimalShift = Math.pow(10, precision);
|
24056
|
-
const isEquidistant = Math.abs(value * decimalShift % 1 - 0.5) < Number.EPSILON;
|
24057
|
-
if (isEquidistant) {
|
24058
|
-
// If the tail of the decimal place is 'equidistant' we round to the nearest even value
|
24059
|
-
const flooredValue = Math.floor(value * decimalShift);
|
24060
|
-
return (flooredValue % 2 === 0 ? flooredValue : flooredValue + 1) / decimalShift;
|
24061
|
-
} else {
|
24062
|
-
// Otherwise, proceed as normal
|
24063
|
-
return Math.round(value * decimalShift) / decimalShift;
|
24064
|
-
}
|
24065
|
-
}
|
24066
|
-
|
24067
24370
|
const DECIMAL = 'Decimal';
|
24068
24371
|
|
24069
24372
|
// 4.1.5. Serializing a Decimal
|
@@ -24446,38 +24749,6 @@ const isValid = value => {
|
|
24446
24749
|
return value != null && value !== '' && value !== false;
|
24447
24750
|
};
|
24448
24751
|
|
24449
|
-
/**
|
24450
|
-
* Constructs a relative path from a URL.
|
24451
|
-
*
|
24452
|
-
* @param url - The destination URL
|
24453
|
-
* @param base - The base URL
|
24454
|
-
* @returns The relative path
|
24455
|
-
*
|
24456
|
-
* @group Utils
|
24457
|
-
*
|
24458
|
-
* @beta
|
24459
|
-
*/
|
24460
|
-
function urlToRelativePath(url, base) {
|
24461
|
-
const to = new URL(url);
|
24462
|
-
const from = new URL(base);
|
24463
|
-
if (to.origin !== from.origin) {
|
24464
|
-
return url;
|
24465
|
-
}
|
24466
|
-
const toPath = to.pathname.split('/').slice(1);
|
24467
|
-
const fromPath = from.pathname.split('/').slice(1, -1);
|
24468
|
-
// remove common parents
|
24469
|
-
while (toPath[0] === fromPath[0]) {
|
24470
|
-
toPath.shift();
|
24471
|
-
fromPath.shift();
|
24472
|
-
}
|
24473
|
-
// add back paths
|
24474
|
-
while (fromPath.length) {
|
24475
|
-
fromPath.shift();
|
24476
|
-
toPath.unshift('..');
|
24477
|
-
}
|
24478
|
-
return toPath.join('/');
|
24479
|
-
}
|
24480
|
-
|
24481
24752
|
const toRounded = value => Math.round(value);
|
24482
24753
|
const toUrlSafe = (value, options) => {
|
24483
24754
|
if (options === null || options === void 0 ? void 0 : options.baseUrl) {
|
@@ -24703,36 +24974,6 @@ function appendCmcdQuery(url, cmcd, options) {
|
|
24703
24974
|
return `${url}${separator}${query}`;
|
24704
24975
|
}
|
24705
24976
|
|
24706
|
-
/**
|
24707
|
-
* Generate a random v4 UUID
|
24708
|
-
*
|
24709
|
-
* @returns A random v4 UUID
|
24710
|
-
*
|
24711
|
-
* @group Utils
|
24712
|
-
*
|
24713
|
-
* @beta
|
24714
|
-
*/
|
24715
|
-
function uuid() {
|
24716
|
-
try {
|
24717
|
-
return crypto.randomUUID();
|
24718
|
-
} catch (error) {
|
24719
|
-
try {
|
24720
|
-
const url = URL.createObjectURL(new Blob());
|
24721
|
-
const uuid = url.toString();
|
24722
|
-
URL.revokeObjectURL(url);
|
24723
|
-
return uuid.slice(uuid.lastIndexOf('/') + 1);
|
24724
|
-
} catch (error) {
|
24725
|
-
let dt = new Date().getTime();
|
24726
|
-
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
24727
|
-
const r = (dt + Math.random() * 16) % 16 | 0;
|
24728
|
-
dt = Math.floor(dt / 16);
|
24729
|
-
return (c == 'x' ? r : r & 0x3 | 0x8).toString(16);
|
24730
|
-
});
|
24731
|
-
return uuid;
|
24732
|
-
}
|
24733
|
-
}
|
24734
|
-
}
|
24735
|
-
|
24736
24977
|
/**
|
24737
24978
|
* Controller to deal with Common Media Client Data (CMCD)
|
24738
24979
|
* @see https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf
|
@@ -28732,7 +28973,7 @@ class Hls {
|
|
28732
28973
|
* Get the video-dev/hls.js package version.
|
28733
28974
|
*/
|
28734
28975
|
static get version() {
|
28735
|
-
return "1.5.8-0.canary.
|
28976
|
+
return "1.5.8-0.canary.10154";
|
28736
28977
|
}
|
28737
28978
|
|
28738
28979
|
/**
|