diff --git a/src/decoder.js b/src/decoder.js index 96da8ff..65f0d83 100644 --- a/src/decoder.js +++ b/src/decoder.js @@ -54,7 +54,6 @@ var MP3Decoder = AV.Decoder.extend(function() { output[j++] = data[i][k]; } } - return output; }; diff --git a/src/demuxer.js b/src/demuxer.js index b20442e..40de750 100644 --- a/src/demuxer.js +++ b/src/demuxer.js @@ -4,54 +4,9 @@ var ID3v22Stream = require('./id3').ID3v22Stream; var MP3FrameHeader = require('./header'); var MP3Stream = require('./stream'); -var MP3Demuxer = AV.Demuxer.extend(function() { +var MP3Demuxer = AV.Demuxer.extend(function () { AV.Demuxer.register(this); - this.probe = function(stream) { - var off = stream.offset; - - // skip id3 metadata if it exists - var id3header = MP3Demuxer.getID3v2Header(stream); - if (id3header) - stream.advance(10 + id3header.length); - - // attempt to read the header of the first audio frame - var s = new MP3Stream(new AV.Bitstream(stream)); - var header = null; - - try { - header = MP3FrameHeader.decode(s); - } catch (e) {}; - - // go back to the beginning, for other probes - stream.seek(off); - - return !!header; - }; - - this.getID3v2Header = function(stream) { - if (stream.peekString(0, 3) == 'ID3') { - stream = AV.Stream.fromBuffer(stream.peekBuffer(0, 10)); - stream.advance(3); // 'ID3' - - var major = stream.readUInt8(); - var minor = stream.readUInt8(); - var flags = stream.readUInt8(); - var bytes = stream.readBuffer(4).data; - var length = (bytes[0] << 21) | (bytes[1] << 14) | (bytes[2] << 7) | bytes[3]; - - return { - version: '2.' + major + '.' + minor, - major: major, - minor: minor, - flags: flags, - length: length - }; - } - - return null; - }; - const XING_OFFSETS = [[32, 17], [17, 9]]; this.prototype.parseDuration = function(header) { var stream = this.stream; @@ -172,4 +127,50 @@ var MP3Demuxer = AV.Demuxer.extend(function() { }; }); +MP3Demuxer.probe = function(stream) { + var off = stream.offset; + + // skip id3 metadata if it exists + var id3header = MP3Demuxer.getID3v2Header(stream); + if (id3header) + stream.advance(10 + id3header.length); + + // attempt to read the header of the first audio frame + var s = new MP3Stream(new AV.Bitstream(stream)); + var header = null; + + try { + header = MP3FrameHeader.decode(s); + } catch (e) {}; + + // go back to the beginning, for other probes + stream.seek(off); + + return !!header; +}; + +MP3Demuxer.getID3v2Header = function(stream) { + if (stream.peekString(0, 3) == 'ID3') { + stream = AV.Stream.fromBuffer(stream.peekBuffer(0, 10)); + stream.advance(3); // 'ID3' + + var major = stream.readUInt8(); + var minor = stream.readUInt8(); + var flags = stream.readUInt8(); + var bytes = stream.readBuffer(4).data; + var length = (bytes[0] << 21) | (bytes[1] << 14) | (bytes[2] << 7) | bytes[3]; + + return { + version: '2.' + major + '.' + minor, + major: major, + minor: minor, + flags: flags, + length: length + }; + } + + return null; +}; + + module.exports = MP3Demuxer; diff --git a/src/frame.js b/src/frame.js index 6677b9a..336acc7 100644 --- a/src/frame.js +++ b/src/frame.js @@ -13,9 +13,9 @@ function MP3Frame() { MP3Frame.layers = []; MP3Frame.prototype.decode = function(stream) { - if (!this.header || !(this.header.flags & MP3FrameHeader.FLAGS.INCOMPLETE)) + if (!this.header || !(this.header.flags & MP3FrameHeader.FLAGS.INCOMPLETE)) { this.header = MP3FrameHeader.decode(stream); - + } this.header.flags &= ~MP3FrameHeader.FLAGS.INCOMPLETE; // make an instance of the decoder for this layer if needed diff --git a/src/layer3.js b/src/layer3.js index a03b88b..a52e97d 100644 --- a/src/layer3.js +++ b/src/layer3.js @@ -81,18 +81,30 @@ Layer3.prototype.decode = function(stream, frame) { header.private_bits |= si.private_bits; // find main_data of next frame - var peek = stream.copy(); - peek.seek(stream.next_frame * 8); + var peek = stream.copy(), + nextHeader = null; + + try { + peek.seek(stream.next_frame * 8); - var nextHeader = peek.read(16); - if ((nextHeader & 0xffe6) === 0xffe2) { // syncword | layer - if ((nextHeader & 1) === 0) // protection bit - peek.advance(16); // crc check + nextHeader = peek.read(16); + if ((nextHeader & 0xffe6) === 0xffe2) { // syncword | layer + if ((nextHeader & 1) === 0) // protection bit + peek.advance(16); // crc check - peek.advance(16); // skip the rest of the header - next_md_begin = peek.read((nextHeader & 8) ? 9 : 8); + peek.advance(16); // skip the rest of the header + next_md_begin = peek.read((nextHeader & 8) ? 9 : 8); + } + } catch (err) { + if (err instanceof AV.UnderflowError) { + next_md_begin = 0; + nextHeader = null; + } else { + throw err; + } + } - + // find main_data of this frame var frame_space = stream.next_frame - stream.nextByte(); @@ -132,27 +144,32 @@ Layer3.prototype.decode = function(stream, frame) { // decode main_data this.decodeMainData(ptr, frame, si, nch); - - // preload main_data buffer with up to 511 bytes for next frame(s) - if (frame_free >= next_md_begin) { - this.memcpy(stream.main_data, 0, stream.stream.stream, stream.next_frame - next_md_begin, next_md_begin); - stream.md_len = next_md_begin; - } else { - if (md_len < si.main_data_begin) { - var extra = si.main_data_begin - md_len; - if (extra + frame_free > next_md_begin) - extra = next_md_begin - frame_free; - - if (extra < stream.md_len) { - this.memcpy(stream.main_data, 0, stream.main_data, stream.md_len - extra, extra); - stream.md_len = extra; - } + + if (next_md_begin > 0) { + // preload main_data buffer with up to 511 bytes for next frame(s) + if (frame_free >= next_md_begin) { + this.memcpy(stream.main_data, 0, stream.stream.stream, stream.next_frame - next_md_begin, next_md_begin); + stream.md_len = next_md_begin; } else { - stream.md_len = 0; - } + if (md_len < si.main_data_begin) { + var extra = si.main_data_begin - md_len; + if (extra + frame_free > next_md_begin) + extra = next_md_begin - frame_free; + + if (extra < stream.md_len) { + this.memcpy(stream.main_data, 0, stream.main_data, stream.md_len - extra, extra); + stream.md_len = extra; + } + } else { + stream.md_len = 0; + } - this.memcpy(stream.main_data, stream.md_len, stream.stream.stream, stream.next_frame - frame_free, frame_free); - stream.md_len += frame_free; + this.memcpy(stream.main_data, stream.md_len, stream.stream.stream, stream.next_frame - frame_free, frame_free); + stream.md_len += frame_free; + } + } else { + stream.md_len = 0; + stream.main_data.fill(0); } }; diff --git a/src/stream.js b/src/stream.js index 2467801..06b4558 100644 --- a/src/stream.js +++ b/src/stream.js @@ -23,6 +23,21 @@ MP3Stream.prototype.getU8 = function(offset) { return stream.peekUInt8(offset - stream.offset); }; +MP3Stream.prototype.getU16 = function(offset) { + var stream = this.stream.stream; + return stream.peekUInt16(offset - stream.offset); +}; + +MP3Stream.prototype.getU24 = function(offset) { + var stream = this.stream.stream; + return stream.peekUInt24(offset - stream.offset); +}; + +MP3Stream.prototype.getU32 = function(offset) { + var stream = this.stream.stream; + return stream.peekUInt32(offset - stream.offset); +}; + MP3Stream.prototype.nextByte = function() { var stream = this.stream; return stream.bitPosition === 0 ? stream.stream.offset : stream.stream.offset + 1;