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

Add support for aac format #208

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
File renamed without changes.
Binary file added fixture/fixture-adts-mpeg4-2.aac
Binary file not shown.
File renamed without changes.
3 changes: 2 additions & 1 deletion index.d.ts
Expand Up @@ -99,7 +99,8 @@ declare namespace fileType {
| 'lnk'
| 'alias'
| 'voc'
| 'ac3';
| 'ac3'
| 'aac';

interface FileTypeResult {
/**
Expand Down
100 changes: 68 additions & 32 deletions index.js
Expand Up @@ -38,6 +38,17 @@ const fileType = input => {

const checkString = (header, options) => check(stringToBytes(header), options);

/**
* Specialized function to read ID3 payload length
* @param buf Buffer
* @param off offset in buffer
* @returns {number} ID3 payload length
*/
function readUINT32SYNCSAFE(buf, off) {
return (buf[off + 3] & 0x7F) | ((buf[off + 2]) << 7) |
((buf[off + 1]) << 14) | ((buf[off]) << 21);
}

if (check([0xFF, 0xD8, 0xFF])) {
return {
ext: 'jpg',
Expand Down Expand Up @@ -409,46 +420,71 @@ const fileType = input => {
};
}

// Check for MPEG header at different starting offsets
let flagId3 = false;

for (let start = 0; start < 2 && start < (buffer.length - 16); start++) {
if (
check([0x49, 0x44, 0x33], {offset: start}) || // ID3 header
check([0xFF, 0xE2], {offset: start, mask: [0xFF, 0xE6]}) // MPEG 1 or 2 Layer 3 header
) {
return {
ext: 'mp3',
mime: 'audio/mpeg'
};
// Check for ID3 header
if (buffer.length >= start + 10 && checkString('ID3', {offset: start})) {
const id3Len = readUINT32SYNCSAFE(buffer, start + 6);
start += (10 + id3Len - 1); // Skip ID3 header
flagId3 = true;
continue;
}

if (
check([0xFF, 0xE4], {offset: start, mask: [0xFF, 0xE6]}) // MPEG 1 or 2 Layer 2 header
) {
return {
ext: 'mp2',
mime: 'audio/mpeg'
};
}
// Check MPEG 1 or 2 Layer 3 header, or 'layer 0' for ADTS (MPEG sync-word 0xFFE)
if (buffer.length >= start + 2 && check([0xFF, 0xE0], {offset: start, mask: [0xFF, 0xE0]})) {
// Check for ADTS header (last bit of sync-word 0xFFF & layer=0)
if (check([0x10], {offset: start + 1, mask: [0x16]})) {
// Check for (ADTS) MPEG-2
if (check([0x08], {offset: start + 1, mask: [0x08]})) {
return {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this returns exactly the same as alternative

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean,

				// Check for (ADTS) MPEG-2
				if (check([0x08], {offset: start + 1, mask: [0x08]})) {
					return {
						ext: 'aac',
						mime: 'audio/aac'
					};
				}

				// Must be (ADTS) MPEG-4
				return {
					ext: 'aac',
					mime: 'audio/aac'
				};

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, I created the distinction 'between MPEG-2 and MPEG-4.
I updated the return types of MPEG-4 pending a sample file for MPEG-2.

Because I wanted to have some evidence that this was done like that for files like that,

Then I found out such a fixture was already present. I updated the return type, and I thought, maybe good to the distinct to keep as is, just for clarity we covered both cases, and allow to pin point to that specific case if there is still an issue.

ext: 'aac',
mime: 'audio/aac'
};
}

if (
check([0xFF, 0xF8], {offset: start, mask: [0xFF, 0xFC]}) // MPEG 2 layer 0 using ADTS
) {
return {
ext: 'mp2',
mime: 'audio/mpeg'
};
}
// Must be (ADTS) MPEG-4
return {
ext: 'aac',
mime: 'audio/aac'
};
}

if (
check([0xFF, 0xF0], {offset: start, mask: [0xFF, 0xFC]}) // MPEG 4 layer 0 using ADTS
) {
return {
ext: 'mp4',
mime: 'audio/mpeg'
};
// MPEG 1 or 2 Layer 3 header
// Check for MPEG layer 3
if (check([0x02], {offset: start + 1, mask: [0x06]})) {
return {
ext: 'mp3',
mime: 'audio/mpeg'
};
}

// Check for MPEG layer 2
if (check([0x04], {offset: start + 1, mask: [0x06]})) {
return {
ext: 'mp2',
mime: 'audio/mpeg'
};
}

// Check for MPEG layer 1
if (check([0x06], {offset: start + 1, mask: [0x06]})) {
return {
ext: 'mp1',
mime: 'audio/mpeg'
};
}
}
}

if (flagId3) {
// Guess it is MP3 if only an ID3 tag header is found
return {
ext: 'mp3',
mime: 'audio/mpeg'
};
}

if (
check([0x66, 0x74, 0x79, 0x70, 0x4D, 0x34, 0x41], {offset: 4})
) {
Expand Down
1 change: 1 addition & 0 deletions readme.md
Expand Up @@ -221,6 +221,7 @@ Type: [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream
- [`alias`](https://en.wikipedia.org/wiki/Alias_%28Mac_OS%29) - macOS Alias file
- [`voc`](https://wiki.multimedia.cx/index.php/Creative_Voice) - Creative Voice File
- [`ac3`](https://www.atsc.org/standard/a522012-digital-audio-compression-ac-3-e-ac-3-standard-12172012/) - ATSC A/52 Audio File
- [`aac`](https://en.wikipedia.org/wiki/Advanced_Audio_Coding) - Advanced Audio Coding

*SVG isn't included as it requires the whole file to be read, but you can get it [here](https://github.com/sindresorhus/is-svg).*

Expand Down
31 changes: 19 additions & 12 deletions test.js
Expand Up @@ -108,7 +108,9 @@ const types = [
'lnk',
'alias',
'voc',
'ac3'
'ac3',
'pcap',
'aac'
];

// Define an entry here only if the fixture has a different
Expand Down Expand Up @@ -137,8 +139,7 @@ const names = {
],
mp2: [
'fixture',
'fixture-mpa',
'fixture-faac-adts'
'fixture-mpa'
],
mp3: [
'fixture',
Expand All @@ -153,8 +154,7 @@ const names = {
'fixture-isomv2',
'fixture-mp4v2',
'fixture-m4v',
'fixture-dash',
'fixture-aac-adts'
'fixture-dash'
],
tif: [
'fixture-big-endian',
Expand Down Expand Up @@ -199,6 +199,11 @@ const names = {
tar: [
'fixture',
'fixture-v7'
],
aac: [
'fixture-adts-mpeg4',
'fixture-adts-mpeg4-2',
'fixture-adts-mpeg2'
]
};

Expand Down Expand Up @@ -257,13 +262,15 @@ for (const type of types) {
}
}

test('.stream() method - empty stream', async t => {
const emptyStream = fs.createReadStream('/dev/null');
await t.throwsAsync(
fileType.stream(emptyStream),
/Expected the `input` argument to be of type `Uint8Array` /
);
});
if (process.platform !== 'win32') {
test('.stream() method - empty stream', async t => {
const emptyStream = fs.createReadStream('/dev/null');
await t.throwsAsync(
fileType.stream(emptyStream),
/Expected the `input` argument to be of type `Uint8Array` /
);
});
}

test('fileType.minimumBytes', t => {
t.true(fileType.minimumBytes > 4000);
Expand Down