Skip to content

Commit 71bac64

Browse files
fix: Prevent skipping frames when we have garbage data between adts sync words (#390)
1 parent 2392e3e commit 71bac64

File tree

2 files changed

+66
-18
lines changed

2 files changed

+66
-18
lines changed

lib/codecs/adts.js

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ AdtsStream = function(handlePartialSegments) {
6464

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

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

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

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

102-
frameEnd = i + frameLength;
103-
104-
// If we don't have enough data to actually finish this ADTS frame, return
105-
// and wait for more data
106-
if (buffer.byteLength < frameEnd) {
107-
return;
105+
// If we don't have enough data to actually finish this ADTS frame,
106+
// then we have to wait for more data
107+
if ((buffer.byteLength - i) < frameLength) {
108+
break;
108109
}
109110

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

125127
frameNum++;
126-
127-
// If the buffer is empty, clear it and return
128-
if (buffer.byteLength === frameEnd) {
129-
buffer = undefined;
130-
return;
131-
}
132-
133-
// Remove the finished frame from the buffer and start the process again
134-
buffer = buffer.subarray(frameEnd);
128+
i += frameLength;
135129
}
130+
131+
// remove processed bytes from the buffer.
132+
buffer = buffer.subarray(i);
136133
};
137134

138135
this.flush = function() {

test/transmuxer.test.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2729,6 +2729,57 @@ QUnit.test('generates AAC frame events from ADTS bytes', function(assert) {
27292729
assert.equal(frames[0].samplesize, 16, 'parsed samplesize');
27302730
});
27312731

2732+
QUnit.test('skips garbage data between sync words', function(assert) {
2733+
var frames = [];
2734+
adtsStream.on('data', function(frame) {
2735+
frames.push(frame);
2736+
});
2737+
2738+
var frameHeader = [
2739+
0xff, 0xf1, // no CRC
2740+
0x10, // AAC Main, 44.1KHz
2741+
0xbc, 0x01, 0x20, // 2 channels, frame length 9 including header
2742+
0x00, // one AAC per ADTS frame
2743+
];
2744+
adtsStream.push({
2745+
type: 'audio',
2746+
data: new Uint8Array(
2747+
[]
2748+
// garbage
2749+
.concat([0x00, 0x00, 0x00])
2750+
// frame
2751+
.concat(frameHeader)
2752+
.concat([0x00, 0x01])
2753+
// garbage
2754+
.concat([0x00, 0x00, 0x00, 0x00, 0x00])
2755+
.concat(frameHeader)
2756+
.concat([0x00, 0x02])
2757+
// garbage
2758+
.concat([0x00, 0x00, 0x00, 0x00])
2759+
.concat(frameHeader)
2760+
.concat([0x00, 0x03])
2761+
.concat([0x00, 0x00, 0x00, 0x00])
2762+
)
2763+
});
2764+
2765+
assert.equal(frames.length, 3, 'generated three frames');
2766+
frames.forEach(function(frame, i) {
2767+
assert.deepEqual(
2768+
new Uint8Array(frame.data),
2769+
new Uint8Array([0x00, i + 1]),
2770+
'extracted AAC frame'
2771+
);
2772+
2773+
assert.equal(frame.channelcount, 2, 'parsed channelcount');
2774+
assert.equal(frame.samplerate, 44100, 'parsed samplerate');
2775+
2776+
// Chrome only supports 8, 16, and 32 bit sample sizes. Assuming the
2777+
// default value of 16 in ISO/IEC 14496-12 AudioSampleEntry is
2778+
// acceptable.
2779+
assert.equal(frame.samplesize, 16, 'parsed samplesize');
2780+
});
2781+
});
2782+
27322783
QUnit.test('parses across packets', function(assert) {
27332784
var frames = [];
27342785
adtsStream.on('data', function(frame) {

0 commit comments

Comments
 (0)