@remotion/media-parser 4.0.199 → 4.0.200

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/boxes/iso-base-media/mdat/mdat.js +0 -1
  2. package/dist/boxes/webm/ebml.d.ts +2 -0
  3. package/dist/boxes/webm/ebml.js +72 -0
  4. package/dist/boxes/webm/make-header.d.ts +2 -0
  5. package/dist/boxes/webm/make-header.js +44 -0
  6. package/dist/boxes/webm/segments/all-segments.d.ts +5 -0
  7. package/dist/boxes/webm/segments/all-segments.js +5 -0
  8. package/dist/boxes/webm/segments/block-simple-block-flags.d.ts +9 -0
  9. package/dist/boxes/webm/segments/block-simple-block-flags.js +38 -0
  10. package/dist/boxes/webm/segments/track-entry.d.ts +20 -8
  11. package/dist/boxes/webm/segments/track-entry.js +64 -23
  12. package/dist/boxes/webm/segments.d.ts +2 -2
  13. package/dist/boxes/webm/segments.js +35 -6
  14. package/dist/buffer-iterator.d.ts +1 -0
  15. package/dist/buffer-iterator.js +9 -2
  16. package/dist/get-audio-codec.d.ts +1 -1
  17. package/dist/parser-state.d.ts +4 -6
  18. package/dist/parser-state.js +24 -16
  19. package/dist/webcodec-sample-types.d.ts +0 -1
  20. package/package.json +2 -2
  21. package/src/boxes/iso-base-media/mdat/mdat.ts +0 -1
  22. package/src/boxes/webm/ebml.ts +78 -0
  23. package/src/boxes/webm/make-header.ts +48 -0
  24. package/src/boxes/webm/segments/all-segments.ts +5 -0
  25. package/src/boxes/webm/segments/block-simple-block-flags.ts +52 -0
  26. package/src/boxes/webm/segments/track-entry.ts +108 -29
  27. package/src/boxes/webm/segments.ts +71 -9
  28. package/src/buffer-iterator.ts +8 -1
  29. package/src/parser-state.ts +30 -20
  30. package/src/test/create-matroska.test.ts +14 -0
  31. package/src/test/matroska.test.ts +75 -100
  32. package/src/test/stream-local.test.ts +47 -5
  33. package/src/webcodec-sample-types.ts +0 -1
  34. package/tsconfig.tsbuildinfo +1 -1
@@ -9,9 +9,7 @@ import type {
9
9
  VideoSample,
10
10
  } from './webcodec-sample-types';
11
11
 
12
- export type InternalStats = {
13
- samplesThatHadToBeQueued: number;
14
- };
12
+ export type InternalStats = {};
15
13
 
