From 0be885edba99fca751bfaf28a5afde5fcd3f9022 Mon Sep 17 00:00:00 2001 From: Justin Wong <46082645+uvjustin@users.noreply.github.com> Date: Sat, 29 Oct 2022 01:22:05 +0800 Subject: [PATCH 1/3] Add codec string parsing for h264, h265, and mp4a --- src/utils/mp4-tools.ts | 101 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 95 insertions(+), 6 deletions(-) diff --git a/src/utils/mp4-tools.ts b/src/utils/mp4-tools.ts index 66ef21cdc70..45f5f6e072f 100644 --- a/src/utils/mp4-tools.ts +++ b/src/utils/mp4-tools.ts @@ -242,12 +242,101 @@ export function parseInitSegment(initSegment: Uint8Array): InitData { let codec; if (stsd) { codec = bin2str(stsd.subarray(12, 16)); - // TODO: Parse codec details to be able to build MIME type. - // stsd.start += 8; - // const codecBox = findBox(stsd, [codec])[0]; - // if (codecBox) { - // TODO: Codec parsing support for avc1, mp4a, hevc, av01... - // } + const toHex = (x: number): string => { + return ('0' + x.toString(16).toUpperCase()).slice(-2); + }; + + // Handle H264 + if ( + codec.slice(0, 3) === 'avc' && + codec[3] >= '1' && + codec[3] <= '4' && + stsd.length > 102 && + bin2str(stsd.subarray(98, 102)) === 'avcC' + ) { + // profile + compatibility + level + codec += + '.' + toHex(stsd[111]) + toHex(stsd[112]) + toHex(stsd[113]); + } + + // Handle H265 + else if ( + (codec === 'hev1' || codec === 'hvc1') && + stsd.length > 102 && + bin2str(stsd.subarray(98, 102)) === 'hvcC' + ) { + // Profile Space + const profileByte = stsd[103]; + const profileSpace = { 0: '', 1: 'A', 2: 'B', 3: 'C' }[ + profileByte >> 6 + ]; + const generalProfileIdc = profileByte & 31; + codec += '.' + profileSpace + generalProfileIdc; + + // Compatibility + let reversed = 0; + for (let i = 0; i < 4; ++i) { + // byte number + for (let j = 0; j < 8; ++j) { + // bit number + reversed |= + ((stsd[i + 104] >> (7 - j)) & 1) << (31 - 8 * i - j); + } + } + codec += '.' + toHex(reversed >>> 0); + + // Tier Flag + codec += (profileByte & 32 ? '.H' : '.L') + stsd[114]; + + // Constraint String + let hasByte = false; + let constraintString = ''; + for (let i = 113; i > 107; --i) { + if (stsd[i] || hasByte) { + constraintString = '.' + toHex(stsd[i]) + constraintString; + hasByte = true; + } + } + codec += constraintString; + } + + // Handle Audio + else if (codec === 'mp4a') { + // Parse ES Descriptors + let i: number; + // oti + for (i = 0; i < stsd.length - 5; ++i) { + if ( + stsd[i] == 4 && + stsd[i + 1] == 128 && + stsd[i + 2] == 128 && + stsd[i + 3] == 128 + ) { + codec += '.' + toHex(stsd[i + 5]); + break; + } + } + + // dsi + for (i = 0; i < stsd.length - 6; ++i) { + if ( + stsd[i] == 5 && + stsd[i + 1] == 128 && + stsd[i + 2] == 128 && + stsd[i + 3] == 128 + ) { + let dsi = (stsd[i + 5] & 248) >> 3; + if (dsi == 31 && stsd[i + 4] >= 2) { + dsi = + 32 + + ((stsd[i + 5] & 7) << 3) + + ((stsd[i + 6] & 224) >> 5); + } + codec += '.' + dsi; + break; + } + } + } } result[trackId] = { timescale, type }; result[type] = { timescale, id: trackId, codec }; From 76b297a3f67842ae74ecd68a2a23084e0423f43a Mon Sep 17 00:00:00 2001 From: Justin Wong <46082645+uvjustin@users.noreply.github.com> Date: Sun, 30 Oct 2022 02:12:20 +0800 Subject: [PATCH 2/3] Update default HEVC codec string --- src/remux/passthrough-remuxer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remux/passthrough-remuxer.ts b/src/remux/passthrough-remuxer.ts index 4b8f276ae9d..a245e8bdce0 100644 --- a/src/remux/passthrough-remuxer.ts +++ b/src/remux/passthrough-remuxer.ts @@ -238,7 +238,7 @@ function getParsedTrackCodec( // Provide defaults based on codec type // This allows for some playback of some fmp4 playlists without CODECS defined in manifest if (parsedCodec === 'hvc1' || parsedCodec === 'hev1') { - return 'hvc1.1.c.L120.90'; + return 'hvc1.1.6.L120.90'; } if (parsedCodec === 'av01') { return 'av01.0.04M.08'; From e99a16cf5642eed89dd542803c0a55aae171a78d Mon Sep 17 00:00:00 2001 From: Justin Wong <46082645+uvjustin@users.noreply.github.com> Date: Sun, 30 Oct 2022 02:16:43 +0800 Subject: [PATCH 3/3] Update comments --- src/remux/passthrough-remuxer.ts | 1 - src/utils/mp4-tools.ts | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/remux/passthrough-remuxer.ts b/src/remux/passthrough-remuxer.ts index a245e8bdce0..4bbd762b0b0 100644 --- a/src/remux/passthrough-remuxer.ts +++ b/src/remux/passthrough-remuxer.ts @@ -234,7 +234,6 @@ function getParsedTrackCodec( if (parsedCodec && parsedCodec.length > 4) { return parsedCodec; } - // Since mp4-tools cannot parse full codec string (see 'TODO: Parse codec details'... in mp4-tools) // Provide defaults based on codec type // This allows for some playback of some fmp4 playlists without CODECS defined in manifest if (parsedCodec === 'hvc1' || parsedCodec === 'hev1') { diff --git a/src/utils/mp4-tools.ts b/src/utils/mp4-tools.ts index 45f5f6e072f..41fa01a26af 100644 --- a/src/utils/mp4-tools.ts +++ b/src/utils/mp4-tools.ts @@ -242,6 +242,8 @@ export function parseInitSegment(initSegment: Uint8Array): InitData { let codec; if (stsd) { codec = bin2str(stsd.subarray(12, 16)); + // Parse codec details to be able to build MIME type + // TODO: Codec parsing support for AV1 const toHex = (x: number): string => { return ('0' + x.toString(16).toUpperCase()).slice(-2); };