From a27985ef5a7e0854b95806638a4bdae7815d4f0f Mon Sep 17 00:00:00 2001 From: Borewit Date: Tue, 16 Jul 2024 23:13:25 +0200 Subject: [PATCH] Ability to detect MPEG when prefixed with some noise --- core.js | 99 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 39 deletions(-) diff --git a/core.js b/core.js index bf9ee61b..96b62ca9 100644 --- a/core.js +++ b/core.js @@ -53,6 +53,11 @@ export async function fileTypeFromTokenizer(tokenizer) { export class FileTypeParser { constructor(options) { + this.options = { + mpegOffsetTollerance: 4, + ...options, + }; + this.detectors = options?.customDetectors; this.fromTokenizer = this.fromTokenizer.bind(this); @@ -1547,46 +1552,12 @@ export class FileTypeParser { } // Check MPEG 1 or 2 Layer 3 header, or 'layer 0' for ADTS (MPEG sync-word 0xFFE) - if (this.buffer.length >= 2 && this.check([0xFF, 0xE0], {offset: 0, mask: [0xFF, 0xE0]})) { - if (this.check([0x10], {offset: 1, mask: [0x16]})) { - // Check for (ADTS) MPEG-2 - if (this.check([0x08], {offset: 1, mask: [0x08]})) { - return { - ext: 'aac', - mime: 'audio/aac', - }; + if (this.buffer.length >= (2 + this.options.mpegOffsetTollerance)) { + for (let depth = 0; depth < this.options.mpegOffsetTollerance; ++depth) { + const type = this.scanMpeg(depth); + if (type) { + return type; } - - // Must be (ADTS) MPEG-4 - return { - ext: 'aac', - mime: 'audio/aac', - }; - } - - // MPEG 1 or 2 Layer 3 header - // Check for MPEG layer 3 - if (this.check([0x02], {offset: 1, mask: [0x06]})) { - return { - ext: 'mp3', - mime: 'audio/mpeg', - }; - } - - // Check for MPEG layer 2 - if (this.check([0x04], {offset: 1, mask: [0x06]})) { - return { - ext: 'mp2', - mime: 'audio/mpeg', - }; - } - - // Check for MPEG layer 1 - if (this.check([0x06], {offset: 1, mask: [0x06]})) { - return { - ext: 'mp1', - mime: 'audio/mpeg', - }; } } } @@ -1656,6 +1627,56 @@ export class FileTypeParser { }; } } + + /** + Scan Check MPEG 1 or 2 Layer 3 header, or 'layer 0' for ADTS (MPEG sync-word 0xFFE) + @param offset to scan for sync-preamble + @returns {{ext: string, mime: string}} + */ + scanMpeg(offset) { + if (this.check([0xFF, 0xE0], {offset, mask: [0xFF, 0xE0]})) { + if (this.check([0x10], {offset: offset + 1, mask: [0x16]})) { + // Check for (ADTS) MPEG-2 + if (this.check([0x08], {offset: offset + 1, mask: [0x08]})) { + return { + ext: 'aac', + mime: 'audio/aac', + }; + } + + // Must be (ADTS) MPEG-4 + return { + ext: 'aac', + mime: 'audio/aac', + }; + } + + // MPEG 1 or 2 Layer 3 header + // Check for MPEG layer 3 + if (this.check([0x02], {offset: offset + 1, mask: [0x06]})) { + return { + ext: 'mp3', + mime: 'audio/mpeg', + }; + } + + // Check for MPEG layer 2 + if (this.check([0x04], {offset: offset + 1, mask: [0x06]})) { + return { + ext: 'mp2', + mime: 'audio/mpeg', + }; + } + + // Check for MPEG layer 1 + if (this.check([0x06], {offset: offset + 1, mask: [0x06]})) { + return { + ext: 'mp1', + mime: 'audio/mpeg', + }; + } + } + } } export const supportedExtensions = new Set(extensions);