@remotion/media 4.0.356 → 4.0.358

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 (61) hide show
  1. package/dist/audio/audio-for-preview.d.ts +30 -0
  2. package/dist/audio/audio-for-preview.js +229 -0
  3. package/dist/audio/audio-for-rendering.js +35 -19
  4. package/dist/audio/audio.js +7 -49
  5. package/dist/audio/props.d.ts +8 -14
  6. package/dist/audio-extraction/audio-cache.d.ts +1 -1
  7. package/dist/audio-extraction/audio-cache.js +5 -1
  8. package/dist/audio-extraction/audio-iterator.d.ts +4 -1
  9. package/dist/audio-extraction/audio-iterator.js +22 -10
  10. package/dist/audio-extraction/audio-manager.d.ts +8 -37
  11. package/dist/audio-extraction/audio-manager.js +35 -8
  12. package/dist/audio-extraction/extract-audio.d.ts +9 -2
  13. package/dist/audio-extraction/extract-audio.js +29 -15
  14. package/dist/caches.d.ts +9 -44
  15. package/dist/convert-audiodata/combine-audiodata.js +2 -23
  16. package/dist/convert-audiodata/convert-audiodata.d.ts +1 -5
  17. package/dist/convert-audiodata/convert-audiodata.js +16 -24
  18. package/dist/esm/index.mjs +2864 -2173
  19. package/dist/extract-frame-and-audio.d.ts +6 -7
  20. package/dist/extract-frame-and-audio.js +28 -19
  21. package/dist/{get-sink-weak.d.ts → get-sink.d.ts} +1 -1
  22. package/dist/get-sink.js +15 -0
  23. package/dist/get-time-in-seconds.d.ts +11 -0
  24. package/dist/get-time-in-seconds.js +25 -0
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.js +1 -0
  27. package/dist/is-network-error.d.ts +6 -0
  28. package/dist/is-network-error.js +17 -0
  29. package/dist/render-timestamp-range.d.ts +1 -0
  30. package/dist/render-timestamp-range.js +9 -0
  31. package/dist/show-in-timeline.d.ts +8 -0
  32. package/dist/show-in-timeline.js +31 -0
  33. package/dist/use-media-in-timeline.d.ts +19 -0
  34. package/dist/use-media-in-timeline.js +103 -0
  35. package/dist/video/media-player.d.ts +34 -7
  36. package/dist/video/media-player.js +164 -63
  37. package/dist/video/props.d.ts +1 -0
  38. package/dist/video/video-for-preview.d.ts +17 -9
  39. package/dist/video/video-for-preview.js +138 -92
  40. package/dist/video/video-for-rendering.d.ts +3 -0
  41. package/dist/video/video-for-rendering.js +58 -25
  42. package/dist/video/video.js +6 -10
  43. package/dist/video-extraction/extract-frame-via-broadcast-channel.d.ts +18 -6
  44. package/dist/video-extraction/extract-frame-via-broadcast-channel.js +21 -7
  45. package/dist/video-extraction/extract-frame.d.ts +20 -2
  46. package/dist/video-extraction/extract-frame.js +41 -9
  47. package/dist/video-extraction/get-frames-since-keyframe.d.ts +5 -3
  48. package/dist/video-extraction/get-frames-since-keyframe.js +7 -4
  49. package/dist/video-extraction/keyframe-bank.d.ts +3 -2
  50. package/dist/video-extraction/keyframe-bank.js +32 -12
  51. package/dist/video-extraction/keyframe-manager.d.ts +3 -8
  52. package/dist/video-extraction/keyframe-manager.js +25 -10
  53. package/package.json +54 -54
  54. package/LICENSE.md +0 -49
  55. package/dist/convert-audiodata/apply-tonefrequency.d.ts +0 -2
  56. package/dist/convert-audiodata/apply-tonefrequency.js +0 -44
  57. package/dist/convert-audiodata/wsola.d.ts +0 -13
  58. package/dist/convert-audiodata/wsola.js +0 -197
  59. package/dist/get-sink-weak.js +0 -23
  60. package/dist/log.d.ts +0 -10
  61. package/dist/log.js +0 -33
@@ -1,6 +1,6 @@
1
1
  import type { LogLevel } from 'remotion';
