@remotion/media-parser 4.0.191

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 (143) hide show
  1. package/.eslintrc +8 -0
  2. package/LICENSE.md +49 -0
  3. package/README.md +18 -0
  4. package/dist/boxes/iso-base-media/base-type.d.ts +4 -0
  5. package/dist/boxes/iso-base-media/base-type.js +2 -0
  6. package/dist/boxes/iso-base-media/ftyp.d.ts +13 -0
  7. package/dist/boxes/iso-base-media/ftyp.js +22 -0
  8. package/dist/boxes/iso-base-media/moov/moov.d.ts +12 -0
  9. package/dist/boxes/iso-base-media/moov/moov.js +22 -0
  10. package/dist/boxes/iso-base-media/mvhd.d.ts +30 -0
  11. package/dist/boxes/iso-base-media/mvhd.js +59 -0
  12. package/dist/boxes/iso-base-media/process-box.d.ts +8 -0
  13. package/dist/boxes/iso-base-media/process-box.js +174 -0
  14. package/dist/boxes/iso-base-media/stsd/keys.d.ts +5 -0
  15. package/dist/boxes/iso-base-media/stsd/keys.js +21 -0
  16. package/dist/boxes/iso-base-media/stsd/mebx.d.ts +14 -0
  17. package/dist/boxes/iso-base-media/stsd/mebx.js +27 -0
  18. package/dist/boxes/iso-base-media/stsd/samples.d.ts +48 -0
  19. package/dist/boxes/iso-base-media/stsd/samples.js +215 -0
  20. package/dist/boxes/iso-base-media/stsd/stsd.d.ts +13 -0
  21. package/dist/boxes/iso-base-media/stsd/stsd.js +26 -0
  22. package/dist/boxes/iso-base-media/stts/stts.d.ts +15 -0
  23. package/dist/boxes/iso-base-media/stts/stts.js +35 -0
  24. package/dist/boxes/iso-base-media/tkhd.d.ts +22 -0
  25. package/dist/boxes/iso-base-media/tkhd.js +63 -0
  26. package/dist/boxes/iso-base-media/to-date.d.ts +1 -0
  27. package/dist/boxes/iso-base-media/to-date.js +11 -0
  28. package/dist/boxes/iso-base-media/trak/trak.d.ts +12 -0
  29. package/dist/boxes/iso-base-media/trak/trak.js +22 -0
  30. package/dist/boxes/webm/parse-webm-header.d.ts +3 -0
  31. package/dist/boxes/webm/parse-webm-header.js +16 -0
  32. package/dist/boxes/webm/segments/duration.d.ts +6 -0
  33. package/dist/boxes/webm/segments/duration.js +15 -0
  34. package/dist/boxes/webm/segments/info.d.ts +8 -0
  35. package/dist/boxes/webm/segments/info.js +14 -0
  36. package/dist/boxes/webm/segments/main.d.ts +7 -0
  37. package/dist/boxes/webm/segments/main.js +13 -0
  38. package/dist/boxes/webm/segments/muxing.d.ts +6 -0
  39. package/dist/boxes/webm/segments/muxing.js +12 -0
  40. package/dist/boxes/webm/segments/parse-children.d.ts +3 -0
  41. package/dist/boxes/webm/segments/parse-children.js +17 -0
  42. package/dist/boxes/webm/segments/seek-head.d.ts +8 -0
  43. package/dist/boxes/webm/segments/seek-head.js +13 -0
  44. package/dist/boxes/webm/segments/seek-position.d.ts +6 -0
  45. package/dist/boxes/webm/segments/seek-position.js +12 -0
  46. package/dist/boxes/webm/segments/seek.d.ts +8 -0
  47. package/dist/boxes/webm/segments/seek.js +16 -0
  48. package/dist/boxes/webm/segments/timestamp-scale.d.ts +6 -0
  49. package/dist/boxes/webm/segments/timestamp-scale.js +11 -0
  50. package/dist/boxes/webm/segments/track-entry.d.ts +71 -0
  51. package/dist/boxes/webm/segments/track-entry.js +175 -0
  52. package/dist/boxes/webm/segments/tracks.d.ts +7 -0
  53. package/dist/boxes/webm/segments/tracks.js +13 -0
  54. package/dist/boxes/webm/segments/unknown.d.ts +6 -0
  55. package/dist/boxes/webm/segments/unknown.js +11 -0
  56. package/dist/boxes/webm/segments/void.d.ts +6 -0
  57. package/dist/boxes/webm/segments/void.js +12 -0
  58. package/dist/boxes/webm/segments/writing.d.ts +6 -0
  59. package/dist/boxes/webm/segments/writing.js +12 -0
  60. package/dist/boxes/webm/segments.d.ts +16 -0
  61. package/dist/boxes/webm/segments.js +106 -0
  62. package/dist/buffer-iterator.d.ts +36 -0
  63. package/dist/buffer-iterator.js +263 -0
  64. package/dist/from-node.d.ts +2 -0
  65. package/dist/from-node.js +19 -0
  66. package/dist/from-web.d.ts +2 -0
  67. package/dist/from-web.js +40 -0
  68. package/dist/get-dimensions.d.ts +7 -0
  69. package/dist/get-dimensions.js +104 -0
  70. package/dist/get-duration.d.ts +3 -0
  71. package/dist/get-duration.js +61 -0
  72. package/dist/get-fps.d.ts +3 -0
  73. package/dist/get-fps.js +70 -0
  74. package/dist/has-all-info.d.ts +3 -0
  75. package/dist/has-all-info.js +27 -0
  76. package/dist/index.d.ts +1 -0
  77. package/dist/index.js +5 -0
  78. package/dist/options.d.ts +19 -0
  79. package/dist/options.js +2 -0
  80. package/dist/parse-media.d.ts +2 -0
  81. package/dist/parse-media.js +44 -0
  82. package/dist/parse-result.d.ts +29 -0
  83. package/dist/parse-result.js +2 -0
  84. package/dist/parse-video.d.ts +10 -0
  85. package/dist/parse-video.js +29 -0
  86. package/dist/reader.d.ts +7 -0
  87. package/dist/reader.js +2 -0
  88. package/package.json +47 -0
  89. package/src/boxes/iso-base-media/base-type.ts +4 -0
  90. package/src/boxes/iso-base-media/ftyp.ts +39 -0
  91. package/src/boxes/iso-base-media/moov/moov.ts +37 -0
  92. package/src/boxes/iso-base-media/mvhd.ts +107 -0
  93. package/src/boxes/iso-base-media/process-box.ts +236 -0
  94. package/src/boxes/iso-base-media/stsd/keys.ts +25 -0
  95. package/src/boxes/iso-base-media/stsd/mebx.ts +46 -0
  96. package/src/boxes/iso-base-media/stsd/samples.ts +298 -0
  97. package/src/boxes/iso-base-media/stsd/stsd.ts +48 -0
  98. package/src/boxes/iso-base-media/stts/stts.ts +62 -0
  99. package/src/boxes/iso-base-media/tkhd.ts +105 -0
  100. package/src/boxes/iso-base-media/to-date.ts +9 -0
  101. package/src/boxes/iso-base-media/trak/trak.ts +37 -0
  102. package/src/boxes/webm/parse-webm-header.ts +18 -0
  103. package/src/boxes/webm/segments/duration.ts +22 -0
  104. package/src/boxes/webm/segments/info.ts +20 -0
  105. package/src/boxes/webm/segments/main.ts +19 -0
  106. package/src/boxes/webm/segments/muxing.ts +18 -0
  107. package/src/boxes/webm/segments/parse-children.ts +21 -0
  108. package/src/boxes/webm/segments/seek-head.ts +21 -0
  109. package/src/boxes/webm/segments/seek-position.ts +19 -0
  110. package/src/boxes/webm/segments/seek.ts +23 -0
  111. package/src/boxes/webm/segments/timestamp-scale.ts +17 -0
  112. package/src/boxes/webm/segments/track-entry.ts +295 -0
  113. package/src/boxes/webm/segments/tracks.ts +22 -0
  114. package/src/boxes/webm/segments/unknown.ts +19 -0
  115. package/src/boxes/webm/segments/void.ts +16 -0
  116. package/src/boxes/webm/segments/writing.ts +18 -0
  117. package/src/boxes/webm/segments.ts +206 -0
  118. package/src/buffer-iterator.ts +305 -0
  119. package/src/from-node.ts +22 -0
  120. package/src/from-web.ts +53 -0
  121. package/src/get-dimensions.ts +147 -0
  122. package/src/get-duration.ts +73 -0
  123. package/src/get-fps.ts +92 -0
  124. package/src/has-all-info.ts +34 -0
  125. package/src/index.ts +1 -0
  126. package/src/options.ts +38 -0
  127. package/src/parse-media.ts +57 -0
  128. package/src/parse-result.ts +44 -0
  129. package/src/parse-video.ts +41 -0
  130. package/src/reader.ts +10 -0
  131. package/src/test/duration.test.ts +34 -0
  132. package/src/test/keys.test.ts +47 -0
  133. package/src/test/matroska.test.ts +163 -0
  134. package/src/test/mvhd.test.ts +89 -0
  135. package/src/test/parse-stts.test.ts +38 -0
  136. package/src/test/parse-video.test.ts +113 -0
  137. package/src/test/parse-webm.test.ts +15 -0
  138. package/src/test/stream-local.test.ts +105 -0
  139. package/src/test/stream-remote.test.ts +12 -0
  140. package/src/test/stsd.test.ts +162 -0
  141. package/src/test/tkhd.test.ts +84 -0
  142. package/tsconfig.json +10 -0
  143. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,34 @@
