@remotion/media 4.0.354 → 4.0.356

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 (86) hide show
  1. package/dist/audio/audio-for-rendering.js +37 -3
  2. package/dist/audio/audio.js +1 -1
  3. package/dist/audio/props.d.ts +15 -0
  4. package/dist/audio-extraction/audio-iterator.d.ts +3 -2
  5. package/dist/audio-extraction/audio-iterator.js +13 -2
  6. package/dist/audio-extraction/audio-manager.d.ts +6 -5
  7. package/dist/audio-extraction/audio-manager.js +5 -3
  8. package/dist/audio-extraction/extract-audio.d.ts +3 -2
  9. package/dist/audio-extraction/extract-audio.js +12 -9
  10. package/dist/caches.d.ts +6 -5
  11. package/dist/convert-audiodata/apply-tonefrequency.d.ts +2 -0
  12. package/dist/convert-audiodata/apply-tonefrequency.js +44 -0
  13. package/dist/convert-audiodata/wsola.d.ts +13 -0
  14. package/dist/convert-audiodata/wsola.js +197 -0
  15. package/dist/esm/index.mjs +1519 -13269
  16. package/dist/extract-frame-and-audio.d.ts +3 -2
  17. package/dist/extract-frame-and-audio.js +60 -26
  18. package/dist/get-sink-weak.d.ts +13 -0
  19. package/dist/get-sink-weak.js +23 -0
  20. package/dist/index.d.ts +12 -3
  21. package/dist/index.js +11 -2
  22. package/dist/video/media-player.d.ts +8 -0
  23. package/dist/video/media-player.js +77 -19
  24. package/dist/video/props.d.ts +36 -18
  25. package/dist/video/video-for-preview.d.ts +13 -7
  26. package/dist/video/video-for-preview.js +115 -10
  27. package/dist/video/video-for-rendering.d.ts +23 -2
  28. package/dist/video/video-for-rendering.js +47 -4
  29. package/dist/video/video.js +13 -14
  30. package/dist/video-extraction/extract-frame-via-broadcast-channel.d.ts +3 -2
  31. package/dist/video-extraction/extract-frame-via-broadcast-channel.js +53 -4
  32. package/dist/video-extraction/extract-frame.d.ts +2 -3
  33. package/dist/video-extraction/extract-frame.js +11 -8
  34. package/dist/video-extraction/get-frames-since-keyframe.d.ts +14 -9
  35. package/dist/video-extraction/get-frames-since-keyframe.js +72 -19
  36. package/package.json +5 -5
  37. package/dist/audio-for-rendering.d.ts +0 -3
  38. package/dist/audio-for-rendering.js +0 -94
  39. package/dist/audio.d.ts +0 -3
  40. package/dist/audio.js +0 -60
  41. package/dist/audiodata-to-array.d.ts +0 -0
  42. package/dist/audiodata-to-array.js +0 -1
  43. package/dist/convert-audiodata/data-types.d.ts +0 -1
  44. package/dist/convert-audiodata/data-types.js +0 -22
  45. package/dist/convert-audiodata/is-planar-format.d.ts +0 -1
  46. package/dist/convert-audiodata/is-planar-format.js +0 -3
  47. package/dist/convert-audiodata/log-audiodata.d.ts +0 -1
  48. package/dist/convert-audiodata/log-audiodata.js +0 -8
  49. package/dist/convert-audiodata/trim-audiodata.d.ts +0 -0
  50. package/dist/convert-audiodata/trim-audiodata.js +0 -1
  51. package/dist/deserialized-audiodata.d.ts +0 -15
  52. package/dist/deserialized-audiodata.js +0 -26
  53. package/dist/extract-audio.d.ts +0 -7
  54. package/dist/extract-audio.js +0 -98
  55. package/dist/extract-frame-via-broadcast-channel.d.ts +0 -15
  56. package/dist/extract-frame-via-broadcast-channel.js +0 -104
  57. package/dist/extract-frame.d.ts +0 -27
  58. package/dist/extract-frame.js +0 -21
  59. package/dist/extrct-audio.d.ts +0 -7
  60. package/dist/extrct-audio.js +0 -94
  61. package/dist/get-frames-since-keyframe.d.ts +0 -22
  62. package/dist/get-frames-since-keyframe.js +0 -41
  63. package/dist/keyframe-bank.d.ts +0 -25
  64. package/dist/keyframe-bank.js +0 -120
  65. package/dist/keyframe-manager.d.ts +0 -23
  66. package/dist/keyframe-manager.js +0 -170
  67. package/dist/new-video-for-rendering.d.ts +0 -3
  68. package/dist/new-video-for-rendering.js +0 -108
  69. package/dist/new-video.d.ts +0 -3
  70. package/dist/new-video.js +0 -37
  71. package/dist/props.d.ts +0 -29
  72. package/dist/props.js +0 -1
  73. package/dist/remember-actual-matroska-timestamps.d.ts +0 -4
  74. package/dist/remember-actual-matroska-timestamps.js +0 -19
  75. package/dist/serialize-videoframe.d.ts +0 -0
  76. package/dist/serialize-videoframe.js +0 -1
  77. package/dist/video/new-video-for-preview.d.ts +0 -10
  78. package/dist/video/new-video-for-preview.js +0 -108
  79. package/dist/video-extraction/media-player.d.ts +0 -64
  80. package/dist/video-extraction/media-player.js +0 -501
  81. package/dist/video-extraction/new-video-for-preview.d.ts +0 -10
  82. package/dist/video-extraction/new-video-for-preview.js +0 -114
  83. package/dist/video-for-rendering.d.ts +0 -3
  84. package/dist/video-for-rendering.js +0 -108
  85. package/dist/video.d.ts +0 -3
  86. package/dist/video.js +0 -37
