Browse Source

Merge branch 'n1073645-MP3Extractor'

n1474335 5 years ago
parent
commit
d136717636
1 changed files with 74 additions and 1 deletions
  1. 74 1
      src/core/lib/FileSignatures.mjs

+ 74 - 1
src/core/lib/FileSignatures.mjs

@@ -780,7 +780,7 @@ export const FILE_SIGNATURES = {
                     1: 0xfb
                 }
             ],
-            extractor: null
+            extractor: extractMP3
         },
         {
             name: "MPEG-4 Part 14 audio",
@@ -3067,6 +3067,79 @@ export function extractWAV(bytes, offset) {
 }
 
 
+/**
+ * MP3 extractor.
+ *
+ * @param {Uint8Array} bytes
+ * @param {Number} offset
+ * @returns {Uint8Array}
+ */
+export function extractMP3(bytes, offset) {
+    const stream = new Stream(bytes.slice(offset));
+
+    // Constants for flag byte.
+    const bitRateIndexes = ["free", 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, "bad"];
+
+    const samplingRateFrequencyIndex = [44100, 48000, 32000, "reserved"];
+
+    // ID3 tag, move over it.
+    if ((stream.getBytes(3).toString() === [0x49, 0x44, 0x33].toString())) {
+        stream.moveTo(6);
+        const tagSize = (stream.readInt(1) << 21) | (stream.readInt(1) << 14) | (stream.readInt(1) << 7) | stream.readInt(1);
+        stream.moveForwardsBy(tagSize);
+    } else {
+        stream.moveTo(0);
+    }
+
+    // Loop over all the frame headers in the file.
+    while (stream.hasMore()) {
+
+        // If it has an old TAG frame at the end of it, fixed size, 128 bytes.
+        if (stream.getBytes(3) === [0x54, 0x41, 0x47].toString()) {
+            stream.moveForwardsBy(125);
+            break;
+        }
+
+        // If not start of frame.
+        if (stream.getBytes(2).toString() !== [0xff, 0xfb].toString()) {
+            stream.moveBackwardsBy(2);
+            break;
+        }
+
+        // Read flag byte.
+        const flags = stream.readInt(1);
+
+        // Extract frame bit rate from flag byte.
+        const bitRate = bitRateIndexes[flags >> 4];
+
+        // Extract frame sample rate from flag byte.
+        const sampleRate = samplingRateFrequencyIndex[(flags & 0x0f) >> 2];
+
+        // Padding if the frame size is not a multiple of the bitrate.
+        const padding = (flags & 0x02) >> 1;
+
+        // Things that are either not standard or undocumented.
+        if (bitRate === "free" || bitRate === "bad" || sampleRate === "reserved") {
+            stream.moveBackwardsBy(1);
+            break;
+        }
+
+        // Formula: FrameLength = (144 * BitRate / SampleRate ) + Padding
+        const frameSize = Math.floor(((144 * bitRate) / sampleRate) + padding);
+
+        // If the next move goes past the end of the bytestream then extract the entire bytestream.
+        // We assume complete frames in the above formula because there is no field that suggests otherwise.
+        if ((stream.position + frameSize) > stream.length) {
+            stream.moveTo(stream.length);
+            break;
+        } else {
+            stream.moveForwardsBy(frameSize - 3);
+        }
+    }
+    return stream.carve();
+}
+
+
 /**
  * FLV extractor.
  *