1
+ import {hasDimensions} from './get-dimensions';
2
+ import {hasDuration} from './get-duration';
3
+ import {hasFps} from './get-fps';
4
+ import type {Options} from './options';
5
+ import type {ParseResult} from './parse-result';
6
+
7
+ export const hasAllInfo = (
8
+ options: Options<boolean, boolean, boolean, boolean>,
9
+ parseResult: ParseResult,
10
+ ) => {
11
+ const keys = Object.entries(options)
12
+ .filter(([, value]) => value)
13
+ .map(([key]) => key) as (keyof Options<true, true, true, true>)[];
14
+
15
+ return keys.every((key) => {
16
+ if (key === 'boxes') {
17
+ return parseResult.status === 'done';
18
+ }
19
+
20
+ if (key === 'durationInSeconds') {
21
+ return hasDuration(parseResult.segments);
22
+ }
23
+
24
+ if (key === 'dimensions') {
25
+ return hasDimensions(parseResult.segments);
26
+ }
27
+
28
+ if (key === 'fps') {
29
+ return hasFps(parseResult.segments) !== null;
30
+ }
31
+
32
+ throw new Error(`Unknown key: ${key satisfies never}`);
33
+ });
34
+ };
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export {parseMedia} from './parse-media';
package/src/options.ts ADDED
@@ -0,0 +1,38 @@
1
+ import type {Dimensions} from './get-dimensions';
2
+ import type {AnySegment} from './parse-result';
3
+ import type {ReaderInterface} from './reader';
4
+
5
+ export type Options<
6
+ EnableDimensions extends boolean,
7
+ EnableDuration extends boolean,
8
+ EnableBoxes extends boolean,
9
+ EnableFps extends boolean,
10
+ > = {
11
+ dimensions?: EnableDimensions;
12
+ durationInSeconds?: EnableDuration;
13
+ boxes?: EnableBoxes;
14
+ fps?: EnableFps;
15
+ };
16
+
17
+ export type Metadata<
18
+ EnableDimensions extends boolean,
19
+ EnableDuration extends boolean,
20
+ EnableBoxes extends boolean,
21
+ EnableFps extends boolean,
22
+ > = (EnableDimensions extends true ? {dimensions: Dimensions} : {}) &
23
+ (EnableDuration extends true ? {durationInSeconds: number | null} : {}) &
24
+ (EnableBoxes extends true ? {boxes: AnySegment[]} : {}) &
25
+ (EnableFps extends true ? {fps: number | null} : {});
26
+
27
+ export type ParseMedia = <
28
+ EnableDimensions extends boolean,
29
+ EnableDuration extends boolean,
30
+ EnableBoxes extends boolean,
31
+ EnableFps extends boolean,
32
+ >(
33
+ src: string,
34
+ options: Options<EnableDimensions, EnableDuration, EnableBoxes, EnableFps>,
35
+ readerInterface?: ReaderInterface,
36
+ ) => Promise<
37
+ Metadata<EnableDimensions, EnableDuration, EnableBoxes, EnableFps>
38
+ >;
@@ -0,0 +1,57 @@
1
+ import {getArrayBufferIterator} from './buffer-iterator';
2
+ import {webReader} from './from-web';
3
+ import {getDimensions} from './get-dimensions';
4
+ import {getDuration} from './get-duration';
5
+ import {getFps} from './get-fps';
6
+ import {hasAllInfo} from './has-all-info';
7
+ import type {Metadata, ParseMedia} from './options';
8
+ import {parseVideo} from './parse-video';
9
+
10
+ export const parseMedia: ParseMedia = async (
11
+ src,
12
+ options,
13
+ readerInterface = webReader,
14
+ ) => {
15
+ const reader = await readerInterface.read(src, null);
16
+
17
+ const returnValue = {} as Metadata<true, true, true, true>;
18
+
19
+ const iterator = getArrayBufferIterator(new Uint8Array([]));
20
+ let parseResult = parseVideo(iterator);
21
+
22
+ while (parseResult.status === 'incomplete') {
23
+ const result = await reader.read();
24
+ if (result.done) {
25
+ break;
26
+ }
27
+
28
+ iterator.addData(result.value);
29
+ parseResult = parseResult.continueParsing();
30
+
31
+ if (hasAllInfo(options, parseResult)) {
32
+ if (!reader.closed) {
33
+ reader.cancel(new Error('has all information'));
34
+ }
35
+
36
+ break;
37
+ }
38
+ }
39
+
40
+ if (options.dimensions) {
41
+ returnValue.dimensions = getDimensions(parseResult.segments);
42
+ }
43
+
44
+ if (options.durationInSeconds) {
45
+ returnValue.durationInSeconds = getDuration(parseResult.segments);
46
+ }
47
+
48
+ if (options.fps) {
49
+ returnValue.fps = getFps(parseResult.segments);
50
+ }
51
+
52
+ if (options.boxes) {
53
+ returnValue.boxes = parseResult.segments;
54
+ }
55
+
56
+ return returnValue;
57
+ };
@@ -0,0 +1,44 @@
1
+ import type {BaseBox} from './boxes/iso-base-media/base-type';
2
+ import type {FtypBox} from './boxes/iso-base-media/ftyp';
3
+ import type {MoovBox} from './boxes/iso-base-media/moov/moov';
4
+ import type {MvhdBox} from './boxes/iso-base-media/mvhd';
5
+ import type {KeysBox} from './boxes/iso-base-media/stsd/keys';
6
+ import type {MebxBox} from './boxes/iso-base-media/stsd/mebx';
7
+ import type {StsdBox} from './boxes/iso-base-media/stsd/stsd';
8
+ import type {SttsBox} from './boxes/iso-base-media/stts/stts';
9
+ import type {TkhdBox} from './boxes/iso-base-media/tkhd';
10
+ import type {TrakBox} from './boxes/iso-base-media/trak/trak';
11
+ import type {MatroskaSegment} from './boxes/webm/segments';
12
+
13
+ interface RegularBox extends BaseBox {
14
+ boxType: string;
15
+ boxSize: number;
16
+ children: AnySegment[];
17
+ offset: number;
18
+ type: 'regular-box';
19
+ }
20
+
21
+ export type IsoBaseMediaBox =
22
+ | RegularBox
23
+ | FtypBox
24
+ | MvhdBox
25
+ | TkhdBox
26
+ | StsdBox
27
+ | MebxBox
28
+ | KeysBox
29
+ | MoovBox
30
+ | TrakBox
31
+ | SttsBox;
32
+
33
+ export type AnySegment = MatroskaSegment | IsoBaseMediaBox;
34
+
35
+ export type ParseResult =
36
+ | {
37
+ status: 'done';
38
+ segments: AnySegment[];
39
+ }
40
+ | {
41
+ status: 'incomplete';
42
+ segments: AnySegment[];
43
+ continueParsing: () => ParseResult;
44
+ };
@@ -0,0 +1,41 @@
1
+ import {parseBoxes} from './boxes/iso-base-media/process-box';
2
+ import {parseWebm} from './boxes/webm/parse-webm-header';
3
+ import type {BufferIterator} from './buffer-iterator';
4
+ import type {IsoBaseMediaBox, ParseResult} from './parse-result';
5
+
6
+ export type BoxAndNext =
7
+ | {
8
+ type: 'complete';
9
+ box: IsoBaseMediaBox;
10
+ size: number;
11
+ }
12
+ | {
13
+ type: 'incomplete';
14
+ };
15
+
16
+ export const parseVideo = (iterator: BufferIterator): ParseResult => {
17
+ if (iterator.bytesRemaining() === 0) {
18
+ return {
19
+ status: 'incomplete',
20
+ segments: [],
21
+ continueParsing: () => {
22
+ return parseVideo(iterator);
23
+ },
24
+ };
25
+ }
26
+
27
+ if (iterator.isIsoBaseMedia()) {
28
+ return parseBoxes({
29
+ iterator,
30
+ maxBytes: Infinity,
31
+ allowIncompleteBoxes: true,
32
+ initialBoxes: [],
33
+ });
34
+ }
35
+
36
+ if (iterator.isWebm()) {
37
+ return parseWebm(iterator);
38
+ }
39
+
40
+ throw new Error('Unknown video format');
41
+ };
package/src/reader.ts ADDED
@@ -0,0 +1,10 @@
1
+ type ReadContent = (
2
+ src: string,
3
+ range: [number, number] | null,
4
+ ) => Promise<ReadableStreamDefaultReader<Uint8Array>>;
5
+ type GetLength = (src: string) => Promise<number>;
6
+
7
+ export type ReaderInterface = {
8
+ read: ReadContent;
9
+ getLength: GetLength;
10
+ };
@@ -0,0 +1,34 @@
1
+ import {RenderInternals} from '@remotion/renderer';
2
+ import {expect, test} from 'bun:test';
3
+ import {nodeReader} from '../from-node';
4
+ import {parseMedia} from '../parse-media';
5
+
6
+ test('Should get duration of video', async () => {
7
+ const parsed = await parseMedia(
8
+ RenderInternals.exampleVideos.framer24fps,
9
+ {
10
+ durationInSeconds: true,
11
+ dimensions: true,
12
+ },
13
+ nodeReader,
14
+ );
15
+
16
+ expect(parsed.durationInSeconds).toBe(4.167);
17
+ expect(parsed.dimensions).toEqual({width: 1080, height: 1080});
18
+ });
19
+
20
+ test('Should get duration of HEVC video', async () => {
21
+ const parsed = await parseMedia(
22
+ RenderInternals.exampleVideos.iphonehevc,
23
+ {durationInSeconds: true, dimensions: true, fps: true},
24
+ nodeReader,
25
+ );
26
+
27
+ expect(parsed.durationInSeconds).toBe(3.4);
28
+ // TODO: Should apply rotation matrix to get the correct dimensions
29
+ expect(parsed.dimensions).toEqual({
30
+ width: 1920,
31
+ height: 1080,
32
+ });
33
+ expect(parsed.fps).toEqual(30);
34
+ });
@@ -0,0 +1,47 @@
1
+ import {expect, test} from 'bun:test';
2
+ import {parseKeys} from '../boxes/iso-base-media/stsd/keys';
3
+
4
+ test('Should parse keyd box', () => {
5
+ const buffer = Uint8Array.from([
6
+ 0, 0, 2, 60, 107, 101, 121, 115, 0, 0, 2, 52, 0, 0, 0, 1, 0, 0, 0, 47, 107,
7
+ 101, 121, 100, 109, 100, 116, 97, 99, 111, 109, 46, 97, 112, 112, 108, 101,
8
+ 46, 113, 117, 105, 99, 107, 116, 105, 109, 101, 46, 108, 105, 118, 101, 45,
9
+ 112, 104, 111, 116, 111, 45, 105, 110, 102, 111, 0, 0, 0, 67, 100, 116, 121,
10
+ 112, 0, 0, 0, 1, 99, 111, 109, 46, 97, 112, 112, 108, 101, 46, 113, 117,
11
+ 105, 99, 107, 116, 105, 109, 101, 46, 99, 111, 109, 46, 97, 112, 112, 108,
12
+ 101, 46, 113, 117, 105, 99, 107, 116, 105, 109, 101, 46, 108, 105, 118, 101,
13
+ 45, 112, 104, 111, 116, 111, 45, 105, 110, 102, 111, 0, 0, 0, 20, 115, 100,
14
+ 112, 100, 0, 0, 0, 12, 115, 100, 112, 105, 0, 0, 0, 0, 0, 0, 1, 142, 115,
15
+ 101, 116, 117, 0, 0, 1, 118, 99, 102, 103, 118, 98, 112, 108, 105, 115, 116,
16
+ 48, 48, 211, 1, 2, 3, 4, 5, 12, 95, 16, 33, 76, 105, 118, 101, 80, 104, 111,
17
+ 116, 111, 77, 101, 116, 97, 100, 97, 116, 97, 83, 101, 116, 117, 112, 68,
18
+ 97, 116, 97, 86, 101, 114, 115, 105, 111, 110, 93, 83, 121, 115, 116, 101,
19
+ 109, 86, 101, 114, 115, 105, 111, 110, 95, 16, 17, 70, 114, 97, 109, 101,
20
+ 119, 111, 114, 107, 86, 101, 114, 115, 105, 111, 110, 115, 16, 1, 211, 6, 7,
21
+ 8, 9, 10, 11, 95, 16, 19, 80, 114, 111, 100, 117, 99, 116, 66, 117, 105,
22
+ 108, 100, 86, 101, 114, 115, 105, 111, 110, 91, 80, 114, 111, 100, 117, 99,
23
+ 116, 78, 97, 109, 101, 94, 80, 114, 111, 100, 117, 99, 116, 86, 101, 114,
24
+ 115, 105, 111, 110, 88, 50, 49, 70, 53, 48, 52, 56, 102, 89, 105, 80, 104,
25
+ 111, 110, 101, 32, 79, 83, 84, 49, 55, 46, 53, 213, 13, 14, 15, 16, 17, 18,
26
+ 19, 20, 21, 22, 94, 72, 49, 51, 73, 83, 80, 83, 101, 114, 118, 105, 99, 101,
27
+ 115, 90, 67, 111, 114, 101, 77, 111, 116, 105, 111, 110, 93, 67, 77, 67, 97,
28
+ 112, 116, 117, 114, 101, 67, 111, 114, 101, 94, 72, 49, 48, 73, 83, 80, 83,
29
+ 101, 114, 118, 105, 99, 101, 115, 89, 67, 111, 114, 101, 77, 101, 100, 105,
30
+ 97, 85, 56, 46, 53, 48, 50, 94, 50, 56, 57, 48, 46, 49, 54, 46, 49, 51, 46,
31
+ 48, 46, 49, 87, 52, 55, 55, 46, 50, 46, 49, 86, 50, 48, 46, 53, 48, 48, 88,
32
+ 51, 49, 49, 48, 46, 52, 46, 49, 0, 8, 0, 15, 0, 51, 0, 65, 0, 85, 0, 87, 0,
33
+ 94, 0, 116, 0, 128, 0, 143, 0, 152, 0, 162, 0, 167, 0, 178, 0, 193, 0, 204,
34
+ 0, 218, 0, 233, 0, 243, 0, 249, 1, 8, 1, 16, 1, 23, 0, 0, 0, 0, 0, 0, 2, 1,
35
+ 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0,
36
+ 0, 0, 16, 100, 105, 109, 115, 0, 0, 7, 128, 0, 0, 4, 56, 0, 0, 0, 24, 99,
37
+ 116, 112, 115, 0, 0, 0, 16, 100, 116, 121, 112, 0, 0, 0, 0, 0, 0, 0, 0,
38
+ ]);
39
+
40
+ const data = parseKeys(buffer, 0);
41
+
42
+ expect(data).toEqual({
43
+ type: 'keys-box',
44
+ boxSize: 572,
45
+ offset: 0,
46
+ });
47
+ });
@@ -0,0 +1,163 @@
1
+ import {RenderInternals} from '@remotion/renderer';
2
+ import {expect, test} from 'bun:test';
3
+ import {nodeReader} from '../from-node';
4
+ import {parseMedia} from '../parse-media';
5
+
6
+ test('Should get duration of AV1 video', async () => {
7
+ const parsed = await parseMedia(
8
+ RenderInternals.exampleVideos.av1,
9
+ {
10
+ durationInSeconds: true,
11
+ boxes: true,
12
+ dimensions: true,
13
+ fps: true,
14
+ },
15
+ nodeReader,
16
+ );
17
+
18
+ expect(parsed.durationInSeconds).toBe(1);
19
+ expect(parsed.fps).toBe(null);
20
+ expect(parsed.dimensions).toEqual({
21
+ width: 1920,
22
+ height: 1080,
23
+ });
24
+ expect(parsed.boxes).toEqual([
25
+ {
26
+ type: 'main-segment',
27
+ children: [
28
+ {
29
+ type: 'seek-head-segment',
30
+ length: 59,
31
+ children: [
32
+ {
33
+ type: 'seek-segment',
34
+ seekId: '0x1549a966',
35
+ child: {
36
+ type: 'seek-position-segment',
37
+ seekPosition: 161,
38
+ },
39
+ },
40
+ {
41
+ type: 'seek-segment',
42
+ seekId: '0x1654ae6b',
43
+ child: {
44
+ type: 'seek-position-segment',
45
+ seekPosition: 214,
46
+ },
47
+ },
48
+ {
49
+ type: 'seek-segment',
50
+ seekId: '0x1254c367',
51
+ child: {
52
+ type: 'seek-position-segment',
53
+ seekPosition: 322,
54
+ },
55
+ },
56
+ {
57
+ type: 'seek-segment',
58
+ seekId: '0x1c53bb6b',
59
+ child: {
60
+ type: 'seek-position-segment',
61
+ seekPosition: 347329,
62
+ },
63
+ },
64
+ ],
65
+ },
66
+ {
67
+ type: 'void-segment',
68
+ length: 88,
69
+ },
70
+ {
71
+ type: 'info-segment',
72
+ length: 48,
73
+ children: [
74
+ {
75
+ type: 'timestamp-scale-segment',
76
+ timestampScale: 1000000,
77
+ },
78
+ {
79
+ type: 'muxing-app-segment',
80
+ value: 'Lavf60.3.100',
81
+ },
82
+ {
83
+ type: 'writing-app-segment',
84
+ value: 'Lavf60.3.100',
85
+ },
86
+ {
87
+ type: 'duration-segment',
88
+ duration: 1000,
89
+ },
90
+ ],
91
+ },
92
+ {
93
+ type: 'tracks-segment',
94
+ children: [
95
+ {
96
+ type: 'track-entry-segment',
97
+ children: [
98
+ {
99
+ type: 'track-number-segment',
100
+ trackNumber: 1,
101
+ },
102
+ {
103
+ type: 'track-uid-segment',
104
+ trackUid: 'ab2171012bb9020a',
105
+ },
106
+ {
107
+ type: 'flag-lacing-segment',
108
+ lacing: false,
109
+ },
110
+ {
111
+ type: 'language-segment',
112
+ language: 'und',
113
+ },
114
+ {
115
+ type: 'codec-segment',
116
+ codec: 'V_AV1',
117
+ },
118
+ {
119
+ type: 'track-type-segment',
120
+ trackType: 1,
121
+ },
122
+ {
123
+ type: 'default-duration-segment',
124
+ defaultDuration: 40000000,
125
+ },
126
+ {
127
+ type: 'video-segment',
128
+ children: [
129
+ {
130
+ type: 'width-segment',
131
+ width: 1920,
132
+ },
133
+ {
134
+ height: 1080,
135
+ type: 'height-segment',
136
+ },
137
+ {
138
+ type: 'color-segment',
139
+ },
140
+ ],
141
+ },
142
+ {
143
+ id: '0x0163a294',
144
+ type: 'unknown-segment',
145
+ },
146
+ ],
147
+ },
148
+ {
149
+ id: '0x0c000a0e',
150
+ type: 'unknown-segment',
151
+ },
152
+ ],
153
+ },
154
+ {
155
+ id: '0x',
156
+ type: 'unknown-segment',
157
+ },
158
+ ],
159
+ },
160
+ ]);
161
+
162
+ expect(parsed.durationInSeconds).toBe(1);
163
+ });
@@ -0,0 +1,89 @@
1
+ import {expect, test} from 'bun:test';
2
+ import {parseMvhd} from '../boxes/iso-base-media/mvhd';
3
+ import {getArrayBufferIterator} from '../buffer-iterator';
4
+
5
+ test('Should be able to parse a MVHD box correctly', () => {
6
+ const buffer = Uint8Array.from([
7
+ // size, 32 bit
8
+ 0, 0, 0, 108,
9
+ // mvhd atom, 32 bit
10
+ 109, 118, 104, 100,
11
+ // version, 8 unsigned bit
12
+ 0,
13
+ // flags, 24 bit
14
+ 0, 0, 0,
15
+ // creation time, 32 bit
16
+ 0, 0, 0, 0,
17
+ // modification time, 32 bit
18
+ 0, 0, 0, 0,
19
+ // time scale
20
+ 0, 0, 3, 232,
21
+ // duration
22
+ 0, 0, 16, 71,
23
+ // rate
24
+ 0, 1, 0, 0,
25
+ // volume
26
+ 1, 0,
27
+ // reserved 16 bit
28
+ 0, 0,
29
+ // reserved 32 bit [0]
30
+ 0, 0, 0, 0,
31
+ // reserved 32 bit [1]
32
+ 0, 0, 0, 0,
33
+ // matrix [0]
34
+ 0, 1, 0, 0,
35
+ // matrix [1]
36
+ 0, 0, 0, 0,
37
+ // matrix [2]
38
+ 0, 0, 0, 0,
39
+ // matrix [3]
40
+ 0, 0, 0, 0,
41
+ // matrix [4]
42
+ 0, 1, 0, 0,
43
+ // matrix [5]
44
+ 0, 0, 0, 0,
45
+ // matrix [6]
46
+ 0, 0, 0, 0,
47
+ // matrix [7]
48
+ 0, 0, 0, 0,
49
+ // matrix [8]
50
+ 64, 0, 0, 0,
51
+ // predefined [0]
52
+ 0, 0, 0, 0,
53
+ // predefined [1]
54
+ 0, 0, 0, 0,
55
+ // predefined [2]
56
+ 0, 0, 0, 0,
57
+ // predefined [3]
58
+ 0, 0, 0, 0,
59
+ // predefined [4]
60
+ 0, 0, 0, 0,
61
+ // predefined [5]
62
+ 0, 0, 0, 0,
63
+ // next track id
64
+ 0, 0, 0, 2,
65
+ ]);
66
+
67
+ const iterator = getArrayBufferIterator(buffer);
68
+ iterator.discard(8);
69
+
70
+ const mvhd = parseMvhd({
71
+ offset: 0,
72
+ size: 108,
73
+ iterator,
74
+ });
75
+ expect(mvhd).toEqual({
76
+ creationTime: null,
77
+ modificationTime: null,
78
+ timeScale: 1000,
79
+ durationInUnits: 4167,
80
+ durationInSeconds: 4.167,
81
+ rate: 1,
82
+ volume: 1,
83
+ matrix: [65536, 0, 0, 0, 65536, 0, 0, 0, 1073741824],
84
+ nextTrackId: 2,
85
+ type: 'mvhd-box',
86
+ boxSize: 108,
87
+ offset: 0,
88
+ });
89
+ });
@@ -0,0 +1,38 @@
1
+ import {expect, test} from 'bun:test';
2
+ import {parseStts} from '../boxes/iso-base-media/stts/stts';
3
+ import {getArrayBufferIterator} from '../buffer-iterator';
4
+
5
+ const buffer = new Uint8Array([
6
+ // mock header
7
+ 0, 0, 0, 0, 0, 0, 0, 0,
8
+ // actual box
9
+ 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 1, 13, 0, 0, 0, 20, 0, 0, 0, 1, 0, 0, 0, 21, 0,
10
+ 0, 0, 107, 0, 0, 0, 20, 0, 0, 11, 216, 99, 116, 116, 115,
11
+ ]);
12
+
13
+ test('Should parse stts box', () => {
14
+ const iterator = getArrayBufferIterator(buffer);
15
+ iterator.counter.increment(8);
16
+ const result = parseStts({
17
+ data: iterator,
18
+ size: buffer.length - 8,
19
+ fileOffset: 0,
20
+ });
21
+ expect(result).toEqual({
22
+ type: 'stts-box',
23
+ sampleDistribution: [
24
+ {
25
+ sampleCount: 269,
26
+ sampleDelta: 20,
27
+ },
28
+ {
29
+ sampleCount: 1,
30
+ sampleDelta: 21,
31
+ },
32
+ {
33
+ sampleCount: 107,
34
+ sampleDelta: 20,
35
+ },
36
+ ],
37
+ });
38
+ });