@remotion/media-parser 4.0.231 → 4.0.233

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 (115) hide show
  1. package/dist/add-avc-profile-to-track.d.ts +3 -0
  2. package/dist/add-avc-profile-to-track.js +35 -0
  3. package/dist/add-new-matroska-tracks.d.ts +6 -1
  4. package/dist/add-new-matroska-tracks.js +16 -1
  5. package/dist/boxes/avc/parse-avc.d.ts +18 -0
  6. package/dist/boxes/avc/parse-avc.js +96 -0
  7. package/dist/boxes/iso-base-media/esds/decoder-specific-config.d.ts +1 -2
  8. package/dist/boxes/iso-base-media/esds/decoder-specific-config.js +1 -5
  9. package/dist/boxes/iso-base-media/esds/esds-descriptors.d.ts +2 -4
  10. package/dist/boxes/iso-base-media/esds/esds-descriptors.js +3 -4
  11. package/dist/boxes/iso-base-media/esds/esds.d.ts +1 -3
  12. package/dist/boxes/iso-base-media/esds/esds.js +2 -2
  13. package/dist/boxes/iso-base-media/get-sample-positions-from-track.js +7 -1
  14. package/dist/boxes/iso-base-media/make-track.js +3 -3
  15. package/dist/boxes/iso-base-media/mdat/mdat.d.ts +2 -2
  16. package/dist/boxes/iso-base-media/mdat/mdat.js +5 -2
  17. package/dist/boxes/iso-base-media/moov/moov.js +2 -2
  18. package/dist/boxes/iso-base-media/process-box.d.ts +5 -5
  19. package/dist/boxes/iso-base-media/process-box.js +38 -38
  20. package/dist/boxes/iso-base-media/stsd/mebx.js +2 -2
  21. package/dist/boxes/iso-base-media/stsd/samples.d.ts +2 -2
  22. package/dist/boxes/iso-base-media/stsd/samples.js +15 -14
  23. package/dist/boxes/iso-base-media/trak/trak.js +2 -2
  24. package/dist/boxes/iso-base-media/traversal.d.ts +1 -1
  25. package/dist/boxes/riff/expect-riff-box.d.ts +16 -0
  26. package/dist/boxes/riff/expect-riff-box.js +49 -0
  27. package/dist/boxes/riff/get-tracks-from-avi.d.ts +21 -0
  28. package/dist/boxes/riff/get-tracks-from-avi.js +108 -0
  29. package/dist/boxes/riff/is-movi.d.ts +2 -0
  30. package/dist/boxes/riff/is-movi.js +12 -0
  31. package/dist/boxes/riff/parse-avih.d.ts +6 -0
  32. package/dist/boxes/riff/parse-avih.js +32 -0
  33. package/dist/boxes/riff/parse-box.d.ts +13 -0
  34. package/dist/boxes/riff/parse-box.js +113 -0
  35. package/dist/boxes/riff/parse-fmt-box.d.ts +7 -0
  36. package/dist/boxes/riff/parse-fmt-box.js +33 -0
  37. package/dist/boxes/riff/parse-list-box.d.ts +8 -0
  38. package/dist/boxes/riff/parse-list-box.js +30 -0
  39. package/dist/boxes/riff/parse-movi.d.ts +17 -0
  40. package/dist/boxes/riff/parse-movi.js +122 -0
  41. package/dist/boxes/riff/parse-riff-box.d.ts +10 -0
  42. package/dist/boxes/riff/parse-riff-box.js +33 -0
  43. package/dist/boxes/riff/parse-strf.d.ts +7 -0
  44. package/dist/boxes/riff/parse-strf.js +67 -0
  45. package/dist/boxes/riff/parse-strh.d.ts +6 -0
  46. package/dist/boxes/riff/parse-strh.js +46 -0
  47. package/dist/boxes/riff/riff-box.d.ts +81 -0
  48. package/dist/boxes/riff/riff-box.js +2 -0
  49. package/dist/boxes/riff/strf.d.ts +7 -0
  50. package/dist/boxes/riff/strf.js +67 -0
  51. package/dist/boxes/riff/timescale.d.ts +1 -0
  52. package/dist/boxes/riff/timescale.js +4 -0
  53. package/dist/boxes/riff/traversal.d.ts +8 -0
  54. package/dist/boxes/riff/traversal.js +36 -0
  55. package/dist/boxes/webm/parse-ebml.js +2 -2
  56. package/dist/boxes/webm/parse-webm-header.d.ts +2 -2
  57. package/dist/boxes/webm/parse-webm-header.js +7 -7
  58. package/dist/boxes/webm/traversal.d.ts +2 -2
  59. package/dist/buffer-iterator.d.ts +6 -1
  60. package/dist/buffer-iterator.js +24 -5
  61. package/dist/create/event-emitter.d.ts +31 -0
  62. package/dist/create/event-emitter.js +25 -0
  63. package/dist/create/iso-base-media/create-iso-base-media.d.ts +1 -1
  64. package/dist/create/iso-base-media/create-iso-base-media.js +3 -5
  65. package/dist/create/matroska/cluster.js +1 -1
  66. package/dist/create/matroska/create-matroska-media.d.ts +1 -1
  67. package/dist/create/matroska/create-matroska-media.js +7 -14
  68. package/dist/create/media-fn.d.ts +2 -1
  69. package/dist/create/mp3/create-mp3.d.ts +2 -0
  70. package/dist/create/mp3/create-mp3.js +49 -0
  71. package/dist/create/progress-tracker.d.ts +7 -0
  72. package/dist/create/progress-tracker.js +43 -0
  73. package/dist/create/wav/create-wav.d.ts +2 -0
  74. package/dist/create/wav/create-wav.js +110 -0
  75. package/dist/create/with-resolvers.d.ts +10 -0
  76. package/dist/create/with-resolvers.js +28 -0
  77. package/dist/emit-available-info.d.ts +2 -2
  78. package/dist/emit-available-info.js +17 -6
  79. package/dist/esm/from-node.mjs +2 -1
  80. package/dist/esm/index.mjs +1828 -605
  81. package/dist/get-audio-codec.d.ts +4 -3
  82. package/dist/get-audio-codec.js +17 -3
  83. package/dist/get-container.d.ts +3 -3
  84. package/dist/get-container.js +9 -7
  85. package/dist/get-dimensions.d.ts +3 -3
  86. package/dist/get-duration.d.ts +3 -3
  87. package/dist/get-duration.js +32 -14
  88. package/dist/get-fps.d.ts +3 -3
  89. package/dist/get-fps.js +31 -4
  90. package/dist/get-is-hdr.d.ts +4 -0
  91. package/dist/get-is-hdr.js +18 -0
  92. package/dist/get-sample-positions-from-lpcm.d.ts +3 -0
  93. package/dist/get-sample-positions-from-lpcm.js +46 -0
  94. package/dist/get-tracks.d.ts +7 -10
  95. package/dist/get-tracks.js +55 -27
  96. package/dist/get-video-codec.d.ts +5 -4
  97. package/dist/get-video-codec.js +47 -13
  98. package/dist/has-all-info.d.ts +2 -2
  99. package/dist/has-all-info.js +10 -5
  100. package/dist/index.d.ts +23 -3
  101. package/dist/index.js +9 -0
  102. package/dist/options.d.ts +16 -9
  103. package/dist/parse-media.js +3 -1
  104. package/dist/parse-result.d.ts +20 -6
  105. package/dist/parse-video.d.ts +2 -2
  106. package/dist/parse-video.js +5 -16
  107. package/dist/parser-context.d.ts +1 -0
  108. package/dist/parser-state.d.ts +11 -0
  109. package/dist/parser-state.js +30 -0
  110. package/dist/readers/from-node.js +2 -1
  111. package/dist/register-track.d.ts +13 -0
  112. package/dist/register-track.js +25 -0
  113. package/dist/version.d.ts +1 -1
  114. package/dist/version.js +1 -1
  115. package/package.json +3 -3