2
- import type { PcmS16AudioData } from './convert-audiodata/convert-audiodata';
3
- export declare const extractFrameAndAudio: ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, loop, audioStreamIndex, }: {
2
+ import type { ExtractFrameViaBroadcastChannelResult } from './video-extraction/extract-frame-via-broadcast-channel';
3
+ export declare const extractFrameAndAudio: ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, loop, audioStreamIndex, trimAfter, trimBefore, fps, }: {
4
4
  src: string;
5
5
  timeInSeconds: number;
6
6
  logLevel: LogLevel;
@@ -10,8 +10,7 @@ export declare const extractFrameAndAudio: ({ src, timeInSeconds, logLevel, dura
10
10
  includeVideo: boolean;
11
11
  loop: boolean;
12
12
  audioStreamIndex: number;
13
- }) => Promise<{
14
- frame: VideoFrame | null;
15
- audio: PcmS16AudioData | null;
16
- durationInSeconds: number | null;
17
- } | "cannot-decode" | "unknown-container-format" | "network-error">;
13
+ trimAfter: number | undefined;
14
+ trimBefore: number | undefined;
15
+ fps: number;
16
+ }) => Promise<ExtractFrameViaBroadcastChannelResult>;
@@ -1,6 +1,7 @@
1
1
  import { extractAudio } from './audio-extraction/extract-audio';
2
+ import { isNetworkError } from './is-network-error';
2
3
  import { extractFrame } from './video-extraction/extract-frame';
