@remotion/media-parser 4.0.201 → 4.0.202

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 (113) hide show
  1. package/dist/boxes/iso-base-media/mvhd.js +2 -2
  2. package/dist/boxes/iso-base-media/stsd/keys.js +1 -1
  3. package/dist/boxes/webm/av1-codec-private.js +1 -1
  4. package/dist/boxes/webm/description.d.ts +2 -2
  5. package/dist/boxes/webm/description.js +2 -2
  6. package/dist/boxes/webm/ebml.d.ts +2 -2
  7. package/dist/boxes/webm/ebml.js +23 -1
  8. package/dist/boxes/webm/get-ready-tracks.d.ts +1 -1
  9. package/dist/boxes/webm/get-ready-tracks.js +3 -3
  10. package/dist/boxes/webm/get-sample-from-block.d.ts +17 -0
  11. package/dist/boxes/webm/get-sample-from-block.js +78 -0
  12. package/dist/boxes/webm/get-track.d.ts +2 -2
  13. package/dist/boxes/webm/get-track.js +26 -25
  14. package/dist/boxes/webm/make-header.d.ts +3 -8
  15. package/dist/boxes/webm/make-header.js +43 -20
  16. package/dist/boxes/webm/parse-ebml.d.ts +9 -4
  17. package/dist/boxes/webm/parse-ebml.js +122 -13
  18. package/dist/boxes/webm/segments/all-segments.d.ts +421 -107
  19. package/dist/boxes/webm/segments/all-segments.js +260 -33
  20. package/dist/boxes/webm/segments/seek-position.js +1 -1
  21. package/dist/boxes/webm/segments/seek.d.ts +1 -1
  22. package/dist/boxes/webm/segments/seek.js +2 -8
  23. package/dist/boxes/webm/segments/timestamp-scale.js +1 -1
  24. package/dist/boxes/webm/segments/track-entry.d.ts +3 -191
  25. package/dist/boxes/webm/segments/track-entry.js +2 -456
  26. package/dist/boxes/webm/segments.d.ts +3 -16
  27. package/dist/boxes/webm/segments.js +12 -196
  28. package/dist/boxes/webm/traversal.d.ts +5 -6
  29. package/dist/boxes/webm/traversal.js +6 -6
  30. package/dist/buffer-iterator.d.ts +1 -1
  31. package/dist/buffer-iterator.js +3 -3
  32. package/dist/create/create-media.d.ts +2 -0
  33. package/dist/create/create-media.js +36 -0
  34. package/dist/create/matroska-header.d.ts +1 -0
  35. package/dist/create/matroska-header.js +66 -0
  36. package/dist/create/matroska-info.d.ts +4 -0
  37. package/dist/create/matroska-info.js +39 -0
  38. package/dist/create/matroska-segment.d.ts +1 -0
  39. package/dist/create/matroska-segment.js +12 -0
  40. package/dist/create/matroska-trackentry.d.ts +21 -0
  41. package/dist/create/matroska-trackentry.js +191 -0
  42. package/dist/create-media.d.ts +1 -0
  43. package/dist/create-media.js +78 -0
  44. package/dist/get-audio-codec.d.ts +1 -1
  45. package/dist/get-audio-codec.js +13 -13
  46. package/dist/get-duration.js +12 -14
  47. package/dist/get-tracks.js +2 -2
  48. package/dist/get-video-codec.js +13 -13
  49. package/dist/parse-media.js +4 -1
  50. package/dist/parser-context.d.ts +1 -0
  51. package/dist/parser-state.js +3 -2
  52. package/dist/readers/from-fetch.d.ts +2 -0
  53. package/dist/readers/from-fetch.js +64 -0
  54. package/dist/readers/from-node.d.ts +2 -0
  55. package/dist/readers/from-node.js +40 -0
  56. package/dist/readers/from-web-file.d.ts +2 -0
  57. package/dist/readers/from-web-file.js +39 -0
  58. package/dist/readers/reader.d.ts +11 -0
  59. package/dist/readers/reader.js +2 -0
  60. package/dist/traversal.d.ts +19 -17
  61. package/dist/traversal.js +38 -39
  62. package/dist/writers/web-fs.d.ts +2 -0
  63. package/dist/writers/web-fs.js +28 -0
  64. package/dist/writers/writer.d.ts +9 -0
  65. package/dist/writers/writer.js +2 -0
  66. package/input.webm +0 -0
  67. package/package.json +2 -2
  68. package/src/boxes/iso-base-media/mvhd.ts +2 -2
  69. package/src/boxes/iso-base-media/stsd/keys.ts +1 -1
  70. package/src/boxes/webm/av1-codec-private.ts +1 -1
  71. package/src/boxes/webm/description.ts +7 -4
  72. package/src/boxes/webm/ebml.ts +24 -4
  73. package/src/boxes/webm/get-ready-tracks.ts +4 -4
  74. package/src/boxes/webm/get-sample-from-block.ts +125 -0
  75. package/src/boxes/webm/get-track.ts +38 -31
  76. package/src/boxes/webm/make-header.ts +58 -51
  77. package/src/boxes/webm/parse-ebml.ts +170 -16
  78. package/src/boxes/webm/segments/all-segments.ts +379 -62
  79. package/src/boxes/webm/segments/track-entry.ts +3 -846
  80. package/src/boxes/webm/segments.ts +18 -410
  81. package/src/boxes/webm/traversal.ts +17 -17
  82. package/src/buffer-iterator.ts +5 -4
  83. package/src/get-audio-codec.ts +14 -16
  84. package/src/get-duration.ts +15 -16
  85. package/src/get-tracks.ts +2 -2
  86. package/src/get-video-codec.ts +13 -15
  87. package/src/parse-media.ts +6 -1
  88. package/src/parser-context.ts +1 -0
  89. package/src/parser-state.ts +2 -2
  90. package/src/test/create-matroska.test.ts +237 -23
  91. package/src/test/matroska.test.ts +283 -348
  92. package/src/test/mvhd.test.ts +1 -1
  93. package/src/test/parse-esds.test.ts +2 -2
  94. package/src/test/parse-stco.test.ts +2 -2
  95. package/src/test/parse-stsc.test.ts +2 -2
  96. package/src/test/parse-stsz.test.ts +2 -2
  97. package/src/test/parse-stts.test.ts +1 -1
  98. package/src/test/stsd.test.ts +4 -2
  99. package/src/test/tkhd.test.ts +1 -1
  100. package/src/traversal.ts +62 -85
  101. package/tsconfig.tsbuildinfo +1 -1
  102. package/src/boxes/webm/segments/duration.ts +0 -29
  103. package/src/boxes/webm/segments/info.ts +0 -34
  104. package/src/boxes/webm/segments/main.ts +0 -6
  105. package/src/boxes/webm/segments/muxing.ts +0 -18
  106. package/src/boxes/webm/segments/seek-head.ts +0 -34
  107. package/src/boxes/webm/segments/seek-position.ts +0 -18
  108. package/src/boxes/webm/segments/seek.ts +0 -55
  109. package/src/boxes/webm/segments/timestamp-scale.ts +0 -17
  110. package/src/boxes/webm/segments/tracks.ts +0 -32
  111. package/src/boxes/webm/segments/void.ts +0 -18
  112. package/src/boxes/webm/segments/writing.ts +0 -18
  113. package/src/combine-uint8array.ts +0 -13
