Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue in Layer3.js where last frame is dropped #19

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/decoder.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ var MP3Decoder = AV.Decoder.extend(function() {
output[j++] = data[i][k];
}
}

return output;
};

Expand Down
93 changes: 47 additions & 46 deletions src/demuxer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
4 changes: 2 additions & 2 deletions src/frame.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
73 changes: 45 additions & 28 deletions src/layer3.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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);
}
};

Expand Down
15 changes: 15 additions & 0 deletions src/stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down