@remotion/renderer 3.0.29 → 3.1.0

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 (262) hide show
  1. package/dist/assets/download-and-map-assets-to-file.js +6 -3
  2. package/dist/browser/NetworkManager.js +1 -5
  3. package/dist/codec-supports-media.d.ts +7 -0
  4. package/dist/codec-supports-media.js +49 -0
  5. package/dist/combine-videos.d.ts +2 -1
  6. package/dist/combine-videos.js +8 -2
  7. package/dist/convert-number-of-gif-loops-to-ffmpeg.d.ts +1 -0
  8. package/dist/convert-number-of-gif-loops-to-ffmpeg.js +17 -0
  9. package/dist/ensure-frames-in-order.d.ts +1 -1
  10. package/dist/ensure-frames-in-order.js +3 -2
  11. package/dist/ensure-presentation-timestamp.d.ts +1 -1
  12. package/dist/ensure-presentation-timestamp.js +29 -19
  13. package/dist/extract-frame-from-video.js +4 -2
  14. package/dist/get-codec-name.js +3 -0
  15. package/dist/get-duration-from-frame-range.d.ts +1 -2
  16. package/dist/get-duration-from-frame-range.js +13 -9
  17. package/dist/get-extension-from-codec.d.ts +1 -1
  18. package/dist/get-extension-from-codec.js +5 -0
  19. package/dist/guess-extension-for-media.d.ts +1 -1
  20. package/dist/guess-extension-for-media.js +3 -3
  21. package/dist/index.d.ts +5 -3
  22. package/dist/index.js +5 -1
  23. package/dist/mime-types.d.ts +1 -1
  24. package/dist/mime-types.js +2 -2
  25. package/dist/prespawn-ffmpeg.js +2 -2
  26. package/dist/render-frames.d.ts +1 -0
  27. package/dist/render-frames.js +12 -16
  28. package/dist/render-media.d.ts +2 -0
  29. package/dist/render-media.js +11 -4
  30. package/dist/render-still.js +1 -1
  31. package/dist/stitch-frames-to-video.d.ts +1 -0
  32. package/dist/stitch-frames-to-video.js +32 -22
  33. package/dist/validate-concurrency.d.ts +1 -0
  34. package/dist/validate-concurrency.js +24 -0
  35. package/dist/validate-output-filename.js +5 -0
  36. package/package.json +8 -10
  37. package/tsconfig.json +2 -1
  38. package/vitest.config.ts +8 -0
  39. package/dist/assets/calculate-asset-positions.d.ts.map +0 -1
  40. package/dist/assets/calculate-asset-positions.js.map +0 -1
  41. package/dist/assets/calculate-atempo.d.ts.map +0 -1
  42. package/dist/assets/calculate-atempo.js.map +0 -1
  43. package/dist/assets/cleanup-assets.d.ts +0 -2
  44. package/dist/assets/cleanup-assets.js +0 -2
  45. package/dist/assets/convert-assets-to-file-urls.d.ts.map +0 -1
  46. package/dist/assets/convert-assets-to-file-urls.js.map +0 -1
  47. package/dist/assets/download-and-map-assets-to-file.d.ts.map +0 -1
  48. package/dist/assets/download-and-map-assets-to-file.js.map +0 -1
  49. package/dist/assets/download-file.d.ts.map +0 -1
  50. package/dist/assets/download-file.js.map +0 -1
  51. package/dist/assets/ffmpeg-volume-expression.d.ts.map +0 -1
  52. package/dist/assets/ffmpeg-volume-expression.js.map +0 -1
  53. package/dist/assets/flatten-volume-array.d.ts.map +0 -1
  54. package/dist/assets/flatten-volume-array.js.map +0 -1
  55. package/dist/assets/get-audio-channels.d.ts.map +0 -1
  56. package/dist/assets/get-audio-channels.js.map +0 -1
  57. package/dist/assets/read-file.d.ts.map +0 -1
  58. package/dist/assets/read-file.js.map +0 -1
  59. package/dist/assets/round-volume-to-avoid-stack-overflow.d.ts.map +0 -1
  60. package/dist/assets/round-volume-to-avoid-stack-overflow.js.map +0 -1
  61. package/dist/assets/sanitize-filename.d.ts.map +0 -1
  62. package/dist/assets/sanitize-filename.js.map +0 -1
  63. package/dist/assets/sanitize-filepath.d.ts.map +0 -1
  64. package/dist/assets/sanitize-filepath.js.map +0 -1
  65. package/dist/assets/truncate-utf8-bytes.d.ts.map +0 -1
  66. package/dist/assets/truncate-utf8-bytes.js.map +0 -1
  67. package/dist/assets/types.d.ts.map +0 -1
  68. package/dist/assets/types.js.map +0 -1
  69. package/dist/browser/Accessibility.d.ts +0 -175
  70. package/dist/browser/Accessibility.js +0 -423
  71. package/dist/browser/AriaQueryHandler.d.ts +0 -20
  72. package/dist/browser/AriaQueryHandler.js +0 -108
  73. package/dist/browser/ConnectionTransport.d.ts +0 -21
  74. package/dist/browser/ConnectionTransport.js +0 -17
  75. package/dist/browser/Coverage.d.ts +0 -180
  76. package/dist/browser/Coverage.js +0 -371
  77. package/dist/browser/Debug.d.ts +0 -19
  78. package/dist/browser/Debug.js +0 -42
  79. package/dist/browser/DeviceDescriptors.d.ts +0 -40
  80. package/dist/browser/DeviceDescriptors.js +0 -1407
  81. package/dist/browser/EmulationManager.d.ts +0 -7
  82. package/dist/browser/EmulationManager.js +0 -40
  83. package/dist/browser/FileChooser.d.ts +0 -56
  84. package/dist/browser/FileChooser.js +0 -86
  85. package/dist/browser/Input.d.ts +0 -355
  86. package/dist/browser/Input.js +0 -592
  87. package/dist/browser/NetworkConditions.d.ts +0 -26
  88. package/dist/browser/NetworkConditions.js +0 -33
  89. package/dist/browser/PDFOptions.d.ts +0 -165
  90. package/dist/browser/PDFOptions.js +0 -34
  91. package/dist/browser/PipeTransport.d.ts +0 -10
  92. package/dist/browser/PipeTransport.js +0 -86
  93. package/dist/browser/Puppeteer.d.ts +0 -35
  94. package/dist/browser/Puppeteer.js +0 -17
  95. package/dist/browser/QueryHandler.d.ts +0 -64
  96. package/dist/browser/QueryHandler.js +0 -183
  97. package/dist/browser/SecurityDetails.d.ts +0 -55
  98. package/dist/browser/SecurityDetails.js +0 -95
  99. package/dist/browser/Tracing.d.ts +0 -45
  100. package/dist/browser/Tracing.js +0 -136
  101. package/dist/browser/USKeyboardLayout.d.ts +0 -39
  102. package/dist/browser/USKeyboardLayout.js +0 -406
  103. package/dist/browser/WebWorker.d.ts +0 -96
  104. package/dist/browser/WebWorker.js +0 -122
  105. package/dist/browser/compat.d.ts +0 -2
  106. package/dist/browser/compat.js +0 -17
  107. package/dist/browser/dialog.d.ts +0 -70
  108. package/dist/browser/dialog.js +0 -114
  109. package/dist/browser/environment.d.ts +0 -16
  110. package/dist/browser/environment.js +0 -19
  111. package/dist/browser/fetch.d.ts +0 -16
  112. package/dist/browser/fetch.js +0 -46
  113. package/dist/browser/find-up.d.ts +0 -4
  114. package/dist/browser/find-up.js +0 -85
  115. package/dist/browser/page.d.ts +0 -74
  116. package/dist/browser/page.js +0 -283
  117. package/dist/browser/pkg-dir.d.ts +0 -3
  118. package/dist/browser/pkg-dir.js +0 -13
  119. package/dist/browser-log.d.ts.map +0 -1
  120. package/dist/browser-log.js.map +0 -1
  121. package/dist/calculate-ffmpeg-filters.d.ts.map +0 -1
  122. package/dist/calculate-ffmpeg-filters.js.map +0 -1
  123. package/dist/can-use-parallel-encoding.d.ts.map +0 -1
  124. package/dist/can-use-parallel-encoding.js.map +0 -1
  125. package/dist/chunk.d.ts.map +0 -1
  126. package/dist/chunk.js.map +0 -1
  127. package/dist/combine-videos.d.ts.map +0 -1
  128. package/dist/combine-videos.js.map +0 -1
  129. package/dist/convert-to-pcm.d.ts.map +0 -1
  130. package/dist/convert-to-pcm.js.map +0 -1
  131. package/dist/create-ffmpeg-complex-filter.d.ts.map +0 -1
  132. package/dist/create-ffmpeg-complex-filter.js.map +0 -1
  133. package/dist/create-ffmpeg-merge-filter.d.ts.map +0 -1
  134. package/dist/create-ffmpeg-merge-filter.js.map +0 -1
  135. package/dist/create-silent-audio.d.ts.map +0 -1
  136. package/dist/create-silent-audio.js.map +0 -1
  137. package/dist/cycle-browser-tabs.d.ts.map +0 -1
  138. package/dist/cycle-browser-tabs.js.map +0 -1
  139. package/dist/delay-render-embedded-stack.d.ts.map +0 -1
  140. package/dist/delay-render-embedded-stack.js.map +0 -1
  141. package/dist/delete-directory.d.ts.map +0 -1
  142. package/dist/delete-directory.js.map +0 -1
  143. package/dist/ensure-frames-in-order.d.ts.map +0 -1
  144. package/dist/ensure-frames-in-order.js.map +0 -1
  145. package/dist/ensure-output-directory.d.ts.map +0 -1
  146. package/dist/ensure-output-directory.js.map +0 -1
  147. package/dist/error-handling/handle-javascript-exception.d.ts.map +0 -1
  148. package/dist/error-handling/handle-javascript-exception.js.map +0 -1
  149. package/dist/error-handling/symbolicate-error.d.ts.map +0 -1
  150. package/dist/error-handling/symbolicate-error.js.map +0 -1
  151. package/dist/error-handling/symbolicateable-error.d.ts.map +0 -1
  152. package/dist/error-handling/symbolicateable-error.js.map +0 -1
  153. package/dist/ffmpeg-filter-file.d.ts.map +0 -1
  154. package/dist/ffmpeg-filter-file.js.map +0 -1
  155. package/dist/ffmpeg-flags.d.ts.map +0 -1
  156. package/dist/ffmpeg-flags.js.map +0 -1
  157. package/dist/get-audio-codec-name.d.ts.map +0 -1
  158. package/dist/get-audio-codec-name.js.map +0 -1
  159. package/dist/get-browser-instance.d.ts.map +0 -1
  160. package/dist/get-browser-instance.js.map +0 -1
  161. package/dist/get-codec-name.d.ts.map +0 -1
  162. package/dist/get-codec-name.js.map +0 -1
  163. package/dist/get-compositions.d.ts.map +0 -1
  164. package/dist/get-compositions.js.map +0 -1
  165. package/dist/get-concurrency.d.ts.map +0 -1
  166. package/dist/get-concurrency.js.map +0 -1
  167. package/dist/get-duration-from-frame-range.d.ts.map +0 -1
  168. package/dist/get-duration-from-frame-range.js.map +0 -1
  169. package/dist/get-duration-of-asset.d.ts +0 -7
  170. package/dist/get-duration-of-asset.js +0 -36
  171. package/dist/get-extension-from-codec.d.ts.map +0 -1
  172. package/dist/get-extension-from-codec.js.map +0 -1
  173. package/dist/get-format-for-codec.d.ts +0 -2
  174. package/dist/get-format-for-codec.js +0 -34
  175. package/dist/get-frame-to-render.d.ts.map +0 -1
  176. package/dist/get-frame-to-render.js.map +0 -1
  177. package/dist/get-local-browser-executable.d.ts.map +0 -1
  178. package/dist/get-local-browser-executable.js.map +0 -1
  179. package/dist/get-port.d.ts.map +0 -1
  180. package/dist/get-port.js.map +0 -1
  181. package/dist/get-prores-profile-name.d.ts.map +0 -1
  182. package/dist/get-prores-profile-name.js.map +0 -1
  183. package/dist/image-format.d.ts.map +0 -1
  184. package/dist/image-format.js.map +0 -1
  185. package/dist/index.d.ts.map +0 -1
  186. package/dist/index.js.map +0 -1
  187. package/dist/is-serve-url.d.ts.map +0 -1
  188. package/dist/is-serve-url.js.map +0 -1
  189. package/dist/is-vp9-video.d.ts +0 -3
  190. package/dist/is-vp9-video.js +0 -24
  191. package/dist/legacy-webpack-config.d.ts.map +0 -1
  192. package/dist/legacy-webpack-config.js.map +0 -1
  193. package/dist/make-assets-download-dir.d.ts.map +0 -1
  194. package/dist/make-assets-download-dir.js.map +0 -1
  195. package/dist/merge-audio-track.d.ts.map +0 -1
  196. package/dist/merge-audio-track.js.map +0 -1
  197. package/dist/normalize-serve-url.d.ts.map +0 -1
  198. package/dist/normalize-serve-url.js.map +0 -1
  199. package/dist/open-browser.d.ts.map +0 -1
  200. package/dist/open-browser.js.map +0 -1
  201. package/dist/p-limit.d.ts.map +0 -1
  202. package/dist/p-limit.js.map +0 -1
  203. package/dist/parse-browser-error-stack.d.ts.map +0 -1
  204. package/dist/parse-browser-error-stack.js.map +0 -1
  205. package/dist/parse-ffmpeg-progress.d.ts.map +0 -1
  206. package/dist/parse-ffmpeg-progress.js.map +0 -1
  207. package/dist/pool.d.ts.map +0 -1
  208. package/dist/pool.js.map +0 -1
  209. package/dist/prepare-server.d.ts.map +0 -1
  210. package/dist/prepare-server.js.map +0 -1
  211. package/dist/preprocess-audio-track.d.ts.map +0 -1
  212. package/dist/preprocess-audio-track.js.map +0 -1
  213. package/dist/prespawn-ffmpeg.d.ts.map +0 -1
  214. package/dist/prespawn-ffmpeg.js.map +0 -1
  215. package/dist/provide-screenshot.d.ts.map +0 -1
  216. package/dist/provide-screenshot.js.map +0 -1
  217. package/dist/puppeteer-evaluate.d.ts.map +0 -1
  218. package/dist/puppeteer-evaluate.js.map +0 -1
  219. package/dist/puppeteer-screenshot.d.ts.map +0 -1
  220. package/dist/puppeteer-screenshot.js.map +0 -1
  221. package/dist/render-frames.d.ts.map +0 -1
  222. package/dist/render-frames.js.map +0 -1
  223. package/dist/render-media.d.ts.map +0 -1
  224. package/dist/render-media.js.map +0 -1
  225. package/dist/render-still.d.ts.map +0 -1
  226. package/dist/render-still.js.map +0 -1
  227. package/dist/resolve-asset-src.d.ts.map +0 -1
  228. package/dist/resolve-asset-src.js.map +0 -1
  229. package/dist/sample-rate.d.ts.map +0 -1
  230. package/dist/sample-rate.js.map +0 -1
  231. package/dist/screenshot-dom-element.d.ts.map +0 -1
  232. package/dist/screenshot-dom-element.js.map +0 -1
  233. package/dist/screenshot-task.d.ts.map +0 -1
  234. package/dist/screenshot-task.js.map +0 -1
  235. package/dist/seek-to-frame.d.ts.map +0 -1
  236. package/dist/seek-to-frame.js.map +0 -1
  237. package/dist/serve-handler/glob-slash.d.ts +0 -1
  238. package/dist/serve-handler/glob-slash.js +0 -12
  239. package/dist/serve-static.d.ts.map +0 -1
  240. package/dist/serve-static.js.map +0 -1
  241. package/dist/set-props-and-env.d.ts.map +0 -1
  242. package/dist/set-props-and-env.js.map +0 -1
  243. package/dist/stitch-frames-to-video.d.ts.map +0 -1
  244. package/dist/stitch-frames-to-video.js.map +0 -1
  245. package/dist/stringify-ffmpeg-filter.d.ts.map +0 -1
  246. package/dist/stringify-ffmpeg-filter.js.map +0 -1
  247. package/dist/symbolicate-stacktrace.d.ts.map +0 -1
  248. package/dist/symbolicate-stacktrace.js.map +0 -1
  249. package/dist/tmp-dir.d.ts.map +0 -1
  250. package/dist/tmp-dir.js.map +0 -1
  251. package/dist/types.d.ts.map +0 -1
  252. package/dist/types.js.map +0 -1
  253. package/dist/validate-even-dimensions-with-codec.d.ts.map +0 -1
  254. package/dist/validate-even-dimensions-with-codec.js.map +0 -1
  255. package/dist/validate-ffmpeg.d.ts.map +0 -1
  256. package/dist/validate-ffmpeg.js.map +0 -1
  257. package/dist/validate-fps-for-gif.d.ts +0 -2
  258. package/dist/validate-fps-for-gif.js +0 -9
  259. package/dist/validate-puppeteer-timeout.d.ts.map +0 -1
  260. package/dist/validate-puppeteer-timeout.js.map +0 -1
  261. package/dist/validate-scale.d.ts.map +0 -1
  262. package/dist/validate-scale.js.map +0 -1
