Skip to content

Commit

Permalink
parse riff
Browse files Browse the repository at this point in the history
  • Loading branch information
JonnyBurger committed Nov 24, 2024
1 parent 4050e52 commit 5bd86d6
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 32 deletions.
23 changes: 12 additions & 11 deletions packages/media-parser/src/boxes/riff/expect-riff-box.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {BufferIterator} from '../../buffer-iterator';
import type {RiffBox, RiffRegularBox} from './riff-box';
import {parseRiffBox} from './parse-riff-box';
import type {RiffBox} from './riff-box';

type RiffResult =
| {
Expand All @@ -10,26 +11,26 @@ type RiffResult =
box: RiffBox;
};

export const expectRiffBox = (iterator: BufferIterator): RiffResult => {
export const expectRiffBox = ({
iterator,
boxes,
}: {
iterator: BufferIterator;
boxes: RiffBox[];
}): RiffResult => {
const ckId = iterator.getByteString(4);
const ckSize = iterator.getUint32Le();

// TODO: Add capability to read partially
if (iterator.bytesRemaining() < ckSize) {
iterator.counter.decrement(8);
return {
type: 'incomplete',
};
}

iterator.discard(ckSize);

const box: RiffRegularBox = {
type: 'riff-box',
size: ckSize,
id: ckId,
};

return {
type: 'complete',
box,
box: parseRiffBox({id: ckId, iterator, size: ckSize, boxes}),
};
};
4 changes: 2 additions & 2 deletions packages/media-parser/src/boxes/riff/parse-box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const parseRiffBody = ({
boxes: RiffBox[];
}): Promise<ParseResult> => {
while (iterator.bytesRemaining() > 0) {
const result = expectRiffBox(iterator);
const result = expectRiffBox({iterator, boxes});
if (result.type === 'incomplete') {
return Promise.resolve({
status: 'incomplete',
Expand Down Expand Up @@ -41,7 +41,7 @@ export const parseRiff = (iterator: BufferIterator): Promise<ParseResult> => {

const size = iterator.getUint32Le();
const fileType = iterator.getByteString(4);
if (fileType !== 'WAVE') {
if (fileType !== 'WAVE' && fileType !== 'AVI') {
return Promise.reject(new Error(`File type ${fileType} not supported`));
}

Expand Down
45 changes: 45 additions & 0 deletions packages/media-parser/src/boxes/riff/parse-fmt-box.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type {BufferIterator} from '../../buffer-iterator';
import type {RiffBox} from './riff-box';

export const parseFmtBox = ({
iterator,
boxes,
size,
}: {
iterator: BufferIterator;
boxes: RiffBox[];
size: number;
}): RiffBox => {
const offset = iterator.counter.getOffset();
const header = boxes.find((b) => b.type === 'riff-header');
if (!header) {
throw new Error('Expected RIFF header');
}

if (header.fileType !== 'WAVE') {
throw new Error('Only supporting WAVE type');
}

const wFormatTag = iterator.getUint16Le();
if (wFormatTag !== 1) {
throw new Error('Expected wFormatTag to be 1, only supporting this');
}

const numberOfChannels = iterator.getUint16Le();
const sampleRate = iterator.getUint32Le();
const byteRate = iterator.getUint32Le();
const blockAlign = iterator.getUint16Le();
const bitsPerSample = iterator.getUint16Le();

iterator.discard(size - (iterator.counter.getOffset() - offset));

return {
type: 'wave-format-box',
formatTag: wFormatTag,
numberOfChannels,
sampleRate,
blockAlign,
byteRate,
bitsPerSample,
};
};
29 changes: 29 additions & 0 deletions packages/media-parser/src/boxes/riff/parse-riff-box.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type {BufferIterator} from '../../buffer-iterator';
import {parseFmtBox} from './parse-fmt-box';
import type {RiffBox, RiffRegularBox} from './riff-box';

export const parseRiffBox = ({
iterator,
size,
id,
boxes,
}: {
iterator: BufferIterator;
size: number;
id: string;
boxes: RiffBox[];
}): RiffBox => {
if (id === 'fmt') {
return parseFmtBox({iterator, boxes, size});
}

iterator.discard(size);

const box: RiffRegularBox = {
type: 'riff-box',
size,
id,
};

return box;
};
12 changes: 11 additions & 1 deletion packages/media-parser/src/boxes/riff/riff-box.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
export type WaveFormatBox = {
type: 'wave-format-box';
formatTag: 1;
numberOfChannels: number;
sampleRate: number;
blockAlign: number;
byteRate: number;
bitsPerSample: number;
};

export type RiffRegularBox = {
type: 'riff-box';
size: number;
Expand All @@ -10,4 +20,4 @@ export type RiffHeader = {
fileType: string;
};

export type RiffBox = RiffRegularBox | RiffHeader;
export type RiffBox = RiffRegularBox | WaveFormatBox | RiffHeader;
5 changes: 5 additions & 0 deletions packages/media-parser/src/buffer-iterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,11 @@ export const getArrayBufferIterator = (
counter.increment(2);
return val;
},
getUint16Le: () => {
const val = view.getUint16(counter.getDiscardedOffset(), true);
counter.increment(2);
return val;
},
getUint24: () => {
const val1 = view.getUint8(counter.getDiscardedOffset());
const val2 = view.getUint8(counter.getDiscardedOffset() + 1);
Expand Down
46 changes: 46 additions & 0 deletions packages/media-parser/src/test/parse-avi-file.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {exampleVideos} from '@remotion/example-videos';
import {expect, test} from 'bun:test';
import {parseMedia} from '../parse-media';
import {nodeReader} from '../readers/from-node';

test('AVI file', async () => {
const {boxes} = await parseMedia({
src: exampleVideos.avi,
reader: nodeReader,
fields: {
boxes: true,
},
});
expect(boxes).toEqual([
{
fileSize: 742470,
fileType: 'AVI',
type: 'riff-header',
},
{
id: 'LIST',
size: 8894,
type: 'riff-box',
},
{
id: 'LIST',
size: 26,
type: 'riff-box',
},
{
id: 'JUNK',
size: 1016,
type: 'riff-box',
},
{
id: 'LIST',
size: 695114,
type: 'riff-box',
},
{
id: 'idx1',
size: 37376,
type: 'riff-box',
},
]);
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {expect, test} from 'bun:test';
import {parseMedia} from '../parse-media';
import {nodeReader} from '../readers/from-node';

test('riff', async () => {
test('WAV file', async () => {
const {boxes} = await parseMedia({
src: exampleVideos.chirp,
reader: nodeReader,
Expand All @@ -18,9 +18,13 @@ test('riff', async () => {
type: 'riff-header',
},
{
id: 'fmt',
size: 16,
type: 'riff-box',
bitsPerSample: 16,
blockAlign: 2,
byteRate: 88200,
formatTag: 1,
numberOfChannels: 1,
sampleRate: 44100,
type: 'wave-format-box',
},
{
id: 'data',
Expand Down
14 changes: 0 additions & 14 deletions packages/media-parser/src/test/stream-local.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -768,17 +768,3 @@ test('Should stream transparent video', async () => {
expect(videoSamples).toBe(39);
expect(keyFrames).toBe(1);
});

test('Acknowledge there are .avi file', () => {
const parsed = parseMedia({
src: exampleVideos.avi,
fields: {
tracks: true,
boxes: true,
},
reader: nodeReader,
});

expect(parsed).rejects.toThrow('AVI');
expect(parsed).rejects.toThrow('not yet supported');
});

0 comments on commit 5bd86d6

Please sign in to comment.