@remotion/renderer 3.0.30 → 3.1.1

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 (259) 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 +9 -10
  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/index.d.ts +5 -3
  21. package/dist/index.js +3 -1
  22. package/dist/mime-types.d.ts +1 -1
  23. package/dist/mime-types.js +2 -2
  24. package/dist/prespawn-ffmpeg.js +2 -2
  25. package/dist/render-frames.d.ts +1 -0
  26. package/dist/render-frames.js +12 -16
  27. package/dist/render-media.d.ts +2 -0
  28. package/dist/render-media.js +11 -4
  29. package/dist/render-still.js +1 -1
  30. package/dist/stitch-frames-to-video.d.ts +1 -0
  31. package/dist/stitch-frames-to-video.js +32 -22
  32. package/dist/validate-output-filename.js +5 -0
  33. package/package.json +8 -10
  34. package/tsconfig.json +2 -1
  35. package/vitest.config.ts +8 -0
  36. package/dist/assets/calculate-asset-positions.d.ts.map +0 -1
  37. package/dist/assets/calculate-asset-positions.js.map +0 -1
  38. package/dist/assets/calculate-atempo.d.ts.map +0 -1
  39. package/dist/assets/calculate-atempo.js.map +0 -1
  40. package/dist/assets/cleanup-assets.d.ts +0 -2
  41. package/dist/assets/cleanup-assets.js +0 -2
  42. package/dist/assets/convert-assets-to-file-urls.d.ts.map +0 -1
  43. package/dist/assets/convert-assets-to-file-urls.js.map +0 -1
  44. package/dist/assets/download-and-map-assets-to-file.d.ts.map +0 -1
  45. package/dist/assets/download-and-map-assets-to-file.js.map +0 -1
  46. package/dist/assets/download-file.d.ts.map +0 -1
  47. package/dist/assets/download-file.js.map +0 -1
  48. package/dist/assets/ffmpeg-volume-expression.d.ts.map +0 -1
  49. package/dist/assets/ffmpeg-volume-expression.js.map +0 -1
  50. package/dist/assets/flatten-volume-array.d.ts.map +0 -1
  51. package/dist/assets/flatten-volume-array.js.map +0 -1
  52. package/dist/assets/get-audio-channels.d.ts.map +0 -1
  53. package/dist/assets/get-audio-channels.js.map +0 -1
  54. package/dist/assets/read-file.d.ts.map +0 -1
  55. package/dist/assets/read-file.js.map +0 -1
  56. package/dist/assets/round-volume-to-avoid-stack-overflow.d.ts.map +0 -1
  57. package/dist/assets/round-volume-to-avoid-stack-overflow.js.map +0 -1
  58. package/dist/assets/sanitize-filename.d.ts.map +0 -1
  59. package/dist/assets/sanitize-filename.js.map +0 -1
  60. package/dist/assets/sanitize-filepath.d.ts.map +0 -1
  61. package/dist/assets/sanitize-filepath.js.map +0 -1
  62. package/dist/assets/truncate-utf8-bytes.d.ts.map +0 -1
  63. package/dist/assets/truncate-utf8-bytes.js.map +0 -1
  64. package/dist/assets/types.d.ts.map +0 -1
  65. package/dist/assets/types.js.map +0 -1
  66. package/dist/browser/Accessibility.d.ts +0 -175
  67. package/dist/browser/Accessibility.js +0 -423
  68. package/dist/browser/AriaQueryHandler.d.ts +0 -20
  69. package/dist/browser/AriaQueryHandler.js +0 -108
  70. package/dist/browser/ConnectionTransport.d.ts +0 -21
  71. package/dist/browser/ConnectionTransport.js +0 -17
  72. package/dist/browser/Coverage.d.ts +0 -180
  73. package/dist/browser/Coverage.js +0 -371
  74. package/dist/browser/Debug.d.ts +0 -19
  75. package/dist/browser/Debug.js +0 -42
  76. package/dist/browser/DeviceDescriptors.d.ts +0 -40
  77. package/dist/browser/DeviceDescriptors.js +0 -1407
  78. package/dist/browser/EmulationManager.d.ts +0 -7
  79. package/dist/browser/EmulationManager.js +0 -40
  80. package/dist/browser/FileChooser.d.ts +0 -56
  81. package/dist/browser/FileChooser.js +0 -86
  82. package/dist/browser/Input.d.ts +0 -355
  83. package/dist/browser/Input.js +0 -592
  84. package/dist/browser/NetworkConditions.d.ts +0 -26
  85. package/dist/browser/NetworkConditions.js +0 -33
  86. package/dist/browser/PDFOptions.d.ts +0 -165
  87. package/dist/browser/PDFOptions.js +0 -34
  88. package/dist/browser/PipeTransport.d.ts +0 -10
  89. package/dist/browser/PipeTransport.js +0 -86
  90. package/dist/browser/Puppeteer.d.ts +0 -35
  91. package/dist/browser/Puppeteer.js +0 -17
  92. package/dist/browser/QueryHandler.d.ts +0 -64
  93. package/dist/browser/QueryHandler.js +0 -183
  94. package/dist/browser/SecurityDetails.d.ts +0 -55
  95. package/dist/browser/SecurityDetails.js +0 -95
  96. package/dist/browser/Tracing.d.ts +0 -45
  97. package/dist/browser/Tracing.js +0 -136
  98. package/dist/browser/USKeyboardLayout.d.ts +0 -39
  99. package/dist/browser/USKeyboardLayout.js +0 -406
  100. package/dist/browser/WebWorker.d.ts +0 -96
  101. package/dist/browser/WebWorker.js +0 -122
  102. package/dist/browser/compat.d.ts +0 -2
  103. package/dist/browser/compat.js +0 -17
  104. package/dist/browser/dialog.d.ts +0 -70
  105. package/dist/browser/dialog.js +0 -114
  106. package/dist/browser/environment.d.ts +0 -16
  107. package/dist/browser/environment.js +0 -19
  108. package/dist/browser/fetch.d.ts +0 -16
  109. package/dist/browser/fetch.js +0 -46
  110. package/dist/browser/find-up.d.ts +0 -4
  111. package/dist/browser/find-up.js +0 -85
  112. package/dist/browser/page.d.ts +0 -74
  113. package/dist/browser/page.js +0 -283
  114. package/dist/browser/pkg-dir.d.ts +0 -3
  115. package/dist/browser/pkg-dir.js +0 -13
  116. package/dist/browser-log.d.ts.map +0 -1
  117. package/dist/browser-log.js.map +0 -1
  118. package/dist/calculate-ffmpeg-filters.d.ts.map +0 -1
  119. package/dist/calculate-ffmpeg-filters.js.map +0 -1
  120. package/dist/can-use-parallel-encoding.d.ts.map +0 -1
  121. package/dist/can-use-parallel-encoding.js.map +0 -1
  122. package/dist/chunk.d.ts.map +0 -1
  123. package/dist/chunk.js.map +0 -1
  124. package/dist/combine-videos.d.ts.map +0 -1
  125. package/dist/combine-videos.js.map +0 -1
  126. package/dist/convert-to-pcm.d.ts.map +0 -1
  127. package/dist/convert-to-pcm.js.map +0 -1
  128. package/dist/create-ffmpeg-complex-filter.d.ts.map +0 -1
  129. package/dist/create-ffmpeg-complex-filter.js.map +0 -1
  130. package/dist/create-ffmpeg-merge-filter.d.ts.map +0 -1
  131. package/dist/create-ffmpeg-merge-filter.js.map +0 -1
  132. package/dist/create-silent-audio.d.ts.map +0 -1
  133. package/dist/create-silent-audio.js.map +0 -1
  134. package/dist/cycle-browser-tabs.d.ts.map +0 -1
  135. package/dist/cycle-browser-tabs.js.map +0 -1
  136. package/dist/delay-render-embedded-stack.d.ts.map +0 -1
  137. package/dist/delay-render-embedded-stack.js.map +0 -1
  138. package/dist/delete-directory.d.ts.map +0 -1
  139. package/dist/delete-directory.js.map +0 -1
  140. package/dist/ensure-frames-in-order.d.ts.map +0 -1
  141. package/dist/ensure-frames-in-order.js.map +0 -1
  142. package/dist/ensure-output-directory.d.ts.map +0 -1
  143. package/dist/ensure-output-directory.js.map +0 -1
  144. package/dist/error-handling/handle-javascript-exception.d.ts.map +0 -1
  145. package/dist/error-handling/handle-javascript-exception.js.map +0 -1
  146. package/dist/error-handling/symbolicate-error.d.ts.map +0 -1
  147. package/dist/error-handling/symbolicate-error.js.map +0 -1
  148. package/dist/error-handling/symbolicateable-error.d.ts.map +0 -1
  149. package/dist/error-handling/symbolicateable-error.js.map +0 -1
  150. package/dist/ffmpeg-filter-file.d.ts.map +0 -1
  151. package/dist/ffmpeg-filter-file.js.map +0 -1
  152. package/dist/ffmpeg-flags.d.ts.map +0 -1
  153. package/dist/ffmpeg-flags.js.map +0 -1
  154. package/dist/get-audio-codec-name.d.ts.map +0 -1
  155. package/dist/get-audio-codec-name.js.map +0 -1
  156. package/dist/get-browser-instance.d.ts.map +0 -1
  157. package/dist/get-browser-instance.js.map +0 -1
  158. package/dist/get-codec-name.d.ts.map +0 -1
  159. package/dist/get-codec-name.js.map +0 -1
  160. package/dist/get-compositions.d.ts.map +0 -1
  161. package/dist/get-compositions.js.map +0 -1
  162. package/dist/get-concurrency.d.ts.map +0 -1
  163. package/dist/get-concurrency.js.map +0 -1
  164. package/dist/get-duration-from-frame-range.d.ts.map +0 -1
  165. package/dist/get-duration-from-frame-range.js.map +0 -1
  166. package/dist/get-duration-of-asset.d.ts +0 -7
  167. package/dist/get-duration-of-asset.js +0 -36
  168. package/dist/get-extension-from-codec.d.ts.map +0 -1
  169. package/dist/get-extension-from-codec.js.map +0 -1
  170. package/dist/get-format-for-codec.d.ts +0 -2
  171. package/dist/get-format-for-codec.js +0 -34
  172. package/dist/get-frame-to-render.d.ts.map +0 -1
  173. package/dist/get-frame-to-render.js.map +0 -1
  174. package/dist/get-local-browser-executable.d.ts.map +0 -1
  175. package/dist/get-local-browser-executable.js.map +0 -1
  176. package/dist/get-port.d.ts.map +0 -1
  177. package/dist/get-port.js.map +0 -1
  178. package/dist/get-prores-profile-name.d.ts.map +0 -1
  179. package/dist/get-prores-profile-name.js.map +0 -1
  180. package/dist/image-format.d.ts.map +0 -1
  181. package/dist/image-format.js.map +0 -1
  182. package/dist/index.d.ts.map +0 -1
  183. package/dist/index.js.map +0 -1
  184. package/dist/is-serve-url.d.ts.map +0 -1
  185. package/dist/is-serve-url.js.map +0 -1
  186. package/dist/is-vp9-video.d.ts +0 -3
  187. package/dist/is-vp9-video.js +0 -24
  188. package/dist/legacy-webpack-config.d.ts.map +0 -1
  189. package/dist/legacy-webpack-config.js.map +0 -1
  190. package/dist/make-assets-download-dir.d.ts.map +0 -1
  191. package/dist/make-assets-download-dir.js.map +0 -1
  192. package/dist/merge-audio-track.d.ts.map +0 -1
  193. package/dist/merge-audio-track.js.map +0 -1
  194. package/dist/normalize-serve-url.d.ts.map +0 -1
  195. package/dist/normalize-serve-url.js.map +0 -1
  196. package/dist/open-browser.d.ts.map +0 -1
  197. package/dist/open-browser.js.map +0 -1
  198. package/dist/p-limit.d.ts.map +0 -1
  199. package/dist/p-limit.js.map +0 -1
  200. package/dist/parse-browser-error-stack.d.ts.map +0 -1
  201. package/dist/parse-browser-error-stack.js.map +0 -1
  202. package/dist/parse-ffmpeg-progress.d.ts.map +0 -1
  203. package/dist/parse-ffmpeg-progress.js.map +0 -1
  204. package/dist/pool.d.ts.map +0 -1
  205. package/dist/pool.js.map +0 -1
  206. package/dist/prepare-server.d.ts.map +0 -1
  207. package/dist/prepare-server.js.map +0 -1
  208. package/dist/preprocess-audio-track.d.ts.map +0 -1
  209. package/dist/preprocess-audio-track.js.map +0 -1
  210. package/dist/prespawn-ffmpeg.d.ts.map +0 -1
  211. package/dist/prespawn-ffmpeg.js.map +0 -1
  212. package/dist/provide-screenshot.d.ts.map +0 -1
  213. package/dist/provide-screenshot.js.map +0 -1
  214. package/dist/puppeteer-evaluate.d.ts.map +0 -1
  215. package/dist/puppeteer-evaluate.js.map +0 -1
  216. package/dist/puppeteer-screenshot.d.ts.map +0 -1
  217. package/dist/puppeteer-screenshot.js.map +0 -1
  218. package/dist/render-frames.d.ts.map +0 -1
  219. package/dist/render-frames.js.map +0 -1
  220. package/dist/render-media.d.ts.map +0 -1
  221. package/dist/render-media.js.map +0 -1
  222. package/dist/render-still.d.ts.map +0 -1
  223. package/dist/render-still.js.map +0 -1
  224. package/dist/resolve-asset-src.d.ts.map +0 -1
  225. package/dist/resolve-asset-src.js.map +0 -1
  226. package/dist/sample-rate.d.ts.map +0 -1
  227. package/dist/sample-rate.js.map +0 -1
  228. package/dist/screenshot-dom-element.d.ts.map +0 -1
  229. package/dist/screenshot-dom-element.js.map +0 -1
  230. package/dist/screenshot-task.d.ts.map +0 -1
  231. package/dist/screenshot-task.js.map +0 -1
  232. package/dist/seek-to-frame.d.ts.map +0 -1
  233. package/dist/seek-to-frame.js.map +0 -1
  234. package/dist/serve-handler/glob-slash.d.ts +0 -1
  235. package/dist/serve-handler/glob-slash.js +0 -12
  236. package/dist/serve-static.d.ts.map +0 -1
  237. package/dist/serve-static.js.map +0 -1
  238. package/dist/set-props-and-env.d.ts.map +0 -1
  239. package/dist/set-props-and-env.js.map +0 -1
  240. package/dist/stitch-frames-to-video.d.ts.map +0 -1
  241. package/dist/stitch-frames-to-video.js.map +0 -1
  242. package/dist/stringify-ffmpeg-filter.d.ts.map +0 -1
  243. package/dist/stringify-ffmpeg-filter.js.map +0 -1
  244. package/dist/symbolicate-stacktrace.d.ts.map +0 -1
  245. package/dist/symbolicate-stacktrace.js.map +0 -1
  246. package/dist/tmp-dir.d.ts.map +0 -1
  247. package/dist/tmp-dir.js.map +0 -1
  248. package/dist/types.d.ts.map +0 -1
  249. package/dist/types.js.map +0 -1
  250. package/dist/validate-even-dimensions-with-codec.d.ts.map +0 -1
  251. package/dist/validate-even-dimensions-with-codec.js.map +0 -1
  252. package/dist/validate-ffmpeg.d.ts.map +0 -1
  253. package/dist/validate-ffmpeg.js.map +0 -1
  254. package/dist/validate-fps-for-gif.d.ts +0 -2
  255. package/dist/validate-fps-for-gif.js +0 -9
  256. package/dist/validate-puppeteer-timeout.d.ts.map +0 -1
  257. package/dist/validate-puppeteer-timeout.js.map +0 -1
  258. package/dist/validate-scale.d.ts.map +0 -1
  259. 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,7 +5,6 @@ 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"));
