Skip to content

Commit d66d0b1

Browse files
committed
codecs: Add support for extradata output from ffprobe (-show_data)
1 parent 782917b commit d66d0b1

3 files changed

Lines changed: 46 additions & 0 deletions

File tree

codecs/codecs.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,33 @@ export function getFullMIMEString(info) {
197197

198198
// TODO: Consider whether any of these should be exported.
199199

200+
function getCleanHex(extradata) {
201+
// 1. Split into lines
202+
// 2. Extract only the middle hex section (after ':' and before ' ')
203+
// 3. Remove all spaces
204+
return extradata.split('\n')
205+
.map(line => line.split(':')[1]?.split(' ')[0])
206+
.filter(Boolean)
207+
.join('')
208+
.replace(/\s/g, '');
209+
}
210+
200211
/**
201212
* AVC1 is the same thing as H264.
202213
* https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter#iso_base_media_file_format_mp4_quicktime_and_3gp
203214
* @param {ProbeStream} stream
204215
* @returns {string}
205216
*/
206217
function getAVC1CodecString(stream) {
218+
if (stream.extradata && stream.codec_tag_string === 'avc1') {
219+
const hex = getCleanHex(stream.extradata); // results in "014d4028..."
220+
221+
// Bytes 1, 2, and 3 are the Profile, Constraints, and Level.
222+
// In the hex string, these are characters at index 2 through 8.
223+
const codecPart = hex.substring(2, 8).toUpperCase();
224+
return `avc1.${codecPart}`;
225+
}
226+
207227
if (!stream.profile) throw `No profile found in AVC1 stream`;
208228

209229
let frag = 'avc1';

docs/unrar.html

100755100644
File mode changed.

tests/codecs.spec.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,32 @@ describe('codecs test suite', () => {
271271
.to.be.a('string')
272272
.and.satisfy(s => s.startsWith('video/mp4; codecs="avc1.4D00'));
273273
});
274+
275+
describe('extradata tests', () => {
276+
it('prefers extradata for codec string', () => {
277+
info.streams[0].extradata = '00000000: 014d 4028 ffe1 001a 674d 4028 9a66 0140 .M@(...gM@(.f.@';
278+
expect(getFullMIMEString(info))
279+
.equals('video/mp4; codecs="avc1.4D4028"');
280+
});
281+
282+
it('handles multiline extradata', () => {
283+
info.streams[0].extradata =
284+
'00000000: 0164 0029 ffe1 001b 6764 0029 ac2b 4028 .d.)....gd.).+@(\n' +
285+
'00000010: 2f82 1801 1000 0003 0010 0000 0303 20f1 /............. .';
286+
expect(getFullMIMEString(info))
287+
.equals('video/mp4; codecs="avc1.640029"');
288+
});
289+
290+
it('does not use extradata if codec_tag_string is not avc1', () => {
291+
info.streams[0].codec_tag_string = 'not-avc1';
292+
info.streams[0].codec_name = 'h264';
293+
info.streams[0].profile = 'Main';
294+
info.streams[0].level = 20;
295+
info.streams[0].extradata = '00000000: 014d 4028 ffe1 001a 674d 4028 9a66 0140 .M@(...gM@(.f.@';
296+
expect(getFullMIMEString(info))
297+
.equals('video/mp4; codecs="avc1.4D0014"');
298+
});
299+
});
274300
});
275301
});
276302

0 commit comments

Comments
 (0)