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.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.10151"}`);
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
- // breaking up those two types in order to clarify what is happening in the decoding path.
1211
-
1212
- /**
1213
- * Returns true if an ID3 header can be found at offset in data
1214
- * @param data - The data to search
1215
- * @param offset - The offset at which to start searching
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
- * Returns true if an ID3 footer can be found at offset in data
1248
- * @param data - The data to search
1249
- * @param offset - The offset at which to start searching
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
- const isFooter = (data, offset) => {
1252
- /*
1253
- * The footer is a copy of the header, but with a different identifier
1254
- */
1255
- if (offset + 10 <= data.length) {
1256
- // look for '3DI' identifier
1257
- if (data[offset] === 0x33 && data[offset + 1] === 0x44 && data[offset + 2] === 0x49) {
1258
- // check version is within range
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
- return false;
1268
- };
1269
-
1270
- /**
1271
- * Returns any adjacent ID3 tags found in data starting at offset, as one block of data
1272
- * @param data - The data to search in
1273
- * @param offset - The offset at which to start searching
1274
- * @returns the block of data containing any ID3 tags found
1275
- * or *undefined* if no header is found at the starting offset
1276
- */
1277
- const getID3Data = (data, offset) => {
1278
- const front = offset;
1279
- let length = 0;
1280
- while (isHeader$2(data, offset)) {
1281
- // ID3 header is 10 bytes
1282
- length += 10;
1283
- const size = readSize(data, offset + 6);
1284
- length += size;
1285
- if (isFooter(data, offset + 10)) {
1286
- // ID3 footer is 10 bytes
1287
- length += 10;
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 undefined;
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
- * Searches for the Elementary Stream timestamp found in the ID3 data chunk
1310
- * @param data - Block of data containing one or more ID3 tags
1299
+ * hex dump helper class
1311
1300
  */
1312
- const getTimeStamp = data => {
1313
- const frames = getID3Frames(data);
1314
- for (let i = 0; i < frames.length; i++) {
1315
- const frame = frames[i];
1316
- if (isTimeStampFrame(frame)) {
1317
- return readTimeStamp(frame);
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
- * Returns true if the ID3 frame is an Elementary Stream timestamp frame
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
- // skip frame id, size, and flags
1339
- const offset = 10;
1340
- return {
1341
- type,
1342
- size,
1343
- data: data.subarray(offset, offset + size)
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
- * Returns an array of ID3 frames found in all the ID3 tags in the id3Data
1349
- * @param id3Data - The ID3 data containing one or more ID3 tags
1350
- */
1351
- const getID3Frames = id3Data => {
1352
- let offset = 0;
1353
- const frames = [];
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
- function getCuesInRange(cues, start, end) {
4413
- const cuesFound = [];
4414
- const firstCueInRange = getFirstCueIndexAfterTime(cues, start);
4415
- if (firstCueInRange > -1) {
4416
- for (let i = firstCueInRange, len = cues.length; i < len; i++) {
4417
- const cue = cues[i];
4418
- if (cue.startTime >= start && cue.endTime <= end) {
4419
- cuesFound.push(cue);
4420
- } else if (cue.startTime > end) {
4421
- return cuesFound;
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
- return cuesFound;
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 tracks;
4674
+ return frames;
4437
4675
  }
4438
4676
 
4439
- var MetadataSchema = {
4440
- audioId3: "org.id3",
4441
- dateRange: "com.apple.quicktime.HLS",
4442
- emsg: "https://aomedia.org/emsg/ID3"
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 = getID3Frames(samples[i].data);
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 (!isTimeStampFrame(frame)) {
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 = getID3Data(data, 0);
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 ? getTimeStamp(id3Data) : undefined;
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 (canParse$2(data, offset)) {
10662
- // after a ID3.canParse, a call to ID3.getID3Data *should* always returns some data
10663
- id3Data = getID3Data(data, offset);
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 = getID3Data(data, 0);
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 = getID3Data(data, 0);
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 && getTimeStamp(id3Data) !== undefined &&
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 = getID3Data(data, 0);
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 && getTimeStamp(id3Data) !== undefined &&
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.10151";
28976
+ return "1.5.8-0.canary.10154";
28736
28977
  }
28737
28978
 
28738
28979
  /**