@@ -26,8 +26,11 @@ const waitForAssetToBeDownloaded = ({ src, downloadDir, }) => {
26
26
  }
27
27
  return new Promise((resolve) => {
28
28
  listeners[src][downloadDir].push(() => {
29
- var _a;
30
- resolve((_a = hasBeenDownloadedMap[src]) === null || _a === void 0 ? void 0 : _a[downloadDir]);
29
+ const srcMap = hasBeenDownloadedMap[src];
30
+ if (!srcMap || !srcMap[downloadDir]) {
31
+ throw new Error('Expected file for ' + src + 'to be available in ' + downloadDir);
32
+ }
33
+ resolve(srcMap[downloadDir]);
31
34
  });
32
35
  });
33
36
  };
@@ -38,7 +41,6 @@ const notifyAssetIsDownloaded = ({ src, downloadDir, to, }) => {
38
41
  if (!listeners[src][downloadDir]) {
39
42
  listeners[src][downloadDir] = [];
40
43
  }
41
- listeners[src][downloadDir].forEach((fn) => fn());
42
44
  if (!isDownloadingMap[src]) {
43
45
  isDownloadingMap[src] = {};
44
46
  }
@@ -47,6 +49,7 @@ const notifyAssetIsDownloaded = ({ src, downloadDir, to, }) => {
47
49
  hasBeenDownloadedMap[src] = {};
48
50
  }
49
51
  hasBeenDownloadedMap[src][downloadDir] = to;
52
+ listeners[src][downloadDir].forEach((fn) => fn());
50
53
  };
