hls.js 1.6.0-beta.4.0.canary.11066 → 1.6.0-beta.4.0.canary.11069
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.d.mts +12 -6
- package/dist/hls.d.ts +12 -6
- package/dist/hls.js +120 -51
- package/dist/hls.js.d.ts +12 -6
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +120 -51
- 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 +107 -44
- 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 +107 -44
- 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/buffer-controller.ts +15 -1
- package/src/controller/level-controller.ts +33 -11
- package/src/controller/stream-controller.ts +1 -1
- package/src/demux/mp4demuxer.ts +2 -1
- package/src/hls.ts +1 -0
- package/src/loader/m3u8-parser.ts +13 -3
- package/src/remux/passthrough-remuxer.ts +1 -0
- package/src/types/buffer.ts +1 -0
- package/src/types/demuxer.ts +1 -0
- package/src/types/level.ts +17 -5
- package/src/utils/attr-list.ts +1 -1
- package/src/utils/codecs.ts +1 -0
- package/src/utils/mediacapabilities-helper.ts +25 -13
- package/src/utils/mp4-tools.ts +65 -25
package/dist/hls.light.mjs
CHANGED
@@ -402,7 +402,7 @@ function enableLogs(debugConfig, context, id) {
|
|
402
402
|
// Some browsers don't allow to use bind on console object anyway
|
403
403
|
// fallback to default if needed
|
404
404
|
try {
|
405
|
-
newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.0-beta.4.0.canary.
|
405
|
+
newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.0-beta.4.0.canary.11069"}`);
|
406
406
|
} catch (e) {
|
407
407
|
/* log fn threw an exception. All logger methods are no-ops. */
|
408
408
|
return createLogger();
|
@@ -1343,6 +1343,7 @@ function parseStsd(stsd) {
|
|
1343
1343
|
const sampleEntriesEnd = sampleEntries.subarray(8 + 78);
|
1344
1344
|
const fourCC = bin2str(sampleEntries.subarray(4, 8));
|
1345
1345
|
let codec = fourCC;
|
1346
|
+
let supplemental;
|
1346
1347
|
const encrypted = fourCC === 'enca' || fourCC === 'encv';
|
1347
1348
|
if (encrypted) {
|
1348
1349
|
const encBox = findBox(sampleEntries, [fourCC])[0];
|
@@ -1362,6 +1363,7 @@ function parseStsd(stsd) {
|
|
1362
1363
|
}
|
1363
1364
|
});
|
1364
1365
|
}
|
1366
|
+
const codecFourCC = codec;
|
1365
1367
|
switch (codec) {
|
1366
1368
|
case 'avc1':
|
1367
1369
|
case 'avc2':
|
@@ -1371,6 +1373,7 @@ function parseStsd(stsd) {
|
|
1371
1373
|
// extract profile + compatibility + level out of avcC box
|
1372
1374
|
const avcCBox = findBox(sampleEntriesEnd, ['avcC'])[0];
|
1373
1375
|
codec += '.' + toHex(avcCBox[1]) + toHex(avcCBox[2]) + toHex(avcCBox[3]);
|
1376
|
+
supplemental = parseSupplementalDoViCodec(codecFourCC === 'avc1' ? 'dva1' : 'dvav', sampleEntriesEnd);
|
1374
1377
|
break;
|
1375
1378
|
}
|
1376
1379
|
case 'mp4a':
|
@@ -1421,35 +1424,39 @@ function parseStsd(stsd) {
|
|
1421
1424
|
case 'hvc1':
|
1422
1425
|
case 'hev1':
|
1423
1426
|
{
|
1424
|
-
const
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
const
|
1440
|
-
|
1427
|
+
const hvcCBoxes = findBox(sampleEntriesEnd, ['hvcC']);
|
1428
|
+
if (hvcCBoxes) {
|
1429
|
+
const hvcCBox = hvcCBoxes[0];
|
1430
|
+
const profileByte = hvcCBox[1];
|
1431
|
+
const profileSpace = ['', 'A', 'B', 'C'][profileByte >> 6];
|
1432
|
+
const generalProfileIdc = profileByte & 0x1f;
|
1433
|
+
const profileCompat = readUint32(hvcCBox, 2);
|
1434
|
+
const tierFlag = (profileByte & 0x20) >> 5 ? 'H' : 'L';
|
1435
|
+
const levelIDC = hvcCBox[12];
|
1436
|
+
const constraintIndicator = hvcCBox.subarray(6, 12);
|
1437
|
+
codec += '.' + profileSpace + generalProfileIdc;
|
1438
|
+
codec += '.' + profileCompat.toString(16).toUpperCase();
|
1439
|
+
codec += '.' + tierFlag + levelIDC;
|
1440
|
+
let constraintString = '';
|
1441
|
+
for (let i = constraintIndicator.length; i--;) {
|
1442
|
+
const byte = constraintIndicator[i];
|
1443
|
+
if (byte || constraintString) {
|
1444
|
+
const encodedByte = byte.toString(16).toUpperCase();
|
1445
|
+
constraintString = '.' + encodedByte + constraintString;
|
1446
|
+
}
|
1441
1447
|
}
|
1448
|
+
codec += constraintString;
|
1442
1449
|
}
|
1443
|
-
|
1450
|
+
supplemental = parseSupplementalDoViCodec(codecFourCC == 'hev1' ? 'dvhe' : 'dvh1', sampleEntriesEnd);
|
1444
1451
|
break;
|
1445
1452
|
}
|
1446
1453
|
case 'dvh1':
|
1447
1454
|
case 'dvhe':
|
1455
|
+
case 'dvav':
|
1456
|
+
case 'dva1':
|
1457
|
+
case 'dav1':
|
1448
1458
|
{
|
1449
|
-
|
1450
|
-
const profile = dvcCBox[2] >> 1 & 0x7f;
|
1451
|
-
const level = dvcCBox[2] << 5 & 0x20 | dvcCBox[3] >> 3 & 0x1f;
|
1452
|
-
codec += '.' + addLeadingZero(profile) + '.' + addLeadingZero(level);
|
1459
|
+
codec = parseSupplementalDoViCodec(codec, sampleEntriesEnd) || codec;
|
1453
1460
|
break;
|
1454
1461
|
}
|
1455
1462
|
case 'vp09':
|
@@ -1482,14 +1489,25 @@ function parseStsd(stsd) {
|
|
1482
1489
|
const matrixCoefficients = 1;
|
1483
1490
|
const videoFullRangeFlag = 0;
|
1484
1491
|
codec += '.' + profile + '.' + addLeadingZero(level) + tierFlag + '.' + addLeadingZero(bitDepth) + '.' + monochrome + '.' + chromaSubsamplingX + chromaSubsamplingY + chromaSamplePosition + '.' + addLeadingZero(colorPrimaries) + '.' + addLeadingZero(transferCharacteristics) + '.' + addLeadingZero(matrixCoefficients) + '.' + videoFullRangeFlag;
|
1492
|
+
supplemental = parseSupplementalDoViCodec('dav1', sampleEntriesEnd);
|
1485
1493
|
break;
|
1486
1494
|
}
|
1487
1495
|
}
|
1488
1496
|
return {
|
1489
1497
|
codec,
|
1490
|
-
encrypted
|
1498
|
+
encrypted,
|
1499
|
+
supplemental
|
1491
1500
|
};
|
1492
1501
|
}
|
1502
|
+
function parseSupplementalDoViCodec(fourCC, sampleEntriesEnd) {
|
1503
|
+
const dvvCResult = findBox(sampleEntriesEnd, ['dvvC']); // used by DoVi Profile 8 to 10
|
1504
|
+
const dvXCBox = dvvCResult.length ? dvvCResult[0] : findBox(sampleEntriesEnd, ['dvcC'])[0]; // used by DoVi Profiles up to 7 and 20
|
1505
|
+
if (dvXCBox) {
|
1506
|
+
const doViProfile = dvXCBox[2] >> 1 & 0x7f;
|
1507
|
+
const doViLevel = dvXCBox[2] << 5 & 0x20 | dvXCBox[3] >> 3 & 0x1f;
|
1508
|
+
return fourCC + '.' + addLeadingZero(doViProfile) + '.' + addLeadingZero(doViLevel);
|
1509
|
+
}
|
1510
|
+
}
|
1493
1511
|
function skipBERInteger(bytes, i) {
|
1494
1512
|
const limit = i + 5;
|
1495
1513
|
while (bytes[i++] & 0x80 && i < limit) {
|
@@ -2236,6 +2254,7 @@ const sampleEntryCodesISO = {
|
|
2236
2254
|
avc4: 1,
|
2237
2255
|
avcp: 1,
|
2238
2256
|
av01: 0.8,
|
2257
|
+
dav1: 0.8,
|
2239
2258
|
drac: 1,
|
2240
2259
|
dva1: 1,
|
2241
2260
|
dvav: 1,
|
@@ -2454,6 +2473,7 @@ class Level {
|
|
2454
2473
|
this.height = void 0;
|
2455
2474
|
this.id = void 0;
|
2456
2475
|
this.name = void 0;
|
2476
|
+
this.supplemental = void 0;
|
2457
2477
|
this.videoCodec = void 0;
|
2458
2478
|
this.width = void 0;
|
2459
2479
|
this.details = void 0;
|
@@ -2483,6 +2503,14 @@ class Level {
|
|
2483
2503
|
this.audioCodec = data.audioCodec;
|
2484
2504
|
this.videoCodec = data.videoCodec;
|
2485
2505
|
this.codecSet = [data.videoCodec, data.audioCodec].filter(c => !!c).map(s => s.substring(0, 4)).join(',');
|
2506
|
+
if ('supplemental' in data) {
|
2507
|
+
var _data$supplemental;
|
2508
|
+
this.supplemental = data.supplemental;
|
2509
|
+
const supplementalVideo = (_data$supplemental = data.supplemental) == null ? void 0 : _data$supplemental.videoCodec;
|
2510
|
+
if (supplementalVideo && supplementalVideo !== data.videoCodec) {
|
2511
|
+
this.codecSet += `,${supplementalVideo.substring(0, 4)}`;
|
2512
|
+
}
|
2513
|
+
}
|
2486
2514
|
this.addGroupId('audio', data.attrs.AUDIO);
|
2487
2515
|
this.addGroupId('text', data.attrs.SUBTITLES);
|
2488
2516
|
}
|
@@ -4665,6 +4693,11 @@ class M3U8Parser {
|
|
4665
4693
|
level.height = resolution.height;
|
4666
4694
|
}
|
4667
4695
|
setCodecs(attrs.CODECS, level);
|
4696
|
+
const supplementalCodecs = attrs['SUPPLEMENTAL-CODECS'];
|
4697
|
+
if (supplementalCodecs) {
|
4698
|
+
level.supplemental = {};
|
4699
|
+
setCodecs(supplementalCodecs, level.supplemental);
|
4700
|
+
}
|
4668
4701
|
if (!((_level$unknownCodecs = level.unknownCodecs) != null && _level$unknownCodecs.length)) {
|
4669
4702
|
levelsWithKnownCodecs.push(level);
|
4670
4703
|
}
|
@@ -5233,7 +5266,7 @@ function setCodecs(codecsAttributeValue, level) {
|
|
5233
5266
|
const filtered = codecs.filter(codec => isCodecType(codec, type));
|
5234
5267
|
if (filtered.length) {
|
5235
5268
|
// Comma separated list of all codecs for type
|
5236
|
-
level[`${type}Codec`] = filtered.join(',');
|
5269
|
+
level[`${type}Codec`] = filtered.map(c => c.split('/')[0]).join(',');
|
5237
5270
|
// Remove known codecs so that only unknownCodecs are left after iterating through each type
|
5238
5271
|
codecs = codecs.filter(codec => filtered.indexOf(codec) === -1);
|
5239
5272
|
}
|
@@ -9671,7 +9704,8 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => key === 'initSeg
|
|
9671
9704
|
codec,
|
9672
9705
|
levelCodec,
|
9673
9706
|
container,
|
9674
|
-
metadata
|
9707
|
+
metadata,
|
9708
|
+
supplemental
|
9675
9709
|
} = parsedTrack;
|
9676
9710
|
let track = tracks[trackName];
|
9677
9711
|
const transferredTrack = (_this$transferData3 = this.transferData) == null ? void 0 : (_this$transferData3$t = _this$transferData3.tracks) == null ? void 0 : _this$transferData3$t[trackName];
|
@@ -9683,6 +9717,7 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => key === 'initSeg
|
|
9683
9717
|
buffer: undefined,
|
9684
9718
|
listeners: [],
|
9685
9719
|
codec,
|
9720
|
+
supplemental,
|
9686
9721
|
container,
|
9687
9722
|
levelCodec,
|
9688
9723
|
metadata,
|
@@ -10313,6 +10348,7 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => key === 'initSeg
|
|
10313
10348
|
buffer,
|
10314
10349
|
container: track.container,
|
10315
10350
|
codec: track.codec,
|
10351
|
+
supplemental: track.supplemental,
|
10316
10352
|
levelCodec: track.levelCodec,
|
10317
10353
|
id: track.id,
|
10318
10354
|
metadata: track.metadata
|
@@ -10384,6 +10420,11 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => key === 'initSeg
|
|
10384
10420
|
this.bufferCreated();
|
10385
10421
|
}
|
10386
10422
|
getTrackCodec(track, trackName) {
|
10423
|
+
// Use supplemental video codec when supported when adding SourceBuffer (#5558)
|
10424
|
+
const supplementalCodec = track.supplemental;
|
10425
|
+
if (supplementalCodec && trackName === 'video' && areCodecsMediaSourceSupported(supplementalCodec, trackName)) {
|
10426
|
+
return supplementalCodec;
|
10427
|
+
}
|
10387
10428
|
const codec = pickMostCompleteCodecName(track.codec, track.levelCodec);
|
10388
10429
|
if (codec) {
|
10389
10430
|
if (trackName.slice(0, 5) === 'audio') {
|
@@ -10404,6 +10445,7 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => key === 'initSeg
|
|
10404
10445
|
codec,
|
10405
10446
|
container: track.container,
|
10406
10447
|
levelCodec: track.levelCodec,
|
10448
|
+
supplemental: track.supplemental,
|
10407
10449
|
metadata: track.metadata,
|
10408
10450
|
id: track.id,
|
10409
10451
|
listeners: []
|
@@ -13002,11 +13044,13 @@ class MP4Demuxer {
|
|
13002
13044
|
const {
|
13003
13045
|
id,
|
13004
13046
|
timescale,
|
13005
|
-
codec
|
13047
|
+
codec,
|
13048
|
+
supplemental
|
13006
13049
|
} = initData.video;
|
13007
13050
|
videoTrack.id = id;
|
13008
13051
|
videoTrack.timescale = captionTrack.timescale = timescale;
|
13009
13052
|
videoTrack.codec = codec;
|
13053
|
+
videoTrack.supplemental = supplemental;
|
13010
13054
|
}
|
13011
13055
|
if (initData.audio) {
|
13012
13056
|
const {
|
@@ -16227,6 +16271,7 @@ class PassThroughRemuxer {
|
|
16227
16271
|
tracks.video = {
|
16228
16272
|
container: 'video/mp4',
|
16229
16273
|
codec: videoCodec,
|
16274
|
+
supplemental: initData.video.supplemental,
|
16230
16275
|
initSegment,
|
16231
16276
|
id: 'main'
|
16232
16277
|
};
|
@@ -18994,6 +19039,7 @@ class LevelController extends BasePlaylistController {
|
|
18994
19039
|
videoCodecFound || (videoCodecFound = !!videoCodec);
|
18995
19040
|
audioCodecFound || (audioCodecFound = !!audioCodec);
|
18996
19041
|
if (unknownUnsupportedCodecCount || audioCodec && !this.isAudioSupported(audioCodec) || videoCodec && !this.isVideoSupported(videoCodec)) {
|
19042
|
+
this.log(`Some or all CODECS not supported "${attributes.CODECS}"`);
|
18997
19043
|
return;
|
18998
19044
|
}
|
18999
19045
|
const {
|
@@ -19007,7 +19053,7 @@ class LevelController extends BasePlaylistController {
|
|
19007
19053
|
const contentSteeringPrefix = `${PATHWAY || '.'}-`;
|
19008
19054
|
const levelKey = `${contentSteeringPrefix}${levelParsed.bitrate}-${RESOLUTION}-${FRAMERATE}-${CODECS}-${VIDEO_RANGE}-${HDCP}`;
|
19009
19055
|
if (!redundantSet[levelKey]) {
|
19010
|
-
const level =
|
19056
|
+
const level = this.createLevel(levelParsed);
|
19011
19057
|
redundantSet[levelKey] = level;
|
19012
19058
|
generatePathwaySet[levelKey] = 1;
|
19013
19059
|
levels.push(level);
|
@@ -19016,7 +19062,7 @@ class LevelController extends BasePlaylistController {
|
|
19016
19062
|
// Content Steering controller to handles Pathway fallback on error
|
19017
19063
|
const pathwayCount = generatePathwaySet[levelKey] += 1;
|
19018
19064
|
levelParsed.attrs['PATHWAY-ID'] = new Array(pathwayCount + 1).join('.');
|
19019
|
-
const level =
|
19065
|
+
const level = this.createLevel(levelParsed);
|
19020
19066
|
redundantSet[levelKey] = level;
|
19021
19067
|
levels.push(level);
|
19022
19068
|
} else {
|
@@ -19026,6 +19072,16 @@ class LevelController extends BasePlaylistController {
|
|
19026
19072
|
});
|
19027
19073
|
this.filterAndSortMediaOptions(levels, data, resolutionFound, videoCodecFound, audioCodecFound);
|
19028
19074
|
}
|
19075
|
+
createLevel(levelParsed) {
|
19076
|
+
const level = new Level(levelParsed);
|
19077
|
+
const supplemental = levelParsed.supplemental;
|
19078
|
+
if (supplemental != null && supplemental.videoCodec && !this.isVideoSupported(supplemental.videoCodec)) {
|
19079
|
+
const error = new Error(`SUPPLEMENTAL-CODECS not supported "${supplemental.videoCodec}"`);
|
19080
|
+
this.log(error.message);
|
19081
|
+
level.supportedResult = emptyEsExports.getUnsupportedResult(error, []);
|
19082
|
+
}
|
19083
|
+
return level;
|
19084
|
+
}
|
19029
19085
|
isAudioSupported(codec) {
|
19030
19086
|
return areCodecsMediaSourceSupported(codec, 'audio', this.hls.config.preferManagedMediaSource);
|
19031
19087
|
}
|
@@ -19050,17 +19106,21 @@ class LevelController extends BasePlaylistController {
|
|
19050
19106
|
// Dispatch error after MANIFEST_LOADED is done propagating
|
19051
19107
|
Promise.resolve().then(() => {
|
19052
19108
|
if (this.hls) {
|
19109
|
+
let message = 'no level with compatible codecs found in manifest';
|
19110
|
+
let reason = message;
|
19053
19111
|
if (data.levels.length) {
|
19054
|
-
|
19112
|
+
reason = `one or more CODECS in variant not supported: ${stringify(data.levels.map(level => level.attrs.CODECS).filter((value, index, array) => array.indexOf(value) === index))}`;
|
19113
|
+
this.warn(reason);
|
19114
|
+
message += ` (${reason})`;
|
19055
19115
|
}
|
19056
|
-
const error = new Error(
|
19116
|
+
const error = new Error(message);
|
19057
19117
|
this.hls.trigger(Events.ERROR, {
|
19058
19118
|
type: ErrorTypes.MEDIA_ERROR,
|
19059
19119
|
details: ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR,
|
19060
19120
|
fatal: true,
|
19061
19121
|
url: data.url,
|
19062
19122
|
error,
|
19063
|
-
reason
|
19123
|
+
reason
|
19064
19124
|
});
|
19065
19125
|
}
|
19066
19126
|
});
|
@@ -19464,7 +19524,7 @@ function assignTrackIdsByGroup(tracks) {
|
|
19464
19524
|
});
|
19465
19525
|
}
|
19466
19526
|
|
19467
|
-
const version = "1.6.0-beta.4.0.canary.
|
19527
|
+
const version = "1.6.0-beta.4.0.canary.11069";
|
19468
19528
|
|
19469
19529
|
// ensure the worker ends up in the bundle
|
19470
19530
|
// If the worker should not be included this gets aliased to empty.js
|
@@ -20981,7 +21041,7 @@ class StreamController extends BaseStreamController {
|
|
20981
21041
|
break;
|
20982
21042
|
}
|
20983
21043
|
}
|
20984
|
-
this.log(`Init video buffer, container:${video.container}, codecs[level/parsed]=[${currentLevel.videoCodec || ''}/${parsedVideoCodec}${video.codec !== parsedVideoCodec ? ' parsed-corrected=' + video.codec : ''}}
|
21044
|
+
this.log(`Init video buffer, container:${video.container}, codecs[level/parsed]=[${currentLevel.videoCodec || ''}/${parsedVideoCodec}]${video.codec !== parsedVideoCodec ? ' parsed-corrected=' + video.codec : ''}${video.supplemental ? ' supplemental=' + video.supplemental : ''}`);
|
20985
21045
|
delete tracks.audiovideo;
|
20986
21046
|
}
|
20987
21047
|
if (audiovideo) {
|
@@ -21957,6 +22017,18 @@ const SUPPORTED_INFO_DEFAULT = {
|
|
21957
22017
|
smooth: true
|
21958
22018
|
}]
|
21959
22019
|
};
|
22020
|
+
function getUnsupportedResult(error, configurations) {
|
22021
|
+
return {
|
22022
|
+
supported: false,
|
22023
|
+
configurations,
|
22024
|
+
decodingInfoResults: [{
|
22025
|
+
supported: false,
|
22026
|
+
smooth: false,
|
22027
|
+
powerEfficient: false
|
22028
|
+
}],
|
22029
|
+
error
|
22030
|
+
};
|
22031
|
+
}
|
21960
22032
|
const SUPPORTED_INFO_CACHE = {};
|
21961
22033
|
function getMediaDecodingInfoPromise(level, audioTracksByGroup, mediaCapabilities) {
|
21962
22034
|
const videoCodecs = level.videoCodec;
|
@@ -21981,16 +22053,7 @@ function getMediaDecodingInfoPromise(level, audioTracksByGroup, mediaCapabilitie
|
|
21981
22053
|
// Override Windows Firefox HEVC MediaCapabilities result (https://github.com/video-dev/hls.js/issues/7046)
|
21982
22054
|
const ua = navigator.userAgent;
|
21983
22055
|
if (videoCodecsArray.some(videoCodec => isHEVC(videoCodec)) && userAgentHevcSupportIsInaccurate()) {
|
21984
|
-
return Promise.resolve({
|
21985
|
-
supported: false,
|
21986
|
-
configurations,
|
21987
|
-
decodingInfoResults: [{
|
21988
|
-
supported: false,
|
21989
|
-
smooth: false,
|
21990
|
-
powerEfficient: false
|
21991
|
-
}],
|
21992
|
-
error: new Error(`Overriding Windows Firefox HEVC MediaCapabilities result based on user-agent sting: (${ua})`)
|
21993
|
-
});
|
22056
|
+
return Promise.resolve(getUnsupportedResult(new Error(`Overriding Windows Firefox HEVC MediaCapabilities result based on user-agent sting: (${ua})`), configurations));
|
21994
22057
|
}
|
21995
22058
|
configurations.push.apply(configurations, videoCodecsArray.map(videoCodec => ({
|
21996
22059
|
type: 'media-source',
|