@@ -1,6 +1,6 @@
1
1
  import type { LogLevel } from 'remotion';
2
2
  import type { PcmS16AudioData } from './convert-audiodata/convert-audiodata';
3
- export declare const extractFrameAndAudio: ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, loop, }: {
3
+ export declare const extractFrameAndAudio: ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, loop, audioStreamIndex, }: {
4
4
  src: string;
5
5
  timeInSeconds: number;
6
6
  logLevel: LogLevel;
@@ -9,8 +9,9 @@ export declare const extractFrameAndAudio: ({ src, timeInSeconds, logLevel, dura
9
9
  includeAudio: boolean;
10
10
  includeVideo: boolean;
11
11
  loop: boolean;
12
+ audioStreamIndex: number;
12
13
  }) => Promise<{
13
14
  frame: VideoFrame | null;
14
15
  audio: PcmS16AudioData | null;
15
16
  durationInSeconds: number | null;
16
- }>;
17
+ } | "cannot-decode" | "unknown-container-format" | "network-error">;
@@ -1,29 +1,63 @@
1
1
  import { extractAudio } from './audio-extraction/extract-audio';
2
2
  import { extractFrame } from './video-extraction/extract-frame';
3
- export const extractFrameAndAudio = async ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, loop, }) => {
4
- const [frame, audio] = await Promise.all([
5
- includeVideo
6
- ? extractFrame({
7
- src,
8
- timeInSeconds,
9
- logLevel,
10
- loop,
11
- })
12
- : null,
13
- includeAudio
14
- ? extractAudio({
15
- src,
16
- timeInSeconds,
17
- durationInSeconds,
18
- logLevel,
19
- loop,
20
- playbackRate,
21
- })
22
- : null,
23
- ]);
24
- return {
25
- frame: frame?.toVideoFrame() ?? null,
26
- audio: audio?.data ?? null,
27
- durationInSeconds: audio?.durationInSeconds ?? null,
28
- };
3
+ export const extractFrameAndAudio = async ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, loop, audioStreamIndex, }) => {
4
+ try {
5
+ const [frame, audio] = await Promise.all([
6
+ includeVideo
7
+ ? extractFrame({
8
+ src,
9
+ timeInSeconds,
10
+ logLevel,
11
+ loop,
12
+ })
13
+ : null,
14
+ includeAudio
15
+ ? extractAudio({
16
+ src,
17
+ timeInSeconds,
18
+ durationInSeconds,
19
+ logLevel,
20
+ loop,
21
+ playbackRate,
22
+ audioStreamIndex,
23
+ })
24
+ : null,
25
+ ]);
26
+ if (frame === 'cannot-decode') {
27
+ return 'cannot-decode';
28
+ }
29
+ if (frame === 'unknown-container-format') {
30
+ return 'unknown-container-format';
31
+ }
32
+ if (audio === 'unknown-container-format') {
33
+ if (frame !== null) {
34
+ frame?.close();
35
+ }
36
+ return 'unknown-container-format';
37
+ }
38
+ if (audio === 'cannot-decode') {
39
+ if (frame !== null) {
40
+ frame?.close();
41
+ }
42
+ return 'cannot-decode';
43
+ }
44
+ return {
45
+ frame: frame?.toVideoFrame() ?? null,
46
+ audio: audio?.data ?? null,
47
+ durationInSeconds: audio?.durationInSeconds ?? null,
48
+ };
49
+ }
50
+ catch (err) {
51
+ 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';
60
+ }
61
+ throw err;
62
+ }
29
63
  };
