@@ -67,7 +67,6 @@ export function findBox(data: Uint8Array, path: string[]): Uint8Array[] {
67
67
const size = readUint32(data, i);
68
68
const type = bin2str(data.subarray(i + 4, i + 8));
69
69
const endbox = size > 1 ? i + size : end;
70
-
71
70
if (type === path[0]) {
72
71
if (path.length === 1) {
73
72
// this is the end of the path and we've found the box we were
@@ -264,44 +263,61 @@ export function parseInitSegment(initSegment: Uint8Array): InitData {
264
263
}
265
264
266
265
// Handle H265
267
-
else if (
268
-
(codec === 'hev1' || codec === 'hvc1') &&
269
-
stsd.length > 102 &&
270
-
bin2str(stsd.subarray(98, 102)) === 'hvcC'
271
-
) {
272
-
// Profile Space
273
-
const profileByte = stsd[103];
274
-
const profileSpace = { 0: '', 1: 'A', 2: 'B', 3: 'C' }[
275
-
profileByte >> 6
276
-
];
277
-
const generalProfileIdc = profileByte & 31;
278
-
codec += '.' + profileSpace + generalProfileIdc;
279
-
280
-
// Compatibility
281
-
let reversed = 0;
282
-
for (let i = 0; i < 4; ++i) {
283
-
// byte number
284
-
for (let j = 0; j < 8; ++j) {
285
-
// bit number
286
-
reversed |=
287
-
((stsd[i + 104] >> (7 - j)) & 1) << (31 - 8 * i - j);
266
+
else if (isHEVC(codec)) {
267
+
// @fogarasyroland's method https://github.com/video-dev/hls.js/pull/5024
268
+
let hvcC;
269
+
const codecBox = findBox(stsd.subarray(8), [codec])[0];
270
+
if (codecBox) {
271
+
const { end } = parseVisualSampleEntry(codecBox);
272
+
hvcC = findBox(codecBox.subarray(end), ['hvcC'])[0];
273
+
if (hvcC) {
274
+
codec = mimeTypeBuilderHEVC(
275
+
codec,
276
+
parseHvcConfigurationRecord(hvcC)
277
+
);
288
278
}
289
279
}
290
-
codec += '.' + toHex(reversed >>> 0);
291
-
292
-
// Tier Flag
293
-
codec += (profileByte & 32 ? '.H' : '.L') + stsd[114];
294
-
295
-
// Constraint String
296
-
let hasByte = false;
297
-
let constraintString = '';
298
-
for (let i = 113; i > 107; --i) {
299
-
if (stsd[i] || hasByte) {
300
-
constraintString = '.' + toHex(stsd[i]) + constraintString;
301
-
hasByte = true;
280
+
// @uvjustin's method https://github.com/video-dev/hls.js/pull/4996
281
+
if (
282
+
!hvcC &&
283
+
stsd.length > 102 &&
284
+
bin2str(stsd.subarray(98, 102)) === 'hvcC'
285
+
) {
286
+
// Profile Space
287
+
const profileByte = stsd[103];
288
+
const profileSpace = { 0: '', 1: 'A', 2: 'B', 3: 'C' }[
289
+
profileByte >> 6
290
+
];
291
+
const generalProfileIdc = profileByte & 31;
292
+
codec += '.' + profileSpace + generalProfileIdc;
293
+
294
+
// Compatibility
295
+
let reversed = 0;
296
+
for (let i = 0; i < 4; ++i) {
297
+
// byte number
298
+
for (let j = 0; j < 8; ++j) {
299
+
// bit number
300
+
reversed |=
301
+
((stsd[i + 104] >> (7 - j)) & 1) << (31 - 8 * i - j);
302
+
}
302
303
}
304
+
codec += '.' + toHex(reversed >>> 0);
305
+
306
+
// Tier Flag
307
+
codec += (profileByte & 32 ? '.H' : '.L') + stsd[114];
308
+
309
+
// Constraint String
310
+
let hasByte = false;
311
+
let constraintString = '';
312
+
for (let i = 113; i > 107; --i) {
313
+
if (stsd[i] || hasByte) {
314
+
constraintString =
315
+
'.' + toHex(stsd[i]) + constraintString;
316
+
hasByte = true;
317
+
}
318
+
}
319
+
codec += constraintString;
303
320
}
304
-
codec += constraintString;
305
321
}
306
322
307
323
// Handle Audio
@@ -1245,3 +1261,130 @@ export function parsePssh(initData: ArrayBuffer) {
1245
1261
}
1246
1262
return result;
1247
1263
}
1264
+
1265
+
function mimeTypeBuilderHEVC(
1266
+
codecName: string,
1267
+
codecDetails: hvcConfigurationRecord
1268
+
): string {
1269
+
const generalProfileSpaceMap = ['', 'A', 'B', 'C'];
1270
+
let codecMimeType = codecName;
1271
+
if (codecDetails) {
1272
+
codecMimeType += '.';
1273
+
codecMimeType += generalProfileSpaceMap[codecDetails.generalProfileSpace];
1274
+
codecMimeType += codecDetails.generalProfileIdc;
1275
+
1276
+
codecMimeType += '.';
1277
+
codecMimeType += codecDetails.generalProfileCompatibility.toString(16)[0];
1278
+
1279
+
codecMimeType += '.';
1280
+
codecMimeType += codecDetails.generalTierFlag === 0 ? 'L' : 'H';
1281
+
codecMimeType += codecDetails.generalLevelIdc;
1282
+
1283
+
let constraintString = '';
1284
+
const lastByteIndex = (
1285
+
codecDetails.generalConstraintIndicator as any
1286
+
).findLastIndex((x: Number) => x !== 0);
1287
+
if (lastByteIndex !== -1) {
1288
+
constraintString =
1289
+
'.' +
1290
+
(
1291
+
codecDetails.generalConstraintIndicator.slice(
1292
+
0,
1293
+
lastByteIndex + 1
1294
+
) as any
1295
+
)
1296
+
.map((x: Number) => x.toString(16))
1297
+
.join('.');
1298
+
}
1299
+
1300
+
codecMimeType += constraintString;
1301
+
}
1302
+
return codecMimeType;
1303
+
}
1304
+
1305
+
function parseDataReferenceIndex(data: Uint8Array) {
1306
+
// UInt8[6] reserved
1307
+
return readUint16(data, 6);
1308
+
}
1309
+
1310
+
interface visualSampleEntry {
1311
+
dataReferenceIndex: number;
1312
+
width: number;
1313
+
height: number;
1314
+
horizResolution: number;
1315
+
vertResolution: number;
1316
+
frameCount: number;
1317
+
compressorName: string;
1318
+
depth: number;
1319
+
end: number;
1320
+
}
1321
+
1322
+
function parseVisualSampleEntry(data: Uint8Array): visualSampleEntry {
1323
+
return {
1324
+
dataReferenceIndex: parseDataReferenceIndex(data),
1325
+
1326
+
// UInt16 preDefined
1327
+
// UInt16 reserved
1328
+
// UInt32[3] preDefined
1329
+
1330
+
width: readUint16(data, 24),
1331
+
height: readUint16(data, 26),
1332
+
1333
+
horizResolution: readUint32(data, 28), // 0x00480000 - 72 dpi
1334
+
vertResolution: readUint32(data, 32), // 0x00480000 - 72 dpi
1335
+
1336
+
// UInt32 reserved
1337
+
1338
+
frameCount: readUint16(data, 40),
1339
+
compressorName: bin2str(data.subarray(43, Math.min(data[42], 31))),
1340
+
depth: readUint16(data, 74),
1341
+
1342
+
// UInt16 preDefined
1343
+
1344
+
end: 78,
1345
+
};
1346
+
}
1347
+
1348
+
interface hvcConfigurationRecord {
1349
+
configurationVersion: number;
1350
+
generalProfileSpace: number;
1351
+
generalTierFlag: number;
1352
+
generalProfileIdc: number;
1353
+
generalProfileCompatibility: number;
1354
+
generalConstraintIndicator: Uint8Array;
1355
+
generalLevelIdc: number;
1356
+
minSpatialSegmentationIdc: number;
1357
+
parallelismType: number;
1358
+
chromaFormatIdc: number;
1359
+
bitDepthLumaMinus8: number;
1360
+
bitDepthChromaMinus8: number;
1361
+
avgFrameRate: number;
1362
+
constantFrameRate: number;
1363
+
numTemporalLayers: number;
1364
+
temporalIdNested: number;
1365
+
lengthSizeMinusOne: number;
1366
+
naluArrays: Array<object>;
1367
+
}
1368
+
1369
+
function parseHvcConfigurationRecord(data: Uint8Array): hvcConfigurationRecord {
1370
+
return {
1371
+
configurationVersion: data[0],
1372
+
generalProfileSpace: data[1] >> 6,
1373
+
generalTierFlag: (data[1] & 0x20) >> 5,
1374
+
generalProfileIdc: data[1] & 0x1f,
1375
+
generalProfileCompatibility: readUint32(data, 2),
1376
+
generalConstraintIndicator: data.subarray(6, 12),
1377
+
generalLevelIdc: data[12],
1378
+
minSpatialSegmentationIdc: readUint16(data, 13) & 0xfff,
1379
+
parallelismType: data[15] & 0x3,
1380
+
chromaFormatIdc: data[16] & 0x3,
1381
+
bitDepthLumaMinus8: data[17] & 0x7,
1382
+
bitDepthChromaMinus8: data[18] & 0x7,
1383
+
avgFrameRate: readUint16(data, 19),
1384
+
constantFrameRate: data[21] >> 6,
1385
+
numTemporalLayers: (data[21] & 0xd) >> 3,
1386
+
temporalIdNested: (data[21] & 0x4) >> 2,
1387
+
lengthSizeMinusOne: data[21] & 0x3,
1388
+
naluArrays: [],
1389
+
};
1390
+
}
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4