@@ -0,0 +1,8 @@
1
+ import type { RiffStructure } from '../../parse-result';
2
+ import type { AvihBox, ListBox, RiffBox, StrfBoxAudio, StrfBoxVideo, StrhBox } from './riff-box';
3
+ export declare const isRiffAvi: (structure: RiffStructure) => boolean;
4
+ export declare const getHdlrBox: (structure: RiffStructure) => ListBox | null;
5
+ export declare const getAvihBox: (structure: RiffStructure) => AvihBox | null;
6
+ export declare const getStrlBoxes: (structure: RiffStructure) => ListBox[];
7
+ export declare const getStrhBox: (strlBoxChildren: RiffBox[]) => StrhBox | null;
8
+ export declare const getStrfBox: (strlBoxChildren: RiffBox[]) => StrfBoxAudio | StrfBoxVideo | null;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getStrfBox = exports.getStrhBox = exports.getStrlBoxes = exports.getAvihBox = exports.getHdlrBox = exports.isRiffAvi = void 0;
4
+ const isRiffAvi = (structure) => {
5
+ return structure.boxes.some((box) => box.type === 'riff-header' && box.fileType === 'AVI');
6
+ };
7
+ exports.isRiffAvi = isRiffAvi;
8
+ const getHdlrBox = (structure) => {
9
+ return structure.boxes.find((box) => box.type === 'list-box' && box.listType === 'hdrl');
10
+ };
11
+ exports.getHdlrBox = getHdlrBox;
12
+ const getAvihBox = (structure) => {
13
+ const hdlrBox = (0, exports.getHdlrBox)(structure);
14
+ if (!hdlrBox) {
15
+ return null;
16
+ }
17
+ return hdlrBox.children.find((box) => box.type === 'avih-box');
18
+ };
19
+ exports.getAvihBox = getAvihBox;
20
+ const getStrlBoxes = (structure) => {
21
+ const hdlrBox = (0, exports.getHdlrBox)(structure);
22
+ if (!hdlrBox) {
23
+ return [];
24
+ }
25
+ return hdlrBox.children.filter((box) => box.type === 'list-box' && box.listType === 'strl');
26
+ };
27
+ exports.getStrlBoxes = getStrlBoxes;
28
+ const getStrhBox = (strlBoxChildren) => {
29
+ return strlBoxChildren.find((box) => box.type === 'strh-box');
30
+ };
31
+ exports.getStrhBox = getStrhBox;
32
+ const getStrfBox = (strlBoxChildren) => {
33
+ var _a;
34
+ return ((_a = strlBoxChildren.find((box) => box.type === 'strf-box-audio' || box.type === 'strf-box-video')) !== null && _a !== void 0 ? _a : null);
35
+ };
36
+ exports.getStrfBox = getStrfBox;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.postprocessEbml = exports.parseEbml = void 0;
4
- const add_new_matroska_tracks_1 = require("../../add-new-matroska-tracks");
4
+ const register_track_1 = require("../../register-track");
5
5
  const get_sample_from_block_1 = require("./get-sample-from-block");