@@ -0,0 +1,13 @@
1
+ import type { LogLevel } from 'remotion';
2
+ import type { GetSink } from './video-extraction/get-frames-since-keyframe';
3
+ export declare const sinkPromises: Record<string, Promise<GetSink>>;
4
+ export declare const getSinkWeak: (src: string, logLevel: LogLevel) => Promise<{
5
+ getVideo: () => Promise<import("./video-extraction/get-frames-since-keyframe").VideoSinkResult>;
6
+ getAudio: (index: number) => Promise<import("./video-extraction/get-frames-since-keyframe").AudioSinkResult>;
7
+ actualMatroskaTimestamps: {
8
+ observeTimestamp: (startTime: number) => void;
9
+ getRealTimestamp: (observedTimestamp: number) => number | null;
10
+ };
11
+ isMatroska: boolean;
12
+ getDuration: () => Promise<number>;
13
+ }>;
@@ -0,0 +1,23 @@
1
+ import { Internals } from 'remotion';
2
+ import { getSinks } from './video-extraction/get-frames-since-keyframe';
3
+ export const sinkPromises = {};
4
+ export const getSinkWeak = async (src, logLevel) => {
5
+ let promise = sinkPromises[src];
6
+ if (!promise) {
7
+ promise = getSinks(src);
8
+ sinkPromises[src] = promise;
9
+ }
10
+ let awaited = await promise;
11
+ let deferredValue = awaited.deref();
12
+ if (!deferredValue) {
13
+ Internals.Log.verbose({
14
+ logLevel,
15
+ tag: '@remotion/media',
16
+ }, `Sink for ${src} was garbage collected, creating new sink`);
17
+ promise = getSinks(src);
18
+ sinkPromises[src] = promise;
19
+ awaited = await promise;
20
+ deferredValue = awaited.deref();
21
+ }
22
+ return deferredValue;
23
+ };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,13 @@
1
- export { Audio as experimental_Audio } from './audio/audio';
2
- export { AudioProps } from './audio/props';
1
+ import { Audio } from './audio/audio';
2
+ import { Video } from './video/video';
3
+ /**
4
+ * @deprecated Now just `Audio`
5
+ */
6
+ export declare const experimental_Audio: import("react").FC<import(".").AudioProps>;
7
+ /**
8
+ * @deprecated Now just `Video`
9
+ */
10
+ export declare const experimental_Video: import("react").FC<import(".").VideoProps>;
11
+ export { AudioProps, FallbackHtml5AudioProps } from './audio/props';
3
12
  export { VideoProps } from './video/props';
