Skip to content

Commit

Permalink
fix: Prevent skipping frames when we have garbage data between adts s…
Browse files Browse the repository at this point in the history
…ync words (#390)
  • Loading branch information
brandonocasey committed Jun 30, 2021
1 parent 2392e3e commit 71bac64
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 18 deletions.
33 changes: 15 additions & 18 deletions lib/codecs/adts.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ AdtsStream = function(handlePartialSegments) {

// Prepend any data in the buffer to the input data so that we can parse
// aac frames the cross a PES packet boundary
if (buffer) {
if (buffer && buffer.length) {
oldBuffer = buffer;
buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
buffer.set(oldBuffer);
Expand All @@ -75,8 +75,10 @@ AdtsStream = function(handlePartialSegments) {

// unpack any ADTS frames which have been fully received
// for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
while (i + 5 < buffer.length) {

// We use i + 7 here because we want to be able to parse the entire header.
// If we don't have enough bytes to do that, then we definitely won't have a full frame.
while ((i + 7) < buffer.length) {
// Look for the start of an ADTS header..
if ((buffer[i] !== 0xFF) || (buffer[i + 1] & 0xF6) !== 0xF0) {
// If a valid header was not found, jump one forward and attempt to
Expand All @@ -91,6 +93,7 @@ AdtsStream = function(handlePartialSegments) {

// Frame length is a 13 bit integer starting 16 bits from the
// end of the sync sequence
// NOTE: frame length includes the size of the header
frameLength = ((buffer[i + 3] & 0x03) << 11) |
(buffer[i + 4] << 3) |
((buffer[i + 5] & 0xe0) >> 5);
Expand All @@ -99,12 +102,10 @@ AdtsStream = function(handlePartialSegments) {
adtsFrameDuration = (sampleCount * ONE_SECOND_IN_TS) /
ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];

frameEnd = i + frameLength;

// If we don't have enough data to actually finish this ADTS frame, return
// and wait for more data
if (buffer.byteLength < frameEnd) {
return;
// If we don't have enough data to actually finish this ADTS frame,
// then we have to wait for more data
if ((buffer.byteLength - i) < frameLength) {
break;
}

// Otherwise, deliver the complete AAC frame
Expand All @@ -119,20 +120,16 @@ AdtsStream = function(handlePartialSegments) {
samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
// assume ISO/IEC 14496-12 AudioSampleEntry default of 16
samplesize: 16,
data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
// data is the frame without it's header
data: buffer.subarray(i + 7 + protectionSkipBytes, i + frameLength)
});

frameNum++;

// If the buffer is empty, clear it and return
if (buffer.byteLength === frameEnd) {
buffer = undefined;
return;
}

// Remove the finished frame from the buffer and start the process again
buffer = buffer.subarray(frameEnd);
i += frameLength;
}

// remove processed bytes from the buffer.
buffer = buffer.subarray(i);
};

this.flush = function() {
Expand Down
51 changes: 51 additions & 0 deletions test/transmuxer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2729,6 +2729,57 @@ QUnit.test('generates AAC frame events from ADTS bytes', function(assert) {
assert.equal(frames[0].samplesize, 16, 'parsed samplesize');
});

QUnit.test('skips garbage data between sync words', function(assert) {
var frames = [];
adtsStream.on('data', function(frame) {
frames.push(frame);
});

var frameHeader = [
0xff, 0xf1, // no CRC
0x10, // AAC Main, 44.1KHz
0xbc, 0x01, 0x20, // 2 channels, frame length 9 including header
0x00, // one AAC per ADTS frame
];
adtsStream.push({
type: 'audio',
data: new Uint8Array(
[]
// garbage
.concat([0x00, 0x00, 0x00])
// frame
.concat(frameHeader)
.concat([0x00, 0x01])
// garbage
.concat([0x00, 0x00, 0x00, 0x00, 0x00])
.concat(frameHeader)
.concat([0x00, 0x02])
// garbage
.concat([0x00, 0x00, 0x00, 0x00])
.concat(frameHeader)
.concat([0x00, 0x03])
.concat([0x00, 0x00, 0x00, 0x00])
)
});

assert.equal(frames.length, 3, 'generated three frames');
frames.forEach(function(frame, i) {
assert.deepEqual(
new Uint8Array(frame.data),
new Uint8Array([0x00, i + 1]),
'extracted AAC frame'
);

assert.equal(frame.channelcount, 2, 'parsed channelcount');
assert.equal(frame.samplerate, 44100, 'parsed samplerate');

// Chrome only supports 8, 16, and 32 bit sample sizes. Assuming the
// default value of 16 in ISO/IEC 14496-12 AudioSampleEntry is
// acceptable.
assert.equal(frame.samplesize, 16, 'parsed samplesize');
});
});

QUnit.test('parses across packets', function(assert) {
var frames = [];
adtsStream.on('data', function(frame) {
Expand Down

0 comments on commit 71bac64

Please sign in to comment.