@remotion/media-parser 4.0.310 → 4.0.312

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/containers/aac/get-seeking-byte.js +5 -1
  2. package/dist/containers/flac/get-seeking-byte.d.ts +2 -1
  3. package/dist/containers/flac/get-seeking-byte.js +1 -1
  4. package/dist/containers/iso-base-media/find-keyframe-before-time.d.ts +1 -1
  5. package/dist/containers/iso-base-media/find-keyframe-before-time.js +1 -1
  6. package/dist/containers/iso-base-media/get-seeking-byte-from-fragmented-mp4.js +3 -1
  7. package/dist/containers/iso-base-media/get-seeking-byte.js +3 -1
  8. package/dist/containers/m3u/get-seeking-byte.js +2 -0
  9. package/dist/containers/mp3/get-seeking-byte.js +4 -1
  10. package/dist/containers/riff/get-seeking-byte.js +3 -0
  11. package/dist/containers/wav/get-seeking-byte.js +1 -0
  12. package/dist/containers/wav/parse-list.js +4 -3
  13. package/dist/containers/webm/seek/get-seeking-byte.js +21 -6
  14. package/dist/controller/media-parser-controller.d.ts +3 -0
  15. package/dist/controller/media-parser-controller.js +15 -0
  16. package/dist/esm/index.mjs +226 -131
  17. package/dist/esm/server-worker.mjs +20 -1
  18. package/dist/esm/worker-server-entry.mjs +248 -130
  19. package/dist/esm/worker-web-entry.mjs +248 -130
  20. package/dist/esm/worker.mjs +31 -1
  21. package/dist/get-seeking-byte.js +13 -2
  22. package/dist/index.cjs +54 -0
  23. package/dist/index.d.ts +1 -0
  24. package/dist/internal-parse-media.js +25 -0
  25. package/dist/parse-media-on-worker-entry.js +19 -1
  26. package/dist/version.d.ts +1 -1
  27. package/dist/version.js +1 -1
  28. package/dist/webcodec-sample-types.d.ts +2 -2
  29. package/dist/work-on-seek-request.d.ts +22 -0
  30. package/dist/work-on-seek-request.js +3 -2
  31. package/dist/worker/forward-controller-to-worker.js +18 -0
  32. package/dist/worker/serialize-error.js +14 -4
  33. package/dist/worker/worker-types.d.ts +17 -3
  34. package/package.json +3 -3
@@ -23,7 +23,11 @@ const getSeekingByteForAac = ({ time, seekingHints, }) => {
23
23
  }
24
24
  }
25
25
  if (bestAudioSample) {
26
- return { type: 'do-seek', byte: bestAudioSample.offset };
26
+ return {
27
+ type: 'do-seek',
28
+ byte: bestAudioSample.offset,
29
+ timeInSeconds: bestAudioSample.timeInSeconds,
30
+ };
27
31
  }
28
32
  return { type: 'valid-but-must-wait' };
29
33
  };
@@ -1,5 +1,6 @@
1
+ import type { AudioSampleOffset } from '../../state/audio-sample-map';
1
2
  import type { FlacSeekingHints } from './seeking-hints';
2
3
  export declare const getSeekingByteForFlac: ({ time, seekingHints, }: {
3
4
  time: number;
4
5
  seekingHints: FlacSeekingHints;
5
- }) => number | null;
6
+ }) => AudioSampleOffset | null;
@@ -23,7 +23,7 @@ const getSeekingByteForFlac = ({ time, seekingHints, }) => {
23
23
  }
24
24
  }
25
25
  if (bestAudioSample) {
26
- return bestAudioSample.offset;
26
+ return bestAudioSample;
27
27
  }
28
28
  return null;
29
29
  };
@@ -8,4 +8,4 @@ export declare const findKeyframeBeforeTime: ({ samplePositions, time, timescale
8
8
  mediaSections: MediaSection[];
9
9
  logLevel: MediaParserLogLevel;
10
10
  startInSeconds: number;
11
- }) => number | null;
11
+ }) => SamplePosition | null;
@@ -28,6 +28,6 @@ const findKeyframeBeforeTime = ({ samplePositions, time, timescale, mediaSection
28
28
  log_1.Log.trace(logLevel, 'Found a sample, but the offset has not yet been marked as a video section yet. Not yet able to seek, but probably once we have started reading the next box.', videoSample);
29
29
  return null;
30
30
  }
31
- return videoSample.offset;
31
+ return videoSample;
32
32
  };