4
- export { Video as experimental_Video } from './video/video';
13
+ export { Audio, Video };
package/dist/index.js CHANGED
@@ -1,2 +1,11 @@
1
- export { Audio as experimental_Audio } from './audio/audio';
2
- export { Video as experimental_Video } from './video/video';
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 { Audio, Video };
@@ -5,6 +5,7 @@ export declare class MediaPlayer {
5
5
  private context;
6
6
  private src;
7
7
  private logLevel;
8
+ private playbackRate;
8
9
  private canvasSink;
9
10
  private videoFrameIterator;
10
11
  private nextFrame;
@@ -15,6 +16,7 @@ export declare class MediaPlayer {
15
16
  private sharedAudioContext;
16
17
  private audioSyncAnchor;
17
18
  private playing;
19
+ private muted;
18
20
  private animationFrameId;
19
21
  private videoAsyncId;
20
22
  private initialized;
@@ -24,6 +26,7 @@ export declare class MediaPlayer {
24
26
  private audioBufferHealth;
25
27
  private audioIteratorStarted;
26
28
  private readonly HEALTHY_BUFER_THRESHOLD_SECONDS;
29
+ private onVideoFrameCallback?;
27
30
  constructor({ canvas, src, logLevel, sharedAudioContext, }: {
28
31
  canvas: HTMLCanvasElement;
29
32
  src: string;
@@ -40,10 +43,15 @@ export declare class MediaPlayer {
40
43
  seekTo(time: number): Promise<void>;
41
44
  play(): Promise<void>;
42
45
  pause(): void;
46
+ setMuted(muted: boolean): void;
47
+ setVolume(volume: number): void;
48
+ setPlaybackRate(rate: number): Promise<void>;
43
49
  dispose(): void;
44
50
  private getPlaybackTime;
51
+ private getAdjustedTimestamp;
45
52
  private scheduleAudioChunk;
46
53
  onBufferingChange(callback: (isBuffering: boolean) => void): void;
54
+ onVideoFrame(callback: (frame: CanvasImageSource) => void): void;
47
55
  private canRenderVideo;
48
56
  private startRenderLoop;
49
57
  private stopRenderLoop;
@@ -2,6 +2,7 @@ import { ALL_FORMATS, AudioBufferSink, CanvasSink, Input, UrlSource, } from 'med
2
2
  import { Internals } from 'remotion';
3
3
  import { sleep, withTimeout } from './timeout-utils';
4
4
  export const SEEK_THRESHOLD = 0.05;
5
+ const AUDIO_BUFFER_TOLERANCE_THRESHOLD = 0.1;
5
6
  export class MediaPlayer {
6
7
  constructor({ canvas, src, logLevel, sharedAudioContext, }) {
7
8
  this.canvasSink = null;
@@ -14,6 +15,7 @@ export class MediaPlayer {
14
15
  // audioDelay = mediaTimestamp + audioSyncAnchor - sharedAudioContext.currentTime
15
16
  this.audioSyncAnchor = 0;
16
17
  this.playing = false;
18
+ this.muted = false;
17
19
  this.animationFrameId = null;
18
20
  this.videoAsyncId = 0;
19
21
  this.initialized = false;
@@ -38,7 +40,7 @@ export class MediaPlayer {
38
40
  this.animationFrameId = null;
39
41
  }
40
42
  };
41
- this.startAudioIterator = async (timeToSeek) => {
43
+ this.startAudioIterator = async (startFromSecond) => {
42
44
  if (!this.hasAudio())
43
45
  return;
44
46
  // Clean up existing audio iterator
@@ -46,8 +48,8 @@ export class MediaPlayer {
46
48
  this.audioIteratorStarted = false;
47
49
  this.audioBufferHealth = 0;
48
50
  try {
49
- this.audioBufferIterator = this.audioSink.buffers(timeToSeek);
50
- this.runAudioIterator();
51
+ this.audioBufferIterator = this.audioSink.buffers(startFromSecond);
52
+ this.runAudioIterator(startFromSecond);
51
53
  }
52
54
  catch (error) {
53
55
  Internals.Log.error({ logLevel: this.logLevel, tag: '@remotion/media' }, '[MediaPlayer] Failed to start audio iterator', error);
@@ -70,6 +72,9 @@ export class MediaPlayer {
70
72
  if (firstFrame) {
71
73
  Internals.Log.trace({ logLevel: this.logLevel, tag: '@remotion/media' }, `[MediaPlayer] Drew initial frame ${firstFrame.timestamp.toFixed(3)}s`);
72
74
  this.context.drawImage(firstFrame.canvas, 0, 0);
75
+ if (this.onVideoFrameCallback) {
76
+ this.onVideoFrameCallback(this.canvas);
77
+ }
73
78
  }
74
79
  this.nextFrame = secondFrame ?? null;
75
80
  if (secondFrame) {
@@ -90,7 +95,8 @@ export class MediaPlayer {
90
95
  if (!newNextFrame) {
91
96
  break;
92
97
  }
93
- if (newNextFrame.timestamp <= this.getPlaybackTime()) {
98
+ if (this.getAdjustedTimestamp(newNextFrame.timestamp) <=
99
+ this.getPlaybackTime()) {
94
100
  continue;
95
101
  }
96
102
  else {
@@ -106,7 +112,7 @@ export class MediaPlayer {
106
112
  };
107
113
  this.bufferingStartedAtMs = null;
108
114
  this.minBufferingTimeoutMs = 500;
109
- this.runAudioIterator = async () => {
115
+ this.runAudioIterator = async (startFromSecond) => {
110
116
  if (!this.hasAudio() || !this.audioBufferIterator)
111
117
  return;
112
118
  try {
@@ -129,20 +135,28 @@ export class MediaPlayer {
129
135
  }
130
136
  const { buffer, timestamp, duration } = result.value;
131
137
  totalBufferDuration += duration;
132
- this.audioBufferHealth = Math.max(0, totalBufferDuration);
133
- this.maybeResumeFromBuffering(totalBufferDuration);
134
- if (this.playing) {
138
+ this.audioBufferHealth = Math.max(0, totalBufferDuration / this.playbackRate);
139
+ this.maybeResumeFromBuffering(totalBufferDuration / this.playbackRate);
140
+ if (this.playing && !this.muted) {
135
141
  if (isFirstBuffer) {
136
142
  this.audioSyncAnchor =
137
- this.sharedAudioContext.currentTime - timestamp;
143
+ this.sharedAudioContext.currentTime -
144
+ this.getAdjustedTimestamp(timestamp);
138
145
  isFirstBuffer = false;
139
146
  }
147
+ // if timestamp is less than timeToSeek, skip
148
+ // context: for some reason, mediabunny returns buffer at 9.984s, when requested at 10s
149
+ if (timestamp < startFromSecond - AUDIO_BUFFER_TOLERANCE_THRESHOLD) {
150
+ continue;
151
+ }
140
152
  this.scheduleAudioChunk(buffer, timestamp);
141
153
  }
142
- if (timestamp - this.getPlaybackTime() >= 1) {
154
+ if (this.getAdjustedTimestamp(timestamp) - this.getPlaybackTime() >=
155
+ 1) {
143
156
  await new Promise((resolve) => {
144
157
  const check = () => {
145
- if (timestamp - this.getPlaybackTime() < 1) {
158
+ if (this.getAdjustedTimestamp(timestamp) - this.getPlaybackTime() <
159
+ 1) {
146
160
  resolve();
147
161
  }
148
162
  else {
@@ -162,6 +176,7 @@ export class MediaPlayer {
162
176
  this.src = src;
163
177
  this.logLevel = logLevel ?? 'info';
164
178
  this.sharedAudioContext = sharedAudioContext;
179
+ this.playbackRate = 1;
165
180
  const context = canvas.getContext('2d', {
166
181
  alpha: false,
167
182
  desynchronized: true,
@@ -211,8 +226,9 @@ export class MediaPlayer {
211
226
  this.audioSyncAnchor = this.sharedAudioContext.currentTime - startTime;
212
227
  }
213
228
  this.initialized = true;
214
- await this.startAudioIterator(startTime);
215
- await this.startVideoIterator(startTime);
229
+ const mediaTime = startTime * this.playbackRate;
230
+ await this.startAudioIterator(mediaTime);
231
+ await this.startVideoIterator(mediaTime);
216
232
  this.startRenderLoop();
217
233
  }
218
234
  catch (error) {
@@ -240,12 +256,14 @@ export class MediaPlayer {
240
256
  const currentPlaybackTime = this.getPlaybackTime();
241
257
  const isSignificantSeek = Math.abs(newTime - currentPlaybackTime) > SEEK_THRESHOLD;
242
258
  if (isSignificantSeek) {
259
+ this.nextFrame = null;
243
260
  this.audioSyncAnchor = this.sharedAudioContext.currentTime - newTime;
244
261
  if (this.audioSink) {
245
262
  await this.cleanAudioIteratorAndNodes();
246
263
  }
247
- await this.startAudioIterator(newTime);
248
- await this.startVideoIterator(newTime);
264
+ const mediaTime = newTime * this.playbackRate;
265
+ await this.startAudioIterator(mediaTime);
266
+ await this.startVideoIterator(mediaTime);
249
267
  }
250
268
  if (!this.playing) {
251
269
  this.render();
@@ -267,6 +285,30 @@ export class MediaPlayer {
267
285
  this.cleanupAudioQueue();
268
286
  this.stopRenderLoop();
269
287
  }
288
+ setMuted(muted) {
289
+ this.muted = muted;
290
+ if (muted) {
291
+ this.cleanupAudioQueue();
292
+ }
293
+ }
294
+ setVolume(volume) {
295
+ if (!this.gainNode) {
296
+ return;
297
+ }
298
+ const appliedVolume = Math.max(0, volume);
299
+ this.gainNode.gain.value = appliedVolume;
300
+ }
301
+ async setPlaybackRate(rate) {
302
+ if (this.playbackRate === rate)
303
+ return;
304
+ this.playbackRate = rate;
305
+ if (this.hasAudio() && this.playing) {
306
+ const currentPlaybackTime = this.getPlaybackTime();
307
+ const mediaTime = currentPlaybackTime * rate;
308
+ await this.cleanAudioIteratorAndNodes();
309
+ await this.startAudioIterator(mediaTime);
310
+ }
311
+ }
270
312
  dispose() {
271
313
  this.input?.dispose();
272
314
  this.stopRenderLoop();
@@ -277,11 +319,16 @@ export class MediaPlayer {
277
319
  getPlaybackTime() {
278
320
  return this.sharedAudioContext.currentTime - this.audioSyncAnchor;
279
321
  }
322
+ getAdjustedTimestamp(mediaTimestamp) {
323
+ return mediaTimestamp / this.playbackRate;
324
+ }
280
325
  scheduleAudioChunk(buffer, mediaTimestamp) {
281
- const targetTime = mediaTimestamp + this.audioSyncAnchor;
326
+ const adjustedTimestamp = this.getAdjustedTimestamp(mediaTimestamp);
327
+ const targetTime = adjustedTimestamp + this.audioSyncAnchor;
282
328
  const delay = targetTime - this.sharedAudioContext.currentTime;
283
329
  const node = this.sharedAudioContext.createBufferSource();
284
330
  node.buffer = buffer;
331
+ node.playbackRate.value = this.playbackRate;
285
332
  node.connect(this.gainNode);
286
333
  if (delay >= 0) {
287
334
  node.start(targetTime);
@@ -295,9 +342,16 @@ export class MediaPlayer {
295
342
  onBufferingChange(callback) {
296
343
  this.onBufferingChangeCallback = callback;
297
344
  }
345
+ onVideoFrame(callback) {
346
+ this.onVideoFrameCallback = callback;
347
+ if (this.initialized && callback) {
348
+ callback(this.canvas);
349
+ }
350
+ }
298
351
  canRenderVideo() {
299
- return (this.audioIteratorStarted &&
300
- this.audioBufferHealth >= this.HEALTHY_BUFER_THRESHOLD_SECONDS);
352
+ return (!this.hasAudio() ||
353
+ (this.audioIteratorStarted &&
354
+ this.audioBufferHealth >= this.HEALTHY_BUFER_THRESHOLD_SECONDS));
301
355
  }
302
356
  startRenderLoop() {
303
357
  if (this.animationFrameId !== null) {
@@ -315,10 +369,14 @@ export class MediaPlayer {
315
369
  return (!this.isBuffering &&
316
370
  this.canRenderVideo() &&
317
371
  this.nextFrame !== null &&
318
- this.nextFrame.timestamp <= this.getPlaybackTime());
372
+ this.getAdjustedTimestamp(this.nextFrame.timestamp) <=
373
+ this.getPlaybackTime());
319
374
  }
320
375
  drawCurrentFrame() {
321
376
  this.context.drawImage(this.nextFrame.canvas, 0, 0);
377
+ if (this.onVideoFrameCallback) {
378
+ this.onVideoFrameCallback(this.canvas);
379
+ }
322
380
  this.nextFrame = null;
323
381
  this.updateNextFrame();
324
382
  }
@@ -1,24 +1,42 @@
1
1
  import type { LogLevel, LoopVolumeCurveBehavior, OnVideoFrame, VolumeProp } from 'remotion';
2
- export type VideoProps = {
2
+ export type FallbackOffthreadVideoProps = {
3
+ acceptableTimeShiftInSeconds?: number;
4
+ toneFrequency?: number;
5
+ transparent?: boolean;
6
+ toneMapped?: boolean;
7
+ onError?: (err: Error) => void;
8
+ };
9
+ type MandatoryVideoProps = {
3
10
  src: string;
4
- className?: string;
5
- trimBefore?: number;
6
- trimAfter?: number;
7
- volume?: VolumeProp;
8
- loopVolumeCurveBehavior?: LoopVolumeCurveBehavior;
9
- name?: string;
10
- pauseWhenBuffering?: boolean;
11
- showInTimeline?: boolean;
12
- onVideoFrame?: OnVideoFrame;
13
- playbackRate?: number;
14
- muted?: boolean;
15
- delayRenderRetries?: number;
16
- delayRenderTimeoutInMilliseconds?: number;
17
- style?: React.CSSProperties;
11
+ };
12
+ type OuterVideoProps = {
13
+ trimBefore: number | undefined;
14
+ trimAfter: number | undefined;
15
+ };
16
+ type OptionalVideoProps = {
17
+ className: string | undefined;
18
+ volume: VolumeProp;
19
+ loopVolumeCurveBehavior: LoopVolumeCurveBehavior;
20
+ name: string | undefined;
21
+ onVideoFrame: OnVideoFrame | undefined;
22
+ playbackRate: number;
23
+ muted: boolean;
24
+ delayRenderRetries: number | null;
25
+ delayRenderTimeoutInMilliseconds: number | null;
26
+ style: React.CSSProperties;
18
27
  /**
19
28
  * @deprecated For internal use only
20
29
  */
21
- stack?: string;
22
- logLevel?: LogLevel;
23
- loop?: boolean;
30
+ stack: string | undefined;
31
+ logLevel: LogLevel;
32
+ loop: boolean;
33
+ audioStreamIndex: number;
34
+ disallowFallbackToOffthreadVideo: boolean;
35
+ fallbackOffthreadVideoProps: FallbackOffthreadVideoProps;
36
+ trimAfter: number | undefined;
37
+ trimBefore: number | undefined;
38
+ showInTimeline: boolean;
24
39
  };
40
+ export type InnerVideoProps = MandatoryVideoProps & OuterVideoProps & OptionalVideoProps;
41
+ export type VideoProps = MandatoryVideoProps & Partial<OuterVideoProps> & Partial<OptionalVideoProps>;
42
+ export {};
@@ -1,11 +1,17 @@
1
1
  import React from 'react';
2
- import type { LogLevel } from 'remotion';
3
- type NewVideoForPreviewProps = {
2
+ import type { LogLevel, LoopVolumeCurveBehavior, OnVideoFrame, VolumeProp } from 'remotion';
3
+ type InnerVideoProps = {
4
+ readonly className: string | undefined;
5
+ readonly loop: boolean;
4
6
  readonly src: string;
5
- readonly style?: React.CSSProperties;
6
- readonly playbackRate?: number;
7
- readonly logLevel?: LogLevel;
8
- readonly className?: string;
7
+ readonly logLevel: LogLevel;
8
+ readonly muted: boolean;
9
+ readonly name: string | undefined;
10
+ readonly volume: VolumeProp;
11
+ readonly loopVolumeCurveBehavior: LoopVolumeCurveBehavior;
12
+ readonly onVideoFrame: OnVideoFrame | undefined;
13
+ readonly playbackRate: number;
14
+ readonly style: React.CSSProperties;
9
15
  };
10
- export declare const NewVideoForPreview: React.FC<NewVideoForPreviewProps>;
16
+ export declare const VideoForPreview: React.FC<InnerVideoProps>;
11
17
  export {};