3
- export const extractFrameAndAudio = async ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, loop, audioStreamIndex, }) => {
4
+ export const extractFrameAndAudio = async ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, loop, audioStreamIndex, trimAfter, trimBefore, fps, }) => {
4
5
  try {
5
6
  const [frame, audio] = await Promise.all([
6
7
  includeVideo
@@ -9,6 +10,10 @@ export const extractFrameAndAudio = async ({ src, timeInSeconds, logLevel, durat
9
10
  timeInSeconds,
10
11
  logLevel,
11
12
  loop,
13
+ trimAfter,
14
+ playbackRate,
15
+ trimBefore,
16
+ fps,
12
17
  })
13
18
  : null,
14
19
  includeAudio
@@ -20,43 +25,47 @@ export const extractFrameAndAudio = async ({ src, timeInSeconds, logLevel, durat
20
25
  loop,
21
26
  playbackRate,
22
27
  audioStreamIndex,
28
+ trimAfter,
29
+ fps,
30
+ trimBefore,
23
31
  })
24
32
  : null,
25
33
  ]);
26
- if (frame === 'cannot-decode') {
27
- return 'cannot-decode';
34
+ if (frame?.type === 'cannot-decode') {
35
+ return {
36
+ type: 'cannot-decode',
37
+ durationInSeconds: frame.durationInSeconds,
38
+ };
28
39
  }
29
- if (frame === 'unknown-container-format') {
30
- return 'unknown-container-format';
40
+ if (frame?.type === 'unknown-container-format') {
41
+ return { type: 'unknown-container-format' };
31
42
  }
32
43
  if (audio === 'unknown-container-format') {
33
44
  if (frame !== null) {
34
- frame?.close();
45
+ frame?.frame?.close();
35
46
  }
36
- return 'unknown-container-format';
47
+ return { type: 'unknown-container-format' };
37
48
  }
38
49
  if (audio === 'cannot-decode') {
39
- if (frame !== null) {
40
- frame?.close();
50
+ if (frame?.type === 'success' && frame.frame !== null) {
51
+ frame?.frame.close();
41
52
  }
42
- return 'cannot-decode';
53
+ return {
54
+ type: 'cannot-decode',
55
+ durationInSeconds: frame?.type === 'success' ? frame.durationInSeconds : null,
56
+ };
43
57
  }
44
58
  return {
45
- frame: frame?.toVideoFrame() ?? null,
59
+ type: 'success',
60
+ frame: frame?.frame?.toVideoFrame() ?? null,
46
61
  audio: audio?.data ?? null,
47
62
  durationInSeconds: audio?.durationInSeconds ?? null,
48
63
  };
49
64
  }
50
65
  catch (err) {
51
66
  const error = err;
52
- if (
53
- // Chrome
54
- error.message.includes('Failed to fetch') ||
55
- // Safari
56
- error.message.includes('Load failed') ||
57
- // Firefox
58
- error.message.includes('NetworkError when attempting to fetch resource')) {
59
- return 'network-error';
67
+ if (isNetworkError(error)) {
68
+ return { type: 'network-error' };
60
69
  }
61
70
  throw err;
62
71
  }
@@ -1,7 +1,7 @@
1
1
  import type { LogLevel } from 'remotion';
2
2
  import type { GetSink } from './video-extraction/get-frames-since-keyframe';
3
3
  export declare const sinkPromises: Record<string, Promise<GetSink>>;
4
- export declare const getSinkWeak: (src: string, logLevel: LogLevel) => Promise<{
4
+ export declare const getSink: (src: string, logLevel: LogLevel) => Promise<{
5
5
  getVideo: () => Promise<import("./video-extraction/get-frames-since-keyframe").VideoSinkResult>;
6
6
  getAudio: (index: number) => Promise<import("./video-extraction/get-frames-since-keyframe").AudioSinkResult>;
7
7
  actualMatroskaTimestamps: {
@@ -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,11 @@
1
+ export declare const getTimeInSeconds: ({ loop, mediaDurationInSeconds, unloopedTimeInSeconds, src, trimAfter, trimBefore, fps, playbackRate, ifNoMediaDuration, }: {
2
+ loop: boolean;
3
+ mediaDurationInSeconds: number | null;
4
+ unloopedTimeInSeconds: number;
5
+ src: string;
6
+ trimAfter: number | undefined;
7
+ trimBefore: number | undefined;
8
+ playbackRate: number;
9
+ fps: number;
10
+ ifNoMediaDuration: "fail" | "infinity";
11
+ }) => number | null;
@@ -0,0 +1,25 @@
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
+ };
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export declare const experimental_Audio: import("react").FC<import(".").AudioPro
8
8
  * @deprecated Now just `Video`
9
9
  */
10
10
  export declare const experimental_Video: import("react").FC<import(".").VideoProps>;
11
+ export { AudioForPreview } from './audio/audio-for-preview';
11
12
  export { AudioProps, FallbackHtml5AudioProps } from './audio/props';
12
13
  export { VideoProps } from './video/props';
13
14
  export { Audio, Video };
package/dist/index.js CHANGED
@@ -8,4 +8,5 @@ export const experimental_Audio = Audio;
8
8
  * @deprecated Now just `Video`
9
9
  */
10
10
  export const experimental_Video = Video;
11
+ export { AudioForPreview } from './audio/audio-for-preview';
11
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 @@
1
+ export declare const renderTimestampRange: (timestamps: number[]) => string;
@@ -0,0 +1,9 @@
1
+ export const renderTimestampRange = (timestamps) => {
2
+ if (timestamps.length === 0) {
3
+ return '(none)';
4
+ }
5
+ if (timestamps.length === 1) {
6
+ return timestamps[0].toFixed(3);
7
+ }
8
+ return `${timestamps[0].toFixed(3)}...${timestamps[timestamps.length - 1].toFixed(3)}`;
9
+ };
@@ -0,0 +1,8 @@
1
+ import type { _InternalTypes } from 'remotion';
2
+ export declare const useLoopDisplay: ({ loop, mediaDurationInSeconds, playbackRate, trimAfter, trimBefore, }: {
3
+ loop: boolean;
4
+ mediaDurationInSeconds: number | null;
5
+ trimAfter: number | undefined;
6
+ trimBefore: number | undefined;
7
+ playbackRate: number;
8
+ }) => _InternalTypes["LoopDisplay"] | undefined;
@@ -0,0 +1,31 @@
1
+ import { useMemo } from 'react';
2
+ import { Internals, useVideoConfig } from 'remotion';
3
+ export const useLoopDisplay = ({ loop, mediaDurationInSeconds, playbackRate, trimAfter, trimBefore, }) => {
4
+ const { durationInFrames: compDuration, fps } = useVideoConfig();
5
+ const loopDisplay = useMemo(() => {
6
+ if (!loop || !mediaDurationInSeconds) {
7
+ return undefined;
8
+ }
9
+ const durationInFrames = Internals.calculateMediaDuration({
10
+ mediaDurationInFrames: mediaDurationInSeconds * fps,
11
+ playbackRate,
12
+ trimAfter,
13
+ trimBefore,
14
+ });
15
+ const maxTimes = compDuration / durationInFrames;
16
+ return {
17
+ numberOfTimes: maxTimes,
18
+ startOffset: 0,
19
+ durationInFrames,
20
+ };
21
+ }, [
22
+ compDuration,
23
+ fps,
24
+ loop,
25
+ mediaDurationInSeconds,
26
+ playbackRate,
27
+ trimAfter,
28
+ trimBefore,
29
+ ]);
30
+ return loopDisplay;
31
+ };
@@ -0,0 +1,19 @@
1
+ import type { _InternalTypes } from 'remotion';
2
+ import { type VolumeProp } from 'remotion';
3
+ export declare const useMediaInTimeline: ({ volume, mediaVolume, src, mediaType, playbackRate, displayName, stack, showInTimeline, premountDisplay, postmountDisplay, loopDisplay, trimBefore, trimAfter, }: {
4
+ volume: VolumeProp | undefined;
5
+ mediaVolume: number;
6
+ src: string | undefined;
7
+ mediaType: "audio" | "video";
8
+ playbackRate: number;
9
+ displayName: string | null;
10
+ stack: string | null;
11
+ showInTimeline: boolean;
12
+ premountDisplay: number | null;
13
+ postmountDisplay: number | null;
14
+ loopDisplay: _InternalTypes["LoopDisplay"] | undefined;
15
+ trimBefore: number | undefined;
16
+ trimAfter: number | undefined;
17
+ }) => {
18
+ id: string;
19
+ };
@@ -0,0 +1,103 @@
1
+ import { useContext, useEffect, useState } from 'react';
2
+ import { Internals, useCurrentFrame } from 'remotion';
3
+ export const useMediaInTimeline = ({ volume, mediaVolume, src, mediaType, playbackRate, displayName, stack, showInTimeline, premountDisplay, postmountDisplay, loopDisplay, trimBefore, trimAfter, }) => {
4
+ const parentSequence = useContext(Internals.SequenceContext);
5
+ const startsAt = Internals.useMediaStartsAt();
6
+ const { registerSequence, unregisterSequence } = useContext(Internals.SequenceManager);
7
+ const [sequenceId] = useState(() => String(Math.random()));
8
+ const [mediaId] = useState(() => String(Math.random()));
9
+ const frame = useCurrentFrame();
10
+ const { volumes, duration, doesVolumeChange, nonce, rootId, isStudio, finalDisplayName, } = Internals.useBasicMediaInTimeline({
11
+ volume,
12
+ mediaVolume,
13
+ mediaType,
14
+ src,
15
+ displayName,
16
+ trimBefore,
17
+ trimAfter,
18
+ playbackRate,
19
+ });
20
+ useEffect(() => {
21
+ if (!src) {
22
+ throw new Error('No src passed');
23
+ }
24
+ if (!isStudio && window.process?.env?.NODE_ENV !== 'test') {
25
+ return;
26
+ }
27
+ if (!showInTimeline) {
28
+ return;
29
+ }
30
+ const loopIteration = loopDisplay
31
+ ? Math.floor(frame / loopDisplay.durationInFrames)
32
+ : 0;
33
+ if (loopDisplay) {
34
+ registerSequence({
35
+ type: 'sequence',
36
+ premountDisplay,
37
+ postmountDisplay,
38
+ parent: parentSequence?.id ?? null,
39
+ displayName: finalDisplayName,
40
+ rootId,
41
+ showInTimeline: true,
42
+ nonce,
43
+ loopDisplay,
44
+ stack,
45
+ from: 0,
46
+ duration,
47
+ id: sequenceId,
48
+ });
49
+ }
50
+ registerSequence({
51
+ type: mediaType,
52
+ src,
53
+ id: mediaId,
54
+ duration: loopDisplay?.durationInFrames ?? duration,
55
+ from: loopDisplay ? loopIteration * loopDisplay.durationInFrames : 0,
56
+ parent: loopDisplay ? sequenceId : (parentSequence?.id ?? null),
57
+ displayName: finalDisplayName,
58
+ rootId,
59
+ volume: volumes,
60
+ showInTimeline: true,
61
+ nonce,
62
+ startMediaFrom: 0 - startsAt,
63
+ doesVolumeChange,
64
+ loopDisplay: undefined,
65
+ playbackRate,
66
+ stack,
67
+ premountDisplay: null,
68
+ postmountDisplay: null,
69
+ });
70
+ return () => {
71
+ if (loopDisplay) {
72
+ unregisterSequence(sequenceId);
73
+ }
74
+ unregisterSequence(mediaId);
75
+ };
76
+ }, [
77
+ doesVolumeChange,
78
+ duration,
79
+ finalDisplayName,
80
+ isStudio,
81
+ loopDisplay,
82
+ mediaId,
83
+ mediaType,
84
+ nonce,
85
+ parentSequence?.id,
86
+ playbackRate,
87
+ postmountDisplay,
88
+ premountDisplay,
89
+ registerSequence,
90
+ rootId,
91
+ sequenceId,
92
+ showInTimeline,
93
+ src,
94
+ stack,
95
+ startsAt,
96
+ unregisterSequence,
97
+ volumes,
98
+ frame,
99
+ ]);
100
+ return {
101
+ id: mediaId,
102
+ };
103
+ };
@@ -1,11 +1,24 @@
1
1
  import type { LogLevel } from 'remotion';
2
2
  export declare const SEEK_THRESHOLD = 0.05;
3
+ export type MediaPlayerInitResult = {
4
+ type: 'success';
5
+ durationInSeconds: number;
6
+ } | {
7
+ type: 'unknown-container-format';
8
+ } | {
9
+ type: 'cannot-decode';
10
+ } | {
11
+ type: 'network-error';
12
+ } | {
13
+ type: 'no-tracks';
14
+ };
3
15
  export declare class MediaPlayer {
4
16
  private canvas;
5
17
  private context;
6
18
  private src;
7
19
  private logLevel;
8
20
  private playbackRate;
21
+ private audioStreamIndex;
9
22
  private canvasSink;
10
23
  private videoFrameIterator;
11
24
  private nextFrame;
@@ -13,12 +26,18 @@ export declare class MediaPlayer {
13
26
  private audioBufferIterator;
14
27
  private queuedAudioNodes;
15
28
  private gainNode;
29
+ private currentVolume;
16
30
  private sharedAudioContext;
17
31
  private audioSyncAnchor;
18
32
  private playing;
19
33
  private muted;
34
+ private loop;
35
+ private fps;
36
+ private trimBefore;
37
+ private trimAfter;
20
38
  private animationFrameId;
21
39
  private videoAsyncId;
40
+ private audioAsyncId;
22
41
  private initialized;
23
42
  private totalDuration;
24
43
  private isBuffering;
@@ -27,17 +46,24 @@ export declare class MediaPlayer {
27
46
  private audioIteratorStarted;
28
47
  private readonly HEALTHY_BUFER_THRESHOLD_SECONDS;
29
48
  private onVideoFrameCallback?;
30
- constructor({ canvas, src, logLevel, sharedAudioContext, }: {
31
- canvas: HTMLCanvasElement;
49
+ constructor({ canvas, src, logLevel, sharedAudioContext, loop, trimBefore, trimAfter, playbackRate, audioStreamIndex, fps, }: {
50
+ canvas: HTMLCanvasElement | null;
32
51
  src: string;
33
52
  logLevel: LogLevel;
34
53
  sharedAudioContext: AudioContext;
54
+ loop: boolean;
55
+ trimBefore: number | undefined;
56
+ trimAfter: number | undefined;
57
+ playbackRate: number;
58
+ audioStreamIndex: number;
59
+ fps: number;
35
60
  });
36
61
  private input;
37
62
  private isReady;
38
63
  private hasAudio;
39
64
  private isCurrentlyBuffering;
40
- initialize(startTime?: number): Promise<void>;
65
+ initialize(startTimeUnresolved: number): Promise<MediaPlayerInitResult>;
66
+ private clearCanvas;
41
67
  private cleanupAudioQueue;
42
68
  private cleanAudioIteratorAndNodes;
43
69
  seekTo(time: number): Promise<void>;
@@ -45,13 +71,14 @@ export declare class MediaPlayer {
45
71
  pause(): void;
46
72
  setMuted(muted: boolean): void;
47
73
  setVolume(volume: number): void;
48
- setPlaybackRate(rate: number): Promise<void>;
74
+ setPlaybackRate(rate: number): void;
75
+ setFps(fps: number): void;
76
+ setLoop(loop: boolean): void;
49
77
  dispose(): void;
50
78
  private getPlaybackTime;
51
- private getAdjustedTimestamp;
52
79
  private scheduleAudioChunk;
53
- onBufferingChange(callback: (isBuffering: boolean) => void): void;
54
- onVideoFrame(callback: (frame: CanvasImageSource) => void): void;
80
+ onBufferingChange(callback: (isBuffering: boolean) => void): () => void;
81
+ onVideoFrame(callback: (frame: CanvasImageSource) => void): () => void;
55
82
  private canRenderVideo;
56
83
  private startRenderLoop;
57
84
  private stopRenderLoop;