10
9
  const remotion_1 = require("remotion");
11
10
  const guess_extension_for_media_1 = require("./guess-extension-for-media");
@@ -28,18 +27,19 @@ const getTemporaryOutputName = async (src) => {
28
27
  .join(path_1.default.sep);
29
28
  };
30
29
  const ensurePresentationTimestamps = async (src) => {
31
- if (ensureFileHasPresentationTimestamp[src] === 'encoding') {
30
+ const elem = ensureFileHasPresentationTimestamp[src];
31
+ if ((elem === null || elem === void 0 ? void 0 : elem.type) === 'encoding') {
32
32
  return new Promise((resolve) => {
33
33
  callbacks.push({
34
34
  src,
35
- fn: () => resolve(),
35
+ fn: (newSrc) => resolve(newSrc),
36
36
  });
37
37
  });
38
38
  }
39
- if (ensureFileHasPresentationTimestamp[src] === 'done') {
40
- return;
39
+ if ((elem === null || elem === void 0 ? void 0 : elem.type) === 'done') {
40
+ return elem.src;
41
41
  }
42
- ensureFileHasPresentationTimestamp[src] = 'encoding';
42
+ ensureFileHasPresentationTimestamp[src] = { type: 'encoding' };
43
43
  // If there is no file extension for the video, then we need to tempoa
44
44
  const output = await getTemporaryOutputName(src);
45
45
  await (0, execa_1.default)('ffmpeg', [
@@ -54,15 +54,14 @@ const ensurePresentationTimestamps = async (src) => {
54
54
  output,
55
55
  '-y',
56
56
  ]);
57
- await (0, promises_1.unlink)(src);
58
- await (0, promises_1.rename)(output, src);
59
57
  callbacks = callbacks.filter((c) => {
60
58
  if (c.src === src) {
61
- c.fn();
59
+ c.fn(output);
62
60
  return false;
63
61
  }
64
62
  return true;
65
63
  });
66
- ensureFileHasPresentationTimestamp[src] = 'done';
64
+ ensureFileHasPresentationTimestamp[src] = { type: 'done', src: output };
65
+ return output;
67
66
  };
68
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" | "webm" | "mp4" | "mov" | "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<"mp3" | "webm" | "wav" | "mp4">;
1
+ export declare const guessExtensionForVideo: (src: string) => Promise<"mp3" | "wav" | "webm" | "mp4">;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import execa from 'execa';
2
3
  import { SymbolicateableError } from './error-handling/symbolicateable-error';
3
4
  import { mimeContentType, mimeLookup } from './mime-types';
@@ -47,14 +48,14 @@ export declare const RenderInternals: {
47
48
  width: number;
48
49
  height: number;
49
50
  scale: number;
50
- codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv";
51
+ codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif";
51
52
  }) => void;
52
53
  normalizeServeUrl: (unnormalized: string) => string;
53
54
  spawnFfmpeg: (options: import("./stitch-frames-to-video").StitcherOptions) => Promise<{
54
55
  task: Promise<Buffer | null>;
55
56
  getLogs: () => string;
56
57
  }>;
57
- getFileExtensionFromCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv", type: "chunk" | "final") => "mp3" | "aac" | "wav" | "mp4" | "mkv" | "mov" | "webm";
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";
58
59
  tmpDir: (str: string) => string;
59
60
  deleteDirectory: (directory: string) => Promise<void>;
60
61
  isServeUrl: (potentialUrl: string) => boolean;
@@ -78,7 +79,7 @@ export declare const RenderInternals: {
78
79
  parseStack: (stack: string[]) => import("./parse-browser-error-stack").UnsymbolicatedStackFrame[];
79
80
  symbolicateError: (symbolicateableError: SymbolicateableError) => Promise<import("./error-handling/handle-javascript-exception").ErrorWithStackFrame>;
80
81
  SymbolicateableError: typeof SymbolicateableError;
81
- getDurationFromFrameRange: (frameRange: import("remotion").FrameRange | null, durationInFrames: number) => number;
82
+ getFramesToRender: (frameRange: [number, number], everyNthFrame: number) => number[];
82
83
  getExtensionOfFilename: (filename: string) => string | null;
83
84
  getDesiredPort: (desiredPort: number | undefined, from: number, to: number) => Promise<number>;
84
85
  isPathInside: (thePath: string, potentialParent: string) => boolean;
@@ -102,6 +103,7 @@ export declare const RenderInternals: {
102
103
  };
103
104
  registerErrorSymbolicationLock: () => number;
104
105
  unlockErrorSymbolicationLock: (id: number) => void;
106
+ canUseParallelEncoding: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => boolean;
105
107
  mimeContentType: typeof mimeContentType;
106
108
  mimeLookup: typeof mimeLookup;
107
109
  validateConcurrency: (value: unknown, setting: string) => void;
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");
@@ -76,13 +77,14 @@ exports.RenderInternals = {
76
77
  parseStack: parse_browser_error_stack_1.parseStack,
77
78
  symbolicateError: symbolicate_error_1.symbolicateError,
78
79
  SymbolicateableError: symbolicateable_error_1.SymbolicateableError,
79
- getDurationFromFrameRange: get_duration_from_frame_range_1.getDurationFromFrameRange,
80
+ getFramesToRender: get_duration_from_frame_range_1.getFramesToRender,
80
81
  getExtensionOfFilename: get_extension_of_filename_1.getExtensionOfFilename,
81
82
  getDesiredPort: get_port_1.getDesiredPort,
82
83
  isPathInside: is_path_inside_1.isPathInside,
83
84
  execa: execa_1.default,
84
85
  registerErrorSymbolicationLock: wait_for_symbolication_error_to_be_done_1.registerErrorSymbolicationLock,
85
86
  unlockErrorSymbolicationLock: wait_for_symbolication_error_to_be_done_1.unlockErrorSymbolicationLock,
87
+ canUseParallelEncoding: can_use_parallel_encoding_1.canUseParallelEncoding,
86
88
  mimeContentType: mime_types_1.mimeContentType,
87
89
  mimeLookup: mime_types_1.mimeLookup,
88
90
  validateConcurrency: validate_concurrency_1.validateConcurrency,
@@ -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'.");
@@ -28,6 +28,8 @@ export declare type RenderMediaOptions = {
28
28
  envVariables?: Record<string, string>;
29
29
  quality?: number;
30
30
  frameRange?: FrameRange | null;
31
+ everyNthFrame?: number;
32
+ numberOfGifLoops?: number | null;
31
33
  puppeteerInstance?: PuppeteerBrowser;
32
34
  overwrite?: boolean;
33
35
  onProgress?: RenderMediaOnProgress;
@@ -30,6 +30,7 @@ const validate_scale_1 = require("./validate-scale");
30
30
  * @link https://www.remotion.dev/docs/renderer/render-media
31
31
  */
32
32
  const renderMedia = ({ parallelism, proResProfile, crf, composition, imageFormat, ffmpegExecutable, ffprobeExecutable, inputProps, pixelFormat, codec, envVariables, quality, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, ...options }) => {
33
+ var _a, _b;
33
34
  remotion_1.Internals.validateQuality(quality);
34
35
  if (typeof crf !== 'undefined' && crf !== null) {
35
36
  remotion_1.Internals.validateSelectedCrfAndCodecCombination(crf, codec);
@@ -38,6 +39,8 @@ const renderMedia = ({ parallelism, proResProfile, crf, composition, imageFormat
38
39
  (0, validate_output_filename_1.validateOutputFilename)(codec, (0, get_extension_of_filename_1.getExtensionOfFilename)(outputLocation));
39
40
  }
40
41
  (0, validate_scale_1.validateScale)(scale);
42
+ const everyNthFrame = (_a = options.everyNthFrame) !== null && _a !== void 0 ? _a : 1;
43
+ const numberOfGifLoops = (_b = options.numberOfGifLoops) !== null && _b !== void 0 ? _b : null;
41
44
  const serveUrl = (0, legacy_webpack_config_1.getServeUrlWithFallback)(options);
42
45
  let stitchStage = 'encoding';
43
46
  let stitcherFfmpeg;
@@ -80,12 +83,14 @@ const renderMedia = ({ parallelism, proResProfile, crf, composition, imageFormat
80
83
  cancelRenderFrames.cancel();
81
84
  });
82
85
  const { waitForRightTimeOfFrameToBeInserted, setFrameToStitch, waitForFinish } = (0, ensure_frames_in_order_1.ensureFramesInOrder)(realFrameRange);
86
+ const fps = composition.fps / (everyNthFrame !== null && everyNthFrame !== void 0 ? everyNthFrame : 1);
87
+ remotion_1.Internals.validateFps(fps, 'in "renderMedia()"', codec);
83
88
  const createPrestitcherIfNecessary = async () => {
84
89
  if (preEncodedFileLocation) {
85
90
  preStitcher = await (0, prespawn_ffmpeg_1.prespawnFfmpeg)({
86
91
  width: composition.width * (scale !== null && scale !== void 0 ? scale : 1),
87
92
  height: composition.height * (scale !== null && scale !== void 0 ? scale : 1),
88
- fps: composition.fps,
93
+ fps,
89
94
  outputLocation: preEncodedFileLocation,
90
95
  pixelFormat,
91
96
  codec,
@@ -137,6 +142,7 @@ const renderMedia = ({ parallelism, proResProfile, crf, composition, imageFormat
137
142
  quality,
138
143
  frameRange: frameRange !== null && frameRange !== void 0 ? frameRange : null,
139
144
  puppeteerInstance,
145
+ everyNthFrame,
140
146
  onFrameBuffer: parallelEncoding
141
147
  ? async (buffer, frame) => {
142
148
  var _a;
@@ -147,7 +153,7 @@ const renderMedia = ({ parallelism, proResProfile, crf, composition, imageFormat
147
153
  const id = remotion_1.Internals.perf.startPerfMeasure('piping');
148
154
  (_a = stitcherFfmpeg === null || stitcherFfmpeg === void 0 ? void 0 : stitcherFfmpeg.stdin) === null || _a === void 0 ? void 0 : _a.write(buffer);
149
155
  remotion_1.Internals.perf.stopPerfMeasure(id);
150
- setFrameToStitch(frame + 1);
156
+ setFrameToStitch(Math.min(realFrameRange[1] + 1, frame + everyNthFrame));
151
157
  }
152
158
  : undefined,
153
159
  serveUrl,
@@ -179,7 +185,7 @@ const renderMedia = ({ parallelism, proResProfile, crf, composition, imageFormat
179
185
  (0, stitch_frames_to_video_1.stitchFramesToVideo)({
180
186
  width: composition.width * (scale !== null && scale !== void 0 ? scale : 1),
181
187
  height: composition.height * (scale !== null && scale !== void 0 ? scale : 1),
182
- fps: composition.fps,
188
+ fps,
183
189
  outputLocation,
184
190
  internalOptions: {
185
191
  preEncodedFileLocation,
@@ -199,6 +205,7 @@ const renderMedia = ({ parallelism, proResProfile, crf, composition, imageFormat
199
205
  callUpdate();
200
206
  },
201
207
  onDownload,
208
+ numberOfGifLoops,
202
209
  verbose: remotion_1.Internals.Logging.isEqualOrBelowLogLevel(remotion_1.Internals.Logging.getLogLevel(), 'verbose'),
203
210
  dir: outputDir !== null && outputDir !== void 0 ? outputDir : undefined,
204
211
  cancelSignal: cancelStitcher.cancelSignal,
@@ -207,7 +214,7 @@ const renderMedia = ({ parallelism, proResProfile, crf, composition, imageFormat
207
214
  ]);
208
215
  })
209
216
  .then(([buffer, stitchStart]) => {
210
- encodedFrames = (0, get_duration_from_frame_range_1.getDurationFromFrameRange)(frameRange !== null && frameRange !== void 0 ? frameRange : null, composition.durationInFrames);
217
+ encodedFrames = (0, get_duration_from_frame_range_1.getFramesToRender)(realFrameRange, everyNthFrame).length;
211
218
  encodedDoneIn = Date.now() - stitchStart;
212
219
  callUpdate();
213
220
  return buffer;
@@ -45,7 +45,7 @@ const validate_scale_1 = require("./validate-scale");
45
45
  const innerRenderStill = async ({ composition, quality, imageFormat = 'png', serveUrl, puppeteerInstance, dumpBrowserLogs = false, onError, inputProps, envVariables, output, frame = 0, overwrite = true, browserExecutable, timeoutInMilliseconds, chromiumOptions, scale, proxyPort, cancelSignal, }) => {
46
46
  remotion_1.Internals.validateDimension(composition.height, 'height', 'in the `config` object passed to `renderStill()`');
47
47
  remotion_1.Internals.validateDimension(composition.width, 'width', 'in the `config` object passed to `renderStill()`');
48
- remotion_1.Internals.validateFps(composition.fps, 'in the `config` object of `renderStill()`');
48
+ remotion_1.Internals.validateFps(composition.fps, 'in the `config` object of `renderStill()`', null);
49
49
  remotion_1.Internals.validateDurationInFrames(composition.durationInFrames, 'in the `config` object passed to `renderStill()`');
50
50
  remotion_1.Internals.validateNonNullImageFormat(imageFormat);
51
51
  remotion_1.Internals.validateFrame(frame, composition.durationInFrames);
@@ -10,6 +10,7 @@ export declare type StitcherOptions = {
10
10
  force: boolean;
11
11
  assetsInfo: RenderAssetInfo;
12
12
  pixelFormat?: PixelFormat;
13
+ numberOfGifLoops?: number | null;
13
14
  codec?: Codec;
14
15
  crf?: number | null;
15
16
  onProgress?: (progress: number) => void;