33
33
  exports.findKeyframeBeforeTime = findKeyframeBeforeTime;
@@ -59,7 +59,9 @@ const getSeekingByteFromFragmentedMp4 = async ({ info, time, logLevel, currentPo
59
59
  if (kf) {
60
60
  return {
61
61
  type: 'do-seek',
62
- byte: kf,
62
+ byte: kf.offset,
63
+ timeInSeconds: Math.min(kf.decodingTimestamp, kf.timestamp) /
64
+ firstTrack.originalTimescale,
63
65
  };
64
66
  }
65
67
  }
@@ -58,7 +58,9 @@ const getSeekingByteFromIsoBaseMedia = ({ info, time, logLevel, currentPosition,
58
58
  if (keyframe) {
59
59
  return Promise.resolve({
60
60
  type: 'do-seek',
61
- byte: keyframe,
61
+ byte: keyframe.offset,
62
+ timeInSeconds: Math.min(keyframe.decodingTimestamp, keyframe.timestamp) /
63
+ track.originalTimescale,
62
64
  });
63
65
  }
64
66
  return Promise.resolve({
@@ -27,6 +27,8 @@ const getSeekingByteForM3u8 = ({ time, currentPosition, m3uState, logLevel, }) =
27
27
  return {
28
28
  type: 'do-seek',
29
29
  byte: currentPosition,
30
+ // TODO: This will be imperfect when seeking in playMedia()
31
+ timeInSeconds: time,
30
32
  };
31
33
  };
32
34
  exports.getSeekingByteForM3u8 = getSeekingByteForM3u8;
@@ -41,9 +41,12 @@ const getSeekingByteForMp3 = ({ time, info, }) => {
41
41
  type: 'valid-but-must-wait',
42
42
  };
43
43
  }
44
+ const byte = Math.max(...candidates);
45
+ const timeInSeconds = byte === (bestAudioSample === null || bestAudioSample === void 0 ? void 0 : bestAudioSample.offset) ? bestAudioSample.timeInSeconds : time;
44
46
  return {
45
47
  type: 'do-seek',
46
- byte: Math.max(...candidates),
48
+ byte,
49
+ timeInSeconds,
47
50
  };
48
51
  };
49
52
  exports.getSeekingByteForMp3 = getSeekingByteForMp3;
@@ -22,6 +22,7 @@ const getSeekingByteForRiff = async ({ info, time, riffState, avcState, }) => {
22
22
  return {
23
23
  type: 'do-seek',
24
24
  byte: lastKeyframe.positionInBytes,
25
+ timeInSeconds: Math.min(lastKeyframe.decodingTimeInSeconds, lastKeyframe.presentationTimeInSeconds),
25
26
  };
26
27
  }
27
28
  if (idx1Entries.videoTrackIndex === null) {
@@ -55,6 +56,8 @@ const getSeekingByteForRiff = async ({ info, time, riffState, avcState, }) => {
55
56
  return {
56
57
  type: 'do-seek',
57
58
  byte: bestEntry.offset + info.moviOffset - 4,
59
+ timeInSeconds: bestEntry.sampleCounts[idx1Entries.videoTrackIndex] /
60
+ info.samplesPerSecond,
58
61
  };
59
62
  };
60
63
  exports.getSeekingByteForRiff = getSeekingByteForRiff;
@@ -10,6 +10,7 @@ const getSeekingByteFromWav = ({ info, time, }) => {
10
10
  return Promise.resolve({
11
11
  type: 'do-seek',
12
12
  byte: byteOffset + info.mediaSection.start,
13
+ timeInSeconds: timeRoundedDown,
13
14
  });
14
15
  };
15
16
  exports.getSeekingByteFromWav = getSeekingByteFromWav;
@@ -16,10 +16,11 @@ const parseList = ({ state, }) => {
16
16
  // Padding
17
17
  // https://discord.com/channels/809501355504959528/1308803317480292482/1343979547246333983
18
18
  // Indie_Hacker_Podcast (2).wav
19
- if (remainingBytes() < 4) {
20
- iterator.discard(remainingBytes());
21
- break;
19
+ const byte = iterator.getUint8();
20
+ if (byte === 0) {
21
+ continue;
22
22
  }
23
+ iterator.counter.decrement(1);
23
24
  const key = iterator.getByteString(4, false);
24
25
  const size = iterator.getUint32Le();
25
26
  const value = iterator.getByteString(size, true);
@@ -19,7 +19,6 @@ const findBiggestCueBeforeTime = ({ cues, time, track, }) => {
19
19
  return biggestCueBeforeTime;
20
20
  };
21
21
  const findKeyframeBeforeTime = ({ keyframes, time, }) => {
22
- var _a;
23
22
  let keyframeBeforeTime;
24
23
  for (const keyframe of keyframes) {
25
24
  if (keyframe.decodingTimeInSeconds < time &&
@@ -29,7 +28,7 @@ const findKeyframeBeforeTime = ({ keyframes, time, }) => {
29
28
  keyframeBeforeTime = keyframe;
30
29
  }
31
30
  }
32
- return (_a = keyframeBeforeTime === null || keyframeBeforeTime === void 0 ? void 0 : keyframeBeforeTime.positionInBytes) !== null && _a !== void 0 ? _a : null;
31
+ return keyframeBeforeTime !== null && keyframeBeforeTime !== void 0 ? keyframeBeforeTime : null;
33
32
  };
34
33
  const getByteFromCues = ({ cuesResponse, time, info, logLevel, }) => {
35
34
  if (!cuesResponse) {
@@ -46,10 +45,13 @@ const getByteFromCues = ({ cuesResponse, time, info, logLevel, }) => {
46
45
  if (!biggestCueBeforeTime) {
47
46
  return null;
48
47
  }
49
- return biggestCueBeforeTime.clusterPositionInSegment + segmentOffset;
48
+ return {
49
+ byte: biggestCueBeforeTime.clusterPositionInSegment + segmentOffset,
50
+ timeInSeconds: toSeconds(biggestCueBeforeTime.timeInTimescale, info.track),
51
+ };
50
52
  };
51
53
  const getSeekingByteFromMatroska = async ({ time, webmState, info, logLevel, mediaSection, }) => {
52
- var _a, _b, _c;
54
+ var _a, _b, _c, _d, _e;
53
55
  if (!info.track) {
54
56
  log_1.Log.trace(logLevel, 'No video track found, cannot seek yet');
55
57
  return {
@@ -74,8 +76,8 @@ const getSeekingByteFromMatroska = async ({ time, webmState, info, logLevel, med
74
76
  // Optimization possibility for later:
75
77
  // Don't seek back, if the last seen time is smaller than the time we want to seek to
76
78
  const seekPossibilities = [
77
- byteFromCues,
78
- byteFromObservedKeyframe,
79
+ (_d = byteFromCues === null || byteFromCues === void 0 ? void 0 : byteFromCues.byte) !== null && _d !== void 0 ? _d : null,
80
+ (_e = byteFromObservedKeyframe === null || byteFromObservedKeyframe === void 0 ? void 0 : byteFromObservedKeyframe.positionInBytes) !== null && _e !== void 0 ? _e : null,
79
81
  byteFromFirstMediaSection,
80
82
  ].filter((n) => n !== null);
81
83
  const byteToSeekTo = seekPossibilities.length === 0 ? null : Math.max(...seekPossibilities);
@@ -92,9 +94,22 @@ const getSeekingByteFromMatroska = async ({ time, webmState, info, logLevel, med
92
94
  start: byteToSeekTo,
93
95
  size: 1,
94
96
  });
97
+ const timeInSeconds = (() => {
98
+ if (byteToSeekTo === (byteFromObservedKeyframe === null || byteFromObservedKeyframe === void 0 ? void 0 : byteFromObservedKeyframe.positionInBytes)) {
99
+ return Math.min(byteFromObservedKeyframe.decodingTimeInSeconds, byteFromObservedKeyframe.presentationTimeInSeconds);
100
+ }
101
+ if (byteToSeekTo === (byteFromCues === null || byteFromCues === void 0 ? void 0 : byteFromCues.byte)) {
102
+ return byteFromCues.timeInSeconds;
103
+ }
104
+ if (byteToSeekTo === byteFromFirstMediaSection) {
105
+ return 0;
106
+ }
107
+ throw new Error('Should not happen');
108
+ })();
95
109
  return {
96
110
  type: 'do-seek',
97
111
  byte: byteToSeekTo,
112
+ timeInSeconds,
98
113
  };
99
114
  };
100
115
  exports.getSeekingByteFromMatroska = getSeekingByteFromMatroska;
@@ -1,4 +1,5 @@
1
1
  import type { SeekingHints } from '../seeking-hints';
2
+ import type { SeekResolution } from '../work-on-seek-request';
2
3
  import { MediaParserEmitter } from './emitter';
3
4
  import type { PauseSignal } from './pause-signal';
4
5
  import type { PerformedSeeksSignal } from './performed-seeks-stats';
@@ -8,6 +9,7 @@ export type MediaParserController = {
8
9
  pause: PauseSignal['pause'];
9
10
  resume: PauseSignal['resume'];
10
11
  seek: SeekSignal['seek'];
12
+ simulateSeek: (seekInSeconds: number) => Promise<SeekResolution>;
11
13
  addEventListener: MediaParserEmitter['addEventListener'];
12
14
  removeEventListener: MediaParserEmitter['removeEventListener'];
13
15
  getSeekingHints: () => Promise<SeekingHints | null>;
@@ -21,6 +23,7 @@ export type MediaParserController = {
21
23
  markAsReadyToEmitEvents: () => void;
22
24
  performedSeeksSignal: PerformedSeeksSignal;
23
25
  attachSeekingHintResolution: (callback: () => Promise<SeekingHints | null>) => void;
26
+ attachSimulateSeekResolution: (callback: (seekInSeconds: number) => Promise<SeekResolution>) => void;
24
27
  };
25
28
  };
26
29
  export declare const mediaParserController: () => MediaParserController;
@@ -23,18 +23,31 @@ const mediaParserController = () => {
23
23
  await pauseSignal.waitUntilResume();
24
24
  };
25
25
  let seekingHintResolution = null;
26
+ let simulateSeekResolution = null;
26
27
  const getSeekingHints = () => {
27
28
  if (!seekingHintResolution) {
28
29
  throw new Error('The mediaParserController() was not yet used in a parseMedia() call');
29
30
  }
30
31
  return seekingHintResolution();
31
32
  };
33
+ const simulateSeek = (seekInSeconds) => {
34
+ if (!simulateSeekResolution) {
35
+ throw new Error('The mediaParserController() was not yet used in a parseMedia() call');
36
+ }
37
+ return simulateSeekResolution(seekInSeconds);
38
+ };
32
39
  const attachSeekingHintResolution = (callback) => {
33
40
  if (seekingHintResolution) {
34
41
  throw new Error('The mediaParserController() was used in multiple parseMedia() calls. Create a separate controller for each call.');
35
42
  }
36
43
  seekingHintResolution = callback;
37
44
  };
45
+ const attachSimulateSeekResolution = (callback) => {
46
+ if (simulateSeekResolution) {
47
+ throw new Error('The mediaParserController() was used in multiple parseMedia() calls. Create a separate controller for each call.');
48
+ }
49
+ simulateSeekResolution = callback;
50
+ };
38
51
  return {
39
52
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
53
  abort: (reason) => {
@@ -42,6 +55,7 @@ const mediaParserController = () => {
42
55
  emitter.dispatchAbort(reason);
43
56
  },
44
57
  seek: seekSignal.seek,
58
+ simulateSeek,
45
59
  pause: pauseSignal.pause,
46
60
  resume: pauseSignal.resume,
47
61
  addEventListener: emitter.addEventListener,
@@ -54,6 +68,7 @@ const mediaParserController = () => {
54
68
  markAsReadyToEmitEvents: emitter.markAsReady,
55
69
  performedSeeksSignal,
56
70
  attachSeekingHintResolution,
71
+ attachSimulateSeekResolution,
57
72
  },
58
73
  };
59
74
  };