Browse Source

Added FLV extractor.

n1474335 6 năm trước cách đây
mục cha
commit
0449c46b38
2 tập tin đã thay đổi với 79 bổ sung8 xóa
  1. 47 1
      src/core/lib/FileSignatures.mjs
  2. 32 7
      src/core/lib/Stream.mjs

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

@@ -417,7 +417,7 @@ export const FILE_SIGNATURES = {
                 2: 0x56,
                 3: 0x1
             },
-            extractor: null
+            extractor: extractFLV
         },
     ],
     "Audio": [
@@ -1285,3 +1285,49 @@ export function extractBMP(bytes, offset) {
 
     return stream.carve();
 }
+
+
+/**
+ * FLV extractor.
+ *
+ * @param {Uint8Array} bytes
+ * @param {number} offset
+ * @returns {Uint8Array}
+ */
+export function extractFLV(bytes, offset) {
+    const stream = new Stream(bytes.slice(offset));
+
+    // Move past signature, version and flags
+    stream.moveForwardsBy(5);
+
+    // Read header size
+    const headerSize = stream.readInt(4, "be");
+
+    // Skip through the rest of the header
+    stream.moveForwardsBy(headerSize - 9);
+
+    let tagSize = -11; // Fake size of previous tag header
+    while (stream.position < stream.length) {
+        const prevTagSize = stream.readInt(4, "be");
+        const tagType = stream.readInt(1, "be");
+
+        if ([8, 9, 18].indexOf(tagType) < 0) {
+            // This tag is not valid
+            stream.moveBackwardsBy(1);
+            break;
+        }
+
+        if (prevTagSize !== tagSize + 11) {
+            // Previous tag was not valid
+            stream.moveBackwardsBy(tagSize + 11);
+            break;
+        }
+
+        tagSize = stream.readInt(3, "be");
+
+        // Move past the rest of the tag header and payload
+        stream.moveForwardsBy(7 + tagSize);
+    }
+
+    return stream.carve();
+}

+ 32 - 7
src/core/lib/Stream.mjs

@@ -22,6 +22,7 @@ export default class Stream {
     constructor(input) {
         this.bytes = input;
         this.position = 0;
+        this.length = this.bytes.length;
     }
 
     /**
@@ -31,6 +32,8 @@ export default class Stream {
      * @returns {Uint8Array}
      */
     getBytes(numBytes) {
+        if (this.position > this.length) return undefined;
+
         const newPosition = this.position + numBytes;
         const bytes = this.bytes.slice(this.position, newPosition);
         this.position = newPosition;
@@ -45,6 +48,8 @@ export default class Stream {
      * @returns {string}
      */
     readString(numBytes) {
+        if (this.position > this.length) return undefined;
+
         let result = "";
         for (let i = this.position; i < this.position + numBytes; i++) {
             const currentByte = this.bytes[i];
@@ -63,6 +68,8 @@ export default class Stream {
      * @returns {number}
      */
     readInt(numBytes, endianness="be") {
+        if (this.position > this.length) return undefined;
+
         let val = 0;
         if (endianness === "be") {
             for (let i = this.position; i < this.position + numBytes; i++) {
@@ -85,8 +92,10 @@ export default class Stream {
      * @param {number|List<number>} val
      */
     continueUntil(val) {
+        if (this.position > this.length) return;
+
         if (typeof val === "number") {
-            while (++this.position < this.bytes.length && this.bytes[this.position] !== val) {
+            while (++this.position < this.length && this.bytes[this.position] !== val) {
                 continue;
             }
             return;
@@ -94,13 +103,13 @@ export default class Stream {
 
         // val is an array
         let found = false;
-        while (!found && this.position < this.bytes.length) {
-            while (++this.position < this.bytes.length && this.bytes[this.position] !== val[0]) {
+        while (!found && this.position < this.length) {
+            while (++this.position < this.length && this.bytes[this.position] !== val[0]) {
                 continue;
             }
             found = true;
             for (let i = 1; i < val.length; i++) {
-                if (this.position + i > this.bytes.length || this.bytes[this.position + i] !== val[i])
+                if (this.position + i > this.length || this.bytes[this.position + i] !== val[i])
                     found = false;
             }
         }
@@ -122,7 +131,23 @@ export default class Stream {
      * @param {number} numBytes
      */
     moveForwardsBy(numBytes) {
-        this.position += numBytes;
+        const pos = this.position + numBytes;
+        if (pos < 0 || pos > this.length)
+            throw new Error("Cannot move to position " + pos + " in stream. Out of bounds.");
+        this.position = pos;
+    }
+
+
+    /**
+     * Move backwards through the stream by the specified number of bytes.
+     *
+     * @param {number} numBytes
+     */
+    moveBackwardsBy(numBytes) {
+        const pos = this.position - numBytes;
+        if (pos < 0 || pos > this.length)
+            throw new Error("Cannot move to position " + pos + " in stream. Out of bounds.");
+        this.position = pos;
     }
 
     /**
@@ -131,7 +156,7 @@ export default class Stream {
      * @param {number} pos
      */
     moveTo(pos) {
-        if (pos < 0 || pos > this.bytes.length - 1)
+        if (pos < 0 || pos > this.length)
             throw new Error("Cannot move to position " + pos + " in stream. Out of bounds.");
         this.position = pos;
     }
@@ -142,7 +167,7 @@ export default class Stream {
      * @returns {boolean}
      */
     hasMore() {
-        return this.position < this.bytes.length;
+        return this.position < this.length;
     }
 
     /**