@remotion/media 4.0.352 → 4.0.354

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 (43) hide show
  1. package/dist/audio/audio-for-rendering.js +37 -27
  2. package/dist/audio/audio.js +6 -3
  3. package/dist/audio/props.d.ts +1 -7
  4. package/dist/audio-extraction/audio-iterator.d.ts +1 -1
  5. package/dist/audio-extraction/audio-iterator.js +2 -2
  6. package/dist/audio-extraction/audio-manager.d.ts +1 -1
  7. package/dist/audio-extraction/extract-audio.d.ts +7 -4
  8. package/dist/audio-extraction/extract-audio.js +16 -7
  9. package/dist/caches.d.ts +6 -6
  10. package/dist/caches.js +5 -6
  11. package/dist/convert-audiodata/apply-volume.d.ts +1 -0
  12. package/dist/convert-audiodata/apply-volume.js +17 -0
  13. package/dist/convert-audiodata/convert-audiodata.d.ts +2 -2
  14. package/dist/convert-audiodata/convert-audiodata.js +13 -7
  15. package/dist/convert-audiodata/resample-audiodata.d.ts +1 -2
  16. package/dist/convert-audiodata/resample-audiodata.js +42 -20
  17. package/dist/esm/index.mjs +242 -182
  18. package/dist/extract-frame-and-audio.d.ts +3 -2
  19. package/dist/extract-frame-and-audio.js +4 -3
  20. package/dist/looped-frame.d.ts +9 -0
  21. package/dist/looped-frame.js +10 -0
  22. package/dist/video/media-player.d.ts +28 -30
  23. package/dist/video/media-player.js +174 -314
  24. package/dist/video/new-video-for-preview.d.ts +1 -1
  25. package/dist/video/new-video-for-preview.js +12 -18
  26. package/dist/video/props.d.ts +0 -5
  27. package/dist/video/timeout-utils.d.ts +2 -0
  28. package/dist/video/timeout-utils.js +18 -0
  29. package/dist/video/video-for-preview.d.ts +11 -0
  30. package/dist/video/video-for-preview.js +113 -0
  31. package/dist/video/video-for-rendering.js +41 -31
  32. package/dist/video/video.js +2 -2
  33. package/dist/video-extraction/extract-frame-via-broadcast-channel.d.ts +4 -3
  34. package/dist/video-extraction/extract-frame-via-broadcast-channel.js +9 -5
  35. package/dist/video-extraction/extract-frame.d.ts +1 -1
  36. package/dist/video-extraction/extract-frame.js +3 -0
  37. package/dist/video-extraction/get-frames-since-keyframe.d.ts +1 -1
  38. package/dist/video-extraction/get-frames-since-keyframe.js +7 -8
  39. package/dist/video-extraction/keyframe-bank.d.ts +1 -1
  40. package/dist/video-extraction/keyframe-bank.js +7 -7
  41. package/dist/video-extraction/keyframe-manager.d.ts +1 -1
  42. package/dist/video-extraction/keyframe-manager.js +6 -6
  43. package/package.json +3 -3
@@ -1,14 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useContext, useEffect, useRef, useState } from 'react';
3
3
  import { Internals, useBufferState, useCurrentFrame } from 'remotion';
4
- import { Log } from '../log';
5
4
  import { MediaPlayer } from './media-player';
6
5
  const { useUnsafeVideoConfig, Timeline, SharedAudioContext } = Internals;
