hls.js 1.5.14-0.canary.10668 → 1.5.14-0.canary.10670
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 +8 -4
- package/dist/hls.d.ts +8 -4
- package/dist/hls.js +141 -58
- package/dist/hls.js.d.ts +8 -4
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +212 -36
- 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 +191 -28
- 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 +127 -49
- 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 +1 -1
- package/src/controller/buffer-controller.ts +2 -2
- package/src/controller/level-controller.ts +44 -21
- package/src/controller/stream-controller.ts +30 -3
- package/src/demux/video/hevc-video-parser.ts +12 -1
- package/src/hls.ts +24 -4
- package/src/remux/passthrough-remuxer.ts +7 -15
- package/src/utils/codecs.ts +36 -6
- package/src/utils/mediacapabilities-helper.ts +30 -23
- package/src/utils/mediakeys-helper.ts +2 -2
package/package.json
CHANGED
@@ -634,7 +634,7 @@ transfer tracks: ${JSON.stringify(transferredTracks, (key, value) => (key === 'i
|
|
634
634
|
if (trackName.slice(0, 5) === 'audio') {
|
635
635
|
trackCodec = getCodecCompatibleName(trackCodec, this.appendSource);
|
636
636
|
}
|
637
|
-
this.log(`switching codec ${sbCodec} to ${
|
637
|
+
this.log(`switching codec ${sbCodec} to ${trackCodec}`);
|
638
638
|
if (trackCodec !== (track.pendingCodec || track.codec)) {
|
639
639
|
track.pendingCodec = trackCodec;
|
640
640
|
}
|
@@ -1431,7 +1431,7 @@ transfer tracks: ${JSON.stringify(transferredTracks, (key, value) => (key === 'i
|
|
1431
1431
|
}
|
1432
1432
|
|
1433
1433
|
private getTrackCodec(track: BaseTrack, trackName: SourceBufferName): string {
|
1434
|
-
const codec = track.codec
|
1434
|
+
const codec = pickMostCompleteCodecName(track.codec, track.levelCodec);
|
1435
1435
|
if (codec) {
|
1436
1436
|
if (trackName.slice(0, 5) === 'audio') {
|
1437
1437
|
return getCodecCompatibleName(codec, this.appendSource);
|
@@ -16,6 +16,7 @@ import {
|
|
16
16
|
codecsSetSelectionPreferenceValue,
|
17
17
|
convertAVC1ToAVCOTI,
|
18
18
|
getCodecCompatibleName,
|
19
|
+
sampleEntryCodesISO,
|
19
20
|
videoCodecPreferenceValue,
|
20
21
|
} from '../utils/codecs';
|
21
22
|
import BasePlaylistController from './base-playlist-controller';
|
@@ -130,23 +131,36 @@ export default class LevelController extends BasePlaylistController {
|
|
130
131
|
|
131
132
|
// only keep levels with supported audio/video codecs
|
132
133
|
const { width, height, unknownCodecs } = levelParsed;
|
134
|
+
let unknownUnsupportedCodecCount = unknownCodecs
|
135
|
+
? unknownCodecs.length
|
136
|
+
: 0;
|
137
|
+
if (unknownCodecs) {
|
138
|
+
// Treat unknown codec as audio or video codec based on passing `isTypeSupported` check
|
139
|
+
// (allows for playback of any supported codec even if not indexed in utils/codecs)
|
140
|
+
for (let i = unknownUnsupportedCodecCount; i--; ) {
|
141
|
+
const unknownCodec = unknownCodecs[i];
|
142
|
+
if (this.isAudioSupported(unknownCodec)) {
|
143
|
+
levelParsed.audioCodec = audioCodec = audioCodec
|
144
|
+
? `${audioCodec},${unknownCodec}`
|
145
|
+
: unknownCodec;
|
146
|
+
unknownUnsupportedCodecCount--;
|
147
|
+
sampleEntryCodesISO.audio[audioCodec.substring(0, 4)] = 2;
|
148
|
+
} else if (this.isVideoSupported(unknownCodec)) {
|
149
|
+
levelParsed.videoCodec = videoCodec = videoCodec
|
150
|
+
? `${videoCodec},${unknownCodec}`
|
151
|
+
: unknownCodec;
|
152
|
+
unknownUnsupportedCodecCount--;
|
153
|
+
sampleEntryCodesISO.video[videoCodec.substring(0, 4)] = 2;
|
154
|
+
}
|
155
|
+
}
|
156
|
+
}
|
133
157
|
resolutionFound ||= !!(width && height);
|
134
158
|
videoCodecFound ||= !!videoCodec;
|
135
159
|
audioCodecFound ||= !!audioCodec;
|
136
160
|
if (
|
137
|
-
|
138
|
-
(audioCodec &&
|
139
|
-
|
140
|
-
audioCodec,
|
141
|
-
'audio',
|
142
|
-
preferManagedMediaSource,
|
143
|
-
)) ||
|
144
|
-
(videoCodec &&
|
145
|
-
!areCodecsMediaSourceSupported(
|
146
|
-
videoCodec,
|
147
|
-
'video',
|
148
|
-
preferManagedMediaSource,
|
149
|
-
))
|
161
|
+
unknownUnsupportedCodecCount ||
|
162
|
+
(audioCodec && !this.isAudioSupported(audioCodec)) ||
|
163
|
+
(videoCodec && !this.isVideoSupported(videoCodec))
|
150
164
|
) {
|
151
165
|
return;
|
152
166
|
}
|
@@ -193,6 +207,22 @@ export default class LevelController extends BasePlaylistController {
|
|
193
207
|
);
|
194
208
|
}
|
195
209
|
|
210
|
+
private isAudioSupported(codec: string): boolean {
|
211
|
+
return areCodecsMediaSourceSupported(
|
212
|
+
codec,
|
213
|
+
'audio',
|
214
|
+
this.hls.config.preferManagedMediaSource,
|
215
|
+
);
|
216
|
+
}
|
217
|
+
|
218
|
+
private isVideoSupported(codec: string): boolean {
|
219
|
+
return areCodecsMediaSourceSupported(
|
220
|
+
codec,
|
221
|
+
'video',
|
222
|
+
this.hls.config.preferManagedMediaSource,
|
223
|
+
);
|
224
|
+
}
|
225
|
+
|
196
226
|
private filterAndSortMediaOptions(
|
197
227
|
filteredLevels: Level[],
|
198
228
|
data: ManifestLoadedData,
|
@@ -240,15 +270,8 @@ export default class LevelController extends BasePlaylistController {
|
|
240
270
|
}
|
241
271
|
|
242
272
|
if (data.audioTracks) {
|
243
|
-
const { preferManagedMediaSource } = this.hls.config;
|
244
273
|
audioTracks = data.audioTracks.filter(
|
245
|
-
(track) =>
|
246
|
-
!track.audioCodec ||
|
247
|
-
areCodecsMediaSourceSupported(
|
248
|
-
track.audioCodec,
|
249
|
-
'audio',
|
250
|
-
preferManagedMediaSource,
|
251
|
-
),
|
274
|
+
(track) => !track.audioCodec || this.isAudioSupported(track.audioCodec),
|
252
275
|
);
|
253
276
|
// Assign ids after filtering as array indices by group-id
|
254
277
|
assignTrackIdsByGroup(audioTracks);
|
@@ -14,6 +14,7 @@ import TransmuxerInterface from '../demux/transmuxer-interface';
|
|
14
14
|
import { ChunkMetadata } from '../types/transmuxer';
|
15
15
|
import GapController, { MAX_START_GAP_JUMP } from './gap-controller';
|
16
16
|
import { ErrorDetails } from '../errors';
|
17
|
+
import { pickMostCompleteCodecName } from '../utils/codecs';
|
17
18
|
import type { NetworkComponentAPI } from '../types/component-api';
|
18
19
|
import type Hls from '../hls';
|
19
20
|
import type { Level } from '../types/level';
|
@@ -1367,7 +1368,16 @@ export default class StreamController
|
|
1367
1368
|
// include levelCodec in audio and video tracks
|
1368
1369
|
const { audio, video, audiovideo } = tracks;
|
1369
1370
|
if (audio) {
|
1370
|
-
let audioCodec =
|
1371
|
+
let audioCodec = pickMostCompleteCodecName(
|
1372
|
+
audio.codec,
|
1373
|
+
currentLevel.audioCodec,
|
1374
|
+
);
|
1375
|
+
// Add level and profile to make up for passthrough-remuxer not being able to parse full codec
|
1376
|
+
// (logger warning "Unhandled audio codec...")
|
1377
|
+
if (audioCodec === 'mp4a') {
|
1378
|
+
audioCodec = 'mp4a.40.5';
|
1379
|
+
}
|
1380
|
+
// Handle `audioCodecSwitch`
|
1371
1381
|
const ua = navigator.userAgent.toLowerCase();
|
1372
1382
|
if (this.audioCodecSwitch) {
|
1373
1383
|
if (audioCodec) {
|
@@ -1420,12 +1430,29 @@ export default class StreamController
|
|
1420
1430
|
if (video) {
|
1421
1431
|
video.levelCodec = currentLevel.videoCodec;
|
1422
1432
|
video.id = 'main';
|
1433
|
+
const parsedVideoCodec = video.codec;
|
1434
|
+
if (parsedVideoCodec?.length === 4) {
|
1435
|
+
// Make up for passthrough-remuxer not being able to parse full codec
|
1436
|
+
// (logger warning "Unhandled video codec...")
|
1437
|
+
switch (parsedVideoCodec) {
|
1438
|
+
case 'hvc1':
|
1439
|
+
case 'hev1':
|
1440
|
+
video.codec = 'hvc1.1.6.L120.90';
|
1441
|
+
break;
|
1442
|
+
case 'av01':
|
1443
|
+
video.codec = 'av01.0.04M.08';
|
1444
|
+
break;
|
1445
|
+
case 'avc1':
|
1446
|
+
video.codec = 'avc1.42e01e';
|
1447
|
+
break;
|
1448
|
+
}
|
1449
|
+
}
|
1423
1450
|
this.log(
|
1424
1451
|
`Init video buffer, container:${
|
1425
1452
|
video.container
|
1426
1453
|
}, codecs[level/parsed]=[${currentLevel.videoCodec || ''}/${
|
1427
|
-
|
1428
|
-
}]`,
|
1454
|
+
parsedVideoCodec
|
1455
|
+
}${video.codec !== parsedVideoCodec ? ' parsed-corrected=' + video.codec : ''}}]`,
|
1429
1456
|
);
|
1430
1457
|
delete tracks.audiovideo;
|
1431
1458
|
}
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import BaseVideoParser from './base-video-parser';
|
2
|
+
import type { ParsedVideoSample } from '../tsdemuxer';
|
2
3
|
import type {
|
3
4
|
DemuxedVideoTrack,
|
4
5
|
DemuxedUserdataTrack,
|
@@ -180,7 +181,7 @@ class HevcVideoParser extends BaseVideoParser {
|
|
180
181
|
track.params[prop] = config[prop];
|
181
182
|
}
|
182
183
|
}
|
183
|
-
if (
|
184
|
+
if (track.vps !== undefined && track.vps[0] === this.initVPS) {
|
184
185
|
track.pps.push(unit.data);
|
185
186
|
}
|
186
187
|
}
|
@@ -239,6 +240,16 @@ class HevcVideoParser extends BaseVideoParser {
|
|
239
240
|
return new Uint8Array(dst.buffer, 0, dstIdx);
|
240
241
|
}
|
241
242
|
|
243
|
+
protected pushAccessUnit(
|
244
|
+
VideoSample: ParsedVideoSample,
|
245
|
+
videoTrack: DemuxedVideoTrack,
|
246
|
+
) {
|
247
|
+
super.pushAccessUnit(VideoSample, videoTrack);
|
248
|
+
if (this.initVPS) {
|
249
|
+
this.initVPS = null; // null initVPS to prevent possible track's sps/pps growth until next VPS
|
250
|
+
}
|
251
|
+
}
|
252
|
+
|
242
253
|
readVPS(vps: Uint8Array): {
|
243
254
|
numTemporalLayers: number;
|
244
255
|
temporalIdNested: boolean;
|
package/src/hls.ts
CHANGED
@@ -54,6 +54,11 @@ import type FragmentLoader from './loader/fragment-loader';
|
|
54
54
|
import type { LevelDetails } from './loader/level-details';
|
55
55
|
import type TaskLoop from './task-loop';
|
56
56
|
import type TransmuxerInterface from './demux/transmuxer-interface';
|
57
|
+
import { getAudioTracksByGroup } from './utils/rendition-helper';
|
58
|
+
import {
|
59
|
+
getMediaDecodingInfoPromise,
|
60
|
+
MediaDecodingInfo,
|
61
|
+
} from './utils/mediacapabilities-helper';
|
57
62
|
|
58
63
|
/**
|
59
64
|
* The `Hls` class is the core of the HLS.js library used to instantiate player instances.
|
@@ -951,7 +956,7 @@ export default class Hls implements HlsEventEmitter {
|
|
951
956
|
/**
|
952
957
|
* Get the complete list of audio tracks across all media groups
|
953
958
|
*/
|
954
|
-
get allAudioTracks():
|
959
|
+
get allAudioTracks(): MediaPlaylist[] {
|
955
960
|
const audioTrackController = this.audioTrackController;
|
956
961
|
return audioTrackController ? audioTrackController.allAudioTracks : [];
|
957
962
|
}
|
@@ -959,7 +964,7 @@ export default class Hls implements HlsEventEmitter {
|
|
959
964
|
/**
|
960
965
|
* Get the list of selectable audio tracks
|
961
966
|
*/
|
962
|
-
get audioTracks():
|
967
|
+
get audioTracks(): MediaPlaylist[] {
|
963
968
|
const audioTrackController = this.audioTrackController;
|
964
969
|
return audioTrackController ? audioTrackController.audioTracks : [];
|
965
970
|
}
|
@@ -985,7 +990,7 @@ export default class Hls implements HlsEventEmitter {
|
|
985
990
|
/**
|
986
991
|
* get the complete list of subtitle tracks across all media groups
|
987
992
|
*/
|
988
|
-
get allSubtitleTracks():
|
993
|
+
get allSubtitleTracks(): MediaPlaylist[] {
|
989
994
|
const subtitleTrackController = this.subtitleTrackController;
|
990
995
|
return subtitleTrackController
|
991
996
|
? subtitleTrackController.allSubtitleTracks
|
@@ -995,7 +1000,7 @@ export default class Hls implements HlsEventEmitter {
|
|
995
1000
|
/**
|
996
1001
|
* get alternate subtitle tracks list from playlist
|
997
1002
|
*/
|
998
|
-
get subtitleTracks():
|
1003
|
+
get subtitleTracks(): MediaPlaylist[] {
|
999
1004
|
const subtitleTrackController = this.subtitleTrackController;
|
1000
1005
|
return subtitleTrackController
|
1001
1006
|
? subtitleTrackController.subtitleTracks
|
@@ -1132,6 +1137,21 @@ export default class Hls implements HlsEventEmitter {
|
|
1132
1137
|
get interstitialsManager(): InterstitialsManager | null {
|
1133
1138
|
return this.interstitialsController?.interstitialsManager || null;
|
1134
1139
|
}
|
1140
|
+
|
1141
|
+
/**
|
1142
|
+
* returns mediaCapabilities.decodingInfo for a variant/rendition
|
1143
|
+
*/
|
1144
|
+
getMediaDecodingInfo(
|
1145
|
+
level: Level,
|
1146
|
+
audioTracks: MediaPlaylist[] = this.allAudioTracks,
|
1147
|
+
): Promise<MediaDecodingInfo> {
|
1148
|
+
const audioTracksByGroup = getAudioTracksByGroup(audioTracks);
|
1149
|
+
return getMediaDecodingInfoPromise(
|
1150
|
+
level,
|
1151
|
+
audioTracksByGroup,
|
1152
|
+
navigator.mediaCapabilities,
|
1153
|
+
);
|
1154
|
+
}
|
1135
1155
|
}
|
1136
1156
|
|
1137
1157
|
export type {
|
@@ -32,7 +32,7 @@ import type {
|
|
32
32
|
} from '../types/demuxer';
|
33
33
|
import type { DecryptData } from '../loader/level-key';
|
34
34
|
import type { TypeSupported } from '../utils/codecs';
|
35
|
-
import type
|
35
|
+
import { logger, type ILogger } from '../utils/logger';
|
36
36
|
import type { RationalTimestamp } from '../utils/timescale-conversion';
|
37
37
|
|
38
38
|
class PassThroughRemuxer implements Remuxer {
|
@@ -86,7 +86,7 @@ class PassThroughRemuxer implements Remuxer {
|
|
86
86
|
}
|
87
87
|
const initData = (this.initData = parseInitSegment(initSegment));
|
88
88
|
|
89
|
-
// Get codec from initSegment
|
89
|
+
// Get codec from initSegment
|
90
90
|
if (initData.audio) {
|
91
91
|
audioCodec = getParsedTrackCodec(
|
92
92
|
initData.audio,
|
@@ -298,21 +298,13 @@ function getParsedTrackCodec(
|
|
298
298
|
const preferManagedMediaSource = false;
|
299
299
|
return getCodecCompatibleName(parsedCodec, preferManagedMediaSource);
|
300
300
|
}
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
);
|
305
|
-
return result;
|
301
|
+
|
302
|
+
logger.warn(`Unhandled audio codec "${parsedCodec}" in mp4 MAP`);
|
303
|
+
return parsedCodec || 'mp4a';
|
306
304
|
}
|
307
305
|
// Provide defaults based on codec type
|
308
306
|
// This allows for some playback of some fmp4 playlists without CODECS defined in manifest
|
309
|
-
|
310
|
-
|
311
|
-
return 'hvc1.1.6.L120.90';
|
312
|
-
}
|
313
|
-
if (parsedCodec === 'av01') {
|
314
|
-
return 'av01.0.04M.08';
|
315
|
-
}
|
316
|
-
return 'avc1.42e01e';
|
307
|
+
logger.warn(`Unhandled video codec "${parsedCodec}" in mp4 MAP`);
|
308
|
+
return parsedCodec || 'avc1';
|
317
309
|
}
|
318
310
|
export default PassThroughRemuxer;
|
package/src/utils/codecs.ts
CHANGED
@@ -2,7 +2,7 @@ import { getMediaSource } from './mediasource-helper';
|
|
2
2
|
|
3
3
|
// from http://mp4ra.org/codecs.html
|
4
4
|
// values indicate codec selection preference (lower is higher priority)
|
5
|
-
const sampleEntryCodesISO = {
|
5
|
+
export const sampleEntryCodesISO = {
|
6
6
|
audio: {
|
7
7
|
a3ds: 1,
|
8
8
|
'ac-3': 0.95,
|
@@ -107,7 +107,7 @@ function isCodecMediaSourceSupported(
|
|
107
107
|
}
|
108
108
|
|
109
109
|
export function mimeTypeForCodec(codec: string, type: CodecType): string {
|
110
|
-
return `${type}/mp4;codecs
|
110
|
+
return `${type}/mp4;codecs=${codec}`;
|
111
111
|
}
|
112
112
|
|
113
113
|
export function videoCodecPreferenceValue(
|
@@ -198,16 +198,33 @@ export function pickMostCompleteCodecName(
|
|
198
198
|
): string | undefined {
|
199
199
|
// Parsing of mp4a codecs strings in mp4-tools from media is incomplete as of d8c6c7a
|
200
200
|
// so use level codec is parsed codec is unavailable or incomplete
|
201
|
-
if (
|
201
|
+
if (
|
202
|
+
parsedCodec &&
|
203
|
+
(parsedCodec.length > 4 ||
|
204
|
+
['ac-3', 'ec-3', 'alac', 'fLaC', 'Opus'].indexOf(parsedCodec) !== -1)
|
205
|
+
) {
|
202
206
|
return parsedCodec;
|
203
207
|
}
|
204
|
-
|
208
|
+
if (levelCodec) {
|
209
|
+
const levelCodecs = levelCodec.split(',');
|
210
|
+
if (levelCodecs.length > 1) {
|
211
|
+
if (parsedCodec) {
|
212
|
+
for (let i = levelCodecs.length; i--; ) {
|
213
|
+
if (levelCodecs[i].substring(0, 4) === parsedCodec.substring(0, 4)) {
|
214
|
+
return levelCodecs[i];
|
215
|
+
}
|
216
|
+
}
|
217
|
+
}
|
218
|
+
return levelCodecs[0];
|
219
|
+
}
|
220
|
+
}
|
221
|
+
return levelCodec || parsedCodec;
|
205
222
|
}
|
206
223
|
|
207
|
-
export function convertAVC1ToAVCOTI(
|
224
|
+
export function convertAVC1ToAVCOTI(videoCodecs: string): string {
|
208
225
|
// Convert avc1 codec string from RFC-4281 to RFC-6381 for MediaSource.isTypeSupported
|
209
226
|
// Examples: avc1.66.30 to avc1.42001e and avc1.77.30,avc1.66.30 to avc1.4d001e,avc1.42001e.
|
210
|
-
const codecs =
|
227
|
+
const codecs = videoCodecs.split(',');
|
211
228
|
for (let i = 0; i < codecs.length; i++) {
|
212
229
|
const avcdata = codecs[i].split('.');
|
213
230
|
if (avcdata.length > 2) {
|
@@ -222,6 +239,19 @@ export function convertAVC1ToAVCOTI(codec: string) {
|
|
222
239
|
return codecs.join(',');
|
223
240
|
}
|
224
241
|
|
242
|
+
export function fillInMissingAV01Params(videoCodec: string): string {
|
243
|
+
// Used to fill in incomplete AV1 playlist CODECS strings for mediaCapabilities.decodingInfo queries
|
244
|
+
if (videoCodec.startsWith('av01.')) {
|
245
|
+
const av1params = videoCodec.split('.');
|
246
|
+
const placeholders = ['0', '111', '01', '01', '01', '0'];
|
247
|
+
for (let i = av1params.length; i > 4 && i < 10; i++) {
|
248
|
+
av1params[i] = placeholders[i - 4];
|
249
|
+
}
|
250
|
+
return av1params.join('.');
|
251
|
+
}
|
252
|
+
return videoCodec;
|
253
|
+
}
|
254
|
+
|
225
255
|
export interface TypeSupported {
|
226
256
|
mpeg: boolean;
|
227
257
|
mp3: boolean;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { mimeTypeForCodec } from './codecs';
|
1
|
+
import { fillInMissingAV01Params, mimeTypeForCodec } from './codecs';
|
2
2
|
import type { Level, VideoRange } from '../types/level';
|
3
3
|
import type { AudioSelectionOption } from '../types/media-playlist';
|
4
4
|
import type { AudioTracksByGroup } from './rendition-helper';
|
@@ -96,33 +96,40 @@ export function getMediaDecodingInfoPromise(
|
|
96
96
|
): Promise<MediaDecodingInfo> {
|
97
97
|
const videoCodecs = level.videoCodec;
|
98
98
|
const audioCodecs = level.audioCodec;
|
99
|
-
if (!videoCodecs
|
99
|
+
if ((!videoCodecs && !audioCodecs) || !mediaCapabilities) {
|
100
100
|
return Promise.resolve(SUPPORTED_INFO_DEFAULT);
|
101
101
|
}
|
102
102
|
|
103
|
-
const
|
104
|
-
width: level.width,
|
105
|
-
height: level.height,
|
106
|
-
bitrate: Math.ceil(Math.max(level.bitrate * 0.9, level.averageBitrate)),
|
107
|
-
// Assume a framerate of 30fps since MediaCapabilities will not accept Level default of 0.
|
108
|
-
framerate: level.frameRate || 30,
|
109
|
-
};
|
103
|
+
const configurations: MediaDecodingConfiguration[] = [];
|
110
104
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
105
|
+
if (videoCodecs) {
|
106
|
+
const baseVideoConfiguration: BaseVideoConfiguration = {
|
107
|
+
width: level.width,
|
108
|
+
height: level.height,
|
109
|
+
bitrate: Math.ceil(Math.max(level.bitrate * 0.9, level.averageBitrate)),
|
110
|
+
// Assume a framerate of 30fps since MediaCapabilities will not accept Level default of 0.
|
111
|
+
framerate: level.frameRate || 30,
|
112
|
+
};
|
113
|
+
const videoRange = level.videoRange;
|
114
|
+
if (videoRange !== 'SDR') {
|
115
|
+
baseVideoConfiguration.transferFunction =
|
116
|
+
videoRange.toLowerCase() as TransferFunction;
|
117
|
+
}
|
116
118
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
119
|
+
configurations.push.apply(
|
120
|
+
configurations,
|
121
|
+
videoCodecs.split(',').map((videoCodec) => ({
|
122
|
+
type: 'media-source',
|
123
|
+
video: {
|
124
|
+
...baseVideoConfiguration,
|
125
|
+
contentType: mimeTypeForCodec(
|
126
|
+
fillInMissingAV01Params(videoCodec),
|
127
|
+
'video',
|
128
|
+
),
|
129
|
+
},
|
130
|
+
})),
|
131
|
+
);
|
132
|
+
}
|
126
133
|
|
127
134
|
if (audioCodecs && level.audioGroups) {
|
128
135
|
level.audioGroups.forEach((audioGroupId) => {
|
@@ -152,12 +152,12 @@ function createMediaKeySystemConfigurations(
|
|
152
152
|
drmSystemOptions.sessionType || 'temporary',
|
153
153
|
],
|
154
154
|
audioCapabilities: audioCodecs.map((codec) => ({
|
155
|
-
contentType: `audio/mp4; codecs
|
155
|
+
contentType: `audio/mp4; codecs=${codec}`,
|
156
156
|
robustness: drmSystemOptions.audioRobustness || '',
|
157
157
|
encryptionScheme: drmSystemOptions.audioEncryptionScheme || null,
|
158
158
|
})),
|
159
159
|
videoCapabilities: videoCodecs.map((codec) => ({
|
160
|
-
contentType: `video/mp4; codecs
|
160
|
+
contentType: `video/mp4; codecs=${codec}`,
|
161
161
|
robustness: drmSystemOptions.videoRobustness || '',
|
162
162
|
encryptionScheme: drmSystemOptions.videoEncryptionScheme || null,
|
163
163
|
})),
|