16
14
  export const makeParserState = ({
17
15
  hasAudioCallbacks,
@@ -43,8 +41,6 @@ export const makeParserState = ({
43
41
  const videoSampleCallbacks: Record<number, OnVideoSample> = {};
44
42
  const audioSampleCallbacks: Record<number, OnAudioSample> = {};
45
43
 
46
- let samplesThatHadToBeQueued = 0;
47
-
48
44
  const queuedAudioSamples: Record<number, AudioSample[]> = {};
49
45
  const queuedVideoSamples: Record<number, VideoSample[]> = {};
50
46
 
@@ -64,6 +60,30 @@ export const makeParserState = ({
64
60
  timescale = newTimescale;
65
61
  };
66
62
 
63
+ const timestampMap = new Map<number, number>();
64
+
65
+ const setTimestampOffset = (byteOffset: number, timestamp: number) => {
66
+ timestampMap.set(byteOffset, timestamp);
67
+ };
68
+
69
+ const getTimestampOffsetForByteOffset = (byteOffset: number) => {
70
+ const entries = Array.from(timestampMap.entries());
71
+ const sortedByByteOffset = entries
72
+ .sort((a, b) => {
73
+ return a[0] - b[0];
74
+ })
75
+ .reverse();
76
+ for (const [offset, timestamp] of sortedByByteOffset) {
77
+ if (offset >= byteOffset) {
78
+ continue;
79
+ }
80
+
81
+ return timestamp;
82
+ }
83
+
84
+ return timestampMap.get(byteOffset);
85
+ };
86
+
67
87
  return {
68
88
  onTrackEntrySegment,
69
89
  getTrackInfoByNumber: (id: number) => trackEntries[id],
@@ -85,6 +105,8 @@ export const makeParserState = ({
85
105
 
86
106
  queuedVideoSamples[id] = [];
87
107
  },
108
+ setTimestampOffset,
109
+ getTimestampOffsetForByteOffset,
88
110
  registerAudioSampleCallback: async (
89
111
  id: number,
90
112
  callback: OnAudioSample | null,
@@ -112,12 +134,8 @@ export const makeParserState = ({
112
134
  }
113
135
 
114
136
  if (!hasAudioCallbacks) {
115
- return;
137
+ throw new Error('No audio callbacks registered');
116
138
  }
117
-
118
- queuedAudioSamples[trackId] ??= [];
119
- queuedAudioSamples[trackId].push(audioSample);
120
- samplesThatHadToBeQueued++;
121
139
  }
122
140
  },
123
141
  onVideoSample: async (trackId: number, videoSample: VideoSample) => {
@@ -130,19 +148,11 @@ export const makeParserState = ({
130
148
  }
131
149
 
132
150
  if (!hasVideoCallbacks) {
133
- return;
151
+ throw new Error('No video callbacks registered');
134
152
  }
135
-
136
- queuedVideoSamples[trackId] ??= [];
137
- queuedVideoSamples[trackId].push(videoSample);
138
- samplesThatHadToBeQueued++;
139
153
  }
140
154
  },
141
- getInternalStats: () => {
142
- return {
143
- samplesThatHadToBeQueued,
144
- };
145
- },
155
+ getInternalStats: () => ({}),
146
156
  getTimescale,
147
157
  setTimescale,
148
158
  };
@@ -0,0 +1,14 @@
1
+ import {RenderInternals} from '@remotion/renderer';
2
+ import {expect, test} from 'bun:test';
3
+ import {makeMatroskaHeader} from '../boxes/webm/make-header';
4
+
5
+ test('Should make Matroska header that is same as input', async () => {
6
+ const exampleVideo = RenderInternals.exampleVideos.matroskaMp3;
7
+ const file = await Bun.file(exampleVideo).arrayBuffer();
8
+
9
+ const headerInput = new Uint8Array(file.slice(0, 0x23 + 5));
10
+
11
+ const headerOutput = makeMatroskaHeader();
12
+
13
+ expect(headerInput).toEqual(headerOutput);
14
+ });
@@ -193,279 +193,254 @@ test('Should get duration of AV1 video', async () => {
193
193
  timestamp: 0,
194
194
  },
195
195
  {
196
- type: 'simple-block-segment',
196
+ type: 'simple-block-or-block-segment',
197
197
  length: 279307,
198
198
  trackNumber: 1,
199
199
  timecode: 0,
200
- headerFlags: 128,
201
200
  keyframe: true,
202
- lacing: [0, 0],
201
+ lacing: 0,
203
202
  invisible: false,
204
- children: [],
203
+ videoSample: null,
205
204
  },
206
205
  {
207
- type: 'simple-block-segment',
206
+ type: 'simple-block-or-block-segment',
208
207
  length: 96,
209
208
  trackNumber: 1,
210
209
  timecode: 40,
211
- headerFlags: 0,
212
210
  keyframe: false,
213
- lacing: [0, 0],
211
+ lacing: 0,
214
212
  invisible: false,
215
- children: [],
213
+ videoSample: null,
216
214
  },
217
215
  {
218
- type: 'simple-block-segment',
216
+ type: 'simple-block-or-block-segment',
219
217
  length: 556,
220
218
  trackNumber: 1,
221
219
  timecode: 80,
222
- headerFlags: 0,
223
220
  keyframe: false,
224
- lacing: [0, 0],
221
+ lacing: 0,
225
222
  invisible: false,
226
- children: [],
223
+ videoSample: null,
227
224
  },
228
225
  {
229
- type: 'simple-block-segment',
226
+ type: 'simple-block-or-block-segment',
230
227
  length: 948,
231
228
  trackNumber: 1,
232
229
  timecode: 120,
233
- headerFlags: 0,
234
230
  keyframe: false,
235
- lacing: [0, 0],
231
+ lacing: 0,
236
232
  invisible: false,
237
- children: [],
233
+ videoSample: null,
238
234
  },
239
235
  {
240
- type: 'simple-block-segment',
236
+ type: 'simple-block-or-block-segment',
241
237
  length: 577,
242
238
  trackNumber: 1,
243
239
  timecode: 160,
244
- headerFlags: 0,
245
240
  keyframe: false,
246
- lacing: [0, 0],
241
+ lacing: 0,
247
242
  invisible: false,
248
- children: [],
243
+ videoSample: null,
249
244
  },
250
245
  {
251
- type: 'simple-block-segment',
246
+ type: 'simple-block-or-block-segment',
252
247
  length: 779,
253
248
  trackNumber: 1,
254
249
  timecode: 200,
255
- headerFlags: 0,
256
250
  keyframe: false,
257
- lacing: [0, 0],
251
+ lacing: 0,
258
252
  invisible: false,
259
- children: [],
253
+ videoSample: null,
260
254
  },
261
255
  {
262
- type: 'simple-block-segment',
256
+ type: 'simple-block-or-block-segment',
263
257
  length: 793,
264
258
  trackNumber: 1,
265
259
  timecode: 240,
266
- headerFlags: 0,
267
260
  keyframe: false,
268
- lacing: [0, 0],
261
+ lacing: 0,
269
262
  invisible: false,
270
- children: [],
263
+ videoSample: null,
271
264
  },
272
265
  {
273
- type: 'simple-block-segment',
266
+ type: 'simple-block-or-block-segment',
274
267
  length: 740,
275
268
  trackNumber: 1,
276
269
  timecode: 280,
277
- headerFlags: 0,
278
270
  keyframe: false,
279
- lacing: [0, 0],
271
+ lacing: 0,
280
272
  invisible: false,
281
- children: [],
273
+ videoSample: null,
282
274
  },
283
275
  {
284
- type: 'simple-block-segment',
276
+ type: 'simple-block-or-block-segment',
285
277
  length: 1095,
286
278
  trackNumber: 1,
287
279
  timecode: 320,
288
- headerFlags: 0,
289
280
  keyframe: false,
290
- lacing: [0, 0],
281
+ lacing: 0,
291
282
  invisible: false,
292
- children: [],
283
+ videoSample: null,
293
284
  },
294
285
  {
295
- type: 'simple-block-segment',
286
+ type: 'simple-block-or-block-segment',
296
287
  length: 1097,
297
288
  trackNumber: 1,
298
289
  timecode: 360,
299
- headerFlags: 0,
300
290
  keyframe: false,
301
- lacing: [0, 0],
291
+ lacing: 0,
302
292
  invisible: false,
303
- children: [],
293
+ videoSample: null,
304
294
  },
305
295
  {
306
- type: 'simple-block-segment',
296
+ type: 'simple-block-or-block-segment',
307
297
  length: 1155,
308
298
  trackNumber: 1,
309
299
  timecode: 400,
310
- headerFlags: 0,
311
300
  keyframe: false,
312
- lacing: [0, 0],
301
+ lacing: 0,
313
302
  invisible: false,
314
- children: [],
303
+ videoSample: null,
315
304
  },
316
305
  {
317
- type: 'simple-block-segment',
306
+ type: 'simple-block-or-block-segment',
318
307
  length: 1526,
319
308
  trackNumber: 1,
320
309
  timecode: 440,
321
- headerFlags: 0,
322
310
  keyframe: false,
323
- lacing: [0, 0],
311
+ lacing: 0,
324
312
  invisible: false,
325
- children: [],
313
+ videoSample: null,
326
314
  },
327
315
  {
328
- type: 'simple-block-segment',
316
+ type: 'simple-block-or-block-segment',
329
317
  length: 1487,
330
318
  trackNumber: 1,
331
319
  timecode: 480,
332
- headerFlags: 0,
333
320
  keyframe: false,
334
- lacing: [0, 0],
321
+ lacing: 0,
335
322
  invisible: false,
336
- children: [],
323
+ videoSample: null,
337
324
  },
338
325
  {
339
- type: 'simple-block-segment',
326
+ type: 'simple-block-or-block-segment',
340
327
  length: 2046,
341
328
  trackNumber: 1,
342
329
  timecode: 520,
343
- headerFlags: 0,
344
330
  keyframe: false,
345
- lacing: [0, 0],
331
+ lacing: 0,
346
332
  invisible: false,
347
- children: [],
333
+ videoSample: null,
348
334
  },
349
335
  {
350
- type: 'simple-block-segment',
336
+ type: 'simple-block-or-block-segment',
351
337
  length: 1372,
352
338
  trackNumber: 1,
353
339
  timecode: 560,
354
- headerFlags: 0,
355
340
  keyframe: false,
356
- lacing: [0, 0],
341
+ lacing: 0,
357
342
  invisible: false,
358
- children: [],
343
+ videoSample: null,
359
344
  },
360
345
  {
361
- type: 'simple-block-segment',
346
+ type: 'simple-block-or-block-segment',
362
347
  length: 1441,
363
348
  trackNumber: 1,
364
349
  timecode: 600,
365
- headerFlags: 0,
366
350
  keyframe: false,
367
- lacing: [0, 0],
351
+ lacing: 0,
368
352
  invisible: false,
369
- children: [],
353
+ videoSample: null,
370
354
  },
371
355
  {
372
- type: 'simple-block-segment',
356
+ type: 'simple-block-or-block-segment',
373
357
  length: 2947,
374
358
  trackNumber: 1,
375
359
  timecode: 640,
376
- headerFlags: 0,
377
360
  keyframe: false,
378
- lacing: [0, 0],
361
+ lacing: 0,
379
362
  invisible: false,
380
- children: [],
363
+ videoSample: null,
381
364
  },
382
365
  {
383
- type: 'simple-block-segment',
366
+ type: 'simple-block-or-block-segment',
384
367
  length: 2652,
385
368
  trackNumber: 1,
386
369
  timecode: 680,
387
- headerFlags: 0,
388
370
  keyframe: false,
389
- lacing: [0, 0],
371
+ lacing: 0,
390
372
  invisible: false,
391
- children: [],
373
+ videoSample: null,
392
374
  },
393
375
  {
394
- type: 'simple-block-segment',
376
+ type: 'simple-block-or-block-segment',
395
377
  length: 4199,
396
378
  trackNumber: 1,
397
379
  timecode: 720,
398
- headerFlags: 0,
399
380
  keyframe: false,
400
- lacing: [0, 0],
381
+ lacing: 0,
401
382
  invisible: false,
402
- children: [],
383
+ videoSample: null,
403
384
  },
404
385
  {
405
- type: 'simple-block-segment',
386
+ type: 'simple-block-or-block-segment',
406
387
  length: 3998,
407
388
  trackNumber: 1,
408
389
  timecode: 760,
409
- headerFlags: 0,
410
390
  keyframe: false,
411
- lacing: [0, 0],
391
+ lacing: 0,
412
392
  invisible: false,
413
- children: [],
393
+ videoSample: null,
414
394
  },
415
395
  {
416
- type: 'simple-block-segment',
396
+ type: 'simple-block-or-block-segment',
417
397
  length: 6373,
418
398
  trackNumber: 1,
419
399
  timecode: 800,
420
- headerFlags: 0,
421
400
  keyframe: false,
422
- lacing: [0, 0],
401
+ lacing: 0,
423
402
  invisible: false,
424
- children: [],
403
+ videoSample: null,
425
404
  },
426
405
  {
427
- type: 'simple-block-segment',
406
+ type: 'simple-block-or-block-segment',
428
407
  length: 5955,
429
408
  trackNumber: 1,
430
409
  timecode: 840,
431
- headerFlags: 0,
432
410
  keyframe: false,
433
- lacing: [0, 0],
411
+ lacing: 0,
434
412
  invisible: false,
435
- children: [],
413
+ videoSample: null,
436
414
  },
437
415
  {
438
- type: 'simple-block-segment',
416
+ type: 'simple-block-or-block-segment',
439
417
  length: 7943,
440
418
  trackNumber: 1,
441
419
  timecode: 880,
442
- headerFlags: 0,
443
420
  keyframe: false,
444
- lacing: [0, 0],
421
+ lacing: 0,
445
422
  invisible: false,
446
- children: [],
423
+ videoSample: null,
447
424
  },
448
425
  {
449
- type: 'simple-block-segment',
426
+ type: 'simple-block-or-block-segment',
450
427
  length: 8241,
451
428
  trackNumber: 1,
452
429
  timecode: 920,
453
- headerFlags: 0,
454
430
  keyframe: false,
455
- lacing: [0, 0],
431
+ lacing: 0,
456
432
  invisible: false,
457
- children: [],
433
+ videoSample: null,
458
434
  },
459
435
  {
460
- type: 'simple-block-segment',
436
+ type: 'simple-block-or-block-segment',
461
437
  length: 9506,
462
438
  trackNumber: 1,
463
439
  timecode: 960,
464
- headerFlags: 0,
465
440
  keyframe: false,
466
- lacing: [0, 0],
441
+ lacing: 0,
467
442
  invisible: false,
468
- children: [],
443
+ videoSample: null,
469
444
  },
470
445
  ],
471
446
  },
@@ -248,6 +248,7 @@ test('Should stream ProRes video', async () => {
248
248
 
249
249
  test('Should stream variable fps video', async () => {
250
250
  let audioTracks = 0;
251
+ let samples = 0;
251
252
  const parsed = await parseMedia({
252
253
  src: RenderInternals.exampleVideos.variablefps,
253
254
  fields: {
@@ -269,8 +270,9 @@ test('Should stream variable fps video', async () => {
269
270
  expect(track.numberOfChannels).toBe(1);
270
271
  expect(track.sampleRate).toBe(48000);
271
272
  audioTracks++;
272
- // TODO: Get samples
273
- return null;
273
+ return () => {
274
+ samples++;
275
+ };
274
276
  },
275
277
  });
276
278
 
@@ -314,6 +316,7 @@ test('Should stream variable fps video', async () => {
314
316
  description: undefined,
315
317
  });
316
318
  expect(audioTracks).toBe(1);
319
+ expect(samples).toBe(381);
317
320
  });
318
321
 
319
322
  test('Should stream MKV video', async () => {
@@ -357,7 +360,7 @@ test('Should stream MKV video', async () => {
357
360
 
358
361
  expect(videoSamples).toBe(10);
359
362
  expect(audioSamples).toBe(16);
360
- expect(parsed.internalStats.samplesThatHadToBeQueued).toBe(0);
363
+ expect(parsed.internalStats).toEqual({});
361
364
  });
362
365
 
363
366
  test('Should stream MP3 in MP4 video', async () => {
@@ -660,7 +663,7 @@ test('MP3 in matroska', async () => {
660
663
  expect(parsed.audioCodec).toEqual('mp3');
661
664
  expect(parsed.videoTracks.length).toBe(1);
662
665
  expect(parsed.audioTracks.length).toBe(1);
663
- expect(audioSamples).toBe(139);
666
+ expect(audioSamples).toBe(140);
664
667
  expect(videoSamples).toBe(100);
665
668
  });
666
669
 
@@ -685,5 +688,44 @@ test('Should stream OPUS', async () => {
685
688
 
686
689
  expect(parsed.audioCodec).toEqual('opus');
687
690
  expect(parsed.audioTracks.length).toBe(1);
688
- expect(audioSamples).toBe(166);
691
+ expect(audioSamples).toBe(167);
692
+ });
693
+
694
+ test('Should stream transparent video', async () => {
695
+ let videoTracks = 0;
696
+ let audioTracks = 0;
697
+ let videoSamples = 0;
698
+ let keyFrames = 0;
699
+
700
+ await parseMedia({
701
+ src: RenderInternals.exampleVideos.transparentwithdar,
702
+ reader: nodeReader,
703
+ onVideoTrack: (track) => {
704
+ expect(track.codedHeight).toBe(512);
705
+ expect(track.codedWidth).toBe(512);
706
+ videoTracks++;
707
+ return (sample) => {
708
+ // https://ffmpeg.org/pipermail/ffmpeg-devel/2015-June/173825.html
709
+ // For Blocks, keyframes is
710
+ // inferred by the absence of ReferenceBlock element (as done by matroskadec).
711
+ if (sample.type === 'key') {
712
+ keyFrames++;
713
+ }
714
+
715
+ videoSamples++;
716
+ };
717
+ },
718
+ onAudioTrack: () => {
719
+ audioTracks++;
720
+ return null;
721
+ },
722
+ fields: {
723
+ tracks: true,
724
+ },
725
+ });
726
+
727
+ expect(videoTracks).toBe(1);
728
+ expect(audioTracks).toBe(0);
729
+ expect(videoSamples).toBe(39);
730
+ expect(keyFrames).toBe(1);
689
731
  });
@@ -3,7 +3,6 @@ import type {AudioTrack, VideoTrack} from './get-tracks';
3
3
  export type AudioSample = {
4
4
  data: Uint8Array;
5
5
  timestamp: number;
6
- offset: number;
7
6
  trackId: number;
8
7
  type: 'key' | 'delta';
9
8
  };