6
6
  const make_track_1 = require("./make-track");
7
7
  const all_segments_1 = require("./segments/all-segments");
@@ -115,7 +115,7 @@ const postprocessEbml = async ({ offset, ebml, parserContext, }) => {
115
115
  timescale: parserContext.parserState.getTimescale(),
116
116
  });
117
117
  if (track) {
118
- await (0, add_new_matroska_tracks_1.registerTrack)({
118
+ await (0, register_track_1.registerTrack)({
119
119
  state: parserContext.parserState,
120
120
  options: parserContext,
121
121
  track,
@@ -1,4 +1,4 @@
1
1
  import type { BufferIterator } from '../../buffer-iterator';
2
- import type { ParseResult } from '../../parse-result';
2
+ import type { MatroskaStructure, ParseResult } from '../../parse-result';
3
3
  import type { ParserContext } from '../../parser-context';
4
- export declare const parseWebm: (counter: BufferIterator, parserContext: ParserContext) => Promise<ParseResult>;
4
+ export declare const parseWebm: (counter: BufferIterator, parserContext: ParserContext) => Promise<ParseResult<MatroskaStructure>>;
@@ -2,33 +2,33 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseWebm = void 0;
4
4
  const parse_children_1 = require("./segments/parse-children");
5
- const continueAfterMatroskaResult = (result, children) => {
5
+ const continueAfterMatroskaResult = (result, structure) => {
6
6
  if (result.status === 'done') {
7
7
  return {
8
8
  status: 'done',
9
- segments: children,
9
+ segments: structure,
10
10
  };
11
11
  }
12
12
  return {
13
13
  status: 'incomplete',
14
- segments: children,
14
+ segments: structure,
15
15
  continueParsing: async () => {
16
16
  const newResult = await result.continueParsing();
17
- return continueAfterMatroskaResult(newResult, children);
17
+ return continueAfterMatroskaResult(newResult, structure);
18
18
  },
19
19
  skipTo: null,
20
20
  };
21
21
  };
22
22
  // Parsing according to https://darkcoding.net/software/reading-mediarecorders-webm-opus-output/
23
23
  const parseWebm = async (counter, parserContext) => {
24
- const children = [];
24
+ const structure = { type: 'matroska', boxes: [] };
25
25
  const results = await (0, parse_children_1.expectChildren)({
26
26
  iterator: counter,
27
27
  length: Infinity,
28
- children,
28
+ children: structure.boxes,
29
29
  parserContext,
30
30
  startOffset: counter.counter.getOffset(),
31
31
  });
32
- return continueAfterMatroskaResult(results, children);
32
+ return continueAfterMatroskaResult(results, structure);
33
33
  };
34
34
  exports.parseWebm = parseWebm;
@@ -1,6 +1,6 @@
1
- import type { AnySegment } from '../../parse-result';
1
+ import type { MatroskaSegment } from './segments';
2
2
  import type { AudioSegment, ClusterSegment, CodecIdSegment, ColourSegment, DisplayHeightSegment, DisplayWidthSegment, HeightSegment, MainSegment, MatrixCoefficientsSegment, PrimariesSegment, RangeSegment, TimestampScaleSegment, TrackEntry, TrackTypeSegment, TransferCharacteristicsSegment, VideoSegment, WidthSegment } from './segments/all-segments';
3
- export declare const getMainSegment: (segments: AnySegment[]) => MainSegment | null;
3
+ export declare const getMainSegment: (segments: MatroskaSegment[]) => MainSegment | null;
4
4
  export declare const getTrackNumber: (track: TrackEntry) => import("./segments/all-segments").UintWithSize | null;
5
5
  export declare const getTrackCodec: (track: TrackEntry) => CodecIdSegment | null;
6
6
  export declare const getTrackTimestampScale: (track: TrackEntry) => import("./segments/all-segments").FloatWithSize | null;
@@ -37,9 +37,10 @@ export declare const getArrayBufferIterator: (initialData: Uint8Array, maxBytes:
37
37
  getEBML: () => number;
38
38
  getInt8: () => number;
39
39
  getUint16: () => number;
40
+ getUint16Le: () => number;
40
41
  getUint24: () => number;
41
42
  getInt16: () => number;
42
- getUint32: (littleEndian?: boolean) => number;
43
+ getUint32: () => number;
43
44
  getUint64: (littleEndian?: boolean) => bigint;
44
45
  getFixedPointUnsigned1616Number: () => number;
45
46
  getFixedPointSigned1616Number: () => number;
@@ -56,5 +57,9 @@ export declare const getArrayBufferIterator: (initialData: Uint8Array, maxBytes:
56
57
  isMp3: () => boolean;
57
58
  disallowDiscard: () => void;
58
59
  allowDiscard: () => void;
60
+ startBox: (size: number) => {
61
+ discardRest: () => void;
62
+ expectNoMoreBytes: () => void;
63
+ };
59
64
  };
60
65
  export type BufferIterator = ReturnType<typeof getArrayBufferIterator>;
@@ -89,6 +89,9 @@ const getArrayBufferIterator = (initialData, maxBytes) => {
89
89
  const allowDiscard = () => {
90
90
  discardAllowed = true;
91
91
  };
92
+ const discard = (length) => {
93
+ counter.increment(length);
94
+ };
92
95
  const getUint8 = () => {
93
96
  const val = view.getUint8(counter.getDiscardedOffset());
94
97
  counter.increment(1);
@@ -142,8 +145,8 @@ const getArrayBufferIterator = (initialData, maxBytes) => {
142
145
  }
143
146
  return lastInt;
144
147
  };
145
- const getUint32 = (littleEndian = false) => {
146
- const val = view.getUint32(counter.getDiscardedOffset(), littleEndian);
148
+ const getUint32 = () => {
149
+ const val = view.getUint32(counter.getDiscardedOffset());
147
150
  counter.increment(4);
148
151
  return val;
149
152
  };
@@ -152,6 +155,18 @@ const getArrayBufferIterator = (initialData, maxBytes) => {
152
155
  counter.increment(8);
153
156
  return val;
154
157
  };
158
+ const startBox = (size) => {
159
+ const startOffset = counter.getOffset();
160
+ return {
161
+ discardRest: () => discard(size - (counter.getOffset() - startOffset)),
162
+ expectNoMoreBytes: () => {
163
+ const remaining = size - (counter.getOffset() - startOffset);
164
+ if (remaining !== 0) {
165
+ throw new Error('expected 0 bytes, got ' + remaining);
166
+ }
167
+ },
168
+ };
169
+ };
155
170
  const getUint32Le = () => {
156
171
  const val = view.getUint32(counter.getDiscardedOffset(), true);
157
172
  counter.increment(4);
@@ -300,9 +315,7 @@ const getArrayBufferIterator = (initialData, maxBytes) => {
300
315
  leb128,
301
316
  removeBytesRead,
302
317
  isWebm,
303
- discard: (length) => {
304
- counter.increment(length);
305
- },
318
+ discard,
306
319
  getEightByteNumber,
307
320
  getFourByteNumber,
308
321
  getSlice,
@@ -414,6 +427,11 @@ const getArrayBufferIterator = (initialData, maxBytes) => {
414
427
  counter.increment(2);
415
428
  return val;
416
429
  },
430
+ getUint16Le: () => {
431
+ const val = view.getUint16(counter.getDiscardedOffset(), true);
432
+ counter.increment(2);
433
+ return val;
434
+ },
417
435
  getUint24: () => {
418
436
  const val1 = view.getUint8(counter.getDiscardedOffset());
419
437
  const val2 = view.getUint8(counter.getDiscardedOffset() + 1);
@@ -472,6 +490,7 @@ const getArrayBufferIterator = (initialData, maxBytes) => {
472
490
  isMp3,
473
491
  disallowDiscard,
474
492
  allowDiscard,
493
+ startBox,
475
494
  };
476
495
  };
477
496
  exports.getArrayBufferIterator = getArrayBufferIterator;
@@ -0,0 +1,31 @@
1
+ type Input = {
2
+ timestamp: number;
3
+ keyFrame: boolean;
4
+ };
5
+ type Output = {
6
+ timestamp: number;
7
+ };
8
+ type Processed = {};
9
+ type Progress = {
10
+ smallestProgress: number;
11
+ };
12
+ type IoEventMap = {
13
+ input: Input;
14
+ output: Output;
15
+ processed: Processed;
16
+ progress: Progress;
17
+ };
18
+ export type IoEventTypes = keyof IoEventMap;
19
+ export type CallbackListener<T extends IoEventTypes> = (data: {
20
+ detail: IoEventMap[T];
21
+ }) => void;
22
+ type IoListeners = {
23
+ [EventType in IoEventTypes]: CallbackListener<EventType>[];
24
+ };
25
+ export declare class IoEventEmitter {
26
+ listeners: IoListeners;
27
+ addEventListener<Q extends IoEventTypes>(name: Q, callback: CallbackListener<Q>): void;
28
+ removeEventListener<Q extends IoEventTypes>(name: Q, callback: CallbackListener<Q>): void;
29
+ dispatchEvent<T extends IoEventTypes>(dispatchName: T, context: IoEventMap[T]): void;
30
+ }
31
+ export {};
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IoEventEmitter = void 0;
4
+ class IoEventEmitter {
5
+ constructor() {
6
+ this.listeners = {
7
+ input: [],
8
+ output: [],
9
+ processed: [],
10
+ progress: [],
11
+ };
12
+ }
13
+ addEventListener(name, callback) {
14
+ this.listeners[name].push(callback);
15
+ }
16
+ removeEventListener(name, callback) {
17
+ this.listeners[name] = this.listeners[name].filter((l) => l !== callback);
18
+ }
19
+ dispatchEvent(dispatchName, context) {
20
+ this.listeners[dispatchName].forEach((callback) => {
21
+ callback({ detail: context });
22
+ });
23
+ }
24
+ }
25
+ exports.IoEventEmitter = IoEventEmitter;
@@ -1,2 +1,2 @@
1
1
  import type { MediaFn, MediaFnGeneratorInput } from '../media-fn';
2
- export declare const createIsoBaseMedia: ({ writer, onBytesProgress, onMillisecondsProgress, logLevel, filename, }: MediaFnGeneratorInput) => Promise<MediaFn>;
2
+ export declare const createIsoBaseMedia: ({ writer, onBytesProgress, onMillisecondsProgress, logLevel, filename, progressTracker, }: MediaFnGeneratorInput) => Promise<MediaFn>;
@@ -6,7 +6,7 @@ const log_1 = require("../../log");
6
6
  const create_ftyp_1 = require("./create-ftyp");
7
7
  const mp4_header_1 = require("./mp4-header");
8
8
  const primitives_1 = require("./primitives");
9
- const createIsoBaseMedia = async ({ writer, onBytesProgress, onMillisecondsProgress, logLevel, filename, }) => {
9
+ const createIsoBaseMedia = async ({ writer, onBytesProgress, onMillisecondsProgress, logLevel, filename, progressTracker, }) => {
10
10
  const header = (0, create_ftyp_1.createIsoBaseMediaFtyp)({
11
11
  compatibleBrands: ['isom', 'iso2', 'avc1', 'mp42'],
12
12
  majorBrand: 'isom',
@@ -70,6 +70,7 @@ const createIsoBaseMedia = async ({ writer, onBytesProgress, onMillisecondsProgr
70
70
  await w.write(chunk.data);
71
71
  mdatSize += chunk.data.length;
72
72
  onBytesProgress(w.getWrittenByteCount());
73
+ progressTracker.updateTrackProgress(trackNumber, chunk.timestamp);
73
74
  if (codecPrivate) {
74
75
  addCodecPrivateToTrack({ trackNumber, codecPrivate });
75
76
  }
@@ -109,6 +110,7 @@ const createIsoBaseMedia = async ({ writer, onBytesProgress, onMillisecondsProgr
109
110
  const addTrack = (track) => {
110
111
  const trackNumber = currentTracks.length + 1;
111
112
  currentTracks.push({ ...track, trackNumber });
113
+ progressTracker.registerTrack(trackNumber);
112
114
  return Promise.resolve({ trackNumber });
113
115
  };
114
116
  const waitForFinishPromises = [];
@@ -158,10 +160,6 @@ const createIsoBaseMedia = async ({ writer, onBytesProgress, onMillisecondsProgr
158
160
  log_1.Log.verbose(logLevel, 'All write operations done. Waiting for finish...');
159
161
  await w.waitForFinish();
160
162
  },
161
- updateDuration: (duration) => {
162
- operationProm.current = operationProm.current.then(() => updateDuration(duration));
163
- return operationProm.current;
164
- },
165
163
  };
166
164
  };
167
165
  exports.createIsoBaseMedia = createIsoBaseMedia;
@@ -15,7 +15,7 @@ const canFitInCluster = ({ clusterStartTimestamp, chunk, timescale, }) => {
15
15
  const timecodeRelativeToCluster = (0, exports.timestampToClusterTimestamp)(chunk.timestamp, timescale) -
16
16
  (0, exports.timestampToClusterTimestamp)(clusterStartTimestamp, timescale);
17
17
  if (timecodeRelativeToCluster < 0) {
18
- throw new Error(`timecodeRelativeToCluster is negative`);
18
+ throw new Error(`timecodeRelativeToCluster is negative, tried to add ${chunk.timestamp} to ${clusterStartTimestamp}`);
19
19
  }
20
20
  return timecodeRelativeToCluster <= maxClusterTimestamp;
21
21
  };
@@ -1,2 +1,2 @@
1
1
  import type { MediaFn, MediaFnGeneratorInput } from '../media-fn';
2
- export declare const createMatroskaMedia: ({ writer, onBytesProgress, onMillisecondsProgress, filename, logLevel, }: MediaFnGeneratorInput) => Promise<MediaFn>;
2
+ export declare const createMatroskaMedia: ({ writer, onBytesProgress, onMillisecondsProgress, filename, logLevel, progressTracker, }: MediaFnGeneratorInput) => Promise<MediaFn>;
@@ -13,7 +13,7 @@ const matroska_seek_1 = require("./matroska-seek");
13
13
  const matroska_segment_1 = require("./matroska-segment");
14
14
  const matroska_trackentry_1 = require("./matroska-trackentry");
15
15
  const timescale = 1000000;
16
- const createMatroskaMedia = async ({ writer, onBytesProgress, onMillisecondsProgress, filename, logLevel, }) => {
16
+ const createMatroskaMedia = async ({ writer, onBytesProgress, onMillisecondsProgress, filename, logLevel, progressTracker, }) => {
17
17
  var _a, _b, _c, _d, _e, _f, _g;
18
18
  const header = (0, matroska_header_1.makeMatroskaHeader)();
19
19
  const w = await writer.createContent({ filename, mimeType: 'video/webm' });
@@ -79,17 +79,11 @@ const createMatroskaMedia = async ({ writer, onBytesProgress, onMillisecondsProg
79
79
  hexString: all_segments_1.matroskaElements.Cluster,
80
80
  byte: clusterOffset - seekHeadOffset,
81
81
  });
82
- const trackNumberProgresses = {};
83
- const getClusterOrMakeNew = async ({ chunk, isVideo, trackNumber, }) => {
84
- const trackProgressValues = Object.values(trackNumberProgresses);
85
- const smallestProgress = trackProgressValues.length === 0 ? 0 : Math.min(...trackProgressValues);
82
+ const getClusterOrMakeNew = async ({ chunk, isVideo, }) => {
86
83
  // In Safari, samples can arrive out of order, e.g public/bigbuckbunny.mp4
87
84
  // Therefore, only updating track number progress if it is a keyframe
88
85
  // to allow for timestamps to be lower than the previous one
89
- // Also doing this AFTER smallestProgress is calculated
90
- if (chunk.type === 'key') {
91
- trackNumberProgresses[trackNumber] = chunk.timestamp;
92
- }
86
+ const smallestProgress = progressTracker.getSmallestProgress();
93
87
  if (!currentCluster.shouldMakeNewCluster({
94
88
  newT: smallestProgress,
95
89
  isVideo,
@@ -115,7 +109,6 @@ const createMatroskaMedia = async ({ writer, onBytesProgress, onMillisecondsProg
115
109
  const { cluster, isNew, smallestProgress } = await getClusterOrMakeNew({
116
110
  chunk,
117
111
  isVideo,
118
- trackNumber,
119
112
  });
120
113
  const newDuration = Math.round((chunk.timestamp + ((_a = chunk.duration) !== null && _a !== void 0 ? _a : 0)) / 1000);
121
114
  await updateDuration(newDuration);
@@ -129,12 +122,16 @@ const createMatroskaMedia = async ({ writer, onBytesProgress, onMillisecondsProg
129
122
  trackNumber,
130
123
  });
131
124
  }
125
+ if (chunk.type === 'key') {
126
+ progressTracker.updateTrackProgress(trackNumber, chunk.timestamp);
127
+ }
132
128
  onBytesProgress(w.getWrittenByteCount());
133
129
  onMillisecondsProgress(newDuration);
134
130
  };
135
131
  const addTrack = async (track) => {
136
132
  currentTracks.push(track);
137
133
  const newTracks = (0, matroska_trackentry_1.makeMatroskaTracks)(currentTracks);
134
+ progressTracker.registerTrack(track.trackNumber);
138
135
  await w.updateDataAt(tracksOffset, (0, make_header_1.combineUint8Arrays)(newTracks.map((b) => b.bytes)));
139
136
  };
140
137
  const operationProm = { current: Promise.resolve() };
@@ -160,10 +157,6 @@ const createMatroskaMedia = async ({ writer, onBytesProgress, onMillisecondsProg
160
157
  operationProm.current = operationProm.current.then(() => addSample({ chunk, trackNumber, isVideo }));
161
158
  return operationProm.current;
162
159
  },
163
- updateDuration: (duration) => {
164
- operationProm.current = operationProm.current.then(() => updateDuration(duration));
165
- return operationProm.current;
166
- },
167
160
  addTrack: (track) => {
168
161
  const trackNumber = currentTracks.length + 1;
169
162
  operationProm.current = operationProm.current.then(() => addTrack({ ...track, trackNumber }));
@@ -2,6 +2,7 @@ import type { LogLevel } from '../log';
2
2
  import type { AudioOrVideoSample } from '../webcodec-sample-types';
3
3
  import type { WriterInterface } from '../writers/writer';
4
4
  import type { MakeTrackAudio, MakeTrackVideo } from './make-track-info';
5
+ import type { ProgressTracker } from './progress-tracker';
5
6
  export type MediaFn = {
6
7
  save: () => Promise<Blob>;
7
8
  remove: () => Promise<void>;
@@ -12,7 +13,6 @@ export type MediaFn = {
12
13
  timescale: number;
13
14
  codecPrivate: Uint8Array | null;
14
15
  }) => Promise<void>;
15
- updateDuration: (duration: number) => Promise<void>;
16
16
  addTrack: (track: Omit<MakeTrackAudio, 'trackNumber'> | Omit<MakeTrackVideo, 'trackNumber'>) => Promise<{
17
17
  trackNumber: number;
18
18
  }>;
@@ -29,4 +29,5 @@ export type MediaFnGeneratorInput = {
29
29
  onMillisecondsProgress: (totalMilliseconds: number) => void;
30
30
  logLevel: LogLevel;
31
31
  filename: string;
32
+ progressTracker: ProgressTracker;
32
33
  };
@@ -0,0 +1,2 @@
1
+ import type { MediaFn, MediaFnGeneratorInput } from '../media-fn';
2
+ export declare const createMp3: ({ filename, logLevel, onBytesProgress, onMillisecondsProgress, writer, }: MediaFnGeneratorInput) => Promise<MediaFn>;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMp3 = void 0;
4
+ const log_1 = require("../../log");
5
+ const createMp3 = async ({ filename, logLevel, onBytesProgress, onMillisecondsProgress, writer, }) => {
6
+ const w = await writer.createContent({ filename, mimeType: 'audio/mpeg' });
7
+ const operationProm = { current: Promise.resolve() };
8
+ const addSample = async (chunk) => {
9
+ var _a;
10
+ await w.write(chunk.data);
11
+ onMillisecondsProgress(chunk.timestamp + ((_a = chunk.duration) !== null && _a !== void 0 ? _a : 0));
12
+ onBytesProgress(w.getWrittenByteCount());
13
+ };
14
+ const waitForFinishPromises = [];
15
+ return {
16
+ save: () => {
17
+ return w.save();
18
+ },
19
+ remove: () => {
20
+ return w.remove();
21
+ },
22
+ addSample: ({ chunk, trackNumber }) => {
23
+ if (trackNumber !== 1) {
24
+ throw new Error('Only one track supported for WAV');
25
+ }
26
+ operationProm.current = operationProm.current.then(() => addSample(chunk));
27
+ return operationProm.current;
28
+ },
29
+ updateTrackSampleRate: () => {
30
+ throw new Error('updateTrackSampleRate() not implemented for WAV encoder');
31
+ },
32
+ addWaitForFinishPromise(promise) {
33
+ waitForFinishPromises.push(promise);
34
+ },
35
+ async waitForFinish() {
36
+ log_1.Log.verbose(logLevel, 'All write operations queued. Waiting for finish...');
37
+ await Promise.all(waitForFinishPromises.map((p) => p()));
38
+ await operationProm.current;
39
+ await w.waitForFinish();
40
+ },
41
+ addTrack: (track) => {
42
+ if (track.type !== 'audio') {
43
+ throw new Error('Only audio tracks supported for WAV');
44
+ }
45
+ return Promise.resolve({ trackNumber: 1 });
46
+ },
47
+ };
48
+ };
49
+ exports.createMp3 = createMp3;
@@ -0,0 +1,7 @@
1
+ export declare const makeProgressTracker: () => {
2
+ registerTrack: (trackNumber: number) => void;
3
+ getSmallestProgress: () => number;
4
+ updateTrackProgress: (trackNumber: number, progress: number) => void;
5
+ waitForProgress: () => Promise<void>;
6
+ };
7
+ export type ProgressTracker = ReturnType<typeof makeProgressTracker>;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeProgressTracker = void 0;
4
+ const event_emitter_1 = require("./event-emitter");
5
+ const with_resolvers_1 = require("./with-resolvers");
6
+ const makeProgressTracker = () => {
7
+ const trackNumberProgresses = {};
8
+ const eventEmitter = new event_emitter_1.IoEventEmitter();
9
+ const calculateSmallestProgress = () => {
10
+ const progressValues = Object.values(trackNumberProgresses);
11
+ if (progressValues.length === 0) {
12
+ return 0;
13
+ }
14
+ return Math.min(...progressValues);
15
+ };
16
+ return {
17
+ registerTrack: (trackNumber) => {
18
+ trackNumberProgresses[trackNumber] = 0;
19
+ },
20
+ getSmallestProgress: () => {
21
+ return calculateSmallestProgress();
22
+ },
23
+ updateTrackProgress: (trackNumber, progress) => {
24
+ if (trackNumberProgresses[trackNumber] === undefined) {
25
+ throw new Error(`Tried to update progress for a track that was not registered: ${trackNumber}`);
26
+ }
27
+ trackNumberProgresses[trackNumber] = progress;
28
+ eventEmitter.dispatchEvent('progress', {
29
+ smallestProgress: calculateSmallestProgress(),
30
+ });
31
+ },
32
+ waitForProgress: () => {
33
+ const { promise, resolve } = (0, with_resolvers_1.withResolvers)();
34
+ const on = () => {
35
+ eventEmitter.removeEventListener('processed', on);
36
+ resolve();
37
+ };
38
+ eventEmitter.addEventListener('processed', on);
39
+ return promise;
40
+ },
41
+ };
42
+ };
43
+ exports.makeProgressTracker = makeProgressTracker;
@@ -0,0 +1,2 @@
1
+ import type { MediaFn, MediaFnGeneratorInput } from '../media-fn';
2
+ export declare const createWav: ({ filename, logLevel, onBytesProgress, onMillisecondsProgress, writer, progressTracker, }: MediaFnGeneratorInput) => Promise<MediaFn>;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createWav = void 0;
4
+ const log_1 = require("../../log");
5
+ const numberTo32BiIntLittleEndian = (num) => {
6
+ return new Uint8Array([
7
+ num & 0xff,
8
+ (num >> 8) & 0xff,
9
+ (num >> 16) & 0xff,
10
+ (num >> 24) & 0xff,
11
+ ]);
12
+ };
13
+ const numberTo16BitLittleEndian = (num) => {
14
+ return new Uint8Array([num & 0xff, (num >> 8) & 0xff]);
15
+ };
16
+ const BIT_DEPTH = 16;
17
+ const BYTES_PER_SAMPLE = BIT_DEPTH / 8;
18
+ const createWav = async ({ filename, logLevel, onBytesProgress, onMillisecondsProgress, writer, progressTracker, }) => {
19
+ const w = await writer.createContent({ filename, mimeType: 'audio/wav' });
20
+ await w.write(new Uint8Array([0x52, 0x49, 0x46, 0x46])); // "RIFF"
21
+ const sizePosition = w.getWrittenByteCount();
22
+ await w.write(new Uint8Array([0x00, 0x00, 0x00, 0x00])); // Remaining size
23
+ await w.write(new Uint8Array([0x57, 0x41, 0x56, 0x45])); // "WAVE"
24
+ await w.write(new Uint8Array([0x66, 0x6d, 0x74, 0x20])); // "fmt "
25
+ await w.write(new Uint8Array([0x10, 0x00, 0x00, 0x00])); // fmt chunk size = 16
26
+ await w.write(new Uint8Array([0x01, 0x00])); // Audio format (PCM) = 1, set 3 if float32 would be true
27
+ const channelNumPosition = w.getWrittenByteCount();
28
+ await w.write(new Uint8Array([0x01, 0x00])); // Number of channels = 1
29
+ const sampleRatePosition = w.getWrittenByteCount();
30
+ await w.write(new Uint8Array([0x0, 0x0, 0x00, 0x00])); // Sample rate
31
+ const byteRatePosition = w.getWrittenByteCount();
32
+ await w.write(new Uint8Array([0x0, 0x0, 0x00, 0x00])); // Byte rate
33
+ const blockAlignPosition = w.getWrittenByteCount();
34
+ await w.write(new Uint8Array([0x00, 0x00])); // Block align
35
+ await w.write(numberTo16BitLittleEndian(BIT_DEPTH)); // Bits per sample
36
+ await w.write(new Uint8Array([0x64, 0x61, 0x74, 0x61])); // "data"
37
+ const dataSizePosition = w.getWrittenByteCount();
38
+ await w.write(new Uint8Array([0x00, 0x00, 0x00, 0x00])); // Remaining size
39
+ const operationProm = { current: Promise.resolve() };
40
+ const updateSize = async () => {
41
+ const size = w.getWrittenByteCount() - sizePosition - 4;
42
+ await w.updateDataAt(sizePosition, numberTo32BiIntLittleEndian(size));
43
+ const dataSize = w.getWrittenByteCount() - dataSizePosition - 4;
44
+ await w.updateDataAt(dataSizePosition, numberTo32BiIntLittleEndian(dataSize));
45
+ };
46
+ const updateChannelNum = async (numberOfChannels) => {
47
+ await w.updateDataAt(channelNumPosition, new Uint8Array([numberOfChannels, 0x00]));
48
+ };
49
+ const updateSampleRate = async (sampleRate) => {
50
+ await w.updateDataAt(sampleRatePosition, numberTo32BiIntLittleEndian(sampleRate));
51
+ };
52
+ const updateByteRate = async ({ sampleRate, numberOfChannels, }) => {
53
+ await w.updateDataAt(byteRatePosition, numberTo32BiIntLittleEndian(sampleRate * numberOfChannels + BYTES_PER_SAMPLE));
54
+ };
55
+ const updateBlockAlign = async (numberOfChannels) => {
56
+ await w.updateDataAt(blockAlignPosition, new Uint8Array(numberTo16BitLittleEndian(numberOfChannels * BYTES_PER_SAMPLE)));
57
+ };
58
+ const addSample = async (chunk) => {
59
+ var _a;
60
+ log_1.Log.verbose(logLevel, 'Adding sample', chunk);
61
+ await w.write(chunk.data);
62
+ onMillisecondsProgress((chunk.timestamp + ((_a = chunk.duration) !== null && _a !== void 0 ? _a : 0)) / 1000);
63
+ onBytesProgress(w.getWrittenByteCount());
64
+ };
65
+ const waitForFinishPromises = [];
66
+ return {
67
+ save: () => {
68
+ return w.save();
69
+ },
70
+ remove: () => {
71
+ return w.remove();
72
+ },
73
+ addSample: ({ chunk, trackNumber }) => {
74
+ if (trackNumber !== 1) {
75
+ throw new Error('Only one track supported for WAV');
76
+ }
77
+ operationProm.current = operationProm.current.then(() => addSample(chunk));
78
+ progressTracker.updateTrackProgress(trackNumber, chunk.timestamp);
79
+ return operationProm.current;
80
+ },
81
+ updateTrackSampleRate: () => {
82
+ throw new Error('updateTrackSampleRate() not implemented for WAV encoder');
83
+ },
84
+ addWaitForFinishPromise: (promise) => {
85
+ waitForFinishPromises.push(promise);
86
+ },
87
+ async waitForFinish() {
88
+ log_1.Log.verbose(logLevel, 'All write operations queued. Waiting for finish...');
89
+ await Promise.all(waitForFinishPromises.map((p) => p()));
90
+ await operationProm.current;
91
+ await updateSize();
92
+ await w.waitForFinish();
93
+ },
94
+ addTrack: async (track) => {
95
+ if (track.type !== 'audio') {
96
+ throw new Error('Only audio tracks supported for WAV');
97
+ }
98
+ await updateChannelNum(track.numberOfChannels);
99
+ await updateSampleRate(track.sampleRate);
100
+ await updateByteRate({
101
+ sampleRate: track.sampleRate,
102
+ numberOfChannels: track.numberOfChannels,
103
+ });
104
+ await updateBlockAlign(track.numberOfChannels);
105
+ progressTracker.registerTrack(1);
106
+ return Promise.resolve({ trackNumber: 1 });
107
+ },
108
+ };
109
+ };
110
+ exports.createWav = createWav;
@@ -0,0 +1,10 @@
1
+ export declare const withResolvers: <T>() => {
2
+ promise: Promise<T>;
3
+ resolve: (value: T | PromiseLike<T>) => void;
4
+ reject: (reason?: any) => void;
5
+ };
6
+ export declare const withResolversAndWaitForReturn: <T>() => {
7
+ getPromiseToImmediatelyReturn: () => Promise<T>;
8
+ reject: (reason: unknown) => void;
9
+ resolve: (value: T | PromiseLike<T>) => void;
10
+ };