@remotion/media 4.0.413 → 4.0.415

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 (54) hide show
  1. package/dist/audio/allow-wait.js +15 -0
  2. package/dist/audio/audio-for-preview.js +304 -0
  3. package/dist/audio/audio-for-rendering.js +194 -0
  4. package/dist/audio/audio-preview-iterator.js +176 -0
  5. package/dist/audio/audio.js +20 -0
  6. package/dist/audio/props.js +1 -0
  7. package/dist/audio-extraction/audio-cache.js +66 -0
  8. package/dist/audio-extraction/audio-iterator.js +132 -0
  9. package/dist/audio-extraction/audio-manager.js +113 -0
  10. package/dist/audio-extraction/extract-audio.js +132 -0
  11. package/dist/audio-iterator-manager.js +228 -0
  12. package/dist/browser-can-use-webgl2.js +13 -0
  13. package/dist/caches.d.ts +3 -2
  14. package/dist/caches.js +61 -0
  15. package/dist/calculate-playbacktime.js +4 -0
  16. package/dist/convert-audiodata/apply-volume.js +17 -0
  17. package/dist/convert-audiodata/combine-audiodata.js +23 -0
  18. package/dist/convert-audiodata/convert-audiodata.js +73 -0
  19. package/dist/convert-audiodata/resample-audiodata.js +94 -0
  20. package/dist/debug-overlay/preview-overlay.js +42 -0
  21. package/dist/esm/index.mjs +40 -30
  22. package/dist/extract-frame-and-audio.js +101 -0
  23. package/dist/get-sink.js +15 -0
  24. package/dist/get-time-in-seconds.js +40 -0
  25. package/dist/helpers/round-to-4-digits.js +4 -0
  26. package/dist/index.js +12 -0
  27. package/dist/is-network-error.d.ts +6 -0
  28. package/dist/is-network-error.js +17 -0
  29. package/dist/is-type-of-error.js +20 -0
  30. package/dist/looped-frame.js +10 -0
  31. package/dist/media-player.js +431 -0
  32. package/dist/nonce-manager.js +13 -0
  33. package/dist/prewarm-iterator-for-looping.js +56 -0
  34. package/dist/render-timestamp-range.js +9 -0
  35. package/dist/show-in-timeline.js +31 -0
  36. package/dist/use-media-in-timeline.js +103 -0
  37. package/dist/video/props.js +1 -0
  38. package/dist/video/video-for-preview.js +331 -0
  39. package/dist/video/video-for-rendering.js +263 -0
  40. package/dist/video/video-preview-iterator.js +122 -0
  41. package/dist/video/video.js +35 -0
  42. package/dist/video-extraction/add-broadcast-channel-listener.js +125 -0
  43. package/dist/video-extraction/extract-frame-via-broadcast-channel.js +113 -0
  44. package/dist/video-extraction/extract-frame.js +85 -0
  45. package/dist/video-extraction/get-allocation-size.js +6 -0
  46. package/dist/video-extraction/get-frames-since-keyframe.js +108 -0
  47. package/dist/video-extraction/keyframe-bank.d.ts +2 -2
  48. package/dist/video-extraction/keyframe-bank.js +159 -0
  49. package/dist/video-extraction/keyframe-manager.d.ts +2 -1
  50. package/dist/video-extraction/keyframe-manager.js +206 -0
  51. package/dist/video-extraction/remember-actual-matroska-timestamps.js +19 -0
  52. package/dist/video-extraction/rotate-frame.js +34 -0
  53. package/dist/video-iterator-manager.js +109 -0
  54. package/package.json +3 -3
