hls.js 1.6.0-beta.4.0.canary.11068 → 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 +1 -1
- 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/package.json
CHANGED
@@ -6,6 +6,7 @@ import { ElementaryStreamTypes } from '../loader/fragment';
|
|
6
6
|
import { PlaylistLevelType } from '../types/loader';
|
7
7
|
import { BufferHelper } from '../utils/buffer-helper';
|
8
8
|
import {
|
9
|
+
areCodecsMediaSourceSupported,
|
9
10
|
getCodecCompatibleName,
|
10
11
|
pickMostCompleteCodecName,
|
11
12
|
} from '../utils/codecs';
|
@@ -607,7 +608,8 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe
|
|
607
608
|
}
|
608
609
|
trackNames.forEach((trackName: SourceBufferName) => {
|
609
610
|
const parsedTrack = data[trackName] as ParsedTrack;
|
610
|
-
const { id, codec, levelCodec, container, metadata } =
|
611
|
+
const { id, codec, levelCodec, container, metadata, supplemental } =
|
612
|
+
parsedTrack;
|
611
613
|
let track = tracks[trackName];
|
612
614
|
const transferredTrack = this.transferData?.tracks?.[trackName];
|
613
615
|
const sbTrack = transferredTrack?.buffer ? transferredTrack : track;
|
@@ -618,6 +620,7 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe
|
|
618
620
|
buffer: undefined,
|
619
621
|
listeners: [],
|
620
622
|
codec,
|
623
|
+
supplemental,
|
621
624
|
container,
|
622
625
|
levelCodec,
|
623
626
|
metadata,
|
@@ -1350,6 +1353,7 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe
|
|
1350
1353
|
buffer,
|
1351
1354
|
container: track.container,
|
1352
1355
|
codec: track.codec,
|
1356
|
+
supplemental: track.supplemental,
|
1353
1357
|
levelCodec: track.levelCodec,
|
1354
1358
|
id: track.id,
|
1355
1359
|
metadata: track.metadata,
|
@@ -1430,6 +1434,15 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe
|
|
1430
1434
|
}
|
1431
1435
|
|
1432
1436
|
private getTrackCodec(track: BaseTrack, trackName: SourceBufferName): string {
|
1437
|
+
// Use supplemental video codec when supported when adding SourceBuffer (#5558)
|
1438
|
+
const supplementalCodec = track.supplemental;
|
1439
|
+
if (
|
1440
|
+
supplementalCodec &&
|
1441
|
+
trackName === 'video' &&
|
1442
|
+
areCodecsMediaSourceSupported(supplementalCodec, trackName)
|
1443
|
+
) {
|
1444
|
+
return supplementalCodec;
|
1445
|
+
}
|
1433
1446
|
const codec = pickMostCompleteCodecName(track.codec, track.levelCodec);
|
1434
1447
|
if (codec) {
|
1435
1448
|
if (trackName.slice(0, 5) === 'audio') {
|
@@ -1451,6 +1464,7 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe
|
|
1451
1464
|
codec,
|
1452
1465
|
container: track.container,
|
1453
1466
|
levelCodec: track.levelCodec,
|
1467
|
+
supplemental: track.supplemental,
|
1454
1468
|
metadata: track.metadata,
|
1455
1469
|
id: track.id,
|
1456
1470
|
listeners: [],
|
@@ -12,6 +12,7 @@ import {
|
|
12
12
|
videoCodecPreferenceValue,
|
13
13
|
} from '../utils/codecs';
|
14
14
|
import { reassignFragmentLevelIndexes } from '../utils/level-helper';
|
15
|
+
import { getUnsupportedResult } from '../utils/mediacapabilities-helper';
|
15
16
|
import { stringify } from '../utils/safe-json-stringify';
|
16
17
|
import type ContentSteeringController from './content-steering-controller';
|
17
18
|
import type Hls from '../hls';
|
@@ -163,6 +164,7 @@ export default class LevelController extends BasePlaylistController {
|
|
163
164
|
(audioCodec && !this.isAudioSupported(audioCodec)) ||
|
164
165
|
(videoCodec && !this.isVideoSupported(videoCodec))
|
165
166
|
) {
|
167
|
+
this.log(`Some or all CODECS not supported "${attributes.CODECS}"`);
|
166
168
|
return;
|
167
169
|
}
|
168
170
|
|
@@ -178,7 +180,7 @@ export default class LevelController extends BasePlaylistController {
|
|
178
180
|
const levelKey = `${contentSteeringPrefix}${levelParsed.bitrate}-${RESOLUTION}-${FRAMERATE}-${CODECS}-${VIDEO_RANGE}-${HDCP}`;
|
179
181
|
|
180
182
|
if (!redundantSet[levelKey]) {
|
181
|
-
const level =
|
183
|
+
const level = this.createLevel(levelParsed);
|
182
184
|
redundantSet[levelKey] = level;
|
183
185
|
generatePathwaySet[levelKey] = 1;
|
184
186
|
levels.push(level);
|
@@ -190,7 +192,7 @@ export default class LevelController extends BasePlaylistController {
|
|
190
192
|
// Content Steering controller to handles Pathway fallback on error
|
191
193
|
const pathwayCount = (generatePathwaySet[levelKey] += 1);
|
192
194
|
levelParsed.attrs['PATHWAY-ID'] = new Array(pathwayCount + 1).join('.');
|
193
|
-
const level =
|
195
|
+
const level = this.createLevel(levelParsed);
|
194
196
|
redundantSet[levelKey] = level;
|
195
197
|
levels.push(level);
|
196
198
|
} else {
|
@@ -208,6 +210,22 @@ export default class LevelController extends BasePlaylistController {
|
|
208
210
|
);
|
209
211
|
}
|
210
212
|
|
213
|
+
private createLevel(levelParsed: LevelParsed): Level {
|
214
|
+
const level = new Level(levelParsed);
|
215
|
+
const supplemental = levelParsed.supplemental;
|
216
|
+
if (
|
217
|
+
supplemental?.videoCodec &&
|
218
|
+
!this.isVideoSupported(supplemental.videoCodec)
|
219
|
+
) {
|
220
|
+
const error = new Error(
|
221
|
+
`SUPPLEMENTAL-CODECS not supported "${supplemental.videoCodec}"`,
|
222
|
+
);
|
223
|
+
this.log(error.message);
|
224
|
+
level.supportedResult = getUnsupportedResult(error, []);
|
225
|
+
}
|
226
|
+
return level;
|
227
|
+
}
|
228
|
+
|
211
229
|
private isAudioSupported(codec: string): boolean {
|
212
230
|
return areCodecsMediaSourceSupported(
|
213
231
|
codec,
|
@@ -247,23 +265,27 @@ export default class LevelController extends BasePlaylistController {
|
|
247
265
|
// Dispatch error after MANIFEST_LOADED is done propagating
|
248
266
|
Promise.resolve().then(() => {
|
249
267
|
if (this.hls) {
|
268
|
+
let message = 'no level with compatible codecs found in manifest';
|
269
|
+
let reason = message;
|
250
270
|
if (data.levels.length) {
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
271
|
+
reason = `one or more CODECS in variant not supported: ${stringify(
|
272
|
+
data.levels
|
273
|
+
.map((level) => level.attrs.CODECS)
|
274
|
+
.filter(
|
275
|
+
(value, index, array) => array.indexOf(value) === index,
|
276
|
+
),
|
277
|
+
)}`;
|
278
|
+
this.warn(reason);
|
279
|
+
message += ` (${reason})`;
|
256
280
|
}
|
257
|
-
const error = new Error(
|
258
|
-
'no level with compatible codecs found in manifest',
|
259
|
-
);
|
281
|
+
const error = new Error(message);
|
260
282
|
this.hls.trigger(Events.ERROR, {
|
261
283
|
type: ErrorTypes.MEDIA_ERROR,
|
262
284
|
details: ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR,
|
263
285
|
fatal: true,
|
264
286
|
url: data.url,
|
265
287
|
error,
|
266
|
-
reason
|
288
|
+
reason,
|
267
289
|
});
|
268
290
|
}
|
269
291
|
});
|
@@ -1453,7 +1453,7 @@ export default class StreamController
|
|
1453
1453
|
video.container
|
1454
1454
|
}, codecs[level/parsed]=[${currentLevel.videoCodec || ''}/${
|
1455
1455
|
parsedVideoCodec
|
1456
|
-
}${video.codec !== parsedVideoCodec ? ' parsed-corrected=' + video.codec : ''}}
|
1456
|
+
}]${video.codec !== parsedVideoCodec ? ' parsed-corrected=' + video.codec : ''}${video.supplemental ? ' supplemental=' + video.supplemental : ''}`,
|
1457
1457
|
);
|
1458
1458
|
delete tracks.audiovideo;
|
1459
1459
|
}
|
package/src/demux/mp4demuxer.ts
CHANGED
@@ -71,10 +71,11 @@ class MP4Demuxer implements Demuxer {
|
|
71
71
|
const initData = parseInitSegment(initSegment);
|
72
72
|
|
73
73
|
if (initData.video) {
|
74
|
-
const { id, timescale, codec } = initData.video;
|
74
|
+
const { id, timescale, codec, supplemental } = initData.video;
|
75
75
|
videoTrack.id = id;
|
76
76
|
videoTrack.timescale = captionTrack.timescale = timescale;
|
77
77
|
videoTrack.codec = codec;
|
78
|
+
videoTrack.supplemental = supplemental;
|
78
79
|
}
|
79
80
|
|
80
81
|
if (initData.audio) {
|
package/src/hls.ts
CHANGED
@@ -1477,6 +1477,7 @@ export type { Bufferable } from './utils/buffer-helper';
|
|
1477
1477
|
export type { CaptionScreen } from './utils/cea-608-parser';
|
1478
1478
|
export type { CuesInterface } from './utils/cues';
|
1479
1479
|
export type {
|
1480
|
+
CodecsParsed,
|
1480
1481
|
HdcpLevels,
|
1481
1482
|
HlsSkip,
|
1482
1483
|
HlsUrlParameters,
|
@@ -14,7 +14,12 @@ import {
|
|
14
14
|
} from '../utils/variable-substitution';
|
15
15
|
import type { MediaFragment } from './fragment';
|
16
16
|
import type { ContentSteeringOptions } from '../types/events';
|
17
|
-
import type {
|
17
|
+
import type {
|
18
|
+
CodecsParsed,
|
19
|
+
LevelAttributes,
|
20
|
+
LevelParsed,
|
21
|
+
VariableMap,
|
22
|
+
} from '../types/level';
|
18
23
|
import type { PlaylistLevelType } from '../types/loader';
|
19
24
|
import type { MediaAttributes, MediaPlaylist } from '../types/media-playlist';
|
20
25
|
import type { CodecType } from '../utils/codecs';
|
@@ -138,6 +143,11 @@ export default class M3U8Parser {
|
|
138
143
|
}
|
139
144
|
|
140
145
|
setCodecs(attrs.CODECS, level);
|
146
|
+
const supplementalCodecs = attrs['SUPPLEMENTAL-CODECS'];
|
147
|
+
if (supplementalCodecs) {
|
148
|
+
level.supplemental = {};
|
149
|
+
setCodecs(supplementalCodecs, level.supplemental);
|
150
|
+
}
|
141
151
|
|
142
152
|
if (!level.unknownCodecs?.length) {
|
143
153
|
levelsWithKnownCodecs.push(level);
|
@@ -822,14 +832,14 @@ function parseStartTimeOffset(startAttributes: string): number | null {
|
|
822
832
|
|
823
833
|
function setCodecs(
|
824
834
|
codecsAttributeValue: string | undefined,
|
825
|
-
level:
|
835
|
+
level: CodecsParsed,
|
826
836
|
) {
|
827
837
|
let codecs = (codecsAttributeValue || '').split(/[ ,]+/).filter((c) => c);
|
828
838
|
['video', 'audio', 'text'].forEach((type: CodecType) => {
|
829
839
|
const filtered = codecs.filter((codec) => isCodecType(codec, type));
|
830
840
|
if (filtered.length) {
|
831
841
|
// Comma separated list of all codecs for type
|
832
|
-
level[`${type}Codec`] = filtered.join(',');
|
842
|
+
level[`${type}Codec`] = filtered.map((c) => c.split('/')[0]).join(',');
|
833
843
|
// Remove known codecs so that only unknownCodecs are left after iterating through each type
|
834
844
|
codecs = codecs.filter((codec) => filtered.indexOf(codec) === -1);
|
835
845
|
}
|
package/src/types/buffer.ts
CHANGED
package/src/types/demuxer.ts
CHANGED
package/src/types/level.ts
CHANGED
@@ -3,21 +3,25 @@ import type { LevelDetails } from '../loader/level-details';
|
|
3
3
|
import type { AttrList } from '../utils/attr-list';
|
4
4
|
import type { MediaDecodingInfo } from '../utils/mediacapabilities-helper';
|
5
5
|
|
6
|
-
export interface LevelParsed {
|
6
|
+
export interface LevelParsed extends CodecsParsed {
|
7
7
|
attrs: LevelAttributes;
|
8
|
-
audioCodec?: string;
|
9
8
|
bitrate: number;
|
10
9
|
details?: LevelDetails;
|
11
10
|
height?: number;
|
12
11
|
id?: number;
|
13
12
|
name: string;
|
14
|
-
|
15
|
-
unknownCodecs?: string[];
|
13
|
+
supplemental?: CodecsParsed;
|
16
14
|
url: string;
|
17
|
-
videoCodec?: string;
|
18
15
|
width?: number;
|
19
16
|
}
|
20
17
|
|
18
|
+
export interface CodecsParsed {
|
19
|
+
audioCodec?: string;
|
20
|
+
videoCodec?: string;
|
21
|
+
textCodec?: string;
|
22
|
+
unknownCodecs?: string[];
|
23
|
+
}
|
24
|
+
|
21
25
|
export interface LevelAttributes extends AttrList {
|
22
26
|
'ALLOWED-CPC'?: string;
|
23
27
|
AUDIO?: string;
|
@@ -110,6 +114,7 @@ export class Level {
|
|
110
114
|
public readonly height: number;
|
111
115
|
public readonly id: number;
|
112
116
|
public readonly name: string;
|
117
|
+
public readonly supplemental: CodecsParsed | undefined;
|
113
118
|
public readonly videoCodec: string | undefined;
|
114
119
|
public readonly width: number;
|
115
120
|
public details?: LevelDetails;
|
@@ -144,6 +149,13 @@ export class Level {
|
|
144
149
|
.filter((c) => !!c)
|
145
150
|
.map((s: string) => s.substring(0, 4))
|
146
151
|
.join(',');
|
152
|
+
if ('supplemental' in data) {
|
153
|
+
this.supplemental = data.supplemental;
|
154
|
+
const supplementalVideo = data.supplemental?.videoCodec;
|
155
|
+
if (supplementalVideo && supplementalVideo !== data.videoCodec) {
|
156
|
+
this.codecSet += `,${supplementalVideo.substring(0, 4)}`;
|
157
|
+
}
|
158
|
+
}
|
147
159
|
this.addGroupId('audio', data.attrs.AUDIO);
|
148
160
|
this.addGroupId('text', data.attrs.SUBTITLES);
|
149
161
|
}
|
package/src/utils/attr-list.ts
CHANGED
@@ -36,7 +36,7 @@ export class AttrList {
|
|
36
36
|
return intValue;
|
37
37
|
}
|
38
38
|
|
39
|
-
hexadecimalInteger(attrName: string)
|
39
|
+
hexadecimalInteger(attrName: string) {
|
40
40
|
if (this[attrName]) {
|
41
41
|
let stringValue = (this[attrName] || '0x').slice(2);
|
42
42
|
stringValue = (stringValue.length & 1 ? '0' : '') + stringValue;
|
package/src/utils/codecs.ts
CHANGED
@@ -30,6 +30,24 @@ export const SUPPORTED_INFO_DEFAULT: MediaDecodingInfo = {
|
|
30
30
|
],
|
31
31
|
} as const;
|
32
32
|
|
33
|
+
export function getUnsupportedResult(
|
34
|
+
error: Error,
|
35
|
+
configurations: MediaDecodingConfiguration[],
|
36
|
+
): MediaDecodingInfo {
|
37
|
+
return {
|
38
|
+
supported: false,
|
39
|
+
configurations,
|
40
|
+
decodingInfoResults: [
|
41
|
+
{
|
42
|
+
supported: false,
|
43
|
+
smooth: false,
|
44
|
+
powerEfficient: false,
|
45
|
+
},
|
46
|
+
],
|
47
|
+
error,
|
48
|
+
};
|
49
|
+
}
|
50
|
+
|
33
51
|
export const SUPPORTED_INFO_CACHE: Record<
|
34
52
|
string,
|
35
53
|
Promise<MediaCapabilitiesDecodingInfo>
|
@@ -128,20 +146,14 @@ export function getMediaDecodingInfoPromise(
|
|
128
146
|
videoCodecsArray.some((videoCodec) => isHEVC(videoCodec)) &&
|
129
147
|
userAgentHevcSupportIsInaccurate()
|
130
148
|
) {
|
131
|
-
return Promise.resolve(
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
smooth: false,
|
138
|
-
powerEfficient: false,
|
139
|
-
},
|
140
|
-
],
|
141
|
-
error: new Error(
|
142
|
-
`Overriding Windows Firefox HEVC MediaCapabilities result based on user-agent sting: (${ua})`,
|
149
|
+
return Promise.resolve(
|
150
|
+
getUnsupportedResult(
|
151
|
+
new Error(
|
152
|
+
`Overriding Windows Firefox HEVC MediaCapabilities result based on user-agent sting: (${ua})`,
|
153
|
+
),
|
154
|
+
configurations,
|
143
155
|
),
|
144
|
-
|
156
|
+
);
|
145
157
|
}
|
146
158
|
configurations.push.apply(
|
147
159
|
configurations,
|
package/src/utils/mp4-tools.ts
CHANGED
@@ -223,6 +223,7 @@ export interface InitDataTrack {
|
|
223
223
|
timescale: number;
|
224
224
|
id: number;
|
225
225
|
codec: string;
|
226
|
+
supplemental: string | undefined;
|
226
227
|
}
|
227
228
|
|
228
229
|
type HdlrType = ElementaryStreamTypes.AUDIO | ElementaryStreamTypes.VIDEO;
|
@@ -290,11 +291,16 @@ export function parseInitSegment(initSegment: Uint8Array): InitData {
|
|
290
291
|
return result;
|
291
292
|
}
|
292
293
|
|
293
|
-
function parseStsd(stsd: Uint8Array): {
|
294
|
+
function parseStsd(stsd: Uint8Array): {
|
295
|
+
codec: string;
|
296
|
+
encrypted: boolean;
|
297
|
+
supplemental: string | undefined;
|
298
|
+
} {
|
294
299
|
const sampleEntries = stsd.subarray(8);
|
295
300
|
const sampleEntriesEnd = sampleEntries.subarray(8 + 78);
|
296
301
|
const fourCC = bin2str(sampleEntries.subarray(4, 8));
|
297
302
|
let codec = fourCC;
|
303
|
+
let supplemental;
|
298
304
|
const encrypted = fourCC === 'enca' || fourCC === 'encv';
|
299
305
|
if (encrypted) {
|
300
306
|
const encBox = findBox(sampleEntries, [fourCC])[0];
|
@@ -314,6 +320,7 @@ function parseStsd(stsd: Uint8Array): { codec: string; encrypted: boolean } {
|
|
314
320
|
}
|
315
321
|
});
|
316
322
|
}
|
323
|
+
const codecFourCC = codec;
|
317
324
|
switch (codec) {
|
318
325
|
case 'avc1':
|
319
326
|
case 'avc2':
|
@@ -322,6 +329,10 @@ function parseStsd(stsd: Uint8Array): { codec: string; encrypted: boolean } {
|
|
322
329
|
// extract profile + compatibility + level out of avcC box
|
323
330
|
const avcCBox = findBox(sampleEntriesEnd, ['avcC'])[0];
|
324
331
|
codec += '.' + toHex(avcCBox[1]) + toHex(avcCBox[2]) + toHex(avcCBox[3]);
|
332
|
+
supplemental = parseSupplementalDoViCodec(
|
333
|
+
codecFourCC === 'avc1' ? 'dva1' : 'dvav',
|
334
|
+
sampleEntriesEnd,
|
335
|
+
);
|
325
336
|
break;
|
326
337
|
}
|
327
338
|
case 'mp4a': {
|
@@ -371,34 +382,41 @@ function parseStsd(stsd: Uint8Array): { codec: string; encrypted: boolean } {
|
|
371
382
|
}
|
372
383
|
case 'hvc1':
|
373
384
|
case 'hev1': {
|
374
|
-
const
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
const
|
390
|
-
|
385
|
+
const hvcCBoxes = findBox(sampleEntriesEnd, ['hvcC']);
|
386
|
+
if (hvcCBoxes) {
|
387
|
+
const hvcCBox = hvcCBoxes[0];
|
388
|
+
const profileByte = hvcCBox[1];
|
389
|
+
const profileSpace = ['', 'A', 'B', 'C'][profileByte >> 6];
|
390
|
+
const generalProfileIdc = profileByte & 0x1f;
|
391
|
+
const profileCompat = readUint32(hvcCBox, 2);
|
392
|
+
const tierFlag = (profileByte & 0x20) >> 5 ? 'H' : 'L';
|
393
|
+
const levelIDC = hvcCBox[12];
|
394
|
+
const constraintIndicator = hvcCBox.subarray(6, 12);
|
395
|
+
codec += '.' + profileSpace + generalProfileIdc;
|
396
|
+
codec += '.' + profileCompat.toString(16).toUpperCase();
|
397
|
+
codec += '.' + tierFlag + levelIDC;
|
398
|
+
let constraintString = '';
|
399
|
+
for (let i = constraintIndicator.length; i--; ) {
|
400
|
+
const byte = constraintIndicator[i];
|
401
|
+
if (byte || constraintString) {
|
402
|
+
const encodedByte = byte.toString(16).toUpperCase();
|
403
|
+
constraintString = '.' + encodedByte + constraintString;
|
404
|
+
}
|
391
405
|
}
|
406
|
+
codec += constraintString;
|
392
407
|
}
|
393
|
-
|
408
|
+
supplemental = parseSupplementalDoViCodec(
|
409
|
+
codecFourCC == 'hev1' ? 'dvhe' : 'dvh1',
|
410
|
+
sampleEntriesEnd,
|
411
|
+
);
|
394
412
|
break;
|
395
413
|
}
|
396
414
|
case 'dvh1':
|
397
|
-
case 'dvhe':
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
codec
|
415
|
+
case 'dvhe':
|
416
|
+
case 'dvav':
|
417
|
+
case 'dva1':
|
418
|
+
case 'dav1': {
|
419
|
+
codec = parseSupplementalDoViCodec(codec, sampleEntriesEnd) || codec;
|
402
420
|
break;
|
403
421
|
}
|
404
422
|
case 'vp09': {
|
@@ -463,6 +481,7 @@ function parseStsd(stsd: Uint8Array): { codec: string; encrypted: boolean } {
|
|
463
481
|
addLeadingZero(matrixCoefficients) +
|
464
482
|
'.' +
|
465
483
|
videoFullRangeFlag;
|
484
|
+
supplemental = parseSupplementalDoViCodec('dav1', sampleEntriesEnd);
|
466
485
|
break;
|
467
486
|
}
|
468
487
|
case 'ac-3':
|
@@ -473,7 +492,28 @@ function parseStsd(stsd: Uint8Array): { codec: string; encrypted: boolean } {
|
|
473
492
|
default:
|
474
493
|
break;
|
475
494
|
}
|
476
|
-
return { codec, encrypted };
|
495
|
+
return { codec, encrypted, supplemental };
|
496
|
+
}
|
497
|
+
|
498
|
+
function parseSupplementalDoViCodec(
|
499
|
+
fourCC: string,
|
500
|
+
sampleEntriesEnd: Uint8Array,
|
501
|
+
): string | undefined {
|
502
|
+
const dvvCResult = findBox(sampleEntriesEnd, ['dvvC']); // used by DoVi Profile 8 to 10
|
503
|
+
const dvXCBox = dvvCResult.length
|
504
|
+
? dvvCResult[0]
|
505
|
+
: findBox(sampleEntriesEnd, ['dvcC'])[0]; // used by DoVi Profiles up to 7 and 20
|
506
|
+
if (dvXCBox) {
|
507
|
+
const doViProfile = (dvXCBox[2] >> 1) & 0x7f;
|
508
|
+
const doViLevel = ((dvXCBox[2] << 5) & 0x20) | ((dvXCBox[3] >> 3) & 0x1f);
|
509
|
+
return (
|
510
|
+
fourCC +
|
511
|
+
'.' +
|
512
|
+
addLeadingZero(doViProfile) +
|
513
|
+
'.' +
|
514
|
+
addLeadingZero(doViLevel)
|
515
|
+
);
|
516
|
+
}
|
477
517
|
}
|
478
518
|
|
479
519
|
function skipBERInteger(bytes: Uint8Array, i: number): number {
|