@remotion/media-parser 4.0.227 → 4.0.229

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.
@@ -73,21 +73,20 @@ const parseMdat = async ({ data, size, fileOffset, existingBoxes, options, signa
73
73
  break;
74
74
  }
75
75
  const bytes = data.getSlice(samplesWithIndex.samplePosition.size);
76
+ const timestamp = Math.floor((samplesWithIndex.samplePosition.cts * 1000000) /
77
+ samplesWithIndex.track.timescale);
78
+ const duration = Math.floor((samplesWithIndex.samplePosition.duration * 1000000) /
79
+ samplesWithIndex.track.timescale);
76
80
  if (samplesWithIndex.track.type === 'audio') {
77
- const timestamp = Math.floor((samplesWithIndex.samplePosition.cts * 1000000) /
78
- samplesWithIndex.track.timescale);
79
81
  await options.parserState.onAudioSample(samplesWithIndex.track.trackId, {
80
82
  data: bytes,
81
83
  timestamp,
82
84
  trackId: samplesWithIndex.track.trackId,
83
85
  type: samplesWithIndex.samplePosition.isKeyframe ? 'key' : 'delta',
86
+ duration,
84
87
  });
85
88
  }
86
89
  if (samplesWithIndex.track.type === 'video') {
87
- const timestamp = Math.floor((samplesWithIndex.samplePosition.cts * 1000000) /
88
- samplesWithIndex.track.timescale);
89
- const duration = Math.floor((samplesWithIndex.samplePosition.duration * 1000000) /
90
- samplesWithIndex.track.timescale);
91
90
  await options.parserState.onVideoSample(samplesWithIndex.track.trackId, {
92
91
  data: bytes,
93
92
  timestamp,
@@ -1,2 +1,2 @@
1
- export declare const measureEBMLVarInt: (value: number) => 2 | 1 | 4 | 3 | 5 | 6;
1
+ export declare const measureEBMLVarInt: (value: number) => 1 | 4 | 2 | 3 | 5 | 6;
2
2
  export declare const getVariableInt: (value: number, minWidth: number | null) => Uint8Array;
@@ -63,6 +63,7 @@ const getSampleFromBlock = (ebml, parserContext, offset) => {
63
63
  trackId: trackNumber,
64
64
  timestamp: timecodeInMicroseconds,
65
65
  type: 'key',
66
+ duration: undefined,
66
67
  };
67
68
  iterator.destroy();
68
69
  return {
@@ -1,19 +1,18 @@
1
+ import type { AudioOrVideoSample } from '../webcodec-sample-types';
1
2
  import type { Writer } from '../writers/writer';
2
- export type AudioOrVideoSample = {
3
- timestamp: number;
4
- type: 'key' | 'delta';
5
- copyTo(destination: AllowSharedBufferSource): void;
6
- byteLength: number;
7
- duration: number | null;
8
- };
9
3
  export declare const timestampToClusterTimestamp: (timestamp: number) => number;
10
- export declare const makeCluster: (w: Writer, timestamp: number) => Promise<{
4
+ export declare const canFitInCluster: ({ clusterStartTimestamp, chunk, }: {
5
+ clusterStartTimestamp: number;
6
+ chunk: AudioOrVideoSample;
7
+ }) => boolean;
8
+ export declare const makeCluster: (w: Writer, clusterStartTimestamp: number) => Promise<{
11
9
  addSample: (chunk: AudioOrVideoSample, trackNumber: number) => Promise<{
12
10
  timecodeRelativeToCluster: number;
13
11
  }>;
14
- shouldMakeNewCluster: ({ isVideo, keyframe, newT, }: {
12
+ shouldMakeNewCluster: ({ isVideo, chunk, newT, }: {
15
13
  newT: number;
16
- keyframe: boolean;
14
+ chunk: AudioOrVideoSample;
17
15
  isVideo: boolean;
18
16
  }) => boolean;
17
+ startTimestamp: number;
19
18
  }>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.makeCluster = exports.timestampToClusterTimestamp = void 0;
3
+ exports.makeCluster = exports.canFitInCluster = exports.timestampToClusterTimestamp = void 0;
4
4
  const ebml_1 = require("../boxes/webm/ebml");
5
5
  const make_header_1 = require("../boxes/webm/make-header");
6
6
  const all_segments_1 = require("../boxes/webm/segments/all-segments");
@@ -11,8 +11,17 @@ const timestampToClusterTimestamp = (timestamp) => {
11
11
  return Math.round((timestamp / timescale_1.CREATE_TIME_SCALE) * 1000);
12
12
  };
13
13
  exports.timestampToClusterTimestamp = timestampToClusterTimestamp;
14
- const makeCluster = async (w, timestamp) => {
15
- const cluster = (0, cluster_segment_1.createClusterSegment)((0, exports.timestampToClusterTimestamp)(timestamp));
14
+ const canFitInCluster = ({ clusterStartTimestamp, chunk, }) => {
15
+ const timecodeRelativeToCluster = (0, exports.timestampToClusterTimestamp)(chunk.timestamp) -
16
+ (0, exports.timestampToClusterTimestamp)(clusterStartTimestamp);
17
+ if (timecodeRelativeToCluster < 0) {
18
+ throw new Error(`timecodeRelativeToCluster is negative`);
19
+ }
20
+ return timecodeRelativeToCluster <= maxClusterTimestamp;
21
+ };
22
+ exports.canFitInCluster = canFitInCluster;
23
+ const makeCluster = async (w, clusterStartTimestamp) => {
24
+ const cluster = (0, cluster_segment_1.createClusterSegment)((0, exports.timestampToClusterTimestamp)(clusterStartTimestamp));
16
25
  const clusterVIntPosition = w.getWrittenByteCount() +
17
26
  cluster.offsets.offset +
18
27
  (0, make_header_1.matroskaToHex)(all_segments_1.matroskaElements.Cluster).byteLength;
@@ -21,19 +30,14 @@ const makeCluster = async (w, timestamp) => {
21
30
  cluster_segment_1.CLUSTER_MIN_VINT_WIDTH;
22
31
  await w.write(cluster.bytes);
23
32
  const addSample = async (chunk, trackNumber) => {
24
- const arr = new Uint8Array(chunk.byteLength);
25
- chunk.copyTo(arr);
26
33
  const timecodeRelativeToCluster = (0, exports.timestampToClusterTimestamp)(chunk.timestamp) -
27
- (0, exports.timestampToClusterTimestamp)(timestamp);
28
- if (timecodeRelativeToCluster < 0) {
29
- throw new Error(`timecodeRelativeToCluster is negative (track ${trackNumber})`);
30
- }
31
- if (timecodeRelativeToCluster > maxClusterTimestamp) {
34
+ (0, exports.timestampToClusterTimestamp)(clusterStartTimestamp);
35
+ if (!(0, exports.canFitInCluster)({ clusterStartTimestamp, chunk })) {
32
36
  throw new Error(`timecodeRelativeToCluster is too big: ${timecodeRelativeToCluster} > ${maxClusterTimestamp}`);
33
37
  }
34
38
  const keyframe = chunk.type === 'key';
35
39
  const simpleBlock = (0, cluster_segment_1.makeSimpleBlock)({
36
- bytes: arr,
40
+ bytes: chunk.data,
37
41
  invisible: false,
38
42
  keyframe,
39
43
  lacing: 0,
@@ -45,11 +49,26 @@ const makeCluster = async (w, timestamp) => {
45
49
  await w.write(simpleBlock);
46
50
  return { timecodeRelativeToCluster };
47
51
  };
48
- const shouldMakeNewCluster = ({ isVideo, keyframe, newT, }) => {
52
+ const shouldMakeNewCluster = ({ isVideo, chunk, newT, }) => {
49
53
  const newTimestamp = (0, exports.timestampToClusterTimestamp)(newT);
50
- const oldTimestamp = (0, exports.timestampToClusterTimestamp)(timestamp);
54
+ const oldTimestamp = (0, exports.timestampToClusterTimestamp)(clusterStartTimestamp);
55
+ const canFit = (0, exports.canFitInCluster)({
56
+ chunk,
57
+ clusterStartTimestamp,
58
+ });
59
+ if (!canFit) {
60
+ // We must create a new cluster
61
+ // This is for example if we have an audio-only file
62
+ return true;
63
+ }
64
+ const keyframe = chunk.type === 'key';
65
+ // TODO: Timestamp falls apart when video only
51
66
  return newTimestamp - oldTimestamp >= 2000 && keyframe && isVideo;
52
67
  };
53
- return { addSample, shouldMakeNewCluster };
68
+ return {
69
+ addSample,
70
+ shouldMakeNewCluster,
71
+ startTimestamp: clusterStartTimestamp,
72
+ };
54
73
  };
55
74
  exports.makeCluster = makeCluster;
@@ -1,8 +1,8 @@
1
+ import type { AudioOrVideoSample } from '../webcodec-sample-types';
1
2
  import type { WriterInterface } from '../writers/writer';
2
- import type { AudioOrVideoSample } from './cluster';
3
3
  import type { MakeTrackAudio, MakeTrackVideo } from './matroska-trackentry';
4
4
  export type MediaFn = {
5
- save: () => Promise<File>;
5
+ save: () => Promise<Blob>;
6
6
  remove: () => Promise<void>;
7
7
  addSample: (chunk: AudioOrVideoSample, trackNumber: number, isVideo: boolean) => Promise<void>;
8
8
  updateDuration: (duration: number) => Promise<void>;
@@ -12,4 +12,8 @@ export type MediaFn = {
12
12
  addWaitForFinishPromise: (promise: () => Promise<void>) => void;
13
13
  waitForFinish: () => Promise<void>;
14
14
  };
15
- export declare const createMedia: (writer: WriterInterface) => Promise<MediaFn>;
15
+ export declare const createMedia: ({ writer, onBytesProgress, onMillisecondsProgress, }: {
16
+ writer: WriterInterface;
17
+ onBytesProgress: (totalBytes: number) => void;
18
+ onMillisecondsProgress: (totalMilliseconds: number) => void;
19
+ }) => 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_1 = require("./timescale");
16
- const createMedia = async (writer) => {
16
+ const createMedia = async ({ writer, onBytesProgress, onMillisecondsProgress, }) => {
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();
@@ -59,11 +59,13 @@ const createMedia = async (writer) => {
59
59
  const updateSeekWrite = async () => {
60
60
  const updatedSeek = (0, matroska_seek_1.createMatroskaSeekHead)(seeks);
61
61
  await w.updateDataAt(seekHeadOffset, (0, make_header_1.combineUint8Arrays)(updatedSeek.map((b) => b.bytes)));
62
+ onBytesProgress(w.getWrittenByteCount());
62
63
  };
63
64
  const segmentOffset = w.getWrittenByteCount();
64
65
  const updateSegmentSize = async (size) => {
65
66
  const data = (0, ebml_1.getVariableInt)(size, matroska_segment_1.MATROSKA_SEGMENT_MIN_VINT_WIDTH);
66
67
  await w.updateDataAt(segmentOffset + (0, make_header_1.matroskaToHex)(all_segments_1.matroskaElements.Segment).byteLength, data);
68
+ onBytesProgress(w.getWrittenByteCount());
67
69
  };
68
70
  await w.write(matroskaSegment.bytes);
69
71
  const clusterOffset = w.getWrittenByteCount();
@@ -77,8 +79,8 @@ const createMedia = async (writer) => {
77
79
  const smallestProgress = Math.min(...Object.values(trackNumberProgresses));
78
80
  if (!currentCluster.shouldMakeNewCluster({
79
81
  newT: smallestProgress,
80
- keyframe: chunk.type === 'key',
81
82
  isVideo,
83
+ chunk,
82
84
  })) {
83
85
  return { cluster: currentCluster, isNew: false, smallestProgress };
84
86
  }
@@ -88,6 +90,7 @@ const createMedia = async (writer) => {
88
90
  const updateDuration = async (newDuration) => {
89
91
  const blocks = (0, make_duration_with_padding_1.makeDurationWithPadding)(newDuration);
90
92
  await w.updateDataAt(durationOffset, blocks.bytes);
93
+ onBytesProgress(w.getWrittenByteCount());
91
94
  };
92
95
  const addSample = async (chunk, trackNumber, isVideo) => {
93
96
  var _a;
@@ -108,6 +111,8 @@ const createMedia = async (writer) => {
108
111
  trackNumber,
109
112
  });
110
113
  }
114
+ onBytesProgress(w.getWrittenByteCount());
115
+ onMillisecondsProgress(newDuration);
111
116
  };
112
117
  const addTrack = async (track) => {
113
118
  currentTracks.push(track);
@@ -0,0 +1,3 @@
1
+ import type { AudioSample } from '../webcodec-sample-types';
2
+ import type { AudioOrVideoSample } from './cluster';
3
+ export declare const polyfillAudioChunk: (audioSample: AudioSample) => AudioOrVideoSample;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.polyfillAudioChunk = void 0;
4
+ const polyfillAudioChunk = (audioSample) => {
5
+ var _a;
6
+ return {
7
+ ...audioSample,
8
+ duration: (_a = audioSample.duration) !== null && _a !== void 0 ? _a : null,
9
+ copyTo(destination) {
10
+ return destination.write(audioSample.data);
11
+ },
12
+ byteLength: audioSample.data.length,
13
+ };
14
+ };
15
+ exports.polyfillAudioChunk = polyfillAudioChunk;
@@ -1,24 +1,21 @@
1
- // src/writers/buffer.ts
1
+ // src/writers/buffer-implementation/writer.ts
2
2
  var createContent = () => {
3
3
  const buf = new ArrayBuffer(0, {
4
- maxByteLength: 1e8
4
+ maxByteLength: 200000000
5
5
  });
6
6
  if (!buf.resize) {
7
7
  throw new Error("Could not create buffer writer");
8
8
  }
9
- let data = new Uint8Array(buf);
10
9
  const write = (newData) => {
11
10
  const oldLength = buf.byteLength;
12
11
  const newLength = oldLength + newData.byteLength;
13
12
  buf.resize(newLength);
14
13
  const newArray = new Uint8Array(buf);
15
14
  newArray.set(newData, oldLength);
16
- data = newArray;
17
15
  };
18
16
  const updateDataAt = (position, newData) => {
19
17
  const newArray = new Uint8Array(buf);
20
18
  newArray.set(newData, position);
21
- data = newArray;
22
19
  };
23
20
  let writPromise = Promise.resolve();
24
21
  let removed = false;
@@ -31,11 +28,11 @@ var createContent = () => {
31
28
  if (removed) {
32
29
  return Promise.reject(new Error("Already called .remove() on the result"));
33
30
  }
34
- return Promise.resolve(new File([data], "hithere", {}));
31
+ const arr = new Uint8Array(buf);
32
+ return Promise.resolve(new File([arr.slice()], "hi", { type: "video/webm" }));
35
33
  },
36
34
  remove() {
37
35
  removed = true;
38
- data = new Uint8Array(0);
39
36
  return Promise.resolve();
40
37
  },
41
38
  getWrittenByteCount: () => buf.byteLength,
@@ -49,6 +46,8 @@ var createContent = () => {
49
46
  };
50
47
  return Promise.resolve(writer);
51
48
  };
49
+
50
+ // src/writers/buffer.ts
52
51
  var bufferWriter = {
53
52
  createContent
54
53
  };
@@ -55,7 +55,7 @@ var fetchReader = {
55
55
  const parsedContentRange = contentRange ? parseContentRange(contentRange) : null;
56
56
  const { supportsContentRange } = validateContentRangeAndDetectIfSupported(actualRange, parsedContentRange, res.status);
57
57
  signal?.addEventListener("abort", () => {
58
- controller.abort();
58
+ controller.abort(new Error("Aborted by user"));
59
59
  }, { once: true });
60
60
  if (res.status.toString().startsWith("4") || res.status.toString().startsWith("5")) {
61
61
  throw new Error(`Server returned status code ${res.status} for ${src} and range ${actualRange}`);
@@ -55,7 +55,7 @@ var fetchReader = {
55
55
  const parsedContentRange = contentRange ? parseContentRange(contentRange) : null;
56
56
  const { supportsContentRange } = validateContentRangeAndDetectIfSupported(actualRange, parsedContentRange, res.status);
57
57
  signal?.addEventListener("abort", () => {
58
- controller.abort();
58
+ controller.abort(new Error("Aborted by user"));
59
59
  }, { once: true });
60
60
  if (res.status.toString().startsWith("4") || res.status.toString().startsWith("5")) {
61
61
  throw new Error(`Server returned status code ${res.status} for ${src} and range ${actualRange}`);
@@ -1070,24 +1070,29 @@ var maxClusterTimestamp = 2 ** 15;
1070
1070
  var timestampToClusterTimestamp = (timestamp) => {
1071
1071
  return Math.round(timestamp / CREATE_TIME_SCALE * 1000);
1072
1072
  };
1073
- var makeCluster = async (w, timestamp) => {
1074
- const cluster = createClusterSegment(timestampToClusterTimestamp(timestamp));
1073
+ var canFitInCluster = ({
1074
+ clusterStartTimestamp,
1075
+ chunk
1076
+ }) => {
1077
+ const timecodeRelativeToCluster = timestampToClusterTimestamp(chunk.timestamp) - timestampToClusterTimestamp(clusterStartTimestamp);
1078
+ if (timecodeRelativeToCluster < 0) {
1079
+ throw new Error(`timecodeRelativeToCluster is negative`);
1080
+ }
1081
+ return timecodeRelativeToCluster <= maxClusterTimestamp;
1082
+ };
1083
+ var makeCluster = async (w, clusterStartTimestamp) => {
1084
+ const cluster = createClusterSegment(timestampToClusterTimestamp(clusterStartTimestamp));
1075
1085
  const clusterVIntPosition = w.getWrittenByteCount() + cluster.offsets.offset + matroskaToHex(matroskaElements.Cluster).byteLength;
1076
1086
  let clusterSize = cluster.bytes.byteLength - matroskaToHex(matroskaElements.Cluster).byteLength - CLUSTER_MIN_VINT_WIDTH;
1077
1087
  await w.write(cluster.bytes);
1078
1088
  const addSample = async (chunk, trackNumber2) => {
1079
- const arr = new Uint8Array(chunk.byteLength);
1080
- chunk.copyTo(arr);
1081
- const timecodeRelativeToCluster = timestampToClusterTimestamp(chunk.timestamp) - timestampToClusterTimestamp(timestamp);
1082
- if (timecodeRelativeToCluster < 0) {
1083
- throw new Error(`timecodeRelativeToCluster is negative (track ${trackNumber2})`);
1084
- }
1085
- if (timecodeRelativeToCluster > maxClusterTimestamp) {
1089
+ const timecodeRelativeToCluster = timestampToClusterTimestamp(chunk.timestamp) - timestampToClusterTimestamp(clusterStartTimestamp);
1090
+ if (!canFitInCluster({ clusterStartTimestamp, chunk })) {
1086
1091
  throw new Error(`timecodeRelativeToCluster is too big: ${timecodeRelativeToCluster} > ${maxClusterTimestamp}`);
1087
1092
  }
1088
1093
  const keyframe = chunk.type === "key";
1089
1094
  const simpleBlock2 = makeSimpleBlock({
1090
- bytes: arr,
1095
+ bytes: chunk.data,
1091
1096
  invisible: false,
1092
1097
  keyframe,
1093
1098
  lacing: 0,
@@ -1101,14 +1106,26 @@ var makeCluster = async (w, timestamp) => {
1101
1106
  };
1102
1107
  const shouldMakeNewCluster = ({
1103
1108
  isVideo,
1104
- keyframe,
1109
+ chunk,
1105
1110
  newT
1106
1111
  }) => {
1107
1112
  const newTimestamp = timestampToClusterTimestamp(newT);
1108
- const oldTimestamp = timestampToClusterTimestamp(timestamp);
1113
+ const oldTimestamp = timestampToClusterTimestamp(clusterStartTimestamp);
1114
+ const canFit = canFitInCluster({
1115
+ chunk,
1116
+ clusterStartTimestamp
1117
+ });
1118
+ if (!canFit) {
1119
+ return true;
1120
+ }
1121
+ const keyframe = chunk.type === "key";
1109
1122
  return newTimestamp - oldTimestamp >= 2000 && keyframe && isVideo;
1110
1123
  };
1111
- return { addSample, shouldMakeNewCluster };
1124
+ return {
1125
+ addSample,
1126
+ shouldMakeNewCluster,
1127
+ startTimestamp: clusterStartTimestamp
1128
+ };
1112
1129
  };
1113
1130
 
1114
1131
  // src/create/make-duration-with-padding.ts
@@ -1815,7 +1832,11 @@ var makeMatroskaTracks = (tracks2) => {
1815
1832
  };
1816
1833
 
1817
1834
  // src/create/create-media.ts
1818
- var createMedia = async (writer) => {
1835
+ var createMedia = async ({
1836
+ writer,
1837
+ onBytesProgress,
1838
+ onMillisecondsProgress
1839
+ }) => {
1819
1840
  const header = makeMatroskaHeader();
1820
1841
  const w = await writer.createContent();
1821
1842
  await w.write(header.bytes);
@@ -1859,11 +1880,13 @@ var createMedia = async (writer) => {
1859
1880
  const updateSeekWrite = async () => {
1860
1881
  const updatedSeek = createMatroskaSeekHead(seeks);
1861
1882
  await w.updateDataAt(seekHeadOffset, combineUint8Arrays(updatedSeek.map((b) => b.bytes)));
1883
+ onBytesProgress(w.getWrittenByteCount());
1862
1884
  };
1863
1885
  const segmentOffset = w.getWrittenByteCount();
1864
1886
  const updateSegmentSize = async (size) => {
1865
1887
  const data = getVariableInt(size, MATROSKA_SEGMENT_MIN_VINT_WIDTH);
1866
1888
  await w.updateDataAt(segmentOffset + matroskaToHex(matroskaElements.Segment).byteLength, data);
1889
+ onBytesProgress(w.getWrittenByteCount());
1867
1890
  };
1868
1891
  await w.write(matroskaSegment.bytes);
1869
1892
  const clusterOffset = w.getWrittenByteCount();
@@ -1880,8 +1903,8 @@ var createMedia = async (writer) => {
1880
1903
  const smallestProgress = Math.min(...Object.values(trackNumberProgresses));
1881
1904
  if (!currentCluster.shouldMakeNewCluster({
1882
1905
  newT: smallestProgress,
1883
- keyframe: chunk.type === "key",
1884
- isVideo
1906
+ isVideo,
1907
+ chunk
1885
1908
  })) {
1886
1909
  return { cluster: currentCluster, isNew: false, smallestProgress };
1887
1910
  }
@@ -1891,6 +1914,7 @@ var createMedia = async (writer) => {
1891
1914
  const updateDuration = async (newDuration) => {
1892
1915
  const blocks = makeDurationWithPadding(newDuration);
1893
1916
  await w.updateDataAt(durationOffset, blocks.bytes);
1917
+ onBytesProgress(w.getWrittenByteCount());
1894
1918
  };
1895
1919
  const addSample = async (chunk, trackNumber2, isVideo) => {
1896
1920
  trackNumberProgresses[trackNumber2] = chunk.timestamp;
@@ -1909,6 +1933,8 @@ var createMedia = async (writer) => {
1909
1933
  trackNumber: trackNumber2
1910
1934
  });
1911
1935
  }
1936
+ onBytesProgress(w.getWrittenByteCount());
1937
+ onMillisecondsProgress(newDuration);
1912
1938
  };
1913
1939
  const addTrack = async (track) => {
1914
1940
  currentTracks.push(track);
@@ -1959,6 +1985,40 @@ var createMedia = async (writer) => {
1959
1985
  };
1960
1986
  };
1961
1987
 
1988
+ // src/log.ts
1989
+ var logLevels = ["trace", "verbose", "info", "warn", "error"];
1990
+ var getNumberForLogLevel = (level) => {
1991
+ return logLevels.indexOf(level);
1992
+ };
1993
+ var isEqualOrBelowLogLevel = (currentLevel, level) => {
1994
+ return getNumberForLogLevel(currentLevel) <= getNumberForLogLevel(level);
1995
+ };
1996
+ var Log = {
1997
+ trace: (logLevel, ...args) => {
1998
+ if (isEqualOrBelowLogLevel(logLevel, "trace")) {
1999
+ return console.log(...args);
2000
+ }
2001
+ },
2002
+ verbose: (logLevel, ...args) => {
2003
+ if (isEqualOrBelowLogLevel(logLevel, "verbose")) {
2004
+ return console.log(...args);
2005
+ }
2006
+ },
2007
+ info: (logLevel, ...args) => {
2008
+ if (isEqualOrBelowLogLevel(logLevel, "info")) {
2009
+ return console.log(...args);
2010
+ }
2011
+ },
2012
+ warn: (logLevel, ...args) => {
2013
+ if (isEqualOrBelowLogLevel(logLevel, "warn")) {
2014
+ return console.warn(...args);
2015
+ }
2016
+ },
2017
+ error: (...args) => {
2018
+ return console.error(...args);
2019
+ }
2020
+ };
2021
+
1962
2022
  // src/boxes/iso-base-media/traversal.ts
1963
2023
  var getMoovBox = (segments) => {
1964
2024
  const moovBox = segments.find((s) => s.type === "moov-box");
@@ -4337,18 +4397,18 @@ var parseMdat = async ({
4337
4397
  break;
4338
4398
  }
4339
4399
  const bytes = data.getSlice(samplesWithIndex.samplePosition.size);
4400
+ const timestamp = Math.floor(samplesWithIndex.samplePosition.cts * 1e6 / samplesWithIndex.track.timescale);
4401
+ const duration2 = Math.floor(samplesWithIndex.samplePosition.duration * 1e6 / samplesWithIndex.track.timescale);
4340
4402
  if (samplesWithIndex.track.type === "audio") {
4341
- const timestamp = Math.floor(samplesWithIndex.samplePosition.cts * 1e6 / samplesWithIndex.track.timescale);
4342
4403
  await options.parserState.onAudioSample(samplesWithIndex.track.trackId, {
4343
4404
  data: bytes,
4344
4405
  timestamp,
4345
4406
  trackId: samplesWithIndex.track.trackId,
4346
- type: samplesWithIndex.samplePosition.isKeyframe ? "key" : "delta"
4407
+ type: samplesWithIndex.samplePosition.isKeyframe ? "key" : "delta",
4408
+ duration: duration2
4347
4409
  });
4348
4410
  }
4349
4411
  if (samplesWithIndex.track.type === "video") {
4350
- const timestamp = Math.floor(samplesWithIndex.samplePosition.cts * 1e6 / samplesWithIndex.track.timescale);
4351
- const duration2 = Math.floor(samplesWithIndex.samplePosition.duration * 1e6 / samplesWithIndex.track.timescale);
4352
4412
  await options.parserState.onVideoSample(samplesWithIndex.track.trackId, {
4353
4413
  data: bytes,
4354
4414
  timestamp,
@@ -6126,7 +6186,8 @@ var getSampleFromBlock = (ebml, parserContext, offset) => {
6126
6186
  data: iterator.getSlice(remainingNow),
6127
6187
  trackId: trackNumber2,
6128
6188
  timestamp: timecodeInMicroseconds,
6129
- type: "key"
6189
+ type: "key",
6190
+ duration: undefined
6130
6191
  };
6131
6192
  iterator.destroy();
6132
6193
  return {
@@ -6818,7 +6879,8 @@ var parseMedia = async ({
6818
6879
  };
6819
6880
  // src/index.ts
6820
6881
  var MediaParserInternals = {
6821
- createMedia
6882
+ createMedia,
6883
+ Log
6822
6884
  };
6823
6885
  export {
6824
6886
  parseMedia,
@@ -54,6 +54,12 @@ var webFsWriter = {
54
54
  createContent
55
55
  };
56
56
  var canUseWebFsWriter = async () => {
57
+ if (!("storage" in navigator)) {
58
+ return false;
59
+ }
60
+ if (!("getDirectory" in navigator.storage)) {
61
+ return false;
62
+ }
57
63
  const directoryHandle = await navigator.storage.getDirectory();
58
64
  const fileHandle = await directoryHandle.getFileHandle("remotion-probe-web-fs-support", {
59
65
  create: true
package/dist/index.d.ts CHANGED
@@ -1,10 +1,24 @@
1
+ import type { LogLevel } from './log';
2
+ export { WriterInterface } from './writers/writer';
1
3
  export { AudioTrack, MediaParserAudioCodec, MediaParserVideoCodec, OtherTrack, Track, VideoTrack, VideoTrackColorParams, } from './get-tracks';
2
- export type { Options, ParseMediaContainer, ParseMediaFields, ParseMediaResult, TracksField, } from './options';
4
+ export type { Options, ParseMediaContainer, ParseMediaDynamicOptions, ParseMediaFields, ParseMediaOptions, ParseMediaResult, TracksField, } from './options';
3
5
  export { parseMedia } from './parse-media';
4
- export { AudioSample, OnAudioSample, OnAudioTrack, OnVideoSample, OnVideoTrack, VideoSample, } from './webcodec-sample-types';
6
+ export { AudioOrVideoSample, AudioSample, OnAudioSample, OnAudioTrack, OnVideoSample, OnVideoTrack, VideoSample, } from './webcodec-sample-types';
5
7
  export type { MediaFn } from './create/create-media';
6
8
  export { Dimensions } from './get-dimensions';
7
9
  export type { ReaderInterface } from './readers/reader';
10
+ export type { LogLevel };
8
11
  export declare const MediaParserInternals: {
9
- createMedia: (writer: import("./writers/writer").WriterInterface) => Promise<import("./create/create-media").MediaFn>;
12
+ createMedia: ({ writer, onBytesProgress, onMillisecondsProgress, }: {
13
+ writer: import("./writers/writer").WriterInterface;
14
+ onBytesProgress: (totalBytes: number) => void;
15
+ onMillisecondsProgress: (totalMilliseconds: number) => void;
16
+ }) => Promise<import("./create/create-media").MediaFn>;
17
+ Log: {
18
+ trace: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
19
+ verbose: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
20
+ info: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
21
+ warn: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
22
+ error: (...args: Parameters<typeof console.log>) => void;
23
+ };
10
24
  };
package/dist/index.js CHANGED
@@ -2,8 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MediaParserInternals = exports.parseMedia = void 0;
4
4
  const create_media_1 = require("./create/create-media");
5
+ const log_1 = require("./log");
5
6
  var parse_media_1 = require("./parse-media");
6
7
  Object.defineProperty(exports, "parseMedia", { enumerable: true, get: function () { return parse_media_1.parseMedia; } });
7
8
  exports.MediaParserInternals = {
8
9
  createMedia: create_media_1.createMedia,
10
+ Log: log_1.Log,
9
11
  };
package/dist/log.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ export declare const logLevels: readonly ["trace", "verbose", "info", "warn", "error"];
2
+ export type LogLevel = (typeof logLevels)[number];
3
+ export declare const isEqualOrBelowLogLevel: (currentLevel: LogLevel, level: LogLevel) => boolean;
4
+ export declare const Log: {
5
+ trace: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
6
+ verbose: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
7
+ info: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
8
+ warn: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
9
+ error: (...args: Parameters<typeof console.log>) => void;
10
+ };
package/dist/log.js ADDED
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Log = exports.isEqualOrBelowLogLevel = exports.logLevels = void 0;
4
+ /* eslint-disable no-console */
5
+ exports.logLevels = ['trace', 'verbose', 'info', 'warn', 'error'];
6
+ const getNumberForLogLevel = (level) => {
7
+ return exports.logLevels.indexOf(level);
8
+ };
9
+ const isEqualOrBelowLogLevel = (currentLevel, level) => {
10
+ return getNumberForLogLevel(currentLevel) <= getNumberForLogLevel(level);
11
+ };
12
+ exports.isEqualOrBelowLogLevel = isEqualOrBelowLogLevel;
13
+ exports.Log = {
14
+ trace: (logLevel, ...args) => {
15
+ if ((0, exports.isEqualOrBelowLogLevel)(logLevel, 'trace')) {
16
+ return console.log(...args);
17
+ }
18
+ },
19
+ verbose: (logLevel, ...args) => {
20
+ if ((0, exports.isEqualOrBelowLogLevel)(logLevel, 'verbose')) {
21
+ return console.log(...args);
22
+ }
23
+ },
24
+ info: (logLevel, ...args) => {
25
+ if ((0, exports.isEqualOrBelowLogLevel)(logLevel, 'info')) {
26
+ return console.log(...args);
27
+ }
28
+ },
29
+ warn: (logLevel, ...args) => {
30
+ if ((0, exports.isEqualOrBelowLogLevel)(logLevel, 'warn')) {
31
+ return console.warn(...args);
32
+ }
33
+ },
34
+ error: (...args) => {
35
+ return console.error(...args);
36
+ },
37
+ };
package/dist/options.d.ts CHANGED
@@ -107,11 +107,14 @@ export type ParseMediaResult<Fields extends Options<ParseMediaFields>> = (Fields
107
107
  } : {}) & (Fields['container'] extends true ? {
108
108
  container: ParseMediaContainer | null;
109
109
  } : {});
110
- export type ParseMedia = <F extends Options<ParseMediaFields>>(options: {
111
- src: string | File;
110
+ export type ParseMediaDynamicOptions<F extends Options<ParseMediaFields>> = {
112
111
  fields?: F;
112
+ } & ParseMediaCallbacks<F>;
113
+ export type ParseMediaOptions<F extends Options<ParseMediaFields>> = {
114
+ src: string | File;
113
115
  reader?: ReaderInterface;
114
116
  onAudioTrack?: OnAudioTrack;
115
117
  onVideoTrack?: OnVideoTrack;
116
118
  signal?: AbortSignal;
117
- } & ParseMediaCallbacks<F>) => Promise<ParseMediaResult<F>>;
119
+ } & ParseMediaDynamicOptions<F>;
120
+ export type ParseMedia = <F extends Options<ParseMediaFields>>(options: ParseMediaOptions<F>) => Promise<ParseMediaResult<F>>;
@@ -80,7 +80,7 @@ exports.fetchReader = {
80
80
  : null;
81
81
  const { supportsContentRange } = validateContentRangeAndDetectIfSupported(actualRange, parsedContentRange, res.status);
82
82
  signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', () => {
83
- controller.abort();
83
+ controller.abort(new Error('Aborted by user'));
84
84
  }, { once: true });
85
85
  if (res.status.toString().startsWith('4') ||
86
86
  res.status.toString().startsWith('5')) {
@@ -4,6 +4,7 @@ export type AudioSample = {
4
4
  timestamp: number;
5
5
  trackId: number;
6
6
  type: 'key' | 'delta';
7
+ duration: number | undefined;
7
8
  };
8
9
  export type VideoSample = {
9
10
  data: Uint8Array;
@@ -18,3 +19,9 @@ export type OnAudioSample = (sample: AudioSample) => void | Promise<void>;
18
19
  export type OnVideoSample = (sample: VideoSample) => void | Promise<void>;
19
20
  export type OnAudioTrack = (track: AudioTrack) => OnAudioSample | Promise<OnAudioSample | null> | null;
20
21
  export type OnVideoTrack = (track: VideoTrack) => OnVideoSample | Promise<OnVideoSample | null> | null;
22
+ export type AudioOrVideoSample = {
23
+ timestamp: number;
24
+ type: 'key' | 'delta';
25
+ data: Uint8Array;
26
+ duration: number | undefined;
27
+ };
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,2 @@
1
+ import type { Writer } from '../writer';
2
+ export declare const createContent: () => Promise<Writer>;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createContent = void 0;
4
+ const createContent = () => {
5
+ const buf = new ArrayBuffer(0, {
6
+ // TODO: Educate that the buffer is limited to 2GB
7
+ maxByteLength: 200000000,
8
+ });
9
+ if (!buf.resize) {
10
+ throw new Error('Could not create buffer writer');
11
+ }
12
+ const write = (newData) => {
13
+ const oldLength = buf.byteLength;
14
+ const newLength = oldLength + newData.byteLength;
15
+ buf.resize(newLength);
16
+ const newArray = new Uint8Array(buf);
17
+ newArray.set(newData, oldLength);
18
+ };
19
+ const updateDataAt = (position, newData) => {
20
+ const newArray = new Uint8Array(buf);
21
+ newArray.set(newData, position);
22
+ };
23
+ let writPromise = Promise.resolve();
24
+ let removed = false;
25
+ const writer = {
26
+ write: (arr) => {
27
+ writPromise = writPromise.then(() => write(arr));
28
+ return writPromise;
29
+ },
30
+ save: () => {
31
+ if (removed) {
32
+ return Promise.reject(new Error('Already called .remove() on the result'));
33
+ }
34
+ const arr = new Uint8Array(buf);
35
+ return Promise.resolve(
36
+ // TODO: Unhardcode MIME type and file name
37
+ new File([arr.slice()], 'hi', { type: 'video/webm' }));
38
+ },
39
+ remove() {
40
+ removed = true;
41
+ return Promise.resolve();
42
+ },
43
+ getWrittenByteCount: () => buf.byteLength,
44
+ updateDataAt: (position, newData) => {
45
+ writPromise = writPromise.then(() => updateDataAt(position, newData));
46
+ return writPromise;
47
+ },
48
+ waitForFinish: async () => {
49
+ await writPromise;
50
+ },
51
+ };
52
+ return Promise.resolve(writer);
53
+ };
54
+ exports.createContent = createContent;
@@ -1,58 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.bufferWriter = void 0;
4
- const createContent = () => {
5
- const buf = new ArrayBuffer(0, {
6
- // TODO: Educate that the buffer is limited to 100MB
7
- maxByteLength: 100000000,
8
- });
9
- if (!buf.resize) {
10
- throw new Error('Could not create buffer writer');
11
- }
12
- let data = new Uint8Array(buf);
13
- const write = (newData) => {
14
- const oldLength = buf.byteLength;
15
- const newLength = oldLength + newData.byteLength;
16
- buf.resize(newLength);
17
- const newArray = new Uint8Array(buf);
18
- newArray.set(newData, oldLength);
19
- data = newArray;
20
- };
21
- const updateDataAt = (position, newData) => {
22
- const newArray = new Uint8Array(buf);
23
- newArray.set(newData, position);
24
- data = newArray;
25
- };
26
- let writPromise = Promise.resolve();
27
- let removed = false;
28
- const writer = {
29
- write: (arr) => {
30
- writPromise = writPromise.then(() => write(arr));
31
- return writPromise;
32
- },
33
- save: () => {
34
- if (removed) {
35
- return Promise.reject(new Error('Already called .remove() on the result'));
36
- }
37
- // TODO: Unhardcode name
38
- return Promise.resolve(new File([data], 'hithere', {}));
39
- },
40
- remove() {
41
- removed = true;
42
- data = new Uint8Array(0);
43
- return Promise.resolve();
44
- },
45
- getWrittenByteCount: () => buf.byteLength,
46
- updateDataAt: (position, newData) => {
47
- writPromise = writPromise.then(() => updateDataAt(position, newData));
48
- return writPromise;
49
- },
50
- waitForFinish: async () => {
51
- await writPromise;
52
- },
53
- };
54
- return Promise.resolve(writer);
55
- };
4
+ const writer_1 = require("./buffer-implementation/writer");
56
5
  exports.bufferWriter = {
57
- createContent,
6
+ createContent: writer_1.createContent,
58
7
  };
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.canUseWebFsWriter = exports.webFsWriter = void 0;
4
4
  const createContent = async () => {
5
5
  const directoryHandle = await navigator.storage.getDirectory();
6
+ // TODO: Unhardcode WebM
6
7
  const filename = `media-parser-${Math.random().toString().replace('0.', '')}.webm`;
7
8
  const fileHandle = await directoryHandle.getFileHandle(filename, {
8
9
  create: true,
@@ -58,6 +59,12 @@ exports.webFsWriter = {
58
59
  createContent,
59
60
  };
60
61
  const canUseWebFsWriter = async () => {
62
+ if (!('storage' in navigator)) {
63
+ return false;
64
+ }
65
+ if (!('getDirectory' in navigator.storage)) {
66
+ return false;
67
+ }
61
68
  const directoryHandle = await navigator.storage.getDirectory();
62
69
  const fileHandle = await directoryHandle.getFileHandle('remotion-probe-web-fs-support', {
63
70
  create: true,
@@ -1,6 +1,6 @@
1
1
  export type Writer = {
2
2
  write: (arr: Uint8Array) => Promise<void>;
3
- save: () => Promise<File>;
3
+ save: () => Promise<File | Blob>;
4
4
  getWrittenByteCount: () => number;
5
5
  updateDataAt: (position: number, data: Uint8Array) => Promise<void>;
6
6
  waitForFinish: () => Promise<void>;
package/package.json CHANGED
@@ -3,14 +3,14 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/media-parser"
4
4
  },
5
5
  "name": "@remotion/media-parser",
6
- "version": "4.0.227",
6
+ "version": "4.0.229",
7
7
  "main": "dist/index.js",
8
8
  "sideEffects": false,
9
9
  "devDependencies": {
10
10
  "@types/wicg-file-system-access": "2023.10.5",
11
11
  "eslint": "9.14.0",
12
- "@remotion/renderer": "4.0.227",
13
- "@remotion/eslint-config-internal": "4.0.227"
12
+ "@remotion/example-videos": "4.0.229",
13
+ "@remotion/eslint-config-internal": "4.0.229"
14
14
  },
15
15
  "publishConfig": {
16
16
  "access": "public"
@@ -77,7 +77,7 @@
77
77
  }
78
78
  },
79
79
  "author": "Jonny Burger <jonny@remotion.dev>",
80
- "license": "SEE LICENSE IN LICENSE.md",
80
+ "license": "Remotion License https://remotion.dev/license",
81
81
  "keywords": [
82
82
  "remotion",
83
83
  "ffmpeg",