51
54
  const validateMimeType = (mimeType, src) => {
52
55
  if (!mimeType.includes('/')) {
@@ -141,11 +141,7 @@ _NetworkManager_client = new WeakMap(), _NetworkManager_frameManager = new WeakM
141
141
  if (!request) {
142
142
  return;
143
143
  }
144
- const extraInfos = __classPrivateFieldGet(this, _NetworkManager_networkEventManager, "f").responseExtraInfo(responseReceived.requestId);
145
- if (extraInfos.length) {
146
- console.log(new Error('Unexpected extraInfo events for request ' +
147
- responseReceived.requestId));
148
- }
144
+ __classPrivateFieldGet(this, _NetworkManager_networkEventManager, "f").responseExtraInfo(responseReceived.requestId);
149
145
  const response = new HTTPResponse_1.HTTPResponse(responseReceived.response, extraInfo);
150
146
  request._response = response;
151
147
  }, _NetworkManager_onResponseReceived = function _NetworkManager_onResponseReceived(event) {
@@ -0,0 +1,7 @@
1
+ import type { Codec } from 'remotion';
2
+ declare type MediaSupport = {
3
+ video: boolean;
4
+ audio: boolean;
5
+ };
6
+ export declare const codecSupportsMedia: (codec: Codec) => MediaSupport;
7
+ export {};
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.codecSupportsMedia = void 0;
4
+ const support = {
5
+ 'h264-mkv': {
6
+ audio: true,
7
+ video: true,
8
+ },
9
+ aac: {
10
+ audio: true,
11
+ video: false,
12
+ },
13
+ gif: {
14
+ video: true,
15
+ audio: false,
16
+ },
17
+ h264: {
18
+ video: true,
19
+ audio: true,
20
+ },
21
+ h265: {
22
+ video: true,
23
+ audio: true,
24
+ },
25
+ mp3: {
26
+ audio: true,
27
+ video: false,
28
+ },
29
+ prores: {
30
+ audio: true,
31
+ video: true,
32
+ },
33
+ vp8: {
34
+ audio: true,
35
+ video: true,
36
+ },
37
+ vp9: {
38
+ audio: true,
39
+ video: true,
40
+ },
41
+ wav: {
42
+ audio: true,
43
+ video: false,
44
+ },
45
+ };
46
+ const codecSupportsMedia = (codec) => {
47
+ return support[codec];
48
+ };
49
+ exports.codecSupportsMedia = codecSupportsMedia;
@@ -1,5 +1,5 @@
1
1
  import type { Codec } from 'remotion';
2
- export declare const combineVideos: ({ files, filelistDir, output, onProgress, numberOfFrames, codec, fps, }: {
2
+ export declare const combineVideos: ({ files, filelistDir, output, onProgress, numberOfFrames, codec, fps, numberOfGifLoops, }: {
3
3
  files: string[];
4
4
  filelistDir: string;
5
5
  output: string;
@@ -7,4 +7,5 @@ export declare const combineVideos: ({ files, filelistDir, output, onProgress, n
7
7
  numberOfFrames: number;
8
8
  codec: Codec;
9
9
  fps: number;
10
+ numberOfGifLoops: number | null;
10
11
  }) => Promise<void>;
@@ -11,7 +11,7 @@ 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, fps, }) => {
14
+ const combineVideos = async ({ files, filelistDir, output, onProgress, numberOfFrames, codec, fps, numberOfGifLoops, }) => {
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');
@@ -26,8 +26,14 @@ const combineVideos = async ({ files, filelistDir, output, onProgress, numberOfF
26
26
  '0',
27
27
  '-i',
28
28
  fileListTxt,
29
+ numberOfGifLoops === null ? null : '-loop',
30
+ numberOfGifLoops === null
31
+ ? null
32
+ : typeof numberOfGifLoops === 'number'
33
+ ? String(numberOfGifLoops)
34
+ : '-1',
29
35
  remotion_1.Internals.isAudioCodec(codec) ? null : '-c:v',
30
- remotion_1.Internals.isAudioCodec(codec) ? null : 'copy',
36
+ remotion_1.Internals.isAudioCodec(codec) ? null : codec === 'gif' ? 'gif' : 'copy',
31
37
  '-c:a',
32
38
  (0, get_audio_codec_name_1.getAudioCodecName)(codec),
33
39
  // Set max bitrate up to 1024kbps, will choose lower if that's too much
@@ -0,0 +1 @@
1
+ export declare const convertNumberOfGifLoopsToFfmpegSyntax: (loops: number | null) => string;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ // https://superuser.com/questions/1607099/how-to-control-gif-loop-settings-in-ffmpeg
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.convertNumberOfGifLoopsToFfmpegSyntax = void 0;
5
+ const convertNumberOfGifLoopsToFfmpegSyntax = (loops) => {
6
+ // Infinite loop
7
+ if (loops === null) {
8
+ return '0';
9
+ }
10
+ // No loops
11
+ if (loops === 0) {
12
+ return '-1';
13
+ }
14
+ // N amount of loops
15
+ return String(loops);
16
+ };
17
+ exports.convertNumberOfGifLoopsToFfmpegSyntax = convertNumberOfGifLoopsToFfmpegSyntax;
@@ -1,4 +1,4 @@
1
- export declare const ensureFramesInOrder: (frameRange: [number, number]) => {
1
+ export declare const ensureFramesInOrder: (framesToRender: number[]) => {
2
2
  waitForRightTimeOfFrameToBeInserted: (frameToBe: number) => Promise<void>;
3
3
  setFrameToStitch: (f: number) => void;
4
4
  waitForFinish: () => Promise<void>;
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ensureFramesInOrder = void 0;
4
- const ensureFramesInOrder = (frameRange) => {
5
- let [frameToStitch, finalFrame] = frameRange;
4
+ const ensureFramesInOrder = (framesToRender) => {
5
+ let [frameToStitch] = framesToRender;
6
+ const finalFrame = framesToRender[framesToRender.length - 1];
6
7
  let waiters = [];
7
8
  const resolveWaiters = () => {
8
9
  for (const waiter of waiters.slice(0)) {
@@ -1 +1 @@
1
- export declare const ensurePresentationTimestamps: (src: string) => Promise<void>;
1
+ export declare const ensurePresentationTimestamps: (src: string) => Promise<string>;
@@ -5,32 +5,43 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ensurePresentationTimestamps = void 0;
7
7
  const execa_1 = __importDefault(require("execa"));
8
- const promises_1 = require("fs/promises");
9
8
  const path_1 = __importDefault(require("path"));
9
+ const remotion_1 = require("remotion");
10
+ const guess_extension_for_media_1 = require("./guess-extension-for-media");
10
11
  const ensureFileHasPresentationTimestamp = {};
11
12
  let callbacks = [];
13
+ const getTemporaryOutputName = async (src) => {
14
+ const parts = src.split(path_1.default.sep);
15
+ // If there is no file extension for the video, then we need to temporarily add an extension
16
+ const lastPart = parts[parts.length - 1];
17
+ const extraExtension = lastPart.includes('.')
18
+ ? null
19
+ : await (0, guess_extension_for_media_1.guessExtensionForVideo)(src);
20
+ return parts
21
+ .map((p, i) => {
22
+ if (i === parts.length - 1) {
23
+ return [`pts-${p}`, extraExtension].filter(remotion_1.Internals.truthy).join('.');
24
+ }
25
+ return p;
26
+ })
27
+ .join(path_1.default.sep);
28
+ };
12
29
  const ensurePresentationTimestamps = async (src) => {
13
- if (ensureFileHasPresentationTimestamp[src] === 'encoding') {
30
+ const elem = ensureFileHasPresentationTimestamp[src];
31
+ if ((elem === null || elem === void 0 ? void 0 : elem.type) === 'encoding') {
14
32
  return new Promise((resolve) => {
15
33
  callbacks.push({
16
34
  src,
17
- fn: () => resolve(),
35
+ fn: (newSrc) => resolve(newSrc),
18
36
  });
19
37
  });
20
38
  }
21
- if (ensureFileHasPresentationTimestamp[src] === 'done') {
22
- return;
39
+ if ((elem === null || elem === void 0 ? void 0 : elem.type) === 'done') {
40
+ return elem.src;
23
41
  }
24
- ensureFileHasPresentationTimestamp[src] = 'encoding';
25
- const parts = src.split(path_1.default.sep);
26
- const output = parts
27
- .map((p, i) => {
28
- if (i === parts.length - 1) {
29
- return `pts-${p}`;
30
- }
31
- return p;
32
- })
33
- .join(path_1.default.sep);
42
+ ensureFileHasPresentationTimestamp[src] = { type: 'encoding' };
43
+ // If there is no file extension for the video, then we need to tempoa
44
+ const output = await getTemporaryOutputName(src);
34
45
  await (0, execa_1.default)('ffmpeg', [
35
46
  '-i',
36
47
  src,
@@ -43,15 +54,14 @@ const ensurePresentationTimestamps = async (src) => {
43
54
  output,
44
55
  '-y',
45
56
  ]);
46
- await (0, promises_1.unlink)(src);
47
- await (0, promises_1.rename)(output, src);
48
57
  callbacks = callbacks.filter((c) => {
49
58
  if (c.src === src) {
50
- c.fn();
59
+ c.fn(output);
51
60
  return false;
52
61
  }
53
62
  return true;
54
63
  });
55
- ensureFileHasPresentationTimestamp[src] = 'done';
64
+ ensureFileHasPresentationTimestamp[src] = { type: 'done', src: output };
65
+ return output;
56
66
  };
57
67
  exports.ensurePresentationTimestamps = ensurePresentationTimestamps;
@@ -158,8 +158,10 @@ const getLastFrameOfVideo = async (options) => {
158
158
  return result;
159
159
  };
160
160
  exports.getLastFrameOfVideo = getLastFrameOfVideo;
161
- const extractFrameFromVideoFn = async ({ time, src, ffmpegExecutable, ffprobeExecutable, imageFormat, }) => {
162
- await (0, ensure_presentation_timestamp_1.ensurePresentationTimestamps)(src);
161
+ const extractFrameFromVideoFn = async ({ time, ffmpegExecutable, ffprobeExecutable, imageFormat, ...options }) => {
162
+ // We make a new copy of the video only for video because the conversion may affect
163
+ // audio rendering, so we work with 2 different files
164
+ const src = await (0, ensure_presentation_timestamp_1.ensurePresentationTimestamps)(options.src);
163
165
  const { specialVcodec, needsResize } = await (0, get_video_info_1.getVideoInfo)(src, ffprobeExecutable);
164
166
  if (specialVcodec === 'vp8') {
165
167
  return getFrameOfVideoSlow({
@@ -21,6 +21,9 @@ const getCodecName = (codec) => {
21
21
  if (codec === 'prores') {
22
22
  return 'prores_ks';
23
23
  }
24
+ if (codec === 'gif') {
25
+ return 'gif';
26
+ }
24
27
  throw new TypeError(`Cannot find FFMPEG codec for ${codec}`);
25
28
  };
26
29
  exports.getCodecName = getCodecName;
@@ -1,2 +1 @@
1
- import type { FrameRange } from 'remotion';
2
- export declare const getDurationFromFrameRange: (frameRange: FrameRange | null, durationInFrames: number) => number;
1
+ export declare const getFramesToRender: (frameRange: [number, number], everyNthFrame: number) => number[];
@@ -1,13 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getDurationFromFrameRange = void 0;
4
- const getDurationFromFrameRange = (frameRange, durationInFrames) => {
5
- if (frameRange === null) {
6
- return durationInFrames;
3
+ exports.getFramesToRender = void 0;
4
+ const getFramesToRender = (frameRange, everyNthFrame) => {
5
+ if (everyNthFrame === 0) {
6
+ throw new Error('everyNthFrame cannot be 0');
7
7
  }
8
- if (typeof frameRange === 'number') {
9
- return 1;
10
- }
11
- return frameRange[1] - frameRange[0] + 1;
8
+ return new Array(frameRange[1] - frameRange[0] + 1)
9
+ .fill(true)
10
+ .map((_, index) => {
11
+ return index + frameRange[0];
12
+ })
13
+ .filter((index) => {
14
+ return index % everyNthFrame === 0;
15
+ });
12
16
  };
13
- exports.getDurationFromFrameRange = getDurationFromFrameRange;
17
+ exports.getFramesToRender = getFramesToRender;
@@ -1,2 +1,2 @@
1
1
  import type { Codec } from 'remotion';
2
- export declare const getFileExtensionFromCodec: (codec: Codec, type: 'chunk' | 'final') => "mp3" | "aac" | "wav" | "mp4" | "mov" | "webm" | "mkv";
2
+ export declare const getFileExtensionFromCodec: (codec: Codec, type: 'chunk' | 'final') => "mp3" | "aac" | "wav" | "gif" | "webm" | "mp4" | "mov" | "mkv";
@@ -23,6 +23,11 @@ const getFileExtensionFromCodec = (codec, type) => {
23
23
  return 'webm';
24
24
  case 'vp9':
25
25
  return 'webm';
26
+ case 'gif':
27
+ if (type === 'chunk') {
28
+ return 'mkv';
29
+ }
30
+ return 'gif';
26
31
  case 'wav':
27
32
  return 'wav';
28
33
  default:
@@ -1 +1 @@
1
- export declare const guessExtensionForVideo: (src: string) => Promise<"mp4" | "mp3" | "webm" | "wav">;
1
+ export declare const guessExtensionForVideo: (src: string) => Promise<"mp3" | "wav" | "webm" | "mp4">;
@@ -7,9 +7,6 @@ exports.guessExtensionForVideo = void 0;
7
7
  const execa_1 = __importDefault(require("execa"));
8
8
  const guessExtensionForVideo = async (src) => {
9
9
  const { stderr } = await (0, execa_1.default)('ffprobe', [src]);
10
- if (stderr.includes('h264')) {
11
- return 'mp4';
12
- }
13
10
  if (stderr.includes('mp3,')) {
14
11
  return 'mp3';
15
12
  }
@@ -22,6 +19,9 @@ const guessExtensionForVideo = async (src) => {
22
19
  if (stderr.includes('wav, ')) {
23
20
  return 'wav';
24
21
  }
22
+ if (stderr.includes('Video: h264')) {
23
+ return 'mp4';
24
+ }
25
25
  throw new Error(`A media file ${src} which has no file extension and whose format could not be guessed. Is this a valid media file?`);
26
26
  };
27
27
  exports.guessExtensionForVideo = guessExtensionForVideo;
package/dist/index.d.ts CHANGED
@@ -48,14 +48,14 @@ export declare const RenderInternals: {
48
48
  width: number;
49
49
  height: number;
50
50
  scale: number;
51
- codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv";
51
+ codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif";
52
52
  }) => void;
53
53
  normalizeServeUrl: (unnormalized: string) => string;
54
54
  spawnFfmpeg: (options: import("./stitch-frames-to-video").StitcherOptions) => Promise<{
55
55
  task: Promise<Buffer | null>;
56
56
  getLogs: () => string;
57
57
  }>;
58
- getFileExtensionFromCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv", type: "chunk" | "final") => "mp3" | "aac" | "wav" | "mp4" | "mov" | "webm" | "mkv";
58
+ getFileExtensionFromCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif", type: "chunk" | "final") => "mp3" | "aac" | "wav" | "gif" | "webm" | "mp4" | "mov" | "mkv";
59
59
  tmpDir: (str: string) => string;
60
60
  deleteDirectory: (directory: string) => Promise<void>;
61
61
  isServeUrl: (potentialUrl: string) => boolean;
@@ -79,7 +79,7 @@ export declare const RenderInternals: {
79
79
  parseStack: (stack: string[]) => import("./parse-browser-error-stack").UnsymbolicatedStackFrame[];
80
80
  symbolicateError: (symbolicateableError: SymbolicateableError) => Promise<import("./error-handling/handle-javascript-exception").ErrorWithStackFrame>;
81
81
  SymbolicateableError: typeof SymbolicateableError;
82
- getDurationFromFrameRange: (frameRange: import("remotion").FrameRange | null, durationInFrames: number) => number;
82
+ getFramesToRender: (frameRange: [number, number], everyNthFrame: number) => number[];
83
83
  getExtensionOfFilename: (filename: string) => string | null;
84
84
  getDesiredPort: (desiredPort: number | undefined, from: number, to: number) => Promise<number>;
85
85
  isPathInside: (thePath: string, potentialParent: string) => boolean;
@@ -103,6 +103,8 @@ export declare const RenderInternals: {
103
103
  };
104
104
  registerErrorSymbolicationLock: () => number;
105
105
  unlockErrorSymbolicationLock: (id: number) => void;
106
+ canUseParallelEncoding: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => boolean;
106
107
  mimeContentType: typeof mimeContentType;
107
108
  mimeLookup: typeof mimeLookup;
109
+ validateConcurrency: (value: unknown, setting: string) => void;
108
110
  };
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.RenderInternals = exports.stitchFramesToVideo = exports.renderStill = exports.renderMedia = exports.renderFrames = exports.openBrowser = exports.makeCancelSignal = exports.getCompositions = exports.ErrorWithStackFrame = exports.combineVideos = void 0;
7
7
  const execa_1 = __importDefault(require("execa"));
8
8
  const download_file_1 = require("./assets/download-file");
9
+ const can_use_parallel_encoding_1 = require("./can-use-parallel-encoding");
9
10
  const delete_directory_1 = require("./delete-directory");
10
11
  const ensure_output_directory_1 = require("./ensure-output-directory");
11
12
  const symbolicate_error_1 = require("./error-handling/symbolicate-error");
@@ -27,6 +28,7 @@ const is_path_inside_1 = require("./serve-handler/is-path-inside");
27
28
  const serve_static_1 = require("./serve-static");
28
29
  const stitch_frames_to_video_1 = require("./stitch-frames-to-video");
29
30
  const tmp_dir_1 = require("./tmp-dir");
31
+ const validate_concurrency_1 = require("./validate-concurrency");
30
32
  const validate_even_dimensions_with_codec_1 = require("./validate-even-dimensions-with-codec");
31
33
  const validate_ffmpeg_1 = require("./validate-ffmpeg");
32
34
  const validate_puppeteer_timeout_1 = require("./validate-puppeteer-timeout");
@@ -75,13 +77,15 @@ exports.RenderInternals = {
75
77
  parseStack: parse_browser_error_stack_1.parseStack,
76
78
  symbolicateError: symbolicate_error_1.symbolicateError,
77
79
  SymbolicateableError: symbolicateable_error_1.SymbolicateableError,
78
- getDurationFromFrameRange: get_duration_from_frame_range_1.getDurationFromFrameRange,
80
+ getFramesToRender: get_duration_from_frame_range_1.getFramesToRender,
79
81
  getExtensionOfFilename: get_extension_of_filename_1.getExtensionOfFilename,
80
82
  getDesiredPort: get_port_1.getDesiredPort,
81
83
  isPathInside: is_path_inside_1.isPathInside,
82
84
  execa: execa_1.default,
83
85
  registerErrorSymbolicationLock: wait_for_symbolication_error_to_be_done_1.registerErrorSymbolicationLock,
84
86
  unlockErrorSymbolicationLock: wait_for_symbolication_error_to_be_done_1.unlockErrorSymbolicationLock,
87
+ canUseParallelEncoding: can_use_parallel_encoding_1.canUseParallelEncoding,
85
88
  mimeContentType: mime_types_1.mimeContentType,
86
89
  mimeLookup: mime_types_1.mimeLookup,
90
+ validateConcurrency: validate_concurrency_1.validateConcurrency,
87
91
  };
@@ -1,2 +1,2 @@
1
1
  export declare function mimeLookup(path: string): string | false;
2
- export declare function mimeContentType(str: string): string | false;
2
+ export declare function mimeContentType(str: string): false | string;
@@ -12,7 +12,7 @@ function mimeLookup(path) {
12
12
  return false;
13
13
  }
14
14
  // get the extension ("ext" or ".ext" or full path)
15
- const ext = (0, path_1.extname)('x.' + path)
15
+ const ext = (0, path_1.extname)('.' + path)
16
16
  .toLowerCase()
17
17
  .substr(1);
18
18
  if (!ext) {
@@ -50,7 +50,7 @@ function populateMaps(exts, _types) {
50
50
  }
51
51
  }
52
52
  // set the extension -> mime
53
- _types[_ext] = type;
53
+ types[_ext] = type;
54
54
  }
55
55
  });
56
56
  }
@@ -15,8 +15,8 @@ const prespawnFfmpeg = async (options) => {
15
15
  var _a, _b, _c, _d, _e, _f, _g;
16
16
  remotion_1.Internals.validateDimension(options.height, 'height', 'passed to `stitchFramesToVideo()`');
17
17
  remotion_1.Internals.validateDimension(options.width, 'width', 'passed to `stitchFramesToVideo()`');
18
- remotion_1.Internals.validateFps(options.fps, 'passed to `stitchFramesToVideo()`');
19
18
  const codec = (_a = options.codec) !== null && _a !== void 0 ? _a : remotion_1.Internals.DEFAULT_CODEC;
19
+ remotion_1.Internals.validateFps(options.fps, 'in `stitchFramesToVideo()`', codec);
20
20
  (0, validate_even_dimensions_with_codec_1.validateEvenDimensionsWithCodec)({
21
21
  width: options.width,
22
22
  height: options.height,
@@ -45,7 +45,7 @@ const prespawnFfmpeg = async (options) => {
45
45
  remotion_1.Internals.validateSelectedCrfAndCodecCombination(crf, codec);
46
46
  remotion_1.Internals.validateSelectedPixelFormatAndCodecCombination(pixelFormat, codec);
47
47
  const ffmpegArgs = [
48
- ['-r', String(options.fps)],
48
+ ['-r', options.fps.toFixed(2)],
49
49
  ...[
50
50
  ['-f', 'image2pipe'],
51
51
  ['-s', `${options.width}x${options.height}`],
@@ -25,6 +25,7 @@ declare type RenderFramesOptions = {
25
25
  parallelism?: number | null;
26
26
  quality?: number;
27
27
  frameRange?: FrameRange | null;
28
+ everyNthFrame?: number;
28
29
  dumpBrowserLogs?: boolean;
29
30
  puppeteerInstance?: Browser;
30
31
  browserExecutable?: BrowserExecutable;
@@ -38,7 +38,7 @@ const getPool = async (pages) => {
38
38
  const pool = new pool_1.Pool(puppeteerPages);
39
39
  return pool;
40
40
  };
41
- const innerRenderFrames = ({ onFrameUpdate, outputDir, onStart, inputProps, quality, imageFormat = image_format_1.DEFAULT_IMAGE_FORMAT, frameRange, puppeteerInstance, onError, envVariables, onBrowserLog, onFrameBuffer, onDownload, pagesArray, serveUrl, composition, timeoutInMilliseconds, scale, actualParallelism, downloadDir, proxyPort, cancelSignal, }) => {
41
+ const innerRenderFrames = ({ onFrameUpdate, outputDir, onStart, inputProps, quality, imageFormat = image_format_1.DEFAULT_IMAGE_FORMAT, frameRange, puppeteerInstance, onError, envVariables, onBrowserLog, onFrameBuffer, onDownload, pagesArray, serveUrl, composition, timeoutInMilliseconds, scale, actualParallelism, downloadDir, everyNthFrame = 1, proxyPort, cancelSignal, }) => {
42
42
  if (!puppeteerInstance) {
43
43
  throw new Error('no puppeteer instance passed to innerRenderFrames - internal error');
44
44
  }
@@ -51,7 +51,8 @@ const innerRenderFrames = ({ onFrameUpdate, outputDir, onStart, inputProps, qual
51
51
  }
52
52
  const downloadPromises = [];
53
53
  const realFrameRange = (0, get_frame_to_render_1.getRealFrameRange)(composition.durationInFrames, frameRange !== null && frameRange !== void 0 ? frameRange : null);
54
- const frameCount = (0, get_duration_from_frame_range_1.getDurationFromFrameRange)(realFrameRange, composition.durationInFrames);
54
+ const framesToRender = (0, get_duration_from_frame_range_1.getFramesToRender)(realFrameRange, everyNthFrame);
55
+ const lastFrame = framesToRender[framesToRender.length - 1];
55
56
  const pages = new Array(actualParallelism).fill(true).map(async () => {
56
57
  const page = await puppeteerInstance.newPage();
57
58
  pagesArray.push(page);
@@ -99,25 +100,20 @@ const innerRenderFrames = ({ onFrameUpdate, outputDir, onStart, inputProps, qual
99
100
  page.off('console', logCallback);
100
101
  return page;
101
102
  });
102
- const [firstFrameIndex, lastFrameIndex] = realFrameRange;
103
103
  // Substract one because 100 frames will be 00-99
104
104
  // --> 2 digits
105
- const filePadLength = String(lastFrameIndex).length;
105
+ const filePadLength = String(lastFrame).length;
106
106
  let framesRendered = 0;
107
107
  const poolPromise = getPool(pages);
108
108
  onStart({
109
- frameCount,
109
+ frameCount: framesToRender.length,
110
110
  });
111
- const assets = new Array(frameCount).fill(undefined);
111
+ const assets = new Array(framesToRender.length).fill(undefined);
112
112
  let stopped = false;
113
113
  cancelSignal === null || cancelSignal === void 0 ? void 0 : cancelSignal(() => {
114
114
  stopped = true;
115
115
  });
116
- const progress = Promise.all(new Array(frameCount)
117
- .fill(Boolean)
118
- .map((_x, i) => i)
119
- .map(async (index) => {
120
- const frame = realFrameRange[0] + index;
116
+ const progress = Promise.all(framesToRender.map(async (frame, index) => {
121
117
  const pool = await poolPromise;
122
118
  const freePage = await pool.acquire();
123
119
  if (stopped) {
@@ -176,13 +172,13 @@ const innerRenderFrames = ({ onFrameUpdate, outputDir, onStart, inputProps, qual
176
172
  const compressedAssets = collectedAssets.map((asset) => remotion_1.Internals.AssetCompression.compressAsset(assets.filter(remotion_1.Internals.truthy).flat(1), asset));
177
173
  assets[index] = compressedAssets;
178
174
  compressedAssets.forEach((asset) => {
179
- downloadPromises.push((0, download_and_map_assets_to_file_1.downloadAndMapAssetsToFileUrl)({
175
+ (0, download_and_map_assets_to_file_1.downloadAndMapAssetsToFileUrl)({
180
176
  asset,
181
177
  downloadDir,
182
178
  onDownload,
183
179
  }).catch((err) => {
184
180
  onError(new Error(`Error while downloading asset: ${err.stack}`));
185
- }));
181
+ });
186
182
  });
187
183
  pool.release(freePage);
188
184
  framesRendered++;
@@ -196,10 +192,10 @@ const innerRenderFrames = ({ onFrameUpdate, outputDir, onStart, inputProps, qual
196
192
  assetsInfo: {
197
193
  assets,
198
194
  downloadDir,
199
- firstFrameIndex,
200
195
  imageSequenceName: `element-%0${filePadLength}d.${imageFormat}`,
196
+ firstFrameIndex: framesToRender[0],
201
197
  },
202
- frameCount,
198
+ frameCount: framesToRender.length,
203
199
  };
204
200
  return returnValue;
205
201
  });
@@ -217,7 +213,7 @@ const renderFrames = (options) => {
217
213
  }
218
214
  remotion_1.Internals.validateDimension(composition.height, 'height', 'in the `config` object passed to `renderFrames()`');
219
215
  remotion_1.Internals.validateDimension(composition.width, 'width', 'in the `config` object passed to `renderFrames()`');
220
- remotion_1.Internals.validateFps(composition.fps, 'in the `config` object of `renderFrames()`');
216
+ remotion_1.Internals.validateFps(composition.fps, 'in the `config` object of `renderFrames()`', null);
221
217
  remotion_1.Internals.validateDurationInFrames(composition.durationInFrames, 'in the `config` object passed to `renderFrames()`');
222
218
  if (options.quality !== undefined && options.imageFormat !== 'jpeg') {
223
219
  throw new Error("You can only pass the `quality` option if `imageFormat` is 'jpeg'.");
@@ -29,6 +29,8 @@ export declare type RenderMediaOptions = {
29
29
  envVariables?: Record<string, string>;
30
30
  quality?: number;
31
31
  frameRange?: FrameRange | null;
32
+ everyNthFrame?: number;
33
+ numberOfGifLoops?: number | null;
32
34
  puppeteerInstance?: PuppeteerBrowser;
33
35
  overwrite?: boolean;
34
36
  onProgress?: RenderMediaOnProgress;