@remotion/renderer 3.0.14 → 3.0.17

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 (210) hide show
  1. package/dist/abort.d.ts +7 -0
  2. package/dist/abort.js +20 -0
  3. package/dist/assets/get-audio-channels.d.ts +2 -1
  4. package/dist/assets/get-audio-channels.js +2 -2
  5. package/dist/cancel.d.ts +7 -0
  6. package/dist/cancel.js +25 -0
  7. package/dist/combine-videos.d.ts +2 -1
  8. package/dist/combine-videos.js +3 -1
  9. package/dist/cycle-browser-tabs.d.ts +2 -1
  10. package/dist/cycle-browser-tabs.js +9 -2
  11. package/dist/extract-frame-from-video.d.ts +4 -6
  12. package/dist/extract-frame-from-video.js +54 -8
  13. package/dist/get-compositions.d.ts +1 -0
  14. package/dist/get-compositions.js +4 -2
  15. package/dist/index.d.ts +3 -1
  16. package/dist/index.js +3 -1
  17. package/dist/is-beyond-last-frame.d.ts +2 -0
  18. package/dist/is-beyond-last-frame.js +12 -0
  19. package/dist/last-frame-from-video-cache.d.ts +13 -0
  20. package/dist/last-frame-from-video-cache.js +52 -0
  21. package/dist/make-cancel-signal.d.ts +7 -0
  22. package/dist/make-cancel-signal.js +25 -0
  23. package/dist/merge-audio-track.js +2 -2
  24. package/dist/offthread/index.d.ts +0 -0
  25. package/dist/offthread/index.js +1 -0
  26. package/dist/offthread-video-server.d.ts +2 -1
  27. package/dist/offthread-video-server.js +2 -1
  28. package/dist/open-browser.js +2 -1
  29. package/dist/prepare-server.d.ts +2 -1
  30. package/dist/prepare-server.js +3 -1
  31. package/dist/preprocess-audio-track.d.ts +1 -0
  32. package/dist/preprocess-audio-track.js +2 -2
  33. package/dist/prespawn-ffmpeg.d.ts +2 -0
  34. package/dist/prespawn-ffmpeg.js +3 -0
  35. package/dist/provide-screenshot.js +1 -1
  36. package/dist/render-frames.d.ts +3 -0
  37. package/dist/render-frames.js +78 -37
  38. package/dist/render-media.d.ts +4 -1
  39. package/dist/render-media.js +105 -57
  40. package/dist/render-still.d.ts +5 -2
  41. package/dist/render-still.js +26 -12
  42. package/dist/serve-static.d.ts +1 -0
  43. package/dist/serve-static.js +4 -0
  44. package/dist/set-props-and-env.d.ts +2 -1
  45. package/dist/set-props-and-env.js +20 -1
  46. package/dist/stitch-frames-to-video.d.ts +4 -1
  47. package/dist/stitch-frames-to-video.js +30 -15
  48. package/package.json +3 -3
  49. package/dist/assets/calculate-asset-positions.d.ts.map +0 -1
  50. package/dist/assets/calculate-asset-positions.js.map +0 -1
  51. package/dist/assets/calculate-atempo.d.ts.map +0 -1
  52. package/dist/assets/calculate-atempo.js.map +0 -1
  53. package/dist/assets/convert-assets-to-file-urls.d.ts.map +0 -1
  54. package/dist/assets/convert-assets-to-file-urls.js.map +0 -1
  55. package/dist/assets/download-and-map-assets-to-file.d.ts.map +0 -1
  56. package/dist/assets/download-and-map-assets-to-file.js.map +0 -1
  57. package/dist/assets/download-file.d.ts.map +0 -1
  58. package/dist/assets/download-file.js.map +0 -1
  59. package/dist/assets/ffmpeg-volume-expression.d.ts.map +0 -1
  60. package/dist/assets/ffmpeg-volume-expression.js.map +0 -1
  61. package/dist/assets/flatten-volume-array.d.ts.map +0 -1
  62. package/dist/assets/flatten-volume-array.js.map +0 -1
  63. package/dist/assets/get-audio-channels.d.ts.map +0 -1
  64. package/dist/assets/get-audio-channels.js.map +0 -1
  65. package/dist/assets/read-file.d.ts.map +0 -1
  66. package/dist/assets/read-file.js.map +0 -1
  67. package/dist/assets/round-volume-to-avoid-stack-overflow.d.ts.map +0 -1
  68. package/dist/assets/round-volume-to-avoid-stack-overflow.js.map +0 -1
  69. package/dist/assets/sanitize-filename.d.ts.map +0 -1
  70. package/dist/assets/sanitize-filename.js.map +0 -1
  71. package/dist/assets/sanitize-filepath.d.ts.map +0 -1
  72. package/dist/assets/sanitize-filepath.js.map +0 -1
  73. package/dist/assets/truncate-utf8-bytes.d.ts.map +0 -1
  74. package/dist/assets/truncate-utf8-bytes.js.map +0 -1
  75. package/dist/assets/types.d.ts.map +0 -1
  76. package/dist/assets/types.js.map +0 -1
  77. package/dist/browser-log.d.ts.map +0 -1
  78. package/dist/browser-log.js.map +0 -1
  79. package/dist/calculate-ffmpeg-filters.d.ts.map +0 -1
  80. package/dist/calculate-ffmpeg-filters.js.map +0 -1
  81. package/dist/can-use-parallel-encoding.d.ts.map +0 -1
  82. package/dist/can-use-parallel-encoding.js.map +0 -1
  83. package/dist/chunk.d.ts.map +0 -1
  84. package/dist/chunk.js.map +0 -1
  85. package/dist/combine-videos.d.ts.map +0 -1
  86. package/dist/combine-videos.js.map +0 -1
  87. package/dist/convert-to-pcm.d.ts.map +0 -1
  88. package/dist/convert-to-pcm.js.map +0 -1
  89. package/dist/create-ffmpeg-complex-filter.d.ts.map +0 -1
  90. package/dist/create-ffmpeg-complex-filter.js.map +0 -1
  91. package/dist/create-ffmpeg-merge-filter.d.ts.map +0 -1
  92. package/dist/create-ffmpeg-merge-filter.js.map +0 -1
  93. package/dist/create-silent-audio.d.ts.map +0 -1
  94. package/dist/create-silent-audio.js.map +0 -1
  95. package/dist/cycle-browser-tabs.d.ts.map +0 -1
  96. package/dist/cycle-browser-tabs.js.map +0 -1
  97. package/dist/delay-render-embedded-stack.d.ts.map +0 -1
  98. package/dist/delay-render-embedded-stack.js.map +0 -1
  99. package/dist/delete-directory.d.ts.map +0 -1
  100. package/dist/delete-directory.js.map +0 -1
  101. package/dist/ensure-frames-in-order.d.ts.map +0 -1
  102. package/dist/ensure-frames-in-order.js.map +0 -1
  103. package/dist/ensure-output-directory.d.ts.map +0 -1
  104. package/dist/ensure-output-directory.js.map +0 -1
  105. package/dist/error-handling/handle-javascript-exception.d.ts.map +0 -1
  106. package/dist/error-handling/handle-javascript-exception.js.map +0 -1
  107. package/dist/error-handling/symbolicate-error.d.ts.map +0 -1
  108. package/dist/error-handling/symbolicate-error.js.map +0 -1
  109. package/dist/error-handling/symbolicateable-error.d.ts.map +0 -1
  110. package/dist/error-handling/symbolicateable-error.js.map +0 -1
  111. package/dist/ffmpeg-filter-file.d.ts.map +0 -1
  112. package/dist/ffmpeg-filter-file.js.map +0 -1
  113. package/dist/ffmpeg-flags.d.ts.map +0 -1
  114. package/dist/ffmpeg-flags.js.map +0 -1
  115. package/dist/get-audio-codec-name.d.ts.map +0 -1
  116. package/dist/get-audio-codec-name.js.map +0 -1
  117. package/dist/get-browser-instance.d.ts.map +0 -1
  118. package/dist/get-browser-instance.js.map +0 -1
  119. package/dist/get-codec-name.d.ts.map +0 -1
  120. package/dist/get-codec-name.js.map +0 -1
  121. package/dist/get-compositions.d.ts.map +0 -1
  122. package/dist/get-compositions.js.map +0 -1
  123. package/dist/get-concurrency.d.ts.map +0 -1
  124. package/dist/get-concurrency.js.map +0 -1
  125. package/dist/get-duration-from-frame-range.d.ts.map +0 -1
  126. package/dist/get-duration-from-frame-range.js.map +0 -1
  127. package/dist/get-extension-from-codec.d.ts.map +0 -1
  128. package/dist/get-extension-from-codec.js.map +0 -1
  129. package/dist/get-frame-to-render.d.ts.map +0 -1
  130. package/dist/get-frame-to-render.js.map +0 -1
  131. package/dist/get-local-browser-executable.d.ts.map +0 -1
  132. package/dist/get-local-browser-executable.js.map +0 -1
  133. package/dist/get-port.d.ts.map +0 -1
  134. package/dist/get-port.js.map +0 -1
  135. package/dist/get-prores-profile-name.d.ts.map +0 -1
  136. package/dist/get-prores-profile-name.js.map +0 -1
  137. package/dist/image-format.d.ts.map +0 -1
  138. package/dist/image-format.js.map +0 -1
  139. package/dist/index.d.ts.map +0 -1
  140. package/dist/index.js.map +0 -1
  141. package/dist/is-serve-url.d.ts.map +0 -1
  142. package/dist/is-serve-url.js.map +0 -1
  143. package/dist/legacy-webpack-config.d.ts.map +0 -1
  144. package/dist/legacy-webpack-config.js.map +0 -1
  145. package/dist/make-assets-download-dir.d.ts.map +0 -1
  146. package/dist/make-assets-download-dir.js.map +0 -1
  147. package/dist/merge-audio-track.d.ts.map +0 -1
  148. package/dist/merge-audio-track.js.map +0 -1
  149. package/dist/normalize-serve-url.d.ts.map +0 -1
  150. package/dist/normalize-serve-url.js.map +0 -1
  151. package/dist/open-browser.d.ts.map +0 -1
  152. package/dist/open-browser.js.map +0 -1
  153. package/dist/p-limit.d.ts.map +0 -1
  154. package/dist/p-limit.js.map +0 -1
  155. package/dist/parse-browser-error-stack.d.ts.map +0 -1
  156. package/dist/parse-browser-error-stack.js.map +0 -1
  157. package/dist/parse-ffmpeg-progress.d.ts.map +0 -1
  158. package/dist/parse-ffmpeg-progress.js.map +0 -1
  159. package/dist/pool.d.ts.map +0 -1
  160. package/dist/pool.js.map +0 -1
  161. package/dist/prepare-server.d.ts.map +0 -1
  162. package/dist/prepare-server.js.map +0 -1
  163. package/dist/preprocess-audio-track.d.ts.map +0 -1
  164. package/dist/preprocess-audio-track.js.map +0 -1
  165. package/dist/prespawn-ffmpeg.d.ts.map +0 -1
  166. package/dist/prespawn-ffmpeg.js.map +0 -1
  167. package/dist/provide-screenshot.d.ts.map +0 -1
  168. package/dist/provide-screenshot.js.map +0 -1
  169. package/dist/puppeteer-evaluate.d.ts.map +0 -1
  170. package/dist/puppeteer-evaluate.js.map +0 -1
  171. package/dist/puppeteer-screenshot.d.ts.map +0 -1
  172. package/dist/puppeteer-screenshot.js.map +0 -1
  173. package/dist/render-frames.d.ts.map +0 -1
  174. package/dist/render-frames.js.map +0 -1
  175. package/dist/render-media.d.ts.map +0 -1
  176. package/dist/render-media.js.map +0 -1
  177. package/dist/render-still.d.ts.map +0 -1
  178. package/dist/render-still.js.map +0 -1
  179. package/dist/resolve-asset-src.d.ts.map +0 -1
  180. package/dist/resolve-asset-src.js.map +0 -1
  181. package/dist/sample-rate.d.ts.map +0 -1
  182. package/dist/sample-rate.js.map +0 -1
  183. package/dist/screenshot-dom-element.d.ts.map +0 -1
  184. package/dist/screenshot-dom-element.js.map +0 -1
  185. package/dist/screenshot-task.d.ts.map +0 -1
  186. package/dist/screenshot-task.js.map +0 -1
  187. package/dist/seek-to-frame.d.ts.map +0 -1
  188. package/dist/seek-to-frame.js.map +0 -1
  189. package/dist/serve-static.d.ts.map +0 -1
  190. package/dist/serve-static.js.map +0 -1
  191. package/dist/set-props-and-env.d.ts.map +0 -1
  192. package/dist/set-props-and-env.js.map +0 -1
  193. package/dist/stitch-frames-to-video.d.ts.map +0 -1
  194. package/dist/stitch-frames-to-video.js.map +0 -1
  195. package/dist/stringify-ffmpeg-filter.d.ts.map +0 -1
  196. package/dist/stringify-ffmpeg-filter.js.map +0 -1
  197. package/dist/symbolicate-stacktrace.d.ts.map +0 -1
  198. package/dist/symbolicate-stacktrace.js.map +0 -1
  199. package/dist/tmp-dir.d.ts.map +0 -1
  200. package/dist/tmp-dir.js.map +0 -1
  201. package/dist/types.d.ts.map +0 -1
  202. package/dist/types.js.map +0 -1
  203. package/dist/validate-even-dimensions-with-codec.d.ts.map +0 -1
  204. package/dist/validate-even-dimensions-with-codec.js.map +0 -1
  205. package/dist/validate-ffmpeg.d.ts.map +0 -1
  206. package/dist/validate-ffmpeg.js.map +0 -1
  207. package/dist/validate-puppeteer-timeout.d.ts.map +0 -1
  208. package/dist/validate-puppeteer-timeout.js.map +0 -1
  209. package/dist/validate-scale.d.ts.map +0 -1
  210. package/dist/validate-scale.js.map +0 -1
