Added FLV extractor.
parent
cd0c86e0d6
commit
0449c46b38
|
@ -417,7 +417,7 @@ export const FILE_SIGNATURES = {
|
||||||
2: 0x56,
|
2: 0x56,
|
||||||
3: 0x1
|
3: 0x1
|
||||||
},
|
},
|
||||||
extractor: null
|
extractor: extractFLV
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"Audio": [
|
"Audio": [
|
||||||
|
@ -1285,3 +1285,49 @@ export function extractBMP(bytes, offset) {
|
||||||
|
|
||||||
return stream.carve();
|
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();
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ export default class Stream {
|
||||||
constructor(input) {
|
constructor(input) {
|
||||||
this.bytes = input;
|
this.bytes = input;
|
||||||
this.position = 0;
|
this.position = 0;
|
||||||
|
this.length = this.bytes.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,6 +32,8 @@ export default class Stream {
|
||||||
* @returns {Uint8Array}
|
* @returns {Uint8Array}
|
||||||
*/
|
*/
|
||||||
getBytes(numBytes) {
|
getBytes(numBytes) {
|
||||||
|
if (this.position > this.length) return undefined;
|
||||||
|
|
||||||
const newPosition = this.position + numBytes;
|
const newPosition = this.position + numBytes;
|
||||||
const bytes = this.bytes.slice(this.position, newPosition);
|
const bytes = this.bytes.slice(this.position, newPosition);
|
||||||
this.position = newPosition;
|
this.position = newPosition;
|
||||||
|
@ -45,6 +48,8 @@ export default class Stream {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
readString(numBytes) {
|
readString(numBytes) {
|
||||||
|
if (this.position > this.length) return undefined;
|
||||||
|
|
||||||
let result = "";
|
let result = "";
|
||||||
for (let i = this.position; i < this.position + numBytes; i++) {
|
for (let i = this.position; i < this.position + numBytes; i++) {
|
||||||
const currentByte = this.bytes[i];
|
const currentByte = this.bytes[i];
|
||||||
|
@ -63,6 +68,8 @@ export default class Stream {
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
readInt(numBytes, endianness="be") {
|
readInt(numBytes, endianness="be") {
|
||||||
|
if (this.position > this.length) return undefined;
|
||||||
|
|
||||||
let val = 0;
|
let val = 0;
|
||||||
if (endianness === "be") {
|
if (endianness === "be") {
|
||||||
for (let i = this.position; i < this.position + numBytes; i++) {
|
for (let i = this.position; i < this.position + numBytes; i++) {
|
||||||
|
@ -85,8 +92,10 @@ export default class Stream {
|
||||||
* @param {number|List<number>} val
|
* @param {number|List<number>} val
|
||||||
*/
|
*/
|
||||||
continueUntil(val) {
|
continueUntil(val) {
|
||||||
|
if (this.position > this.length) return;
|
||||||
|
|
||||||
if (typeof val === "number") {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -94,13 +103,13 @@ export default class Stream {
|
||||||
|
|
||||||
// val is an array
|
// val is an array
|
||||||
let found = false;
|
let found = false;
|
||||||
while (!found && this.position < this.bytes.length) {
|
while (!found && this.position < this.length) {
|
||||||
while (++this.position < this.bytes.length && this.bytes[this.position] !== val[0]) {
|
while (++this.position < this.length && this.bytes[this.position] !== val[0]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
found = true;
|
found = true;
|
||||||
for (let i = 1; i < val.length; i++) {
|
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;
|
found = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,7 +131,23 @@ export default class Stream {
|
||||||
* @param {number} numBytes
|
* @param {number} numBytes
|
||||||
*/
|
*/
|
||||||
moveForwardsBy(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
|
* @param {number} pos
|
||||||
*/
|
*/
|
||||||
moveTo(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.");
|
throw new Error("Cannot move to position " + pos + " in stream. Out of bounds.");
|
||||||
this.position = pos;
|
this.position = pos;
|
||||||
}
|
}
|
||||||
|
@ -142,7 +167,7 @@ export default class Stream {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
hasMore() {
|
hasMore() {
|
||||||
return this.position < this.bytes.length;
|
return this.position < this.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue