You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 lines
7.0 KiB

'use strict';
var
aacStream,
AacStream = require('../lib/aac'),
QUnit = require('qunit'),
utils = require('./utils'),
createId3Header,
createId3FrameHeader,
createAdtsHeader;
createId3Header = function(tagSize) {
var header = [];
header[0] = 'I'.charCodeAt(0);
header[1] = 'D'.charCodeAt(0);
header[2] = '3'.charCodeAt(0);
// 2 version bytes, ID3v2.4.0 (major 4, revision 0)
header[3] = 4;
header[4] = 0;
// unsynchronization, extended header, experimental indicator, footer present flags
header[5] = 0;
// "The ID3v2 tag size is the sum of the byte length of the extended
// header, the padding and the frames after unsynchronisation. If a
// footer is present this equals to ('total size' - 20) bytes, otherwise
// ('total size' - 10) bytes."
// http://id3.org/id3v2.4.0-structure
header[6] = 0;
header[7] = 0;
header[8] = 0;
header[9] = tagSize;
return header;
};
createId3FrameHeader = function() {
var header = [];
// four byte frame ID, XYZ are experimental
header[0] = 'X'.charCodeAt(0);
header[1] = 'Y'.charCodeAt(0);
header[2] = 'Z'.charCodeAt(0);
header[3] = '0'.charCodeAt(0);
// four byte sync safe integer size (excluding frame header)
header[4] = 0;
header[5] = 0;
header[6] = 0;
header[7] = 10;
// two bytes for flags
header[8] = 0;
header[9] = 0;
return header;
};
createAdtsHeader = function(frameLength) {
// Header consists of 7 or 9 bytes (without or with CRC).
// see: https://wiki.multimedia.cx/index.php/ADTS
return utils.binaryStringToArrayOfBytes(''.concat(
// 12 bits for syncword (0xFFF)
'111111111111',
// 1 bit MPEG version
'0',
// 2 bit layer (always 0)
'00',
// 1 bit protection absent (1 for no CRC)
'1',
// 2 bit profile
'10',
// 4 bit sampling frequency index
'0110',
// 1 bit private bit
'0',
// 3 bit channel config
'100',
// 2 bit (ignore)
'00',
// 2 bit (copright bits)
'00',
// 13 bit frame length (includes header length)
utils.leftPad(frameLength.toString(2), 13),
// 11 bit buffer fullness
'11111111111',
// 2 bit number of AAC frames minus 1
'00'
// 16 bit CRC (if present)
));
};
QUnit.module('AAC Stream', {
beforeEach: function() {
aacStream = new AacStream();
}
});
QUnit.test('parses ID3 tag', function(assert) {
var
id3Count = 0,
adtsCount = 0,
frameHeader = createId3FrameHeader(),
id3Tag = createId3Header(frameHeader.length).concat(frameHeader);
aacStream.on('data', function(frame) {
if (frame.type === 'timed-metadata') {
id3Count += 1;
} else if (frame.type === 'audio') {
adtsCount += 1;
}
});
aacStream.push(new Uint8Array(id3Tag));
assert.equal(adtsCount, 0, 'no adts frames');
assert.equal(id3Count, 1, 'one id3 chunk');
});
QUnit.test('parses two ID3 tags in sequence', function(assert) {
var
id3Count = 0,
adtsCount = 0,
frameHeader = createId3FrameHeader(),
id3Tag = createId3Header(frameHeader.length).concat(frameHeader);
aacStream.on('data', function(frame) {
if (frame.type === 'timed-metadata') {
id3Count += 1;
} else if (frame.type === 'audio') {
adtsCount += 1;
}
});
aacStream.push(new Uint8Array(id3Tag.concat(id3Tag)));
assert.equal(adtsCount, 0, 'no adts frames');
assert.equal(id3Count, 2, 'two id3 chunks');
});
QUnit.test('does not parse second ID3 tag if it\'s incomplete', function(assert) {
var
id3Count = 0,
adtsCount = 0,
frameHeader = createId3FrameHeader(),
id3Tag = createId3Header(frameHeader.length).concat(frameHeader);
aacStream.on('data', function(frame) {
if (frame.type === 'timed-metadata') {
id3Count += 1;
} else if (frame.type === 'audio') {
adtsCount += 1;
}
});
aacStream.push(new Uint8Array(id3Tag.concat(id3Tag.slice(0, id3Tag.length - 1))));
assert.equal(adtsCount, 0, 'no adts frames');
assert.equal(id3Count, 1, 'one id3 chunk');
});
QUnit.test('handles misaligned adts header', function(assert) {
var
id3Count = 0,
adtsCount = 0,
// fake adts frame
adtsFrame = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
packetStream = createAdtsHeader(adtsFrame.length).concat(adtsFrame);
aacStream.on('data', function(frame) {
if (frame.type === 'timed-metadata') {
id3Count += 1;
} else if (frame.type === 'audio') {
adtsCount += 1;
}
});
// misalign by two bytes specific to a bug related to detecting sync bytes
// (where we were only properly checking the second byte)
aacStream.push(new Uint8Array([0x01, 0xf0].concat(packetStream)));
assert.equal(adtsCount, 1, 'one adts frames');
assert.equal(id3Count, 0, 'no id3 chunk');
});
QUnit.test('handles incomplete adts frame after id3 frame', function(assert) {
var
id3Count = 0,
adtsCount = 0,
id3FrameHeader = createId3FrameHeader(),
id3Tag = createId3Header(id3FrameHeader.length).concat(id3FrameHeader),
// in this case:
// id3 tag = 20 bytes
// adts header = 7 bytes
// total = 27 bytes
// report the ADTS frame size as 20 bytes
adtsHeader = createAdtsHeader(20),
// no adts frame, stream was cut off
packetStream = id3Tag.concat(adtsHeader);
aacStream.on('data', function(frame) {
if (frame.type === 'timed-metadata') {
id3Count += 1;
} else if (frame.type === 'audio') {
adtsCount += 1;
}
});
aacStream.push(new Uint8Array(packetStream));
assert.equal(adtsCount, 0, 'no adts frame');
assert.equal(id3Count, 1, 'one id3 chunk');
});
QUnit.test('emits data after receiving push', function(assert) {
var
array = new Uint8Array(109),
count = 0;
array[0] = 255;
array[1] = 241;
array[2] = 92;
array[3] = 128;
array[4] = 13;
array[5] = 191;
array[6] = 252;
array[7] = 33;
array[8] = 32;
array[9] = 3;
array[10] = 64;
array[11] = 104;
array[12] = 27;
array[13] = 212;
aacStream.setTimestamp(90);
aacStream.on('data', function(frame) {
if (frame.pts === 90 && frame.dts === 90) {
count += 1;
}
});
aacStream.push(array);
assert.equal(count, 1);
});
QUnit.test('continues parsing after corrupted stream', function(assert) {
var
array = new Uint8Array(10000),
adtsCount = 0,
id3Count = 0;
// an ID3 frame
array[0] = 73;
array[1] = 68;
array[2] = 51;
array[3] = 4;
array[4] = 0;
array[5] = 0;
array[6] = 0;
array[7] = 0;
array[8] = 0;
array[9] = 63;
array[10] = 80;
array[11] = 82;
array[12] = 73;
array[13] = 86;
// an atds frame
array[1020] = 255;
array[1021] = 241;
array[1022] = 92;
array[1023] = 128;
array[1024] = 13;
array[1025] = 191;
array[1026] = 252;
array[1027] = 33;
array[1028] = 32;
array[1029] = 3;
array[1030] = 64;
array[1031] = 104;
array[1032] = 27;
array[1033] = 212;
aacStream.on('data', function(frame) {
if (frame.type === 'timed-metadata') {
id3Count += 1;
} else if (frame.type === 'audio') {
adtsCount += 1;
}
});
aacStream.push(array);
assert.equal(adtsCount, 1);
assert.equal(id3Count, 1);
});