@@ -16,15 +16,16 @@ import {
16
16
  } from '../../traversal';
17
17
  import {parseAv1PrivateData} from './av1-codec-private';
18
18
  import {getAudioDescription} from './description';
19
- import type {CodecSegment, TrackEntrySegment} from './segments/track-entry';
19
+ import type {CodecIdSegment, TrackEntry} from './segments/all-segments';
20
+ import {trackTypeToString} from './segments/track-entry';
20
21
 
21
- const getDescription = (track: TrackEntrySegment): undefined | Uint8Array => {
22
+ const getDescription = (track: TrackEntry): undefined | Uint8Array => {
22
23
  const codec = getCodecSegment(track);
23
24
  if (!codec) {
24
25
  return undefined;
25
26
  }
26
27
 
27
- if (codec.codec === 'V_MPEG4/ISO/AVC' || codec.codec === 'V_MPEGH/ISO/HEVC') {
28
+ if (codec.value === 'V_MPEG4/ISO/AVC' || codec.value === 'V_MPEGH/ISO/HEVC') {
28
29
  const priv = getPrivateData(track);
29
30
  if (priv) {
30
31
  return priv;
@@ -38,14 +39,14 @@ const getMatroskaVideoCodecString = ({
38
39
  track,
39
40
  codecSegment: codec,
40
41
  }: {
41
- track: TrackEntrySegment;
42
- codecSegment: CodecSegment;
42
+ track: TrackEntry;
43
+ codecSegment: CodecIdSegment;
43
44
  }): string | null => {
44
- if (codec.codec === 'V_VP8') {
45
+ if (codec.value === 'V_VP8') {
45
46
  return 'vp8';
46
47
  }
47
48
 
48
- if (codec.codec === 'V_VP9') {
49
+ if (codec.value === 'V_VP9') {
49
50
  const priv = getPrivateData(track);
50
51
  if (priv) {
51
52
  throw new Error(
@@ -56,7 +57,7 @@ const getMatroskaVideoCodecString = ({
56
57
  return 'vp09.00.10.08';
57
58
  }
58
59
 
59
- if (codec.codec === 'V_MPEG4/ISO/AVC') {
60
+ if (codec.value === 'V_MPEG4/ISO/AVC') {
60
61
  const priv = getPrivateData(track);
61
62
  if (priv) {
62
63
  return `avc1.${priv[1].toString(16).padStart(2, '0')}${priv[2].toString(16).padStart(2, '0')}${priv[3].toString(16).padStart(2, '0')}`;
@@ -65,7 +66,7 @@ const getMatroskaVideoCodecString = ({
65
66
  throw new Error('Could not find a CodecPrivate field in TrackEntry');
66
67
  }
67
68
 
68
- if (codec.codec === 'V_AV1') {
69
+ if (codec.value === 'V_AV1') {
69
70
  const priv = getPrivateData(track);
70
71
 
71
72
  if (!priv) {
@@ -75,31 +76,34 @@ const getMatroskaVideoCodecString = ({
75
76
  return parseAv1PrivateData(priv, null);
76
77
  }
77
78
 
78
- if (codec.codec === 'V_MPEGH/ISO/HEVC') {
79
+ if (codec.value === 'V_MPEGH/ISO/HEVC') {
79
80
  const priv = getPrivateData(track);
80
- const iterator = getArrayBufferIterator(priv as Uint8Array);
81
+ const iterator = getArrayBufferIterator(
82
+ priv as Uint8Array,
83
+ (priv as Uint8Array).length,
84
+ );
81
85
 
82
86
  return 'hvc1.' + getHvc1CodecString(iterator);
83
87
  }
84
88
 
85
- throw new Error(`Unknown codec: ${codec.codec}`);
89
+ throw new Error(`Unknown codec: ${codec.value}`);
86
90
  };
87
91
 
88
- const getMatroskaAudioCodecString = (track: TrackEntrySegment): string => {
92
+ const getMatroskaAudioCodecString = (track: TrackEntry): string => {
89
93
  const codec = getCodecSegment(track);
90
94
  if (!codec) {
91
95
  throw new Error('Expected codec segment');
92
96
  }
93
97
 
94
- if (codec.codec === 'A_OPUS') {
98
+ if (codec.value === 'A_OPUS') {
95
99
  return 'opus';
96
100
  }
97
101
 
98
- if (codec.codec === 'A_VORBIS') {
102
+ if (codec.value === 'A_VORBIS') {
99
103
  return 'vorbis';
100
104
  }
101
105
 
102
- if (codec.codec === 'A_PCM/INT/LIT') {
106
+ if (codec.value === 'A_PCM/INT/LIT') {
103
107
  // https://github.com/ietf-wg-cellar/matroska-specification/issues/142#issuecomment-330004950
104
108
  // Audio samples MUST be considered as signed values, except if the audio bit depth is 8 which MUST be interpreted as unsigned values.
105
109
 
@@ -115,10 +119,13 @@ const getMatroskaAudioCodecString = (track: TrackEntrySegment): string => {
115
119
  return 'pcm-s' + bitDepth;
116
120
  }
117
121
 
118
- if (codec.codec === 'A_AAC') {
122
+ if (codec.value === 'A_AAC') {
119
123
  const priv = getPrivateData(track);
120
124
 
121
- const iterator = getArrayBufferIterator(priv as Uint8Array);
125
+ const iterator = getArrayBufferIterator(
126
+ priv as Uint8Array,
127
+ (priv as Uint8Array).length,
128
+ );
122
129
 
123
130
  iterator.startReadingBits();
124
131
  /**
@@ -150,11 +157,11 @@ const getMatroskaAudioCodecString = (track: TrackEntrySegment): string => {
150
157
  return `mp4a.40.${profile.toString().padStart(2, '0')}`;
151
158
  }
152
159
 
153
- if (codec.codec === 'A_MPEG/L3') {
160
+ if (codec.value === 'A_MPEG/L3') {
154
161
  return 'mp3';
155
162
  }
156
163
 
157
- throw new Error(`Unknown codec: ${codec.codec}`);
164
+ throw new Error(`Unknown codec: ${codec.value}`);
158
165
  };
159
166
 
160
167
  export const getTrack = ({
@@ -162,7 +169,7 @@ export const getTrack = ({
162
169
  track,
163
170
  }: {
164
171
  timescale: number;
165
- track: TrackEntrySegment;
172
+ track: TrackEntry;
166
173
  }): VideoTrack | AudioTrack | null => {
167
174
  const trackType = getTrackTypeSegment(track);
168
175
 
@@ -172,7 +179,7 @@ export const getTrack = ({
172
179
 
173
180
  const trackId = getTrackId(track);
174
181
 
175
- if (trackType.trackType === 'video') {
182
+ if (trackTypeToString(trackType.value.value) === 'video') {
176
183
  const width = getWidthSegment(track);
177
184
 
178
185
  if (width === null) {
@@ -207,27 +214,27 @@ export const getTrack = ({
207
214
  trackId,
208
215
  codec: codecString,
209
216
  description: getDescription(track),
210
- height: displayHeight ? displayHeight.displayHeight : height.height,
211
- width: displayWidth ? displayWidth.displayWidth : width.width,
217
+ height: displayHeight ? displayHeight.value.value : height.value.value,
218
+ width: displayWidth ? displayWidth.value.value : width.value.value,
212
219
  sampleAspectRatio: {
213
220
  numerator: 1,
214
221
  denominator: 1,
215
222
  },
216
223
  timescale,
217
224
  samplePositions: [],
218
- codedHeight: height.height,
219
- codedWidth: width.width,
225
+ codedHeight: height.value.value,
226
+ codedWidth: width.value.value,
220
227
  displayAspectHeight: displayHeight
221
- ? displayHeight.displayHeight
222
- : height.height,
228
+ ? displayHeight.value.value
229
+ : height.value.value,
223
230
  displayAspectWidth: displayWidth
224
- ? displayWidth.displayWidth
225
- : width.width,
231
+ ? displayWidth.value.value
232
+ : width.value.value,
226
233
  rotation: 0,
227
234
  };
228
235
  }
229
236
 
230
- if (trackType.trackType === 'audio') {
237
+ if (trackTypeToString(trackType.value.value) === 'audio') {
231
238
  const sampleRate = getSampleRate(track);
232
239
  const numberOfChannels = getNumberOfChannels(track);
233
240
  if (sampleRate === null) {
@@ -1,15 +1,12 @@
1
1
  import {getVariableInt} from './ebml';
2
2
  import type {
3
- Ebml,
4
- EbmlWithChildren,
5
- EbmlWithHexString,
6
- EbmlWithString,
7
- EbmlWithUint8,
8
- EmblTypes,
9
- HeaderStructure,
3
+ FloatWithSize,
4
+ PossibleEbml,
5
+ PossibleEbmlOrUint8Array,
6
+ UintWithSize,
10
7
  matroskaElements,
11
8
  } from './segments/all-segments';
12
- import {getIdForName} from './segments/all-segments';
9
+ import {ebmlMap, getIdForName} from './segments/all-segments';
13
10
 
14
11
  export const webmPattern = new Uint8Array([0x1a, 0x45, 0xdf, 0xa3]);
15
12
 
@@ -26,24 +23,7 @@ const matroskaToHex = (
26
23
  return numbers;
27
24
  };
28
25
 
29
- type Numbers = '0' | '1' | '2' | '3' | '4' | '5' | '6';
30
-
31
- type ChildFields<Struct extends HeaderStructure> = {
32
- [key in keyof Struct &
33
- Numbers as Struct[key]['name']]: EmblTypes[Struct[key]['type']];
34
- };
35
-
36
- type SerializeValue<Struct extends Ebml> = Struct extends EbmlWithChildren
37
- ? ChildFields<Struct['children']>
38
- : Struct extends EbmlWithString
39
- ? string
40
- : Struct extends EbmlWithUint8
41
- ? number
42
- : Struct extends EbmlWithHexString
43
- ? string
44
- : undefined;
45
-
46
- function putUintDynamic(number: number) {
26
+ function putUintDynamic(number: number, minimumLength: number | null) {
47
27
  if (number < 0) {
48
28
  throw new Error(
49
29
  'This function is designed for non-negative integers only.',
@@ -51,7 +31,10 @@ function putUintDynamic(number: number) {
51
31
  }
52
32
 
53
33
  // Calculate the minimum number of bytes needed to store the integer
54
- const length = Math.ceil(Math.log2(number + 1) / 8);
34
+ const length = Math.max(
35
+ minimumLength ?? 0,
36
+ Math.ceil(Math.log2(number + 1) / 8),
37
+ );
55
38
  const bytes = new Uint8Array(length);
56
39
 
57
40
  for (let i = 0; i < length; i++) {
@@ -62,36 +45,42 @@ function putUintDynamic(number: number) {
62
45
  return bytes;
63
46
  }
64
47
 
65
- const makeFromHeaderStructure = <Struct extends Ebml>(
66
- struct: Struct,
67
- fields: SerializeValue<Struct>,
48
+ const makeFromHeaderStructure = (
49
+ fields: PossibleEbmlOrUint8Array,
68
50
  ): Uint8Array => {
51
+ if (fields instanceof Uint8Array) {
52
+ return fields;
53
+ }
54
+
69
55
  const arrays: Uint8Array[] = [];
70
56
 
57
+ const struct = ebmlMap[getIdForName(fields.type)];
58
+
59
+ if (struct.type === 'uint8array') {
60
+ return fields.value as Uint8Array;
61
+ }
62
+
71
63
  if (struct.type === 'children') {
72
- for (const item of struct.children) {
73
- arrays.push(
74
- makeMatroskaHeader(
75
- item,
76
- // @ts-expect-error
77
- fields[item.name],
78
- ),
79
- );
64
+ for (const item of fields.value as PossibleEbml[]) {
65
+ arrays.push(makeMatroskaBytes(item));
80
66
  }
81
67
 
82
68
  return combineUint8Arrays(arrays);
83
69
  }
84
70
 
85
71
  if (struct.type === 'string') {
86
- return new TextEncoder().encode(fields as string);
72
+ return new TextEncoder().encode(fields.value as string);
87
73
  }
88
74
 
89
75
  if (struct.type === 'uint') {
90
- return putUintDynamic(fields as number);
76
+ return putUintDynamic(
77
+ (fields.value as UintWithSize).value,
78
+ (fields.value as UintWithSize).byteLength,
79
+ );
91
80
  }
92
81
 
93
82
  if (struct.type === 'hex-string') {
94
- const hex = (fields as string).substring(2);
83
+ const hex = (fields.value as string).substring(2);
95
84
  const arr = new Uint8Array(hex.length / 2);
96
85
  for (let i = 0; i < hex.length; i += 2) {
97
86
  const byte = parseInt(hex.substring(i, i + 2), 16);
@@ -101,27 +90,45 @@ const makeFromHeaderStructure = <Struct extends Ebml>(
101
90
  return arr;
102
91
  }
103
92
 
104
- if (struct.type === 'void') {
105
- throw new Error('Serializing Void is not implemented');
93
+ if (struct.type === 'float') {
94
+ const value = fields.value as FloatWithSize;
95
+ if (value.size === '32') {
96
+ const dataView = new DataView(new ArrayBuffer(4));
97
+ dataView.setFloat32(0, value.value);
98
+ return new Uint8Array(dataView.buffer);
99
+ }
100
+
101
+ const dataView2 = new DataView(new ArrayBuffer(8));
102
+ dataView2.setFloat64(0, value.value);
103
+ return new Uint8Array(dataView2.buffer);
106
104
  }
107
105
 
108
106
  throw new Error('Unexpected type');
109
107
  };
110
108
 
111
- export const makeMatroskaHeader = <Struct extends Ebml>(
112
- struct: Struct,
113
- fields: SerializeValue<Struct>,
114
- ) => {
115
- const value = makeFromHeaderStructure(struct, fields);
109
+ export const makeMatroskaBytes = (fields: PossibleEbmlOrUint8Array) => {
110
+ if (fields instanceof Uint8Array) {
111
+ return fields;
112
+ }
113
+
114
+ const value = makeFromHeaderStructure(fields);
116
115
 
117
116
  return combineUint8Arrays([
118
- matroskaToHex(getIdForName(struct.name)),
119
- getVariableInt(value.length),
117
+ matroskaToHex(getIdForName(fields.type)),
118
+ getVariableInt(value.length, fields.minVintWidth),
120
119
  value,
121
120
  ]);
122
121
  };
123
122
 
124
- const combineUint8Arrays = (arrays: Uint8Array[]) => {
123
+ export const combineUint8Arrays = (arrays: Uint8Array[]) => {
124
+ if (arrays.length === 0) {
125
+ return new Uint8Array([]);
126
+ }
127
+
128
+ if (arrays.length === 1) {
129
+ return arrays[0];
130
+ }
131
+
125
132
  let totalLength = 0;
126
133
  for (const array of arrays) {
127
134
  totalLength += array.length;
@@ -1,12 +1,20 @@
1
- import type {BufferIterator} from '../../buffer-iterator';
1
+ import {registerTrack} from '../../add-new-matroska-tracks';
2
+ import {type BufferIterator} from '../../buffer-iterator';
3
+ import type {ParserContext} from '../../parser-context';
4
+ import type {VideoSample} from '../../webcodec-sample-types';
5
+ import {getSampleFromBlock} from './get-sample-from-block';
6
+ import {getTrack} from './get-track';
2
7
  import type {PossibleEbml} from './segments/all-segments';
3
- import {ebmlMap, type Ebml, type EbmlParsed} from './segments/all-segments';
8
+ import {ebmlMap} from './segments/all-segments';
4
9
 
5
- type Prettify<T> = {
10
+ export type Prettify<T> = {
6
11
  [K in keyof T]: T[K];
7
12
  } & {};
8
13
 
9
- export const parseEbml = (iterator: BufferIterator): Prettify<PossibleEbml> => {
14
+ export const parseEbml = async (
15
+ iterator: BufferIterator,
16
+ parserContext: ParserContext,
17
+ ): Promise<Prettify<PossibleEbml>> => {
10
18
  const hex = iterator.getMatroskaSegmentId();
11
19
  if (hex === null) {
12
20
  throw new Error(
@@ -21,7 +29,10 @@ export const parseEbml = (iterator: BufferIterator): Prettify<PossibleEbml> => {
21
29
  );
22
30
  }
23
31
 
32
+ const off = iterator.counter.getOffset();
24
33
  const size = iterator.getVint();
34
+ const minVintWidth = iterator.counter.getOffset() - off;
35
+
25
36
  if (size === null) {
26
37
  throw new Error(
27
38
  'Not enough bytes left to parse EBML - this should not happen',
@@ -29,9 +40,17 @@ export const parseEbml = (iterator: BufferIterator): Prettify<PossibleEbml> => {
29
40
  }
30
41
 
31
42
  if (hasInMap.type === 'uint') {
43
+ const beforeUintOffset = iterator.counter.getOffset();
32
44
  const value = iterator.getUint(size);
33
45
 
34
- return {type: hasInMap.name, value, hex};
46
+ return {
47
+ type: hasInMap.name,
48
+ value: {
49
+ value,
50
+ byteLength: iterator.counter.getOffset() - beforeUintOffset,
51
+ },
52
+ minVintWidth,
53
+ };
35
54
  }
36
55
 
37
56
  if (hasInMap.type === 'string') {
@@ -40,7 +59,7 @@ export const parseEbml = (iterator: BufferIterator): Prettify<PossibleEbml> => {
40
59
  return {
41
60
  type: hasInMap.name,
42
61
  value,
43
- hex,
62
+ minVintWidth,
44
63
  };
45
64
  }
46
65
 
@@ -49,29 +68,49 @@ export const parseEbml = (iterator: BufferIterator): Prettify<PossibleEbml> => {
49
68
 
50
69
  return {
51
70
  type: hasInMap.name,
52
- value,
53
- hex,
71
+ value: {
72
+ value,
73
+ size: size === 4 ? '32' : '64',
74
+ },
75
+ minVintWidth,
54
76
  };
55
77
  }
56
78
 
57
- if (hasInMap.type === 'void') {
58
- iterator.discard(size);
79
+ if (hasInMap.type === 'hex-string') {
80
+ return {
81
+ type: hasInMap.name,
82
+ value:
83
+ '0x' +
84
+ [...iterator.getSlice(size)]
85
+ .map((b) => b.toString(16).padStart(2, '0'))
86
+ .join(''),
87
+ minVintWidth,
88
+ };
89
+ }
59
90
 
91
+ if (hasInMap.type === 'uint8array') {
60
92
  return {
61
93
  type: hasInMap.name,
62
- value: undefined,
63
- hex,
94
+ value: iterator.getSlice(size),
95
+ minVintWidth,
64
96
  };
65
97
  }
66
98
 
67
99
  if (hasInMap.type === 'children') {
68
- const children: EbmlParsed<Ebml>[] = [];
100
+ const children: PossibleEbml[] = [];
69
101
  const startOffset = iterator.counter.getOffset();
70
102
 
71
103
  // eslint-disable-next-line no-constant-condition
72
104
  while (true) {
73
- const value = parseEbml(iterator);
74
- children.push(value);
105
+ const offset = iterator.counter.getOffset();
106
+ const value = await parseEbml(iterator, parserContext);
107
+ const remapped = await postprocessEbml({
108
+ offset,
109
+ ebml: value,
110
+ parserContext,
111
+ });
112
+ children.push(remapped);
113
+
75
114
  const offsetNow = iterator.counter.getOffset();
76
115
 
77
116
  if (offsetNow - startOffset > size) {
@@ -85,9 +124,124 @@ export const parseEbml = (iterator: BufferIterator): Prettify<PossibleEbml> => {
85
124
  }
86
125
  }
87
126
 
88
- return {type: hasInMap.name, value: children as EbmlParsed<Ebml>[], hex};
127
+ return {type: hasInMap.name, value: children, minVintWidth};
89
128
  }
90
129
 
91
130
  // @ts-expect-error
92
131
  throw new Error(`Unknown segment type ${hasInMap.type}`);
93
132
  };
133
+
134
+ export const postprocessEbml = async ({
135
+ offset,
136
+ ebml,
137
+ parserContext,
138
+ }: {
139
+ offset: number;
140
+ ebml: Prettify<PossibleEbml>;
141
+ parserContext: ParserContext;
142
+ }): Promise<Prettify<PossibleEbml>> => {
143
+ if (ebml.type === 'TimestampScale') {
144
+ parserContext.parserState.setTimescale(ebml.value.value);
145
+ }
146
+
147
+ if (ebml.type === 'TrackEntry') {
148
+ parserContext.parserState.onTrackEntrySegment(ebml);
149
+
150
+ const track = getTrack({
151
+ track: ebml,
152
+ timescale: parserContext.parserState.getTimescale(),
153
+ });
154
+
155
+ if (track) {
156
+ await registerTrack({
157
+ state: parserContext.parserState,
158
+ options: parserContext,
159
+ track,
160
+ });
161
+ }
162
+ }
163
+
164
+ if (ebml.type === 'Timestamp') {
165
+ parserContext.parserState.setTimestampOffset(offset, ebml.value.value);
166
+ }
167
+
168
+ if (ebml.type === 'Block' || ebml.type === 'SimpleBlock') {
169
+ const sample = getSampleFromBlock(ebml, parserContext, offset);
170
+
171
+ if (sample.type === 'video-sample' && parserContext.nullifySamples) {
172
+ await parserContext.parserState.onVideoSample(
173
+ sample.videoSample.trackId,
174
+ sample.videoSample,
175
+ );
176
+ return {
177
+ type: 'Block',
178
+ value: new Uint8Array([]),
179
+ minVintWidth: ebml.minVintWidth,
180
+ };
181
+ }
182
+
183
+ if (sample.type === 'audio-sample' && parserContext.nullifySamples) {
184
+ await parserContext.parserState.onAudioSample(
185
+ sample.audioSample.trackId,
186
+ sample.audioSample,
187
+ );
188
+ return {
189
+ type: 'Block',
190
+ value: new Uint8Array([]),
191
+ minVintWidth: ebml.minVintWidth,
192
+ };
193
+ }
194
+
195
+ if (sample.type === 'no-sample' && parserContext.nullifySamples) {
196
+ return {
197
+ type: 'Block',
198
+ value: new Uint8Array([]),
199
+ minVintWidth: ebml.minVintWidth,
200
+ };
201
+ }
202
+ }
203
+
204
+ if (ebml.type === 'BlockGroup') {
205
+ // Blocks don't have information about keyframes.
206
+ // https://ffmpeg.org/pipermail/ffmpeg-devel/2015-June/173825.html
207
+ // "For Blocks, keyframes is
208
+ // inferred by the absence of ReferenceBlock element (as done by matroskadec).""
209
+
210
+ const block = ebml.value.find(
211
+ (c) => c.type === 'SimpleBlock' || c.type === 'Block',
212
+ );
213
+ if (!block || (block.type !== 'SimpleBlock' && block.type !== 'Block')) {
214
+ throw new Error('Expected block segment');
215
+ }
216
+
217
+ const hasReferenceBlock = ebml.value.find(
218
+ (c) => c.type === 'ReferenceBlock',
219
+ );
220
+
221
+ const sample =
222
+ block.value.length === 0
223
+ ? null
224
+ : getSampleFromBlock(block, parserContext, offset);
225
+
226
+ if (sample && sample.type === 'partial-video-sample') {
227
+ const completeFrame: VideoSample = {
228
+ ...sample.partialVideoSample,
229
+ type: hasReferenceBlock ? 'delta' : 'key',
230
+ };
231
+ await parserContext.parserState.onVideoSample(
232
+ sample.partialVideoSample.trackId,
233
+ completeFrame,
234
+ );
235
+ }
236
+
237
+ if (parserContext.nullifySamples) {
238
+ return {
239
+ type: 'BlockGroup',
240
+ value: [],
241
+ minVintWidth: ebml.minVintWidth,
242
+ };
243
+ }
244
+ }
245
+
246
+ return ebml;
247
+ };