7
6
  export const NewVideoForPreview = ({ src, style, playbackRate = 1, logLevel = 'info', }) => {
8
7
  const canvasRef = useRef(null);
9
8
  const videoConfig = useUnsafeVideoConfig();
10
9
  const frame = useCurrentFrame();
11
- const lastCurrentTimeRef = useRef(-1);
12
10
  const mediaPlayerRef = useRef(null);
13
11
  const [mediaPlayerReady, setMediaPlayerReady] = useState(false);
14
12
  const [playing] = Timeline.usePlayingState();
@@ -32,7 +30,6 @@ export const NewVideoForPreview = ({ src, style, playbackRate = 1, logLevel = 'i
32
30
  if (!sharedAudioContext.audioContext)
33
31
  return;
34
32
  try {
35
- Log.trace(logLevel, `[NewVideoForPreview] Creating MediaPlayer for src: ${src}`);
36
33
  const player = new MediaPlayer({
37
34
  canvas: canvasRef.current,
38
35
  src,
@@ -44,14 +41,14 @@ export const NewVideoForPreview = ({ src, style, playbackRate = 1, logLevel = 'i
44
41
  .initialize(initialTimestamp)
45
42
  .then(() => {
46
43
  setMediaPlayerReady(true);
47
- Log.trace(logLevel, `[NewVideoForPreview] MediaPlayer initialized successfully`);
44
+ Internals.Log.trace({ logLevel, tag: '@remotion/media' }, `[NewVideoForPreview] MediaPlayer initialized successfully`);
48
45
  })
49
46
  .catch((error) => {
50
- Log.error('[NewVideoForPreview] Failed to initialize MediaPlayer', error);
47
+ Internals.Log.error({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] Failed to initialize MediaPlayer', error);
51
48
  });
52
49
  }
53
50
  catch (error) {
54
- Log.error('[NewVideoForPreview] MediaPlayer initialization failed', error);
51
+ Internals.Log.error({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] MediaPlayer initialization failed', error);
55
52
  }
56
53
  return () => {
57
54
  if (delayHandleRef.current) {
@@ -59,7 +56,7 @@ export const NewVideoForPreview = ({ src, style, playbackRate = 1, logLevel = 'i
59
56
  delayHandleRef.current = null;
60
57
  }
61
58
  if (mediaPlayerRef.current) {
62
- Log.trace(logLevel, `[NewVideoForPreview] Disposing MediaPlayer`);
59
+ Internals.Log.trace({ logLevel, tag: '@remotion/media' }, `[NewVideoForPreview] Disposing MediaPlayer`);
63
60
  mediaPlayerRef.current.dispose();
64
61
  mediaPlayerRef.current = null;
65
62
  }
@@ -72,13 +69,11 @@ export const NewVideoForPreview = ({ src, style, playbackRate = 1, logLevel = 'i
72
69
  if (!mediaPlayer)
73
70
  return;
74
71
  if (playing) {
75
- Log.trace(logLevel, `[NewVideoForPreview] Remotion playing - calling MediaPlayer.play()`);
76
72
  mediaPlayer.play().catch((error) => {
77
- Log.error('[NewVideoForPreview] Failed to play', error);
73
+ Internals.Log.error({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] Failed to play', error);
78
74
  });
79
75
  }
80
76
  else {
81
- Log.trace(logLevel, `[NewVideoForPreview] Remotion paused - calling MediaPlayer.pause()`);
82
77
  mediaPlayer.pause();
83
78
  }
84
79
  }, [playing, logLevel, mediaPlayerReady]);
@@ -88,25 +83,24 @@ export const NewVideoForPreview = ({ src, style, playbackRate = 1, logLevel = 'i
88
83
  if (!mediaPlayer || !mediaPlayerReady)
89
84
  return;
90
85
  mediaPlayer.seekTo(currentTime);
91
- Log.trace(logLevel, `[NewVideoForPreview] Updating target time to ${currentTime.toFixed(3)}s`);
92
- lastCurrentTimeRef.current = currentTime;
86
+ Internals.Log.trace({ logLevel, tag: '@remotion/media' }, `[NewVideoForPreview] Updating target time to ${currentTime.toFixed(3)}s`);
93
87
  }, [currentTime, logLevel, mediaPlayerReady]);
94
- // sync MediaPlayer stalling with Remotion buffering
88
+ // sync MediaPlayer buffering with Remotion buffering
95
89
  useEffect(() => {
96
90
  const mediaPlayer = mediaPlayerRef.current;
97
91
  if (!mediaPlayer || !mediaPlayerReady)
98
92
  return;
99
- mediaPlayer.onStalledChange((isStalled) => {
100
- if (isStalled && !delayHandleRef.current) {
93
+ mediaPlayer.onBufferingChange((newBufferingState) => {
94
+ if (newBufferingState && !delayHandleRef.current) {
101
95
  // Start blocking Remotion playback
102
96
  delayHandleRef.current = buffer.delayPlayback();
103
- Log.trace(logLevel, '[NewVideoForPreview] MediaPlayer stalled - blocking Remotion playback');
97
+ Internals.Log.trace({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] MediaPlayer buffering - blocking Remotion playback');
104
98
  }
105
- else if (!isStalled && delayHandleRef.current) {
99
+ else if (!newBufferingState && delayHandleRef.current) {
106
100
  // Unblock Remotion playback
107
101
  delayHandleRef.current.unblock();
108
102
  delayHandleRef.current = null;
109
- Log.trace(logLevel, '[NewVideoForPreview] MediaPlayer unstalled - unblocking Remotion playback');
103
+ Internals.Log.trace({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] MediaPlayer unbuffering - unblocking Remotion playback');
110
104
  }
111
105
  });
112
106
  }, [mediaPlayerReady, buffer, logLevel]);
@@ -9,17 +9,12 @@ export type VideoProps = {
9
9
  name?: string;
10
10
  pauseWhenBuffering?: boolean;
11
11
  showInTimeline?: boolean;
12
- onAutoPlayError?: null | (() => void);
13
12
  onVideoFrame?: OnVideoFrame;
14
13
  playbackRate?: number;
15
14
  muted?: boolean;
16
15
  delayRenderRetries?: number;
17
16
  delayRenderTimeoutInMilliseconds?: number;
18
- crossOrigin?: '' | 'anonymous' | 'use-credentials';
19
17
  style?: React.CSSProperties;
20
- onError?: (err: Error) => void;
21
- useWebAudioApi?: boolean;
22
- acceptableTimeShiftInSeconds?: number;
23
18
  /**
24
19
  * @deprecated For internal use only
25
20
  */
@@ -0,0 +1,2 @@
1
+ export declare const sleep: (ms: number) => Promise<unknown>;
2
+ export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, errorMessage?: string): Promise<T>;
@@ -0,0 +1,18 @@
1
+ /* eslint-disable no-promise-executor-return */
2
+ export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
3
+ export function withTimeout(promise, timeoutMs, errorMessage = 'Operation timed out') {
4
+ let timeoutId = null;
5
+ const timeoutPromise = new Promise((_, reject) => {
6
+ timeoutId = window.setTimeout(() => {
7
+ reject(new Error(errorMessage));
8
+ }, timeoutMs);
9
+ });
10
+ return Promise.race([
11
+ promise.finally(() => {
12
+ if (timeoutId) {
13
+ clearTimeout(timeoutId);
14
+ }
15
+ }),
16
+ timeoutPromise,
17
+ ]);
18
+ }
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import type { LogLevel } from 'remotion';
3
+ type NewVideoForPreviewProps = {
4
+ readonly src: string;
5
+ readonly style?: React.CSSProperties;
6
+ readonly playbackRate?: number;
7
+ readonly logLevel?: LogLevel;
8
+ readonly className?: string;
9
+ };
10
+ export declare const NewVideoForPreview: React.FC<NewVideoForPreviewProps>;
11
+ export {};
@@ -0,0 +1,113 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useContext, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { Internals, useBufferState, useCurrentFrame } from 'remotion';
4
+ import { MediaPlayer } from './media-player';
5
+ const { useUnsafeVideoConfig, Timeline, SharedAudioContext } = Internals;
6
+ export const NewVideoForPreview = ({ src, style, playbackRate = 1, logLevel = 'info', className, }) => {
7
+ const canvasRef = useRef(null);
8
+ const videoConfig = useUnsafeVideoConfig();
9
+ const frame = useCurrentFrame();
10
+ const mediaPlayerRef = useRef(null);
11
+ const [mediaPlayerReady, setMediaPlayerReady] = useState(false);
12
+ const [playing] = Timeline.usePlayingState();
13
+ const sharedAudioContext = useContext(SharedAudioContext);
14
+ const buffer = useBufferState();
15
+ const delayHandleRef = useRef(null);
16
+ if (!videoConfig) {
17
+ throw new Error('No video config found');
18
+ }
19
+ if (!src) {
20
+ throw new TypeError('No `src` was passed to <NewVideoForPreview>.');
21
+ }
22
+ const actualFps = videoConfig.fps / playbackRate;
23
+ const currentTime = frame / actualFps;
24
+ const [initialTimestamp] = useState(currentTime);
25
+ useEffect(() => {
26
+ if (!canvasRef.current)
27
+ return;
28
+ if (!sharedAudioContext)
29
+ return;
30
+ if (!sharedAudioContext.audioContext)
31
+ return;
32
+ try {
33
+ const player = new MediaPlayer({
34
+ canvas: canvasRef.current,
35
+ src,
36
+ logLevel,
37
+ sharedAudioContext: sharedAudioContext.audioContext,
38
+ });
39
+ mediaPlayerRef.current = player;
40
+ player
41
+ .initialize(initialTimestamp)
42
+ .then(() => {
43
+ setMediaPlayerReady(true);
44
+ Internals.Log.trace({ logLevel, tag: '@remotion/media' }, `[NewVideoForPreview] MediaPlayer initialized successfully`);
45
+ })
46
+ .catch((error) => {
47
+ Internals.Log.error({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] Failed to initialize MediaPlayer', error);
48
+ });
49
+ }
50
+ catch (error) {
51
+ Internals.Log.error({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] MediaPlayer initialization failed', error);
52
+ }
53
+ return () => {
54
+ if (delayHandleRef.current) {
55
+ delayHandleRef.current.unblock();
56
+ delayHandleRef.current = null;
57
+ }
58
+ if (mediaPlayerRef.current) {
59
+ Internals.Log.trace({ logLevel, tag: '@remotion/media' }, `[NewVideoForPreview] Disposing MediaPlayer`);
60
+ mediaPlayerRef.current.dispose();
61
+ mediaPlayerRef.current = null;
62
+ }
63
+ setMediaPlayerReady(false);
64
+ };
65
+ }, [src, logLevel, sharedAudioContext, initialTimestamp]);
66
+ const classNameValue = useMemo(() => {
67
+ return [Internals.OBJECTFIT_CONTAIN_CLASS_NAME, className]
68
+ .filter(Internals.truthy)
69
+ .join(' ');
70
+ }, [className]);
71
+ // sync play/pause state with Remotion timeline (like old VideoForPreview video does)
72
+ useEffect(() => {
73
+ const mediaPlayer = mediaPlayerRef.current;
74
+ if (!mediaPlayer)
75
+ return;
76
+ if (playing) {
77
+ mediaPlayer.play().catch((error) => {
78
+ Internals.Log.error({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] Failed to play', error);
79
+ });
80
+ }
81
+ else {
82
+ mediaPlayer.pause();
83
+ }
84
+ }, [playing, logLevel, mediaPlayerReady]);
85
+ // sync target time with MediaPlayer
86
+ useEffect(() => {
87
+ const mediaPlayer = mediaPlayerRef.current;
88
+ if (!mediaPlayer || !mediaPlayerReady)
89
+ return;
90
+ mediaPlayer.seekTo(currentTime);
91
+ Internals.Log.trace({ logLevel, tag: '@remotion/media' }, `[NewVideoForPreview] Updating target time to ${currentTime.toFixed(3)}s`);
92
+ }, [currentTime, logLevel, mediaPlayerReady]);
93
+ // sync MediaPlayer buffering with Remotion buffering
94
+ useEffect(() => {
95
+ const mediaPlayer = mediaPlayerRef.current;
96
+ if (!mediaPlayer || !mediaPlayerReady)
97
+ return;
98
+ mediaPlayer.onBufferingChange((newBufferingState) => {
99
+ if (newBufferingState && !delayHandleRef.current) {
100
+ // Start blocking Remotion playback
101
+ delayHandleRef.current = buffer.delayPlayback();
102
+ Internals.Log.trace({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] MediaPlayer buffering - blocking Remotion playback');
103
+ }
104
+ else if (!newBufferingState && delayHandleRef.current) {
105
+ // Unblock Remotion playback
106
+ delayHandleRef.current.unblock();
107
+ delayHandleRef.current = null;
108
+ Internals.Log.trace({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] MediaPlayer unbuffering - unblocking Remotion playback');
109
+ }
110
+ });
111
+ }, [mediaPlayerReady, buffer, logLevel]);
112
+ return (_jsx("canvas", { ref: canvasRef, width: videoConfig.width, height: videoConfig.height, style: style, className: classNameValue }));
113
+ };
@@ -1,40 +1,24 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useContext, useLayoutEffect, useMemo, useRef, useState, } from 'react';
3
3
  import { cancelRender, Internals, useCurrentFrame, useDelayRender, useRemotionEnvironment, useVideoConfig, } from 'remotion';
4
+ import { applyVolume } from '../convert-audiodata/apply-volume';
5
+ import { frameForVolumeProp } from '../looped-frame';
4
6
  import { extractFrameViaBroadcastChannel } from '../video-extraction/extract-frame-via-broadcast-channel';
5
7
  export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted, loopVolumeCurveBehavior, delayRenderRetries, delayRenderTimeoutInMilliseconds,
6
8
  // call when a frame of the video, i.e. frame drawn on canvas
7
9
  onVideoFrame, logLevel = window.remotion_logLevel, loop, style, className, }) => {
10
+ if (!src) {
11
+ throw new TypeError('No `src` was passed to <Video>.');
12
+ }
13
+ const frame = useCurrentFrame();
8
14
  const absoluteFrame = Internals.useTimelinePosition();
9
15
  const { fps } = useVideoConfig();
10
- const canvasRef = useRef(null);
11
16
  const { registerRenderAsset, unregisterRenderAsset } = useContext(Internals.RenderAssetManager);
12
- const frame = useCurrentFrame();
13
- const volumePropsFrame = Internals.useFrameForVolumeProp(loopVolumeCurveBehavior ?? 'repeat');
14
- const environment = useRemotionEnvironment();
17
+ const startsAt = Internals.useMediaStartsAt();
15
18
  const [id] = useState(() => `${Math.random()}`.replace('0.', ''));
16
- if (!src) {
17
- throw new TypeError('No `src` was passed to <Video>.');
18
- }
19
- const volume = Internals.evaluateVolume({
20
- volume: volumeProp,
21
- frame: volumePropsFrame,
22
- mediaVolume: 1,
23
- });
24
- Internals.warnAboutTooHighVolume(volume);
25
- const shouldRenderAudio = useMemo(() => {
26
- if (!window.remotion_audioEnabled) {
27
- return false;
28
- }
29
- if (muted) {
30
- return false;
31
- }
32
- if (volume <= 0) {
33
- return false;
34
- }
35
- return true;
36
- }, [muted, volume]);
19
+ const environment = useRemotionEnvironment();
37
20
  const { delayRender, continueRender } = useDelayRender();
21
+ const canvasRef = useRef(null);
38
22
  useLayoutEffect(() => {
39
23
  if (!canvasRef.current) {
40
24
  return;
@@ -46,18 +30,27 @@ onVideoFrame, logLevel = window.remotion_logLevel, loop, style, className, }) =>
46
30
  retries: delayRenderRetries ?? undefined,
47
31
  timeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? undefined,
48
32
  });
33
+ const shouldRenderAudio = (() => {
34
+ if (!window.remotion_audioEnabled) {
35
+ return false;
36
+ }
37
+ if (muted) {
38
+ return false;
39
+ }
40
+ return true;
41
+ })();
49
42
  extractFrameViaBroadcastChannel({
50
43
  src,
51
44
  timeInSeconds: timestamp,
52
45
  durationInSeconds,
46
+ playbackRate: playbackRate ?? 1,
53
47
  logLevel: logLevel ?? 'info',
54
48
  includeAudio: shouldRenderAudio,
55
49
  includeVideo: window.remotion_videoEnabled,
56
50
  isClientSideRendering: environment.isClientSideRendering,
57
- volume,
58
51
  loop: loop ?? false,
59
52
  })
60
- .then(({ frame: imageBitmap, audio }) => {
53
+ .then(({ frame: imageBitmap, audio, durationInSeconds: assetDurationInSeconds, }) => {
61
54
  if (imageBitmap) {
62
55
  onVideoFrame?.(imageBitmap);
63
56
  const context = canvasRef.current?.getContext('2d');
@@ -79,7 +72,22 @@ onVideoFrame, logLevel = window.remotion_logLevel, loop, style, className, }) =>
79
72
  else if (window.remotion_videoEnabled) {
80
73
  cancelRender(new Error('No video frame found'));
81
74
  }
82
- if (audio) {
75
+ const volumePropsFrame = frameForVolumeProp({
76
+ behavior: loopVolumeCurveBehavior ?? 'repeat',
77
+ loop: loop ?? false,
78
+ assetDurationInSeconds: assetDurationInSeconds ?? 0,
79
+ fps,
80
+ frame,
81
+ startsAt,
82
+ });
83
+ const volume = Internals.evaluateVolume({
84
+ volume: volumeProp,
85
+ frame: volumePropsFrame,
86
+ mediaVolume: 1,
87
+ });
88
+ Internals.warnAboutTooHighVolume(volume);
89
+ if (audio && volume > 0) {
90
+ applyVolume(audio.data, volume);
83
91
  registerRenderAsset({
84
92
  type: 'inline-audio',
85
93
  id,
@@ -111,14 +119,16 @@ onVideoFrame, logLevel = window.remotion_logLevel, loop, style, className, }) =>
111
119
  frame,
112
120
  id,
113
121
  logLevel,
122
+ loop,
123
+ loopVolumeCurveBehavior,
124
+ muted,
114
125
  onVideoFrame,
115
126
  playbackRate,
116
127
  registerRenderAsset,
117
- shouldRenderAudio,
118
128
  src,
129
+ startsAt,
119
130
  unregisterRenderAsset,
120
- volume,
121
- loop,
131
+ volumeProp,
122
132
  ]);
123
133
  const classNameValue = useMemo(() => {
124
134
  return [Internals.OBJECTFIT_CONTAIN_CLASS_NAME, className]
@@ -32,6 +32,6 @@ export const Video = (props) => {
32
32
  if (environment.isRendering) {
33
33
  return _jsx(VideoForRendering, { ...otherProps });
34
34
  }
35
- const { onAutoPlayError, onVideoFrame, crossOrigin, delayRenderRetries, delayRenderTimeoutInMilliseconds, ...propsForPreview } = otherProps;
36
- return (_jsx(VideoForPreview, { _remotionInternalStack: stack ?? null, _remotionInternalNativeLoopPassed: false, onDuration: onDuration, onlyWarnForMediaSeekingError: true, pauseWhenBuffering: pauseWhenBuffering ?? false, showInTimeline: showInTimeline ?? true, onAutoPlayError: onAutoPlayError ?? undefined, onVideoFrame: onVideoFrame ?? null, crossOrigin: crossOrigin, ...propsForPreview }));
35
+ const { onVideoFrame, delayRenderRetries, delayRenderTimeoutInMilliseconds, ...propsForPreview } = otherProps;
36
+ return (_jsx(VideoForPreview, { _remotionInternalStack: stack ?? null, _remotionInternalNativeLoopPassed: false, onDuration: onDuration, onlyWarnForMediaSeekingError: true, pauseWhenBuffering: pauseWhenBuffering ?? false, showInTimeline: showInTimeline ?? true, onVideoFrame: onVideoFrame ?? null, ...propsForPreview }));
37
37
  };
@@ -1,16 +1,17 @@
1
+ import { type LogLevel } from 'remotion';
1
2
  import type { PcmS16AudioData } from '../convert-audiodata/convert-audiodata';
2
- import type { LogLevel } from '../log';
3
- export declare const extractFrameViaBroadcastChannel: ({ src, timeInSeconds, logLevel, durationInSeconds, includeAudio, includeVideo, isClientSideRendering, volume, loop, }: {
3
+ export declare const extractFrameViaBroadcastChannel: ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, isClientSideRendering, loop, }: {
4
4
  src: string;
5
5
  timeInSeconds: number;
6
6
  durationInSeconds: number;
7
+ playbackRate: number;
7
8
  logLevel: LogLevel;
8
9
  includeAudio: boolean;
9
10
  includeVideo: boolean;
10
11
  isClientSideRendering: boolean;
11
- volume: number;
12
12
  loop: boolean;
13
13
  }) => Promise<{
14
14
  frame: ImageBitmap | VideoFrame | null;
15
15
  audio: PcmS16AudioData | null;
16
+ durationInSeconds: number | null;
16
17
  }>;
@@ -5,14 +5,14 @@ if (window.remotion_broadcastChannel && window.remotion_isMainTab) {
5
5
  const data = event.data;
6
6
  if (data.type === 'request') {
7
7
  try {
8
- const { frame, audio } = await extractFrameAndAudio({
8
+ const { frame, audio, durationInSeconds } = await extractFrameAndAudio({
9
9
  src: data.src,
10
10
  timeInSeconds: data.timeInSeconds,
11
11
  logLevel: data.logLevel,
12
12
  durationInSeconds: data.durationInSeconds,
13
+ playbackRate: data.playbackRate,
13
14
  includeAudio: data.includeAudio,
14
15
  includeVideo: data.includeVideo,
15
- volume: data.volume,
16
16
  loop: data.loop,
17
17
  });
18
18
  const videoFrame = frame;
@@ -27,6 +27,7 @@ if (window.remotion_broadcastChannel && window.remotion_isMainTab) {
27
27
  id: data.id,
28
28
  frame: imageBitmap,
29
29
  audio,
30
+ durationInSeconds: durationInSeconds ?? null,
30
31
  };
31
32
  window.remotion_broadcastChannel.postMessage(response);
32
33
  videoFrame?.close();
@@ -45,16 +46,16 @@ if (window.remotion_broadcastChannel && window.remotion_isMainTab) {
45
46
  }
46
47
  });
47
48
  }
48
- export const extractFrameViaBroadcastChannel = ({ src, timeInSeconds, logLevel, durationInSeconds, includeAudio, includeVideo, isClientSideRendering, volume, loop, }) => {
49
+ export const extractFrameViaBroadcastChannel = ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, isClientSideRendering, loop, }) => {
49
50
  if (isClientSideRendering || window.remotion_isMainTab) {
50
51
  return extractFrameAndAudio({
51
52
  logLevel,
52
53
  src,
53
54
  timeInSeconds,
54
55
  durationInSeconds,
56
+ playbackRate,
55
57
  includeAudio,
56
58
  includeVideo,
57
- volume,
58
59
  loop,
59
60
  });
60
61
  }
@@ -69,6 +70,9 @@ export const extractFrameViaBroadcastChannel = ({ src, timeInSeconds, logLevel,
69
70
  resolve({
70
71
  frame: data.frame ? data.frame : null,
71
72
  audio: data.audio ? data.audio : null,
73
+ durationInSeconds: data.durationInSeconds
74
+ ? data.durationInSeconds
75
+ : null,
72
76
  });
73
77
  window.remotion_broadcastChannel.removeEventListener('message', onMessage);
74
78
  }
@@ -86,9 +90,9 @@ export const extractFrameViaBroadcastChannel = ({ src, timeInSeconds, logLevel,
86
90
  id: requestId,
87
91
  logLevel,
88
92
  durationInSeconds,
93
+ playbackRate,
89
94
  includeAudio,
90
95
  includeVideo,
91
- volume,
92
96
  loop,
93
97
  };
94
98
  window.remotion_broadcastChannel.postMessage(request);
@@ -1,4 +1,4 @@
1
- import type { LogLevel } from '../log';
1
+ import { type LogLevel } from 'remotion';
2
2
  import { type GetSink } from './get-frames-since-keyframe';
3
3
  export declare const sinkPromises: Record<string, Promise<GetSink>>;
4
4
  export declare const extractFrame: ({ src, timeInSeconds: unloopedTimeinSeconds, logLevel, loop, }: {
@@ -6,6 +6,9 @@ export const extractFrame = async ({ src, timeInSeconds: unloopedTimeinSeconds,
6
6
  sinkPromises[src] = getSinks(src);
7
7
  }
8
8
  const { video, getDuration } = await sinkPromises[src];
9
+ if (video === null) {
10
+ throw new Error(`No video track found for ${src}`);
11
+ }
9
12
  const timeInSeconds = loop
10
13
  ? unloopedTimeinSeconds % (await getDuration())
11
14
  : unloopedTimeinSeconds;
@@ -4,7 +4,7 @@ export declare const getSinks: (src: string) => Promise<{
4
4
  video: {
5
5
  sampleSink: VideoSampleSink;
6
6
  packetSink: EncodedPacketSink;
7
- };
7
+ } | null;
8
8
  audio: {
9
9
  sampleSink: AudioSampleSink;
10
10
  } | null;
@@ -8,16 +8,15 @@ export const getSinks = async (src) => {
8
8
  });
9
9
  const format = await input.getFormat();
10
10
  const videoTrack = await input.getPrimaryVideoTrack();
11
- if (!videoTrack) {
12
- throw new Error(`No video track found for ${src}`);
13
- }
14
11
  const audioTrack = await input.getPrimaryAudioTrack();
15
12
  const isMatroska = format === MATROSKA;
16
13
  return {
17
- video: {
18
- sampleSink: new VideoSampleSink(videoTrack),
19
- packetSink: new EncodedPacketSink(videoTrack),
20
- },
14
+ video: videoTrack
15
+ ? {
16
+ sampleSink: new VideoSampleSink(videoTrack),
17
+ packetSink: new EncodedPacketSink(videoTrack),
18
+ }
19
+ : null,
21
20
  audio: audioTrack
22
21
  ? {
23
22
  sampleSink: new AudioSampleSink(audioTrack),
@@ -30,7 +29,7 @@ export const getSinks = async (src) => {
30
29
  };
31
30
  export const getFramesSinceKeyframe = async ({ packetSink, videoSampleSink, startPacket, }) => {
32
31
  const nextKeyPacket = await packetSink.getNextKeyPacket(startPacket, {
33
- verifyKeyPackets: false,
32
+ verifyKeyPackets: true,
34
33
  });
35
34
  const sampleIterator = videoSampleSink.samples(startPacket.timestamp, nextKeyPacket ? nextKeyPacket.timestamp : Infinity);
36
35
  const keyframeBank = makeKeyframeBank({
@@ -1,5 +1,5 @@
1
1
  import type { VideoSample } from 'mediabunny';
2
- import type { LogLevel } from '../log';
2
+ import { type LogLevel } from 'remotion';
3
3
  export type KeyframeBank = {
4
4
  startTimestampInSeconds: number;
5
5
  endTimestampInSeconds: number;
@@ -1,4 +1,4 @@
1
- import { Log } from '../log';
1
+ import { Internals } from 'remotion';
2
2
  // Round to only 4 digits, because WebM has a timescale of 1_000, e.g. framer.webm
3
3
  const roundTo4Digits = (timestamp) => {
4
4
  return Math.round(timestamp * 1000) / 1000;
@@ -7,7 +7,7 @@ export const makeKeyframeBank = ({ startTimestampInSeconds, endTimestampInSecond
7
7
  const frames = {};
8
8
  const frameTimestamps = [];
9
9
  let lastUsed = Date.now();
10
- let alloctionSize = 0;
10
+ let allocationSize = 0;
11
11
  const hasDecodedEnoughForTimestamp = (timestamp) => {
12
12
  const lastFrameTimestamp = frameTimestamps[frameTimestamps.length - 1];
13
13
  if (!lastFrameTimestamp) {
@@ -24,7 +24,7 @@ export const makeKeyframeBank = ({ startTimestampInSeconds, endTimestampInSecond
24
24
  const addFrame = (frame) => {
25
25
  frames[frame.timestamp] = frame;
26
26
  frameTimestamps.push(frame.timestamp);
27
- alloctionSize += frame.allocationSize();
27
+ allocationSize += frame.allocationSize();
28
28
  lastUsed = Date.now();
29
29
  };
30
30
  const ensureEnoughFramesForTimestamp = async (timestamp) => {
@@ -72,7 +72,7 @@ export const makeKeyframeBank = ({ startTimestampInSeconds, endTimestampInSecond
72
72
  if (!frames[frameTimestamp]) {
73
73
  continue;
74
74
  }
75
- alloctionSize -= frames[frameTimestamp].allocationSize();
75
+ allocationSize -= frames[frameTimestamp].allocationSize();
76
76
  frames[frameTimestamp].close();
77
77
  delete frames[frameTimestamp];
78
78
  }
@@ -89,17 +89,17 @@ export const makeKeyframeBank = ({ startTimestampInSeconds, endTimestampInSecond
89
89
  if (!frames[frameTimestamp]) {
90
90
  continue;
91
91
  }
92
- alloctionSize -= frames[frameTimestamp].allocationSize();
92
+ allocationSize -= frames[frameTimestamp].allocationSize();
93
93
  frameTimestamps.splice(frameTimestamps.indexOf(frameTimestamp), 1);
94
94
  frames[frameTimestamp].close();
95
95
  delete frames[frameTimestamp];
96
- Log.verbose(logLevel, `[Video] Deleted frame ${frameTimestamp} for src ${src}`);
96
+ Internals.Log.verbose({ logLevel, tag: '@remotion/media' }, `Deleted frame ${frameTimestamp} for src ${src}`);
97
97
  }
98
98
  }
99
99
  };
100
100
  const getOpenFrameCount = () => {
101
101
  return {
102
- size: alloctionSize,
102
+ size: allocationSize,
103
103
  timestamps: frameTimestamps,
104
104
  };
105
105
  };
@@ -1,5 +1,5 @@
1
1
  import type { EncodedPacketSink, VideoSampleSink } from 'mediabunny';
2
- import type { LogLevel } from '../log';
2
+ import { type LogLevel } from 'remotion';
3
3
  import { type KeyframeBank } from './keyframe-bank';
4
4
  export declare const makeKeyframeManager: () => {
5
5
  requestKeyframeBank: ({ packetSink, timestamp, videoSampleSink, src, logLevel, }: {