From c242d3eb1cd7991b2121be85d3a29d9fc7a5810d Mon Sep 17 00:00:00 2001 From: Dominik Thalhammer Date: Tue, 24 Sep 2019 13:31:33 +0200 Subject: [PATCH] Fix issue in Layer3.js where last frame is dropped Merge of open pull request in parent project audiocogs/mp3.js#19 --- src/demuxer.ts | 14 +++++----- src/layer3.ts | 69 +++++++++++++++++++++++++++++--------------------- src/stream.ts | 35 +++++++++++++++++-------- 3 files changed, 71 insertions(+), 47 deletions(-) diff --git a/src/demuxer.ts b/src/demuxer.ts index c76a81f..7d85921 100644 --- a/src/demuxer.ts +++ b/src/demuxer.ts @@ -14,22 +14,20 @@ export class MP3Demuxer extends AV.Demuxer { private sentInfo: boolean; static probe(stream: AV.Stream) { - var off = stream.offset; + let off = stream.offset; // skip id3 metadata if it exists - var id3header = MP3Demuxer.getID3v2Header(stream); + let 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; + let s = new MP3Stream(new AV.Bitstream(stream)); + let header = null; try { header = MP3FrameHeader.decode(s); - } catch (e) { - console.log(e); - }; + } catch (e) {}; // go back to the beginning, for other probes stream.seek(off); @@ -37,7 +35,7 @@ export class MP3Demuxer extends AV.Demuxer { return !!header; } - static getID3v2Header(stream) :{ + static getID3v2Header(stream) : { version: string; major: number; minor: number; diff --git a/src/layer3.ts b/src/layer3.ts index e830e40..071ff97 100644 --- a/src/layer3.ts +++ b/src/layer3.ts @@ -90,17 +90,23 @@ export class Layer3 { // find main_data of next frame var peek = stream.copy(); - 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 - - peek.advance(16); // skip the rest of the header - next_md_begin = peek.read((nextHeader & 8) ? 9 : 8); + var nextHeader = null; + try { + peek.seek(stream.next_frame * 8); + 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); + } + } 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(); @@ -141,26 +147,31 @@ export class Layer3 { // 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); } } @@ -1017,7 +1028,7 @@ export class Layer3 { if (!(modes[sfbi] & tables.I_STEREO)) continue; - let is_pos = right_ch.scalefac[sfbi]; + let is_pos = right_ch.scalefac[sfbi]; if (is_pos >= 7) { // illegal intensity position modes[sfbi] &= ~tables.I_STEREO; diff --git a/src/stream.ts b/src/stream.ts index f0c6579..b07ebd6 100644 --- a/src/stream.ts +++ b/src/stream.ts @@ -3,13 +3,13 @@ import {MP3FrameHeader} from './header'; export class MP3Stream { private stream: AV.Bitstream; // actual bitstream - private sync; // stream sync found - private freerate; // free bitrate (fixed) - private this_frame; // start of current frame - private next_frame; // start of next frame + private sync: boolean; // stream sync found + private freerate: number; // free bitrate (fixed) + private this_frame: number; // start of current frame + private next_frame: number; // start of next frame - private main_data; // actual audio data - private md_len; // length of main data + private main_data: Uint8Array; // actual audio data + private md_len: number; // length of main data constructor(stream: AV.Bitstream) { @@ -39,12 +39,27 @@ export class MP3Stream { getU8(offset) { var stream = this.stream.stream; return stream.peekUInt8(offset - stream.offset); - }; + } + + getU16(offset) { + var stream = this.stream.stream; + return stream.peekUInt16(offset - stream.offset); + } + + getU24(offset) { + var stream = this.stream.stream; + return stream.peekUInt24(offset - stream.offset); + } + getU32(offset) { + var stream = this.stream.stream; + return stream.peekUInt32(offset - stream.offset); + } + nextByte() { var stream = this.stream; return stream.bitPosition === 0 ? stream.stream.offset : stream.stream.offset + 1; - }; + } doSync() { var stream = this.stream.stream; @@ -58,11 +73,11 @@ export class MP3Stream { return false; return true; - }; + } reset(byteOffset) { this.stream.seek(byteOffset * 8); this.next_frame = byteOffset; this.sync = true; - }; + } } \ No newline at end of file