@@ -2101,6 +2101,7 @@ var makeAudioCache = () => {
2101
2101
 
2102
2102
  // src/audio-extraction/audio-iterator.ts
2103
2103
  var extraThreshold = 1.5;
2104
+ var safetyOutOfOrderThreshold = 0.2;
2104
2105
  var warned = {};
2105
2106
  var warnAboutMatroskaOnce = (src, logLevel) => {
2106
2107
  if (warned[src]) {
@@ -2143,7 +2144,7 @@ var makeAudioIterator2 = ({
2143
2144
  const getSamples = async (timestamp, durationInSeconds) => {
2144
2145
  lastUsed = Date.now();
2145
2146
  if (fullDuration !== null && timestamp > fullDuration) {
2146
- cache.clearBeforeThreshold(fullDuration - SAFE_WINDOW_OF_MONOTONICITY);
2147
+ cache.clearBeforeThreshold(fullDuration - safetyOutOfOrderThreshold);
2147
2148
  return [];
2148
2149
  }
2149
2150
  const samples = cache.getSamples(timestamp, durationInSeconds);
@@ -2156,7 +2157,7 @@ var makeAudioIterator2 = ({
2156
2157
  while (true) {
2157
2158
  const sample = await getNextSample();
2158
2159
  const deleteBefore = fullDuration === null ? timestamp : Math.min(timestamp, fullDuration);
2159
- cache.clearBeforeThreshold(deleteBefore - SAFE_WINDOW_OF_MONOTONICITY);
2160
+ cache.clearBeforeThreshold(deleteBefore - safetyOutOfOrderThreshold);
2160
2161
  if (sample === null) {
2161
2162
  break;
2162
2163
  }
@@ -2418,10 +2419,11 @@ var makeKeyframeBank = async ({
2418
2419
  continue;
2419
2420
  }
2420
2421
  }
2421
- if (frameTimestamp < timestampInSeconds) {
2422
- if (!frames[frameTimestamp]) {
2423
- continue;
2424
- }
2422
+ if (!frames[frameTimestamp]) {
2423
+ continue;
2424
+ }
2425
+ const { duration } = frames[frameTimestamp];
2426
+ if (frameTimestamp + duration < timestampInSeconds) {
2425
2427
  deleteFrameAtTimestamp(frameTimestamp);
2426
2428
  deletedTimestamps.push(frameTimestamp);
2427
2429
  }
@@ -2451,7 +2453,7 @@ var makeKeyframeBank = async ({
2451
2453
  lastUsed = Date.now();
2452
2454
  Internals9.Log.trace({ logLevel, tag: "@remotion/media" }, `Added frame at ${frame.timestamp}sec to bank`);
2453
2455
  };
2454
- const ensureEnoughFramesForTimestamp = async (timestampInSeconds, logLevel) => {
2456
+ const ensureEnoughFramesForTimestamp = async (timestampInSeconds, logLevel, fps) => {
2455
2457
  while (!hasDecodedEnoughForTimestamp(timestampInSeconds)) {
2456
2458
  const sample = await sampleIterator.next();
2457
2459
  if (sample.value) {
@@ -2463,18 +2465,18 @@ var makeKeyframeBank = async ({
2463
2465
  }
2464
2466
  deleteFramesBeforeTimestamp({
2465
2467
  logLevel: parentLogLevel,
2466
- timestampInSeconds: timestampInSeconds - SAFE_WINDOW_OF_MONOTONICITY
2468
+ timestampInSeconds: timestampInSeconds - getSafeWindowOfMonotonicity(fps)
2467
2469
  });
2468
2470
  }
2469
2471
  lastUsed = Date.now();
2470
2472
  };
2471
- const getFrameFromTimestamp = async (timestampInSeconds) => {
2473
+ const getFrameFromTimestamp = async (timestampInSeconds, fps) => {
2472
2474
  lastUsed = Date.now();
2473
2475
  let adjustedTimestamp = timestampInSeconds;
2474
2476
  if (hasReachedEndOfVideo && roundTo4Digits(adjustedTimestamp) > roundTo4Digits(frameTimestamps[frameTimestamps.length - 1])) {
2475
2477
  adjustedTimestamp = frameTimestamps[frameTimestamps.length - 1];
2476
2478
  }
2477
- await ensureEnoughFramesForTimestamp(adjustedTimestamp, parentLogLevel);
2479
+ await ensureEnoughFramesForTimestamp(adjustedTimestamp, parentLogLevel, fps);
2478
2480
  for (let i = frameTimestamps.length - 1;i >= 0; i--) {
2479
2481
  const sample = frames[frameTimestamps[i]];
2480
2482
  if (!sample) {
@@ -2486,8 +2488,8 @@ var makeKeyframeBank = async ({
2486
2488
  }
2487
2489
  return frames[frameTimestamps[0]] ?? null;
2488
2490
  };
2489
- const hasTimestampInSecond = async (timestamp) => {
2490
- return await getFrameFromTimestamp(timestamp) !== null;
2491
+ const hasTimestampInSecond = async (timestamp, fps) => {
2492
+ return await getFrameFromTimestamp(timestamp, fps) !== null;
2491
2493
  };
2492
2494
  const getOpenFrameCount = () => {
2493
2495
  return {
@@ -2510,9 +2512,12 @@ var makeKeyframeBank = async ({
2510
2512
  if (frameTimestamps.length === 0) {
2511
2513
  return null;
2512
2514
  }
2515
+ const firstTimestamp = frameTimestamps[0];
2516
+ const lastTimestamp = frameTimestamps[frameTimestamps.length - 1];
2517
+ const lastFrame = frames[lastTimestamp];
2513
2518
  return {
2514
- firstTimestamp: frameTimestamps[0],
2515
- lastTimestamp: frameTimestamps[frameTimestamps.length - 1]
2519
+ firstTimestamp,
2520
+ lastTimestamp: lastTimestamp + lastFrame.duration
2516
2521
  };
2517
2522
  };
2518
2523
  const prepareForDeletion = (logLevel, reason) => {
@@ -2553,13 +2558,13 @@ var makeKeyframeBank = async ({
2553
2558
  return true;
2554
2559
  };
2555
2560
  const keyframeBank = {
2556
- getFrameFromTimestamp: (timestamp) => {
2557
- queue = queue.then(() => getFrameFromTimestamp(timestamp));
2561
+ getFrameFromTimestamp: (timestamp, fps) => {
2562
+ queue = queue.then(() => getFrameFromTimestamp(timestamp, fps));
2558
2563
  return queue;
2559
2564
  },
2560
2565
  prepareForDeletion,
2561
- hasTimestampInSecond: (timestamp) => {
2562
- queue = queue.then(() => hasTimestampInSecond(timestamp));
2566
+ hasTimestampInSecond: (timestamp, fps) => {
2567
+ queue = queue.then(() => hasTimestampInSecond(timestamp, fps));
2563
2568
  return queue;
2564
2569
  },
2565
2570
  addFrame,
@@ -2673,9 +2678,10 @@ var makeKeyframeManager = () => {
2673
2678
  const clearKeyframeBanksBeforeTime = ({
2674
2679
  timestampInSeconds,
2675
2680
  src,
2676
- logLevel
2681
+ logLevel,
2682
+ fps
2677
2683
  }) => {
2678
- const threshold = timestampInSeconds - SAFE_WINDOW_OF_MONOTONICITY;
2684
+ const threshold = timestampInSeconds - getSafeWindowOfMonotonicity(fps);
2679
2685
  if (!sources[src]) {
2680
2686
  return;
2681
2687
  }
@@ -2685,9 +2691,8 @@ var makeKeyframeManager = () => {
2685
2691
  if (!range) {
2686
2692
  continue;
2687
2693
  }
2688
- const { lastTimestamp } = range;
2689
- if (lastTimestamp < threshold) {
2690
- bank.prepareForDeletion(logLevel, "cleared before threshold");
2694
+ if (range.lastTimestamp < threshold) {
2695
+ bank.prepareForDeletion(logLevel, "cleared before threshold " + threshold);
2691
2696
  Internals10.Log.verbose({ logLevel, tag: "@remotion/media" }, `[Video] Cleared frames for src ${src} from ${range.firstTimestamp}sec to ${range.lastTimestamp}sec`);
2692
2697
  const bankIndex = banks.indexOf(bank);
2693
2698
  delete sources[src][bankIndex];
@@ -2741,13 +2746,15 @@ var makeKeyframeManager = () => {
2741
2746
  videoSampleSink,
2742
2747
  src,
2743
2748
  logLevel,
2744
- maxCacheSize
2749
+ maxCacheSize,
2750
+ fps
2745
2751
  }) => {
2746
2752
  ensureToStayUnderMaxCacheSize(logLevel, maxCacheSize);
2747
2753
  clearKeyframeBanksBeforeTime({
2748
2754
  timestampInSeconds: timestamp,
2749
2755
  src,
2750
- logLevel
2756
+ logLevel,
2757
+ fps
2751
2758
  });
2752
2759
  const keyframeBank = await getKeyframeBankOrRefetch({
2753
2760
  timestamp,
@@ -2775,14 +2782,16 @@ var makeKeyframeManager = () => {
2775
2782
  videoSampleSink,
2776
2783
  src,
2777
2784
  logLevel,
2778
- maxCacheSize
2785
+ maxCacheSize,
2786
+ fps
2779
2787
  }) => {
2780
2788
  queue = queue.then(() => requestKeyframeBank({
2781
2789
  timestamp,
2782
2790
  videoSampleSink,
2783
2791
  src,
2784
2792
  logLevel,
2785
- maxCacheSize
2793
+ maxCacheSize,
2794
+ fps
2786
2795
  }));
2787
2796
  return queue;
2788
2797
  },
@@ -2792,7 +2801,7 @@ var makeKeyframeManager = () => {
2792
2801
  };
2793
2802
 
2794
2803
  // src/caches.ts
2795
- var SAFE_WINDOW_OF_MONOTONICITY = 0.2;
2804
+ var getSafeWindowOfMonotonicity = (fps) => 0.2 * 30 / fps;
2796
2805
  var keyframeManager = makeKeyframeManager();
2797
2806
  var audioManager = makeAudioManager();
2798
2807
  var getTotalCacheStats = () => {
@@ -3417,7 +3426,8 @@ var extractFrameInternal = async ({
3417
3426
  timestamp: timeInSeconds,
3418
3427
  src,
3419
3428
  logLevel,
3420
- maxCacheSize
3429
+ maxCacheSize,
3430
+ fps
3421
3431
  });
3422
3432
  if (!keyframeBank) {
3423
3433
  return {
@@ -3427,7 +3437,7 @@ var extractFrameInternal = async ({
3427
3437
  durationInSeconds: await sink.getDuration()
3428
3438
  };
3429
3439
  }
3430
- const frame = await keyframeBank.getFrameFromTimestamp(timeInSeconds);
3440
+ const frame = await keyframeBank.getFrameFromTimestamp(timeInSeconds, fps);
3431
3441
  const rotation = frame?.rotation ?? 0;
3432
3442
  return {
3433
3443
  type: "success",
@@ -0,0 +1,101 @@
1
+ import { extractAudio } from './audio-extraction/extract-audio';
2
+ import { isNetworkError } from './is-type-of-error';
3
+ import { extractFrame } from './video-extraction/extract-frame';
4
+ import { rotateFrame } from './video-extraction/rotate-frame';
5
+ export const extractFrameAndAudio = async ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, loop, audioStreamIndex, trimAfter, trimBefore, fps, maxCacheSize, }) => {
6
+ try {
7
+ const [frame, audio] = await Promise.all([
8
+ includeVideo
9
+ ? extractFrame({
10
+ src,
11
+ timeInSeconds,
12
+ logLevel,
13
+ loop,
14
+ trimAfter,
15
+ playbackRate,
16
+ trimBefore,
17
+ fps,
18
+ maxCacheSize,
19
+ })
20
+ : null,
21
+ includeAudio
22
+ ? extractAudio({
23
+ src,
24
+ timeInSeconds,
25
+ durationInSeconds,
26
+ logLevel,
27
+ loop,
28
+ playbackRate,
29
+ audioStreamIndex,
30
+ trimAfter,
31
+ fps,
32
+ trimBefore,
33
+ maxCacheSize,
34
+ })
35
+ : null,
36
+ ]);
37
+ if (frame?.type === 'cannot-decode') {
38
+ return {
39
+ type: 'cannot-decode',
40
+ durationInSeconds: frame.durationInSeconds,
41
+ };
42
+ }
43
+ if (frame?.type === 'unknown-container-format') {
44
+ return { type: 'unknown-container-format' };
45
+ }
46
+ if (frame?.type === 'cannot-decode-alpha') {
47
+ return {
48
+ type: 'cannot-decode-alpha',
49
+ durationInSeconds: frame.durationInSeconds,
50
+ };
51
+ }
52
+ if (frame?.type === 'network-error') {
53
+ return { type: 'network-error' };
54
+ }
55
+ if (audio === 'unknown-container-format') {
56
+ if (frame !== null) {
57
+ frame?.frame?.close();
58
+ }
59
+ return { type: 'unknown-container-format' };
60
+ }
61
+ if (audio === 'network-error') {
62
+ if (frame !== null) {
63
+ frame?.frame?.close();
64
+ }
65
+ return { type: 'network-error' };
66
+ }
67
+ if (audio === 'cannot-decode') {
68
+ if (frame?.type === 'success' && frame.frame !== null) {
69
+ frame?.frame.close();
70
+ }
71
+ return {
72
+ type: 'cannot-decode',
73
+ durationInSeconds: frame?.type === 'success' ? frame.durationInSeconds : null,
74
+ };
75
+ }
76
+ if (!frame?.frame) {
77
+ return {
78
+ type: 'success',
79
+ frame: null,
80
+ audio: audio?.data ?? null,
81
+ durationInSeconds: audio?.durationInSeconds ?? null,
82
+ };
83
+ }
84
+ return {
85
+ type: 'success',
86
+ frame: await rotateFrame({
87
+ frame: frame.frame.toVideoFrame(),
88
+ rotation: frame.frame.rotation,
89
+ }),
90
+ audio: audio?.data ?? null,
91
+ durationInSeconds: audio?.durationInSeconds ?? null,
92
+ };
93
+ }
94
+ catch (err) {
95
+ const error = err;
96
+ if (isNetworkError(error)) {
97
+ return { type: 'network-error' };
98
+ }
99
+ throw err;
100
+ }
101
+ };
@@ -0,0 +1,15 @@
1
+ import { Internals } from 'remotion';
2
+ import { getSinks } from './video-extraction/get-frames-since-keyframe';
3
+ export const sinkPromises = {};
4
+ export const getSink = (src, logLevel) => {
5
+ let promise = sinkPromises[src];
6
+ if (!promise) {
7
+ Internals.Log.verbose({
8
+ logLevel,
9
+ tag: '@remotion/media',
10
+ }, `Sink for ${src} was not found, creating new sink`);
11
+ promise = getSinks(src);
12
+ sinkPromises[src] = promise;
13
+ }
14
+ return promise;
15
+ };
@@ -0,0 +1,40 @@
1
+ import { Internals } from 'remotion';
2
+ export const getTimeInSeconds = ({ loop, mediaDurationInSeconds, unloopedTimeInSeconds, src, trimAfter, trimBefore, fps, playbackRate, ifNoMediaDuration, }) => {
3
+ if (mediaDurationInSeconds === null && loop && ifNoMediaDuration === 'fail') {
4
+ throw new Error(`Could not determine duration of ${src}, but "loop" was set.`);
5
+ }
6
+ const loopDuration = loop
7
+ ? Internals.calculateMediaDuration({
8
+ trimAfter,
9
+ mediaDurationInFrames: mediaDurationInSeconds
10
+ ? mediaDurationInSeconds * fps
11
+ : Infinity,
12
+ // Playback rate was already specified before
13
+ playbackRate: 1,
14
+ trimBefore,
15
+ }) / fps
16
+ : Infinity;
17
+ const timeInSeconds = (unloopedTimeInSeconds * playbackRate) % loopDuration;
18
+ if ((trimAfter ?? null) !== null && !loop) {
19
+ const time = (trimAfter - (trimBefore ?? 0)) / fps;
20
+ if (timeInSeconds >= time) {
21
+ return null;
22
+ }
23
+ }
24
+ return timeInSeconds + (trimBefore ?? 0) / fps;
25
+ };
26
+ export const calculateEndTime = ({ mediaDurationInSeconds, ifNoMediaDuration, src, trimAfter, trimBefore, fps, }) => {
27
+ if (mediaDurationInSeconds === null && ifNoMediaDuration === 'fail') {
28
+ throw new Error(`Could not determine duration of ${src}, but "loop" was set.`);
29
+ }
30
+ const mediaDuration = Internals.calculateMediaDuration({
31
+ trimAfter,
32
+ mediaDurationInFrames: mediaDurationInSeconds
33
+ ? mediaDurationInSeconds * fps
34
+ : Infinity,
35
+ // Playback rate was already specified before
36
+ playbackRate: 1,
37
+ trimBefore,
38
+ }) / fps;
39
+ return mediaDuration + (trimBefore ?? 0) / fps;
40
+ };
@@ -0,0 +1,4 @@
1
+ // Round to only 4 digits, because WebM has a timescale of 1_000, e.g. framer.webm
2
+ export const roundTo4Digits = (timestamp) => {
3
+ return Math.round(timestamp * 1000) / 1000;
4
+ };
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ import { Audio } from './audio/audio';
2
+ import { Video } from './video/video';
3
+ /**
4
+ * @deprecated Now just `Audio`
5
+ */
6
+ export const experimental_Audio = Audio;
7
+ /**
8
+ * @deprecated Now just `Video`
9
+ */
10
+ export const experimental_Video = Video;
11
+ export { AudioForPreview } from './audio/audio-for-preview';
12
+ export { Audio, Video };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Utility to check if error is network error
3
+ * @param error
4
+ * @returns
5
+ */
6
+ export declare function isNetworkError(error: Error): boolean;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Utility to check if error is network error
3
+ * @param error
4
+ * @returns
5
+ */
6
+ export function isNetworkError(error) {
7
+ if (
8
+ // Chrome
9
+ error.message.includes('Failed to fetch') ||
10
+ // Safari
11
+ error.message.includes('Load failed') ||
12
+ // Firefox
13
+ error.message.includes('NetworkError when attempting to fetch resource')) {
14
+ return true;
15
+ }
16
+ return false;
17
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Utility to check if error is network error
3
+ * @param error
4
+ * @returns
5
+ */
6
+ export function isNetworkError(error) {
7
+ if (
8
+ // Chrome
9
+ error.message.includes('Failed to fetch') ||
10
+ // Safari
11
+ error.message.includes('Load failed') ||
12
+ // Firefox
13
+ error.message.includes('NetworkError when attempting to fetch resource')) {
14
+ return true;
15
+ }
16
+ return false;
17
+ }
18
+ export function isUnsupportedConfigurationError(error) {
19
+ return error.message.includes('Unsupported configuration');
20
+ }
@@ -0,0 +1,10 @@
1
+ export const frameForVolumeProp = ({ behavior, loop, assetDurationInSeconds, fps, frame, startsAt, }) => {
2
+ if (!loop) {
3
+ return frame + startsAt;
4
+ }
5
+ if (behavior === 'extend') {
6
+ return frame + startsAt;
7
+ }
8
+ const assetDurationInFrames = Math.floor(assetDurationInSeconds * fps) - startsAt;
9
+ return (frame % assetDurationInFrames) + startsAt;
10
+ };