@@ -0,0 +1,7 @@
1
+ declare type Callback = () => void;
2
+ export declare type CancelSignal = (callback: Callback) => void;
3
+ export declare const getCancelSignal: () => {
4
+ cancelSignal: CancelSignal;
5
+ cancel: () => void;
6
+ };
7
+ export {};
package/dist/abort.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCancelSignal = void 0;
4
+ const getCancelSignal = () => {
5
+ const callbacks = [];
6
+ let cancelled = false;
7
+ return {
8
+ cancelSignal: (callback) => callbacks.push(callback),
9
+ cancel: () => {
10
+ if (cancelled) {
11
+ return;
12
+ }
13
+ callbacks.forEach((cb) => {
14
+ cb();
15
+ });
16
+ cancelled = true;
17
+ },
18
+ };
19
+ };
20
+ exports.getCancelSignal = getCancelSignal;
@@ -1,4 +1,5 @@
1
- export declare function getAudioChannelsAndDuration(path: string): Promise<{
1
+ import { FfmpegExecutable } from 'remotion';
2
+ export declare function getAudioChannelsAndDuration(path: string, ffprobeExecutable: FfmpegExecutable): Promise<{
2
3
  channels: number;
3
4
  duration: number | null;
4
5
  }>;
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getAudioChannelsAndDuration = void 0;
7
7
  const execa_1 = __importDefault(require("execa"));
8
- async function getAudioChannelsAndDuration(path) {
8
+ async function getAudioChannelsAndDuration(path, ffprobeExecutable) {
9
9
  const args = [
10
10
  ['-v', 'error'],
11
11
  ['-show_entries', 'stream=channels:format=duration'],
@@ -14,7 +14,7 @@ async function getAudioChannelsAndDuration(path) {
14
14
  ]
15
15
  .reduce((acc, val) => acc.concat(val), [])
16
16
  .filter(Boolean);
17
- const task = await (0, execa_1.default)('ffprobe', args);
17
+ const task = await (0, execa_1.default)(ffprobeExecutable !== null && ffprobeExecutable !== void 0 ? ffprobeExecutable : 'ffprobe', args);
18
18
  const channels = task.stdout.match(/channels=([0-9]+)/);
19
19
  const duration = task.stdout.match(/duration=([0-9.]+)/);
20
20
  return {
@@ -0,0 +1,7 @@
1
+ declare type Callback = () => void;
2
+ export declare type CancelSignal = (callback: Callback) => void;
3
+ export declare const makeCancelSignal: () => {
4
+ cancelSignal: CancelSignal;
5
+ cancel: () => void;
6
+ };
7
+ export {};
package/dist/cancel.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeCancelSignal = void 0;
4
+ const makeCancelSignal = () => {
5
+ const callbacks = [];
6
+ let cancelled = false;
7
+ return {
8
+ cancelSignal: (callback) => {
9
+ callbacks.push(callback);
10
+ if (cancelled) {
11
+ callback();
12
+ }
13
+ },
14
+ cancel: () => {
15
+ if (cancelled) {
16
+ return;
17
+ }
18
+ callbacks.forEach((cb) => {
19
+ cb();
20
+ });
21
+ cancelled = true;
22
+ },
23
+ };
24
+ };
25
+ exports.makeCancelSignal = makeCancelSignal;
@@ -1,9 +1,10 @@
1
1
  import { Codec } from 'remotion';
2
- export declare const combineVideos: ({ files, filelistDir, output, onProgress, numberOfFrames, codec, }: {
2
+ export declare const combineVideos: ({ files, filelistDir, output, onProgress, numberOfFrames, codec, fps, }: {
3
3
  files: string[];
4
4
  filelistDir: string;
5
5
  output: string;
6
6
  onProgress: (p: number) => void;
7
7
  numberOfFrames: number;
8
8
  codec: Codec;
9
+ fps: number;
9
10
  }) => Promise<void>;
@@ -11,13 +11,15 @@ const path_1 = require("path");
11
11
  const remotion_1 = require("remotion");
12
12
  const get_audio_codec_name_1 = require("./get-audio-codec-name");
13
13
  const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
14
- const combineVideos = async ({ files, filelistDir, output, onProgress, numberOfFrames, codec, }) => {
14
+ const combineVideos = async ({ files, filelistDir, output, onProgress, numberOfFrames, codec, fps, }) => {
15
15
  var _a;
16
16
  const fileList = files.map((p) => `file '${p}'`).join('\n');
17
17
  const fileListTxt = (0, path_1.join)(filelistDir, 'files.txt');
18
18
  (0, fs_1.writeFileSync)(fileListTxt, fileList);
19
19
  try {
20
20
  const task = (0, execa_1.default)('ffmpeg', [
21
+ remotion_1.Internals.isAudioCodec(codec) ? null : '-r',
22
+ remotion_1.Internals.isAudioCodec(codec) ? null : String(fps),
21
23
  '-f',
22
24
  'concat',
23
25
  '-safe',
@@ -1,6 +1,7 @@
1
1
  import { openBrowser } from './open-browser';
2
2
  declare type Await<T> = T extends PromiseLike<infer U> ? U : T;
3
- export declare const cycleBrowserTabs: (puppeteerInstance: Await<ReturnType<typeof openBrowser>>, concurrency: number) => {
3
+ declare type Browser = Await<ReturnType<typeof openBrowser>>;
4
+ export declare const cycleBrowserTabs: (puppeteerInstance: Browser, concurrency: number) => {
4
5
  stopCycling: () => void;
5
6
  };
6
7
  export {};
@@ -9,15 +9,21 @@ const cycleBrowserTabs = (puppeteerInstance, concurrency) => {
9
9
  }
10
10
  let interval = null;
11
11
  let i = 0;
12
+ let stopped = false;
12
13
  const set = () => {
13
14
  interval = setTimeout(() => {
14
15
  puppeteerInstance
15
16
  .pages()
16
17
  .then((pages) => {
17
- var _a, _b;
18
+ var _a;
19
+ if (pages.length === 0) {
20
+ return;
21
+ }
18
22
  const currentPage = pages[i % pages.length];
19
23
  i++;
20
- if (!((_b = (_a = currentPage === null || currentPage === void 0 ? void 0 : currentPage.isClosed) === null || _a === void 0 ? void 0 : _a.call(currentPage)) !== null && _b !== void 0 ? _b : true)) {
24
+ if (!((_a = currentPage === null || currentPage === void 0 ? void 0 : currentPage.isClosed) === null || _a === void 0 ? void 0 : _a.call(currentPage)) &&
25
+ !stopped &&
26
+ (currentPage === null || currentPage === void 0 ? void 0 : currentPage.url()) !== 'about:blank') {
21
27
  return currentPage.bringToFront();
22
28
  }
23
29
  })
@@ -33,6 +39,7 @@ const cycleBrowserTabs = (puppeteerInstance, concurrency) => {
33
39
  if (!interval) {
34
40
  return;
35
41
  }
42
+ stopped = true;
36
43
  return clearInterval(interval);
37
44
  },
38
45
  };
@@ -2,17 +2,15 @@
2
2
  /// <reference types="node" />
3
3
  import { FfmpegExecutable } from 'remotion';
4
4
  import { Readable } from 'stream';
5
+ import { LastFrameOptions } from './last-frame-from-video-cache';
5
6
  export declare function streamToString(stream: Readable): Promise<string>;
6
- export declare const getLastFrameOfVideo: ({ ffmpegExecutable, offset, src, }: {
7
- ffmpegExecutable: FfmpegExecutable;
8
- offset: number;
9
- src: string;
10
- }) => Promise<Buffer>;
7
+ export declare const getLastFrameOfVideo: (options: LastFrameOptions) => Promise<Buffer>;
11
8
  declare type Options = {
12
9
  time: number;
13
10
  src: string;
14
11
  ffmpegExecutable: FfmpegExecutable;
12
+ ffprobeExecutable: FfmpegExecutable;
15
13
  };
16
- export declare const extractFrameFromVideoFn: ({ time, src, ffmpegExecutable, }: Options) => Promise<Buffer>;
14
+ export declare const extractFrameFromVideoFn: ({ time, src, ffmpegExecutable, ffprobeExecutable, }: Options) => Promise<Buffer>;
17
15
  export declare const extractFrameFromVideo: (options: Options) => Promise<Buffer>;
18
16
  export {};
@@ -5,7 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.extractFrameFromVideo = exports.extractFrameFromVideoFn = exports.getLastFrameOfVideo = exports.streamToString = void 0;
7
7
  const execa_1 = __importDefault(require("execa"));
8
+ const remotion_1 = require("remotion");
8
9
  const frame_to_ffmpeg_timestamp_1 = require("./frame-to-ffmpeg-timestamp");
10
+ const is_beyond_last_frame_1 = require("./is-beyond-last-frame");
11
+ const last_frame_from_video_cache_1 = require("./last-frame-from-video-cache");
9
12
  const p_limit_1 = require("./p-limit");
10
13
  function streamToString(stream) {
11
14
  const chunks = [];
@@ -16,15 +19,32 @@ function streamToString(stream) {
16
19
  });
17
20
  }
18
21
  exports.streamToString = streamToString;
19
- const getLastFrameOfVideo = async ({ ffmpegExecutable, offset, src, }) => {
22
+ const lastFrameLimit = (0, p_limit_1.pLimit)(5);
23
+ const mainLimit = (0, p_limit_1.pLimit)(5);
24
+ const getLastFrameOfVideoUnlimited = async ({ ffmpegExecutable, ffprobeExecutable, offset, src, }) => {
20
25
  if (offset > 100) {
21
26
  throw new Error('could not get last frame of ' +
22
27
  src +
23
28
  '. Tried to seek 100ms before the end of the video and no frame was found. The video container has a duration that is longer than it contains video.');
24
29
  }
25
- const actualOffset = `-${offset + 10}ms`;
30
+ const durationCmd = await (0, execa_1.default)(ffprobeExecutable !== null && ffprobeExecutable !== void 0 ? ffprobeExecutable : 'ffprobe', [
31
+ '-v',
32
+ 'error',
33
+ '-select_streams',
34
+ 'v:0',
35
+ '-show_entries',
36
+ 'stream=duration',
37
+ '-of',
38
+ 'default=noprint_wrappers=1:nokey=1',
39
+ src,
40
+ ]);
41
+ const duration = parseFloat(durationCmd.stdout);
42
+ if (Number.isNaN(duration)) {
43
+ throw new TypeError(`Could not get duration of ${src}: ${durationCmd.stdout}`);
44
+ }
45
+ const actualOffset = `${duration * 1000 - offset - 10}ms`;
26
46
  const { stdout, stderr } = (0, execa_1.default)(ffmpegExecutable !== null && ffmpegExecutable !== void 0 ? ffmpegExecutable : 'ffmpeg', [
27
- '-sseof',
47
+ '-ss',
28
48
  actualOffset,
29
49
  '-i',
30
50
  src,
@@ -61,13 +81,34 @@ const getLastFrameOfVideo = async ({ ffmpegExecutable, offset, src, }) => {
61
81
  const [stdErr, stdoutBuffer] = await Promise.all([stdErrString, stdoutChunk]);
62
82
  const isEmpty = stdErr.includes('Output file is empty');
63
83
  if (isEmpty) {
64
- return (0, exports.getLastFrameOfVideo)({ ffmpegExecutable, offset: offset + 10, src });
84
+ return getLastFrameOfVideoUnlimited({
85
+ ffmpegExecutable,
86
+ offset: offset + 10,
87
+ src,
88
+ ffprobeExecutable,
89
+ });
65
90
  }
66
91
  return stdoutBuffer;
67
92
  };
93
+ const getLastFrameOfVideo = async (options) => {
94
+ const fromCache = (0, last_frame_from_video_cache_1.getLastFrameFromCache)(options);
95
+ if (fromCache) {
96
+ return fromCache;
97
+ }
98
+ const result = await lastFrameLimit(getLastFrameOfVideoUnlimited, options);
99
+ (0, last_frame_from_video_cache_1.setLastFrameInCache)(options, result);
100
+ return result;
101
+ };
68
102
  exports.getLastFrameOfVideo = getLastFrameOfVideo;
69
- const limit = (0, p_limit_1.pLimit)(5);
70
- const extractFrameFromVideoFn = async ({ time, src, ffmpegExecutable, }) => {
103
+ const extractFrameFromVideoFn = async ({ time, src, ffmpegExecutable, ffprobeExecutable, }) => {
104
+ if ((0, is_beyond_last_frame_1.isBeyondLastFrame)(src, time)) {
105
+ return (0, exports.getLastFrameOfVideo)({
106
+ ffmpegExecutable,
107
+ ffprobeExecutable,
108
+ offset: 0,
109
+ src,
110
+ });
111
+ }
71
112
  const ffmpegTimestamp = (0, frame_to_ffmpeg_timestamp_1.frameToFfmpegTimestamp)(time);
72
113
  const { stdout, stderr } = (0, execa_1.default)(ffmpegExecutable !== null && ffmpegExecutable !== void 0 ? ffmpegExecutable : 'ffmpeg', [
73
114
  '-ss',
@@ -117,8 +158,10 @@ const extractFrameFromVideoFn = async ({ time, src, ffmpegExecutable, }) => {
117
158
  stdoutBuffer,
118
159
  ]);
119
160
  if (stderrStr.includes('Output file is empty')) {
161
+ (0, is_beyond_last_frame_1.markAsBeyondLastFrame)(src, time);
120
162
  return (0, exports.getLastFrameOfVideo)({
121
163
  ffmpegExecutable,
164
+ ffprobeExecutable,
122
165
  offset: 0,
123
166
  src,
124
167
  });
@@ -126,7 +169,10 @@ const extractFrameFromVideoFn = async ({ time, src, ffmpegExecutable, }) => {
126
169
  return stdOut;
127
170
  };
128
171
  exports.extractFrameFromVideoFn = extractFrameFromVideoFn;
129
- const extractFrameFromVideo = (options) => {
130
- return limit(exports.extractFrameFromVideoFn, options);
172
+ const extractFrameFromVideo = async (options) => {
173
+ const perf = remotion_1.Internals.perf.startPerfMeasure('extract-frame');
174
+ const res = await mainLimit(exports.extractFrameFromVideoFn, options);
175
+ remotion_1.Internals.perf.stopPerfMeasure(perf);
176
+ return res;
131
177
  };
132
178
  exports.extractFrameFromVideo = extractFrameFromVideo;
@@ -11,6 +11,7 @@ declare type GetCompositionsConfig = {
11
11
  timeoutInMilliseconds?: number;
12
12
  chromiumOptions?: ChromiumOptions;
13
13
  ffmpegExecutable?: FfmpegExecutable;
14
+ ffprobeExecutable?: FfmpegExecutable;
14
15
  port?: number | null;
15
16
  };
16
17
  export declare const getCompositions: (serveUrlOrWebpackUrl: string, config?: GetCompositionsConfig) => Promise<TCompMetadata[]>;
@@ -28,6 +28,7 @@ const innerGetCompositions = async (serveUrl, page, config, proxyPort) => {
28
28
  initialFrame: 0,
29
29
  timeoutInMilliseconds: config === null || config === void 0 ? void 0 : config.timeoutInMilliseconds,
30
30
  proxyPort,
31
+ retriesRemaining: 2,
31
32
  });
32
33
  await (0, puppeteer_evaluate_1.puppeteerEvaluateWithCatch)({
33
34
  page,
@@ -59,7 +60,7 @@ const getCompositions = async (serveUrlOrWebpackUrl, config) => {
59
60
  chromiumOptions: (_b = config === null || config === void 0 ? void 0 : config.chromiumOptions) !== null && _b !== void 0 ? _b : {},
60
61
  });
61
62
  return new Promise((resolve, reject) => {
62
- var _a, _b;
63
+ var _a, _b, _c;
63
64
  const onError = (err) => reject(err);
64
65
  const cleanupPageError = (0, handle_javascript_exception_1.handleJavascriptException)({
65
66
  page,
@@ -73,7 +74,8 @@ const getCompositions = async (serveUrlOrWebpackUrl, config) => {
73
74
  onDownload: () => undefined,
74
75
  onError,
75
76
  ffmpegExecutable: (_a = config === null || config === void 0 ? void 0 : config.ffmpegExecutable) !== null && _a !== void 0 ? _a : null,
76
- port: (_b = config === null || config === void 0 ? void 0 : config.port) !== null && _b !== void 0 ? _b : null,
77
+ ffprobeExecutable: (_b = config === null || config === void 0 ? void 0 : config.ffprobeExecutable) !== null && _b !== void 0 ? _b : null,
78
+ port: (_c = config === null || config === void 0 ? void 0 : config.port) !== null && _c !== void 0 ? _c : null,
77
79
  })
78
80
  .then(({ serveUrl, closeServer, offthreadPort }) => {
79
81
  close = closeServer;
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export { combineVideos } from './combine-videos';
5
5
  export { ErrorWithStackFrame } from './error-handling/handle-javascript-exception';
6
6
  export { FfmpegVersion } from './ffmpeg-flags';
7
7
  export { getCompositions } from './get-compositions';
8
+ export { CancelSignal, makeCancelSignal } from './make-cancel-signal';
8
9
  export { openBrowser } from './open-browser';
9
10
  export type { ChromiumOptions } from './open-browser';
10
11
  export { renderFrames } from './render-frames';
@@ -32,6 +33,7 @@ export declare const RenderInternals: {
32
33
  serveStatic: (path: string | null, options: {
33
34
  port: number | null;
34
35
  ffmpegExecutable: import("remotion").FfmpegExecutable;
36
+ ffprobeExecutable: import("remotion").FfmpegExecutable;
35
37
  downloadDir: string;
36
38
  onDownload: import("./assets/download-and-map-assets-to-file").RenderMediaOnDownload;
37
39
  onError: (err: Error) => void;
@@ -47,7 +49,7 @@ export declare const RenderInternals: {
47
49
  }) => void;
48
50
  normalizeServeUrl: (unnormalized: string) => string;
49
51
  spawnFfmpeg: (options: import("./stitch-frames-to-video").StitcherOptions) => Promise<{
50
- task: Promise<unknown>;
52
+ task: Promise<void>;
51
53
  getLogs: () => string;
52
54
  }>;
53
55
  getFileExtensionFromCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv", type: "chunk" | "final") => "mp3" | "aac" | "wav" | "mp4" | "mkv" | "mov" | "webm";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RenderInternals = exports.stitchFramesToVideo = exports.renderStill = exports.renderMedia = exports.renderFrames = exports.openBrowser = exports.getCompositions = exports.ErrorWithStackFrame = exports.combineVideos = void 0;
3
+ exports.RenderInternals = exports.stitchFramesToVideo = exports.renderStill = exports.renderMedia = exports.renderFrames = exports.openBrowser = exports.makeCancelSignal = exports.getCompositions = exports.ErrorWithStackFrame = exports.combineVideos = void 0;
4
4
  const download_file_1 = require("./assets/download-file");
5
5
  const delete_directory_1 = require("./delete-directory");
6
6
  const ensure_output_directory_1 = require("./ensure-output-directory");
@@ -30,6 +30,8 @@ var handle_javascript_exception_1 = require("./error-handling/handle-javascript-
30
30
  Object.defineProperty(exports, "ErrorWithStackFrame", { enumerable: true, get: function () { return handle_javascript_exception_1.ErrorWithStackFrame; } });
31
31
  var get_compositions_1 = require("./get-compositions");
32
32
  Object.defineProperty(exports, "getCompositions", { enumerable: true, get: function () { return get_compositions_1.getCompositions; } });
33
+ var make_cancel_signal_1 = require("./make-cancel-signal");
34
+ Object.defineProperty(exports, "makeCancelSignal", { enumerable: true, get: function () { return make_cancel_signal_1.makeCancelSignal; } });
33
35
  var open_browser_2 = require("./open-browser");
34
36
  Object.defineProperty(exports, "openBrowser", { enumerable: true, get: function () { return open_browser_2.openBrowser; } });
35
37
  var render_frames_1 = require("./render-frames");
@@ -0,0 +1,2 @@
1
+ export declare const isBeyondLastFrame: (src: string, time: number) => boolean | 0;
2
+ export declare const markAsBeyondLastFrame: (src: string, time: number) => void;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.markAsBeyondLastFrame = exports.isBeyondLastFrame = void 0;
4
+ const map = {};
5
+ const isBeyondLastFrame = (src, time) => {
6
+ return map[src] && time >= map[src];
7
+ };
8
+ exports.isBeyondLastFrame = isBeyondLastFrame;
9
+ const markAsBeyondLastFrame = (src, time) => {
10
+ map[src] = time;
11
+ };
12
+ exports.markAsBeyondLastFrame = markAsBeyondLastFrame;
@@ -0,0 +1,13 @@
1
+ /// <reference types="node" />
2
+ import { FfmpegExecutable } from 'remotion';
3
+ export declare type LastFrameOptions = {
4
+ ffmpegExecutable: FfmpegExecutable;
5
+ ffprobeExecutable: FfmpegExecutable;
6
+ offset: number;
7
+ src: string;
8
+ };
9
+ export declare const setLastFrameInCache: (options: LastFrameOptions, data: Buffer) => void;
10
+ export declare const getLastFrameFromCache: (options: LastFrameOptions) => Buffer | null;
11
+ export declare const removedLastFrameFromCache: (key: string) => void;
12
+ export declare const ensureMaxSize: () => void;
13
+ export declare const clearLastFileCache: () => void;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ // OffthreadVideo requires sometimes that the last frame of a video gets extracted, however, this can be slow. We allocate a cache for it but that can be garbage collected
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.clearLastFileCache = exports.ensureMaxSize = exports.removedLastFrameFromCache = exports.getLastFrameFromCache = exports.setLastFrameInCache = void 0;
5
+ let map = {};
6
+ const MAX_CACHE_SIZE = 50 * 1024 * 1024; // 50MB
7
+ let bufferSize = 0;
8
+ const makeLastFrameCacheKey = (options) => {
9
+ return [options.ffmpegExecutable, options.offset, options.src].join('-');
10
+ };
11
+ const setLastFrameInCache = (options, data) => {
12
+ const key = makeLastFrameCacheKey(options);
13
+ if (map[key]) {
14
+ bufferSize -= map[key].data.byteLength;
15
+ }
16
+ map[key] = { data, lastAccessed: Date.now() };
17
+ bufferSize += data.byteLength;
18
+ (0, exports.ensureMaxSize)();
19
+ };
20
+ exports.setLastFrameInCache = setLastFrameInCache;
21
+ const getLastFrameFromCache = (options) => {
22
+ var _a;
23
+ const key = makeLastFrameCacheKey(options);
24
+ if (!map[key]) {
25
+ return null;
26
+ }
27
+ map[key].lastAccessed = Date.now();
28
+ return (_a = map[key].data) !== null && _a !== void 0 ? _a : null;
29
+ };
30
+ exports.getLastFrameFromCache = getLastFrameFromCache;
31
+ const removedLastFrameFromCache = (key) => {
32
+ if (!map[key]) {
33
+ return;
34
+ }
35
+ bufferSize -= map[key].data.byteLength;
36
+ delete map[key];
37
+ };
38
+ exports.removedLastFrameFromCache = removedLastFrameFromCache;
39
+ const ensureMaxSize = () => {
40
+ // eslint-disable-next-line no-unmodified-loop-condition
41
+ while (bufferSize > MAX_CACHE_SIZE) {
42
+ const earliest = Object.entries(map).sort((a, b) => {
43
+ return a[1].lastAccessed - b[1].lastAccessed;
44
+ })[0];
45
+ (0, exports.removedLastFrameFromCache)(earliest[0]);
46
+ }
47
+ };
48
+ exports.ensureMaxSize = ensureMaxSize;
49
+ const clearLastFileCache = () => {
50
+ map = {};
51
+ };
52
+ exports.clearLastFileCache = clearLastFileCache;
@@ -0,0 +1,7 @@
1
+ declare type Callback = () => void;
2
+ export declare type CancelSignal = (callback: Callback) => void;
3
+ export declare const makeCancelSignal: () => {
4
+ cancelSignal: CancelSignal;
5
+ cancel: () => void;
6
+ };
7
+ export {};
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeCancelSignal = void 0;
4
+ const makeCancelSignal = () => {
5
+ const callbacks = [];
6
+ let cancelled = false;
7
+ return {
8
+ cancelSignal: (callback) => {
9
+ callbacks.push(callback);
10
+ if (cancelled) {
11
+ callback();
12
+ }
13
+ },
14
+ cancel: () => {
15
+ if (cancelled) {
16
+ return;
17
+ }
18
+ callbacks.forEach((cb) => {
19
+ cb();
20
+ });
21
+ cancelled = true;
22
+ },
23
+ };
24
+ };
25
+ exports.makeCancelSignal = makeCancelSignal;
@@ -30,8 +30,8 @@ const mergeAudioTrackUnlimited = async ({ ffmpegExecutable, outName, files, numb
30
30
  });
31
31
  return;
32
32
  }
33
- // FFMPEG has a limit of 64 tracks that can be merged at once
34
- if (files.length > 64) {
33
+ // In FFMPEG, the total number of left and right tracks that can be merged at one time is limited to 64
34
+ if (files.length >= 32) {
35
35
  const chunked = (0, chunk_1.chunk)(files, 10);
36
36
  const tempPath = (0, tmp_dir_1.tmpDir)('remotion-large-audio-mixing');
37
37
  const chunkNames = await Promise.all(chunked.map(async (chunkFiles, i) => {
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -5,8 +5,9 @@ export declare const extractUrlAndSourceFromUrl: (url: string) => {
5
5
  src: string;
6
6
  time: number;
7
7
  };
8
- export declare const startOffthreadVideoServer: ({ ffmpegExecutable, downloadDir, onDownload, onError, }: {
8
+ export declare const startOffthreadVideoServer: ({ ffmpegExecutable, ffprobeExecutable, downloadDir, onDownload, onError, }: {
9
9
  ffmpegExecutable: FfmpegExecutable;
10
+ ffprobeExecutable: FfmpegExecutable;
10
11
  downloadDir: string;
11
12
  onDownload: RenderMediaOnDownload;
12
13
  onError: (err: Error) => void;
@@ -22,7 +22,7 @@ const extractUrlAndSourceFromUrl = (url) => {
22
22
  return { src, time: parseFloat(time) };
23
23
  };
24
24
  exports.extractUrlAndSourceFromUrl = extractUrlAndSourceFromUrl;
25
- const startOffthreadVideoServer = ({ ffmpegExecutable, downloadDir, onDownload, onError, }) => {
25
+ const startOffthreadVideoServer = ({ ffmpegExecutable, ffprobeExecutable, downloadDir, onDownload, onError, }) => {
26
26
  return (req, res) => {
27
27
  if (!req.url) {
28
28
  throw new Error('Request came in without URL');
@@ -45,6 +45,7 @@ const startOffthreadVideoServer = ({ ffmpegExecutable, downloadDir, onDownload,
45
45
  time,
46
46
  src: to,
47
47
  ffmpegExecutable,
48
+ ffprobeExecutable,
48
49
  });
49
50
  })
50
51
  .then((readable) => {
@@ -98,6 +98,7 @@ const openBrowser = async (browser, options) => {
98
98
  '--hide-scrollbars',
99
99
  '--no-default-browser-check',
100
100
  '--no-pings',
101
+ '--font-render-hinting=none',
101
102
  '--no-zygote',
102
103
  (options === null || options === void 0 ? void 0 : options.forceDeviceScaleFactor)
103
104
  ? `--force-device-scale-factor=${options.forceDeviceScaleFactor}`
@@ -115,7 +116,7 @@ const openBrowser = async (browser, options) => {
115
116
  ].filter(Boolean),
116
117
  });
117
118
  const pages = await browserInstance.pages();
118
- pages.forEach((p) => p.close());
119
+ await pages[0].close();
119
120
  browserInstances.push(browserInstance);
120
121
  return browserInstance;
121
122
  };
@@ -1,11 +1,12 @@
1
1
  import { FfmpegExecutable } from 'remotion';
2
2
  import { RenderMediaOnDownload } from './assets/download-and-map-assets-to-file';
3
- export declare const prepareServer: ({ downloadDir, ffmpegExecutable, onDownload, onError, webpackConfigOrServeUrl, port, }: {
3
+ export declare const prepareServer: ({ downloadDir, ffmpegExecutable, ffprobeExecutable, onDownload, onError, webpackConfigOrServeUrl, port, }: {
4
4
  webpackConfigOrServeUrl: string;
5
5
  downloadDir: string;
6
6
  onDownload: RenderMediaOnDownload;
7
7
  onError: (err: Error) => void;
8
8
  ffmpegExecutable: FfmpegExecutable;
9
+ ffprobeExecutable: FfmpegExecutable;
9
10
  port: number | null;
10
11
  }) => Promise<{
11
12
  serveUrl: string;
@@ -3,13 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.prepareServer = void 0;
4
4
  const is_serve_url_1 = require("./is-serve-url");
5
5
  const serve_static_1 = require("./serve-static");
6
- const prepareServer = async ({ downloadDir, ffmpegExecutable, onDownload, onError, webpackConfigOrServeUrl, port, }) => {
6
+ const prepareServer = async ({ downloadDir, ffmpegExecutable, ffprobeExecutable, onDownload, onError, webpackConfigOrServeUrl, port, }) => {
7
7
  if ((0, is_serve_url_1.isServeUrl)(webpackConfigOrServeUrl)) {
8
8
  const { port: offthreadPort, close: closeProxy } = await (0, serve_static_1.serveStatic)(null, {
9
9
  downloadDir,
10
10
  onDownload,
11
11
  onError,
12
12
  ffmpegExecutable,
13
+ ffprobeExecutable,
13
14
  port,
14
15
  });
15
16
  return Promise.resolve({
@@ -23,6 +24,7 @@ const prepareServer = async ({ downloadDir, ffmpegExecutable, onDownload, onErro
23
24
  onDownload,
24
25
  onError,
25
26
  ffmpegExecutable,
27
+ ffprobeExecutable,
26
28
  port,
27
29
  });
28
30
  return Promise.resolve({
@@ -2,6 +2,7 @@ import { FfmpegExecutable } from 'remotion';
2
2
  import { MediaAsset } from './assets/types';
3
3
  declare type Options = {
4
4
  ffmpegExecutable: FfmpegExecutable;
5
+ ffprobeExecutable: FfmpegExecutable;
5
6
  outName: string;
6
7
  asset: MediaAsset;
7
8
  expectedFrames: number;
@@ -10,8 +10,8 @@ const calculate_ffmpeg_filters_1 = require("./calculate-ffmpeg-filters");
10
10
  const ffmpeg_filter_file_1 = require("./ffmpeg-filter-file");
11
11
  const p_limit_1 = require("./p-limit");
12
12
  const resolve_asset_src_1 = require("./resolve-asset-src");
13
- const preprocessAudioTrackUnlimited = async ({ ffmpegExecutable, outName, asset, expectedFrames, fps, }) => {
14
- const { channels, duration } = await (0, get_audio_channels_1.getAudioChannelsAndDuration)((0, resolve_asset_src_1.resolveAssetSrc)(asset.src));
13
+ const preprocessAudioTrackUnlimited = async ({ ffmpegExecutable, ffprobeExecutable, outName, asset, expectedFrames, fps, }) => {
14
+ const { channels, duration } = await (0, get_audio_channels_1.getAudioChannelsAndDuration)((0, resolve_asset_src_1.resolveAssetSrc)(asset.src), ffprobeExecutable);
15
15
  const filter = (0, calculate_ffmpeg_filters_1.calculateFfmpegFilter)({
16
16
  asset,
17
17
  durationInFrames: expectedFrames,
@@ -1,5 +1,6 @@
1
1
  import execa from 'execa';
2
2
  import { Codec, FfmpegExecutable, ImageFormat, PixelFormat, ProResProfile } from 'remotion';
3
+ import { CancelSignal } from './make-cancel-signal';
3
4
  declare type PreSticherOptions = {
4
5
  fps: number;
5
6
  width: number;
@@ -13,6 +14,7 @@ declare type PreSticherOptions = {
13
14
  verbose: boolean;
14
15
  ffmpegExecutable: FfmpegExecutable | undefined;
15
16
  imageFormat: ImageFormat;
17
+ signal: CancelSignal;
16
18
  };
17
19
  export declare const prespawnFfmpeg: (options: PreSticherOptions) => Promise<{
18
20
  task: execa.ExecaChildProcess<string>;