@remotion/renderer 4.0.482 → 4.0.484

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.
package/dist/client.d.ts CHANGED
@@ -820,7 +820,10 @@ export declare const BrowserSafeApis: {
820
820
  type: "yuv420p" | "yuv420p10le" | "yuv422p" | "yuv422p10le" | "yuv444p" | "yuv444p10le" | "yuva420p" | "yuva444p10le";
821
821
  getValue: ({ commandLine }: {
822
822
  commandLine: Record<string, unknown>;
823
- }) => {
823
+ }, options?: {
824
+ uiPixelFormat: "yuv420p" | "yuv420p10le" | "yuv422p" | "yuv422p10le" | "yuv444p" | "yuv444p10le" | "yuva420p" | "yuva444p10le" | null;
825
+ compositionDefaultPixelFormat: "yuv420p" | "yuv420p10le" | "yuv422p" | "yuv422p10le" | "yuv444p" | "yuv444p10le" | "yuva420p" | "yuva444p10le" | null;
826
+ } | undefined) => {
824
827
  source: string;
825
828
  value: "yuv420p" | "yuv420p10le" | "yuv422p" | "yuv422p10le" | "yuv444p" | "yuv444p10le" | "yuva420p" | "yuva444p10le";
826
829
  };
@@ -874,7 +877,10 @@ export declare const BrowserSafeApis: {
874
877
  type: import("./client").ProResProfile | undefined;
875
878
  getValue: ({ commandLine }: {
876
879
  commandLine: Record<string, unknown>;
877
- }) => {
880
+ }, options?: {
881
+ uiProResProfile: import("./client").ProResProfile | undefined;
882
+ compositionDefaultProResProfile: import("./client").ProResProfile | null;
883
+ } | undefined) => {
878
884
  source: string;
879
885
  value: import("./client").ProResProfile;
880
886
  } | {
@@ -1477,7 +1483,11 @@ export declare const BrowserSafeApis: {
1477
1483
  type: "jpeg" | "none" | "png" | null;
1478
1484
  getValue: ({ commandLine }: {
1479
1485
  commandLine: Record<string, unknown>;
1480
- }) => {
1486
+ }, options?: {
1487
+ codec: import("./codec").CodecOrUndefined;
1488
+ uiVideoImageFormat: "jpeg" | "none" | "png" | null;
1489
+ compositionDefaultVideoImageFormat: "jpeg" | "none" | "png" | null;
1490
+ } | undefined) => {
1481
1491
  source: string;
1482
1492
  value: "jpeg" | "none" | "png";
1483
1493
  } | {
package/dist/crf.d.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  export type Crf = number | undefined;
2
2
  export declare const getDefaultCrfForCodec: (codec: "aac" | "av1" | "gif" | "h264" | "h264-mkv" | "h264-ts" | "h265" | "mp3" | "prores" | "vp8" | "vp9" | "wav") => number | null;
3
3
  export declare const getValidCrfRanges: (codec: "aac" | "av1" | "gif" | "h264" | "h264-mkv" | "h264-ts" | "h265" | "mp3" | "prores" | "vp8" | "vp9" | "wav") => [number, number];
4
- export declare const validateQualitySettings: ({ codec, crf, videoBitrate, encodingMaxRate, encodingBufferSize, hardwareAcceleration, }: {
4
+ export declare const validateQualitySettings: ({ codec, crf, videoBitrate, encodingMaxRate, encodingBufferSize, hardwareAcceleration, hardwareAccelerated, }: {
5
5
  crf: unknown;
6
6
  codec: "aac" | "av1" | "gif" | "h264" | "h264-mkv" | "h264-ts" | "h265" | "mp3" | "prores" | "vp8" | "vp9" | "wav";
7
7
  videoBitrate: string | null;
8
8
  encodingMaxRate: string | null;
9
9
  encodingBufferSize: string | null;
10
10
  hardwareAcceleration: "disable" | "if-possible" | "required";
11
+ hardwareAccelerated: boolean;
11
12
  }) => string[];
package/dist/crf.js CHANGED
@@ -46,11 +46,15 @@ const getValidCrfRanges = (codec) => {
46
46
  return val;
47
47
  };
48
48
  exports.getValidCrfRanges = getValidCrfRanges;
49
- const validateQualitySettings = ({ codec, crf, videoBitrate, encodingMaxRate, encodingBufferSize, hardwareAcceleration, }) => {
50
- if (crf && videoBitrate) {
49
+ const validateQualitySettings = ({ codec, crf, videoBitrate, encodingMaxRate, encodingBufferSize, hardwareAcceleration, hardwareAccelerated, }) => {
50
+ const hasExplicitCrf = crf !== null && typeof crf !== 'undefined';
51
+ if (hasExplicitCrf && videoBitrate) {
51
52
  throw new Error('"crf" and "videoBitrate" can not both be set. Choose one of either.');
52
53
  }
53
- if (crf && hardwareAcceleration === 'required') {
54
+ if (hasExplicitCrf && hardwareAcceleration === 'required') {
55
+ throw new Error('"crf" option is not supported with hardware acceleration');
56
+ }
57
+ if (hasExplicitCrf && hardwareAccelerated) {
54
58
  throw new Error('"crf" option is not supported with hardware acceleration');
55
59
  }
56
60
  if (encodingMaxRate && !encodingBufferSize) {
@@ -72,6 +76,9 @@ const validateQualitySettings = ({ codec, crf, videoBitrate, encodingMaxRate, en
72
76
  return ['-b:v', videoBitrate, ...bufSizeArray, ...maxRateArray];
73
77
  }
74
78
  if (crf === null || typeof crf === 'undefined') {
79
+ if (hardwareAccelerated) {
80
+ return [...bufSizeArray, ...maxRateArray];
81
+ }
75
82
  const actualCrf = (0, exports.getDefaultCrfForCodec)(codec);
76
83
  if (actualCrf === null) {
77
84
  return [...bufSizeArray, ...maxRateArray];
@@ -51,6 +51,11 @@ var validCodecs = [
51
51
  ];
52
52
  var DEFAULT_CODEC = "h264";
53
53
 
54
+ // src/is-audio-codec.ts
55
+ var isAudioCodec = (codec) => {
56
+ return codec === "mp3" || codec === "aac" || codec === "wav";
57
+ };
58
+
54
59
  // src/crf.ts
55
60
  var defaultCrfMap = {
56
61
  h264: 18,
@@ -3652,16 +3657,28 @@ var pixelFormatOption = {
3652
3657
  ssrName: "pixelFormat",
3653
3658
  docLink: "https://www.remotion.dev/docs/config#setpixelformat",
3654
3659
  type: DEFAULT_PIXEL_FORMAT,
3655
- getValue: ({ commandLine }) => {
3660
+ getValue: ({ commandLine }, options) => {
3661
+ if (options?.uiPixelFormat) {
3662
+ return {
3663
+ source: "via UI",
3664
+ value: options.uiPixelFormat
3665
+ };
3666
+ }
3656
3667
  if (commandLine[cliFlag65] !== undefined) {
3657
3668
  return {
3658
- source: "cli",
3669
+ source: "from --pixel-format flag",
3659
3670
  value: commandLine[cliFlag65]
3660
3671
  };
3661
3672
  }
3673
+ if (options && options.compositionDefaultPixelFormat !== null) {
3674
+ return {
3675
+ source: "via calculateMetadata",
3676
+ value: options.compositionDefaultPixelFormat
3677
+ };
3678
+ }
3662
3679
  if (currentPixelFormat !== DEFAULT_PIXEL_FORMAT) {
3663
3680
  return {
3664
- source: "config",
3681
+ source: "Config file",
3665
3682
  value: currentPixelFormat
3666
3683
  };
3667
3684
  }
@@ -3876,16 +3893,28 @@ var proResProfileOption = {
3876
3893
  ssrName: "proResProfile",
3877
3894
  docLink: "https://www.remotion.dev/docs/config#setproresprofile",
3878
3895
  type: undefined,
3879
- getValue: ({ commandLine }) => {
3896
+ getValue: ({ commandLine }, options) => {
3897
+ if (options?.uiProResProfile) {
3898
+ return {
3899
+ source: "via UI",
3900
+ value: options.uiProResProfile
3901
+ };
3902
+ }
3880
3903
  if (commandLine[cliFlag70] !== undefined) {
3881
3904
  return {
3882
- source: "cli",
3905
+ source: "from --prores-profile flag",
3883
3906
  value: String(commandLine[cliFlag70])
3884
3907
  };
3885
3908
  }
3909
+ if (options && options.compositionDefaultProResProfile !== null) {
3910
+ return {
3911
+ source: "via calculateMetadata",
3912
+ value: options.compositionDefaultProResProfile
3913
+ };
3914
+ }
3886
3915
  if (proResProfile !== undefined) {
3887
3916
  return {
3888
- source: "config",
3917
+ source: "Config file",
3889
3918
  value: proResProfile
3890
3919
  };
3891
3920
  }
@@ -4885,23 +4914,56 @@ var videoImageFormatOption = {
4885
4914
  ssrName: "imageFormat",
4886
4915
  docLink: "https://www.remotion.dev/docs/renderer/render-media#imageformat",
4887
4916
  type: null,
4888
- getValue: ({ commandLine }) => {
4917
+ getValue: ({ commandLine }, options) => {
4918
+ if (options?.uiVideoImageFormat) {
4919
+ return {
4920
+ source: "via UI",
4921
+ value: options.uiVideoImageFormat
4922
+ };
4923
+ }
4889
4924
  if (commandLine[cliFlag88] !== undefined) {
4890
4925
  const value3 = commandLine[cliFlag88];
4891
4926
  if (!validVideoImageFormats.includes(value3)) {
4892
4927
  throw new Error(`Invalid video image format: ${value3}. Must be one of: ${validVideoImageFormats.join(", ")}`);
4893
4928
  }
4894
4929
  return {
4895
- source: "cli",
4930
+ source: "from --image-format flag",
4896
4931
  value: value3
4897
4932
  };
4898
4933
  }
4934
+ if (options && options.compositionDefaultVideoImageFormat !== null) {
4935
+ return {
4936
+ source: "via calculateMetadata",
4937
+ value: options.compositionDefaultVideoImageFormat
4938
+ };
4939
+ }
4899
4940
  if (currentVideoImageFormat !== null) {
4900
4941
  return {
4901
- source: "config",
4942
+ source: "Config file",
4902
4943
  value: currentVideoImageFormat
4903
4944
  };
4904
4945
  }
4946
+ if (options) {
4947
+ if (isAudioCodec(options.codec)) {
4948
+ return {
4949
+ source: "default",
4950
+ value: "none"
4951
+ };
4952
+ }
4953
+ if (options.codec === "h264" || options.codec === "h264-mkv" || options.codec === "h264-ts" || options.codec === "h265" || options.codec === "av1" || options.codec === "vp8" || options.codec === "vp9" || options.codec === "prores" || options.codec === "gif") {
4954
+ return {
4955
+ source: "default",
4956
+ value: "jpeg"
4957
+ };
4958
+ }
4959
+ if (options.codec === undefined) {
4960
+ return {
4961
+ source: "default",
4962
+ value: "png"
4963
+ };
4964
+ }
4965
+ throw new Error("Unrecognized codec " + options.codec);
4966
+ }
4905
4967
  return {
4906
4968
  source: "default",
4907
4969
  value: null
@@ -21265,7 +21265,7 @@ var renderFrames = (options2) => {
21265
21265
  // src/render-media.ts
21266
21266
  import fs18 from "node:fs";
21267
21267
  import os9 from "node:os";
21268
- import path27 from "node:path";
21268
+ import path28 from "node:path";
21269
21269
  import { LicensingInternals } from "@remotion/licensing";
21270
21270
  import { NoReactInternals as NoReactInternals15 } from "remotion/no-react";
21271
21271
 
@@ -21318,12 +21318,17 @@ var validateQualitySettings = ({
21318
21318
  videoBitrate,
21319
21319
  encodingMaxRate,
21320
21320
  encodingBufferSize,
21321
- hardwareAcceleration
21321
+ hardwareAcceleration,
21322
+ hardwareAccelerated
21322
21323
  }) => {
21323
- if (crf && videoBitrate) {
21324
+ const hasExplicitCrf = crf !== null && typeof crf !== "undefined";
21325
+ if (hasExplicitCrf && videoBitrate) {
21324
21326
  throw new Error('"crf" and "videoBitrate" can not both be set. Choose one of either.');
21325
21327
  }
21326
- if (crf && hardwareAcceleration === "required") {
21328
+ if (hasExplicitCrf && hardwareAcceleration === "required") {
21329
+ throw new Error('"crf" option is not supported with hardware acceleration');
21330
+ }
21331
+ if (hasExplicitCrf && hardwareAccelerated) {
21327
21332
  throw new Error('"crf" option is not supported with hardware acceleration');
21328
21333
  }
21329
21334
  if (encodingMaxRate && !encodingBufferSize) {
@@ -21343,6 +21348,9 @@ var validateQualitySettings = ({
21343
21348
  return ["-b:v", videoBitrate, ...bufSizeArray, ...maxRateArray];
21344
21349
  }
21345
21350
  if (crf === null || typeof crf === "undefined") {
21351
+ if (hardwareAccelerated) {
21352
+ return [...bufSizeArray, ...maxRateArray];
21353
+ }
21346
21354
  const actualCrf = getDefaultCrfForCodec(codec);
21347
21355
  if (actualCrf === null) {
21348
21356
  return [...bufSizeArray, ...maxRateArray];
@@ -21558,6 +21566,11 @@ var getCodecName = ({
21558
21566
  if (preferredHwAcceleration && process.platform === "darwin" && !unsupportedQualityOption) {
21559
21567
  return { encoderName: "prores_videotoolbox", hardwareAccelerated: true };
21560
21568
  }
21569
+ if (preferredHwAcceleration && process.platform !== "darwin" && !unsupportedQualityOption) {
21570
+ if (hardwareAcceleration === "required") {
21571
+ throw new Error(`Codec "prores" does not support hardware acceleration on ${process.platform}, but "hardwareAcceleration" is set to "required"`);
21572
+ }
21573
+ }
21561
21574
  warnAboutDisabledHardwareAcceleration();
21562
21575
  return { encoderName: "prores_ks", hardwareAccelerated: false };
21563
21576
  }
@@ -21565,6 +21578,9 @@ var getCodecName = ({
21565
21578
  if (preferredHwAcceleration && process.platform === "darwin" && !unsupportedQualityOption) {
21566
21579
  return { encoderName: "h264_videotoolbox", hardwareAccelerated: true };
21567
21580
  }
21581
+ if (preferredHwAcceleration && (process.platform === "linux" || process.platform === "win32") && !unsupportedQualityOption) {
21582
+ return { encoderName: "h264_nvenc", hardwareAccelerated: true };
21583
+ }
21568
21584
  warnAboutDisabledHardwareAcceleration();
21569
21585
  return { encoderName: "libx264", hardwareAccelerated: false };
21570
21586
  }
@@ -21572,6 +21588,9 @@ var getCodecName = ({
21572
21588
  if (preferredHwAcceleration && process.platform === "darwin" && !unsupportedQualityOption) {
21573
21589
  return { encoderName: "hevc_videotoolbox", hardwareAccelerated: true };
21574
21590
  }
21591
+ if (preferredHwAcceleration && (process.platform === "linux" || process.platform === "win32") && !unsupportedQualityOption) {
21592
+ return { encoderName: "hevc_nvenc", hardwareAccelerated: true };
21593
+ }
21575
21594
  warnAboutDisabledHardwareAcceleration();
21576
21595
  return { encoderName: "libx265", hardwareAccelerated: false };
21577
21596
  }
@@ -21598,9 +21617,15 @@ var getCodecName = ({
21598
21617
  return null;
21599
21618
  }
21600
21619
  if (codec === "h264-mkv") {
21620
+ if (hardwareAcceleration === "required") {
21621
+ throw new Error(`Codec "h264-mkv" does not support hardware acceleration on ${process.platform}, but "hardwareAcceleration" is set to "required"`);
21622
+ }
21601
21623
  return { encoderName: "libx264", hardwareAccelerated: false };
21602
21624
  }
21603
21625
  if (codec === "h264-ts") {
21626
+ if (hardwareAcceleration === "required") {
21627
+ throw new Error(`Codec "h264-ts" does not support hardware acceleration on ${process.platform}, but "hardwareAcceleration" is set to "required"`);
21628
+ }
21604
21629
  return { encoderName: "libx264", hardwareAccelerated: false };
21605
21630
  }
21606
21631
  throw new Error(`Could not get codec for ${codec}`);
@@ -21618,7 +21643,8 @@ var firstEncodingStepOnly = ({
21618
21643
  videoBitrate,
21619
21644
  encodingMaxRate,
21620
21645
  encodingBufferSize,
21621
- hardwareAcceleration
21646
+ hardwareAcceleration,
21647
+ hardwareAccelerated
21622
21648
  }) => {
21623
21649
  if (hasPreencoded || codec === "gif") {
21624
21650
  return [];
@@ -21637,7 +21663,8 @@ var firstEncodingStepOnly = ({
21637
21663
  codec,
21638
21664
  encodingMaxRate,
21639
21665
  encodingBufferSize,
21640
- hardwareAcceleration
21666
+ hardwareAcceleration,
21667
+ hardwareAccelerated
21641
21668
  })
21642
21669
  ].filter(truthy);
21643
21670
  };
@@ -21706,7 +21733,8 @@ var generateFfmpegArgs = ({
21706
21733
  encodingBufferSize,
21707
21734
  x264Preset,
21708
21735
  gopSize,
21709
- hardwareAcceleration
21736
+ hardwareAcceleration,
21737
+ hardwareAccelerated
21710
21738
  })
21711
21739
  ].filter(truthy);
21712
21740
  };
@@ -21734,6 +21762,86 @@ var getProResProfileName = (codec, proResProfile) => {
21734
21762
  }
21735
21763
  };
21736
21764
 
21765
+ // src/probe-encoder.ts
21766
+ import { execFileSync } from "node:child_process";
21767
+ import path23 from "path";
21768
+ var probeEncoderAvailability = ({
21769
+ encoderName,
21770
+ binariesDirectory,
21771
+ indent,
21772
+ logLevel
21773
+ }) => {
21774
+ try {
21775
+ const executablePath = getExecutablePath({
21776
+ type: "ffmpeg",
21777
+ indent,
21778
+ logLevel,
21779
+ binariesDirectory
21780
+ });
21781
+ makeFileExecutableIfItIsNot(executablePath);
21782
+ const cwd = path23.dirname(executablePath);
21783
+ const result = execFileSync(executablePath, ["-encoders"], {
21784
+ encoding: "utf-8",
21785
+ env: getExplicitEnv(cwd),
21786
+ stdio: ["pipe", "pipe", "pipe"],
21787
+ timeout: 1e4
21788
+ });
21789
+ const escaped = encoderName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
21790
+ const encoderRegex = new RegExp(`\\b${escaped}\\b`);
21791
+ const hasEncoder = encoderRegex.test(result);
21792
+ if (!hasEncoder) {
21793
+ Log.verbose({ indent, logLevel, tag: "probeEncoderAvailability()" }, `Encoder "${encoderName}" not found in FFmpeg build. Falling back to software encoding.`);
21794
+ }
21795
+ return hasEncoder;
21796
+ } catch (err) {
21797
+ Log.verbose({ indent, logLevel, tag: "probeEncoderAvailability()" }, `Failed to probe FFmpeg for encoder "${encoderName}": ${err}. Falling back to software encoding.`);
21798
+ return false;
21799
+ }
21800
+ };
21801
+ var resolveHardwareAcceleration = ({
21802
+ codec,
21803
+ hardwareAcceleration,
21804
+ binariesDirectory,
21805
+ indent,
21806
+ logLevel,
21807
+ crf,
21808
+ encodingMaxRate,
21809
+ encodingBufferSize
21810
+ }) => {
21811
+ if (hardwareAcceleration === "disable") {
21812
+ return "disable";
21813
+ }
21814
+ const preferred = getCodecName({
21815
+ codec,
21816
+ hardwareAcceleration,
21817
+ crf,
21818
+ encodingMaxRate,
21819
+ encodingBufferSize,
21820
+ logLevel,
21821
+ indent
21822
+ });
21823
+ if (preferred === null) {
21824
+ return hardwareAcceleration;
21825
+ }
21826
+ if (!preferred.hardwareAccelerated) {
21827
+ return hardwareAcceleration;
21828
+ }
21829
+ const encoderAvailable = probeEncoderAvailability({
21830
+ encoderName: preferred.encoderName,
21831
+ binariesDirectory,
21832
+ indent,
21833
+ logLevel
21834
+ });
21835
+ if (encoderAvailable) {
21836
+ return hardwareAcceleration;
21837
+ }
21838
+ if (hardwareAcceleration === "if-possible") {
21839
+ Log.verbose({ indent, logLevel, tag: "resolveHardwareAcceleration()" }, `Hardware encoder "${preferred.encoderName}" not available. Falling back to software encoding.`);
21840
+ return "disable";
21841
+ }
21842
+ throw new Error(`Hardware encoder "${preferred.encoderName}" is not available in your FFmpeg build. ` + `Install an FFmpeg with ${preferred.encoderName} support, or use "if-possible" mode instead of "required".`);
21843
+ };
21844
+
21737
21845
  // src/validate-even-dimensions-with-codec.ts
21738
21846
  var validateEvenDimensionsWithCodec = ({
21739
21847
  width,
@@ -21794,6 +21902,16 @@ var prespawnFfmpeg = (options2) => {
21794
21902
  const pixelFormat = options2.pixelFormat ?? DEFAULT_PIXEL_FORMAT;
21795
21903
  const proResProfileName = getProResProfileName(codec, options2.proResProfile);
21796
21904
  validateSelectedPixelFormatAndCodecCombination(pixelFormat, codec);
21905
+ const resolvedHardwareAcceleration = resolveHardwareAcceleration({
21906
+ codec,
21907
+ hardwareAcceleration: options2.hardwareAcceleration,
21908
+ binariesDirectory: options2.binariesDirectory,
21909
+ indent: options2.indent,
21910
+ logLevel: options2.logLevel,
21911
+ crf: options2.crf,
21912
+ encodingMaxRate: options2.encodingMaxRate,
21913
+ encodingBufferSize: options2.encodingBufferSize
21914
+ });
21797
21915
  const ffmpegArgs = [
21798
21916
  ["-r", options2.fps],
21799
21917
  ...[
@@ -21814,7 +21932,7 @@ var prespawnFfmpeg = (options2) => {
21814
21932
  encodingMaxRate: options2.encodingMaxRate,
21815
21933
  encodingBufferSize: options2.encodingBufferSize,
21816
21934
  colorSpace: options2.colorSpace,
21817
- hardwareAcceleration: options2.hardwareAcceleration,
21935
+ hardwareAcceleration: resolvedHardwareAcceleration,
21818
21936
  indent: options2.indent,
21819
21937
  logLevel: options2.logLevel
21820
21938
  }),
@@ -21916,7 +22034,7 @@ var validateSelectedCodecAndProResCombination = ({
21916
22034
 
21917
22035
  // src/stitch-frames-to-video.ts
21918
22036
  import { cpSync as cpSync2, promises as promises3, rmSync as rmSync4 } from "node:fs";
21919
- import path26 from "node:path";
22037
+ import path27 from "node:path";
21920
22038
 
21921
22039
  // src/convert-number-of-gif-loops-to-ffmpeg.ts
21922
22040
  var convertNumberOfGifLoopsToFfmpegSyntax = (loops) => {
@@ -21930,7 +22048,7 @@ var convertNumberOfGifLoopsToFfmpegSyntax = (loops) => {
21930
22048
  };
21931
22049
 
21932
22050
  // src/create-audio.ts
21933
- import path25 from "path";
22051
+ import path26 from "path";
21934
22052
 
21935
22053
  // src/resolve-asset-src.ts
21936
22054
  import url from "node:url";
@@ -22131,7 +22249,7 @@ var compressAudio = async ({
22131
22249
  };
22132
22250
 
22133
22251
  // src/merge-audio-track.ts
22134
- import path24 from "node:path";
22252
+ import path25 from "node:path";
22135
22253
 
22136
22254
  // src/chunk.ts
22137
22255
  var chunk2 = (input, size) => {
@@ -22163,7 +22281,7 @@ var createFfmpegMergeFilter = ({
22163
22281
 
22164
22282
  // src/ffmpeg-filter-file.ts
22165
22283
  import fs17, { existsSync as existsSync5 } from "node:fs";
22166
- import path23 from "node:path";
22284
+ import path24 from "node:path";
22167
22285
  var makeFfmpegFilterFile = (complexFilter, downloadMap) => {
22168
22286
  if (complexFilter.filter === null) {
22169
22287
  return {
@@ -22177,7 +22295,7 @@ var makeFfmpegFilterFile = (complexFilter, downloadMap) => {
22177
22295
  };
22178
22296
  var makeFfmpegFilterFileStr = async (complexFilter, downloadMap) => {
22179
22297
  const random2 = Math.random().toString().replace(".", "");
22180
- const filterFile = path23.join(downloadMap.complexFilter, "complex-filter-" + random2 + ".txt");
22298
+ const filterFile = path24.join(downloadMap.complexFilter, "complex-filter-" + random2 + ".txt");
22181
22299
  if (!existsSync5(downloadMap.complexFilter)) {
22182
22300
  fs17.mkdirSync(downloadMap.complexFilter, { recursive: true });
22183
22301
  }
@@ -22287,7 +22405,7 @@ var mergeAudioTrackUnlimited = async ({
22287
22405
  onProgress(combinedProgress);
22288
22406
  };
22289
22407
  const chunkNames = await Promise.all(chunked.map(async (chunkFiles, i) => {
22290
- const chunkOutname = path24.join(tempPath, `chunk-${i}.wav`);
22408
+ const chunkOutname = path25.join(tempPath, `chunk-${i}.wav`);
22291
22409
  await mergeAudioTrack({
22292
22410
  files: chunkFiles,
22293
22411
  chunkLengthInSeconds,
@@ -22749,7 +22867,7 @@ var createAudio = async ({
22749
22867
  onProgress(totalProgress);
22750
22868
  };
22751
22869
  const audioTracks = await Promise.all(assetPositions.map(async (asset, index) => {
22752
- const filterFile = path25.join(downloadMap.audioMixing, `${index}.wav`);
22870
+ const filterFile = path26.join(downloadMap.audioMixing, `${index}.wav`);
22753
22871
  const result = await preprocessAudioTrack({
22754
22872
  outName: filterFile,
22755
22873
  asset,
@@ -22793,9 +22911,9 @@ var createAudio = async ({
22793
22911
  }
22794
22912
  }))
22795
22913
  ];
22796
- const merged = path25.join(downloadMap.audioPreprocessing, "merged.wav");
22914
+ const merged = path26.join(downloadMap.audioPreprocessing, "merged.wav");
22797
22915
  const extension = getExtensionFromAudioCodec(audioCodec);
22798
- const outName = path25.join(downloadMap.audioPreprocessing, `audio.${extension}`);
22916
+ const outName = path26.join(downloadMap.audioPreprocessing, `audio.${extension}`);
22799
22917
  await mergeAudioTrack({
22800
22918
  files: preprocessed,
22801
22919
  outName: merged,
@@ -22975,7 +23093,7 @@ var innerStitchFramesToVideo = async ({
22975
23093
  setting: audioCodecSetting,
22976
23094
  separateAudioTo
22977
23095
  });
22978
- const tempFile = outputLocation ? null : path26.join(assetsInfo.downloadMap.stitchFrames, `out.${getFileExtensionFromCodec(codec, resolvedAudioCodec)}`);
23096
+ const tempFile = outputLocation ? null : path27.join(assetsInfo.downloadMap.stitchFrames, `out.${getFileExtensionFromCodec(codec, resolvedAudioCodec)}`);
22979
23097
  Log.verbose({
22980
23098
  indent,
22981
23099
  logLevel,
@@ -23007,7 +23125,8 @@ var innerStitchFramesToVideo = async ({
23007
23125
  videoBitrate,
23008
23126
  encodingMaxRate: maxRate,
23009
23127
  encodingBufferSize: bufferSize,
23010
- hardwareAcceleration
23128
+ hardwareAcceleration,
23129
+ hardwareAccelerated: false
23011
23130
  });
23012
23131
  validateSelectedPixelFormatAndCodecCombination(pixelFormat, codec);
23013
23132
  const updateProgress = (muxProgress) => {
@@ -23050,7 +23169,7 @@ var innerStitchFramesToVideo = async ({
23050
23169
  }
23051
23170
  cpSync2(audio, outputLocation ?? tempFile);
23052
23171
  onProgress?.(Math.round(assetsInfo.chunkLengthInSeconds * fps));
23053
- deleteDirectory(path26.dirname(audio));
23172
+ deleteDirectory(path27.dirname(audio));
23054
23173
  const file = await new Promise((resolve2, reject) => {
23055
23174
  if (tempFile) {
23056
23175
  promises3.readFile(tempFile).then((f) => {
@@ -23064,6 +23183,16 @@ var innerStitchFramesToVideo = async ({
23064
23183
  assetsInfo.downloadMap.allowCleanup();
23065
23184
  return Promise.resolve(file);
23066
23185
  }
23186
+ const resolvedHardwareAcceleration = resolveHardwareAcceleration({
23187
+ codec,
23188
+ hardwareAcceleration,
23189
+ binariesDirectory,
23190
+ indent: indent ?? false,
23191
+ logLevel,
23192
+ crf,
23193
+ encodingMaxRate: maxRate,
23194
+ encodingBufferSize: bufferSize
23195
+ });
23067
23196
  const ffmpegArgs = [
23068
23197
  ...preEncodedFileLocation ? [["-i", preEncodedFileLocation]] : [
23069
23198
  ["-r", String(fps)],
@@ -23087,7 +23216,7 @@ var innerStitchFramesToVideo = async ({
23087
23216
  x264Preset,
23088
23217
  gopSize,
23089
23218
  colorSpace,
23090
- hardwareAcceleration,
23219
+ hardwareAcceleration: resolvedHardwareAcceleration,
23091
23220
  indent,
23092
23221
  logLevel
23093
23222
  }),
@@ -23140,7 +23269,7 @@ var innerStitchFramesToVideo = async ({
23140
23269
  if (!audio) {
23141
23270
  throw new Error(`\`separateAudioTo\` was set to ${JSON.stringify(separateAudioTo)}, but this render included no audio`);
23142
23271
  }
23143
- const finalDestination = path26.resolve(remotionRoot, separateAudioTo);
23272
+ const finalDestination = path27.resolve(remotionRoot, separateAudioTo);
23144
23273
  cpSync2(audio, finalDestination);
23145
23274
  rmSync4(audio);
23146
23275
  }
@@ -23448,7 +23577,8 @@ var internalRenderMediaRaw = ({
23448
23577
  videoBitrate,
23449
23578
  encodingMaxRate,
23450
23579
  encodingBufferSize,
23451
- hardwareAcceleration
23580
+ hardwareAcceleration,
23581
+ hardwareAccelerated: false
23452
23582
  });
23453
23583
  validateGopSize(gopSize);
23454
23584
  validateBitrate(audioBitrate, "audioBitrate");
@@ -23473,7 +23603,7 @@ var internalRenderMediaRaw = ({
23473
23603
  separateAudioTo
23474
23604
  });
23475
23605
  }
23476
- const absoluteOutputLocation = outputLocation ? path27.resolve(process.cwd(), outputLocation) : null;
23606
+ const absoluteOutputLocation = outputLocation ? path28.resolve(process.cwd(), outputLocation) : null;
23477
23607
  validateScale(scale);
23478
23608
  validateFfmpegOverride(ffmpegOverride);
23479
23609
  validateEveryNthFrame(everyNthFrame);
@@ -23539,8 +23669,8 @@ var internalRenderMediaRaw = ({
23539
23669
  }
23540
23670
  const imageFormat = isAudioCodec(codec) ? "none" : provisionalImageFormat ?? compositionWithPossibleUnevenDimensions.defaultVideoImageFormat ?? DEFAULT_VIDEO_IMAGE_FORMAT;
23541
23671
  validateSelectedPixelFormatAndImageFormatCombination(pixelFormat, imageFormat);
23542
- const workingDir = fs18.mkdtempSync(path27.join(os9.tmpdir(), "react-motion-render"));
23543
- const preEncodedFileLocation = parallelEncoding ? path27.join(workingDir, "pre-encode." + getFileExtensionFromCodec(codec, audioCodec)) : null;
23672
+ const workingDir = fs18.mkdtempSync(path28.join(os9.tmpdir(), "react-motion-render"));
23673
+ const preEncodedFileLocation = parallelEncoding ? path28.join(workingDir, "pre-encode." + getFileExtensionFromCodec(codec, audioCodec)) : null;
23544
23674
  if (onCtrlCExit && workingDir) {
23545
23675
  onCtrlCExit(`Delete ${workingDir}`, () => deleteDirectory(workingDir));
23546
23676
  }
@@ -23871,7 +24001,7 @@ var internalRenderMediaRaw = ({
23871
24001
  reject(err);
23872
24002
  }).finally(() => {
23873
24003
  if (preEncodedFileLocation !== null && fs18.existsSync(preEncodedFileLocation)) {
23874
- deleteDirectory(path27.dirname(preEncodedFileLocation));
24004
+ deleteDirectory(path28.dirname(preEncodedFileLocation));
23875
24005
  }
23876
24006
  if (workingDir && fs18.existsSync(workingDir)) {
23877
24007
  deleteDirectory(workingDir);
@@ -24045,7 +24175,7 @@ var renderMedia = ({
24045
24175
 
24046
24176
  // src/render-still.ts
24047
24177
  import fs19, { statSync as statSync2 } from "node:fs";
24048
- import path28 from "node:path";
24178
+ import path29 from "node:path";
24049
24179
  import { LicensingInternals as LicensingInternals2 } from "@remotion/licensing";
24050
24180
  import { NoReactInternals as NoReactInternals16 } from "remotion/no-react";
24051
24181
  var innerRenderStill = async ({
@@ -24096,7 +24226,7 @@ var innerRenderStill = async ({
24096
24226
  });
24097
24227
  validatePuppeteerTimeout(timeoutInMilliseconds);
24098
24228
  validateScale(scale);
24099
- output = typeof output === "string" ? path28.resolve(process.cwd(), output) : null;
24229
+ output = typeof output === "string" ? path29.resolve(process.cwd(), output) : null;
24100
24230
  validateJpegQuality(jpegQuality);
24101
24231
  if (output) {
24102
24232
  if (fs19.existsSync(output)) {
@@ -7,7 +7,7 @@ const logger_1 = require("./logger");
7
7
  const color_space_1 = require("./options/color-space");
8
8
  const gop_size_1 = require("./options/gop-size");
9
9
  const truthy_1 = require("./truthy");
10
- const firstEncodingStepOnly = ({ hasPreencoded, proResProfileName, pixelFormat, x264Preset, gopSize, codec, crf, videoBitrate, encodingMaxRate, encodingBufferSize, hardwareAcceleration, }) => {
10
+ const firstEncodingStepOnly = ({ hasPreencoded, proResProfileName, pixelFormat, x264Preset, gopSize, codec, crf, videoBitrate, encodingMaxRate, encodingBufferSize, hardwareAcceleration, hardwareAccelerated, }) => {
11
11
  if (hasPreencoded || codec === 'gif') {
12
12
  return [];
13
13
  }
@@ -30,6 +30,7 @@ const firstEncodingStepOnly = ({ hasPreencoded, proResProfileName, pixelFormat,
30
30
  encodingMaxRate,
31
31
  encodingBufferSize,
32
32
  hardwareAcceleration,
33
+ hardwareAccelerated,
33
34
  }),
34
35
  ].filter(truthy_1.truthy);
35
36
  };
@@ -97,6 +98,7 @@ const generateFfmpegArgs = ({ hasPreencoded, proResProfileName, pixelFormat, x26
97
98
  x264Preset,
98
99
  gopSize,
99
100
  hardwareAcceleration,
101
+ hardwareAccelerated,
100
102
  }),
101
103
  ].filter(truthy_1.truthy);
102
104
  };
@@ -37,6 +37,13 @@ const getCodecName = ({ codec, encodingMaxRate, encodingBufferSize, crf, hardwar
37
37
  !unsupportedQualityOption) {
38
38
  return { encoderName: 'prores_videotoolbox', hardwareAccelerated: true };
39
39
  }
40
+ if (preferredHwAcceleration &&
41
+ process.platform !== 'darwin' &&
42
+ !unsupportedQualityOption) {
43
+ if (hardwareAcceleration === 'required') {
44
+ throw new Error(`Codec "prores" does not support hardware acceleration on ${process.platform}, but "hardwareAcceleration" is set to "required"`);
45
+ }
46
+ }
40
47
  warnAboutDisabledHardwareAcceleration();
41
48
  return { encoderName: 'prores_ks', hardwareAccelerated: false };
42
49
  }
@@ -46,6 +53,12 @@ const getCodecName = ({ codec, encodingMaxRate, encodingBufferSize, crf, hardwar
46
53
  !unsupportedQualityOption) {
47
54
  return { encoderName: 'h264_videotoolbox', hardwareAccelerated: true };
48
55
  }
56
+ // NVENC for Linux/Windows
57
+ if (preferredHwAcceleration &&
58
+ (process.platform === 'linux' || process.platform === 'win32') &&
59
+ !unsupportedQualityOption) {
60
+ return { encoderName: 'h264_nvenc', hardwareAccelerated: true };
61
+ }
49
62
  warnAboutDisabledHardwareAcceleration();
50
63
  return { encoderName: 'libx264', hardwareAccelerated: false };
51
64
  }
@@ -55,6 +68,12 @@ const getCodecName = ({ codec, encodingMaxRate, encodingBufferSize, crf, hardwar
55
68
  !unsupportedQualityOption) {
56
69
  return { encoderName: 'hevc_videotoolbox', hardwareAccelerated: true };
57
70
  }
71
+ // NVENC for Linux/Windows
72
+ if (preferredHwAcceleration &&
73
+ (process.platform === 'linux' || process.platform === 'win32') &&
74
+ !unsupportedQualityOption) {
75
+ return { encoderName: 'hevc_nvenc', hardwareAccelerated: true };
76
+ }
58
77
  warnAboutDisabledHardwareAcceleration();
59
78
  return { encoderName: 'libx265', hardwareAccelerated: false };
60
79
  }
@@ -81,9 +100,15 @@ const getCodecName = ({ codec, encodingMaxRate, encodingBufferSize, crf, hardwar
81
100
  return null;
82
101
  }
83
102
  if (codec === 'h264-mkv') {
103
+ if (hardwareAcceleration === 'required') {
104
+ throw new Error(`Codec "h264-mkv" does not support hardware acceleration on ${process.platform}, but "hardwareAcceleration" is set to "required"`);
105
+ }
84
106
  return { encoderName: 'libx264', hardwareAccelerated: false };
85
107
  }
86
108
  if (codec === 'h264-ts') {
109
+ if (hardwareAcceleration === 'required') {
110
+ throw new Error(`Codec "h264-ts" does not support hardware acceleration on ${process.platform}, but "hardwareAcceleration" is set to "required"`);
111
+ }
87
112
  return { encoderName: 'libx264', hardwareAccelerated: false };
88
113
  }
89
114
  throw new Error(`Could not get codec for ${codec}`);
@@ -574,7 +574,10 @@ export declare const allOptions: {
574
574
  type: "yuv420p" | "yuv420p10le" | "yuv422p" | "yuv422p10le" | "yuv444p" | "yuv444p10le" | "yuva420p" | "yuva444p10le";
575
575
  getValue: ({ commandLine }: {
576
576
  commandLine: Record<string, unknown>;
577
- }) => {
577
+ }, options?: {
578
+ uiPixelFormat: "yuv420p" | "yuv420p10le" | "yuv422p" | "yuv422p10le" | "yuv444p" | "yuv444p10le" | "yuva420p" | "yuva444p10le" | null;
579
+ compositionDefaultPixelFormat: "yuv420p" | "yuv420p10le" | "yuv422p" | "yuv422p10le" | "yuv444p" | "yuv444p10le" | "yuva420p" | "yuva444p10le" | null;
580
+ } | undefined) => {
578
581
  source: string;
579
582
  value: "yuv420p" | "yuv420p10le" | "yuv422p" | "yuv422p10le" | "yuv444p" | "yuv444p10le" | "yuva420p" | "yuva444p10le";
580
583
  };
@@ -628,7 +631,10 @@ export declare const allOptions: {
628
631
  type: import("./prores-profile").ProResProfile | undefined;
629
632
  getValue: ({ commandLine }: {
630
633
  commandLine: Record<string, unknown>;
631
- }) => {
634
+ }, options?: {
635
+ uiProResProfile: import("./prores-profile").ProResProfile | undefined;
636
+ compositionDefaultProResProfile: import("./prores-profile").ProResProfile | null;
637
+ } | undefined) => {
632
638
  source: string;
633
639
  value: import("./prores-profile").ProResProfile;
634
640
  } | {
@@ -1231,7 +1237,11 @@ export declare const allOptions: {
1231
1237
  type: "jpeg" | "none" | "png" | null;
1232
1238
  getValue: ({ commandLine }: {
1233
1239
  commandLine: Record<string, unknown>;
1234
- }) => {
1240
+ }, options?: {
1241
+ codec: import("..").CodecOrUndefined;
1242
+ uiVideoImageFormat: "jpeg" | "none" | "png" | null;
1243
+ compositionDefaultVideoImageFormat: "jpeg" | "none" | "png" | null;
1244
+ } | undefined) => {
1235
1245
  source: string;
1236
1246
  value: "jpeg" | "none" | "png";
1237
1247
  } | {
@@ -7,7 +7,10 @@ export declare const pixelFormatOption: {
7
7
  type: "yuv420p" | "yuv420p10le" | "yuv422p" | "yuv422p10le" | "yuv444p" | "yuv444p10le" | "yuva420p" | "yuva444p10le";
8
8
  getValue: ({ commandLine }: {
9
9
  commandLine: Record<string, unknown>;
10
- }) => {
10
+ }, options?: {
11
+ uiPixelFormat: "yuv420p" | "yuv420p10le" | "yuv422p" | "yuv422p10le" | "yuv444p" | "yuv444p10le" | "yuva420p" | "yuva444p10le" | null;
12
+ compositionDefaultPixelFormat: "yuv420p" | "yuv420p10le" | "yuv422p" | "yuv422p10le" | "yuv444p" | "yuv444p10le" | "yuva420p" | "yuva444p10le" | null;
13
+ } | undefined) => {
11
14
  source: string;
12
15
  value: "yuv420p" | "yuv420p10le" | "yuv422p" | "yuv422p10le" | "yuv444p" | "yuv444p10le" | "yuva420p" | "yuva444p10le";
13
16
  };
@@ -13,16 +13,28 @@ exports.pixelFormatOption = {
13
13
  ssrName: 'pixelFormat',
14
14
  docLink: 'https://www.remotion.dev/docs/config#setpixelformat',
15
15
  type: pixel_format_1.DEFAULT_PIXEL_FORMAT,
16
- getValue: ({ commandLine }) => {
16
+ getValue: ({ commandLine }, options) => {
17
+ if (options === null || options === void 0 ? void 0 : options.uiPixelFormat) {
18
+ return {
19
+ source: 'via UI',
20
+ value: options.uiPixelFormat,
21
+ };
22
+ }
17
23
  if (commandLine[cliFlag] !== undefined) {
18
24
  return {
19
- source: 'cli',
25
+ source: 'from --pixel-format flag',
20
26
  value: commandLine[cliFlag],
21
27
  };
22
28
  }
29
+ if (options && options.compositionDefaultPixelFormat !== null) {
30
+ return {
31
+ source: 'via calculateMetadata',
32
+ value: options.compositionDefaultPixelFormat,
33
+ };
34
+ }
23
35
  if (currentPixelFormat !== pixel_format_1.DEFAULT_PIXEL_FORMAT) {
24
36
  return {
25
- source: 'config',
37
+ source: 'Config file',
26
38
  value: currentPixelFormat,
27
39
  };
28
40
  }
@@ -8,7 +8,10 @@ export declare const proResProfileOption: {
8
8
  type: ProResProfile | undefined;
9
9
  getValue: ({ commandLine }: {
10
10
  commandLine: Record<string, unknown>;
11
- }) => {
11
+ }, options?: {
12
+ uiProResProfile: ProResProfile | undefined;
13
+ compositionDefaultProResProfile: ProResProfile | null;
14
+ } | undefined) => {
12
15
  source: string;
13
16
  value: ProResProfile;
14
17
  } | {
@@ -23,16 +23,28 @@ exports.proResProfileOption = {
23
23
  ssrName: 'proResProfile',
24
24
  docLink: 'https://www.remotion.dev/docs/config#setproresprofile',
25
25
  type: undefined,
26
- getValue: ({ commandLine }) => {
26
+ getValue: ({ commandLine }, options) => {
27
+ if (options === null || options === void 0 ? void 0 : options.uiProResProfile) {
28
+ return {
29
+ source: 'via UI',
30
+ value: options.uiProResProfile,
31
+ };
32
+ }
27
33
  if (commandLine[cliFlag] !== undefined) {
28
34
  return {
29
- source: 'cli',
35
+ source: 'from --prores-profile flag',
30
36
  value: String(commandLine[cliFlag]),
31
37
  };
32
38
  }
39
+ if (options && options.compositionDefaultProResProfile !== null) {
40
+ return {
41
+ source: 'via calculateMetadata',
42
+ value: options.compositionDefaultProResProfile,
43
+ };
44
+ }
33
45
  if (proResProfile !== undefined) {
34
46
  return {
35
- source: 'config',
47
+ source: 'Config file',
36
48
  value: proResProfile,
37
49
  };
38
50
  }
@@ -1,3 +1,4 @@
1
+ import type { CodecOrUndefined } from '../codec';
1
2
  export declare const videoImageFormatOption: {
2
3
  name: string;
3
4
  cliFlag: "image-format";
@@ -7,7 +8,11 @@ export declare const videoImageFormatOption: {
7
8
  type: "jpeg" | "none" | "png" | null;
8
9
  getValue: ({ commandLine }: {
9
10
  commandLine: Record<string, unknown>;
10
- }) => {
11
+ }, options?: {
12
+ codec: CodecOrUndefined;
13
+ uiVideoImageFormat: "jpeg" | "none" | "png" | null;
14
+ compositionDefaultVideoImageFormat: "jpeg" | "none" | "png" | null;
15
+ } | undefined) => {
11
16
  source: string;
12
17
  value: "jpeg" | "none" | "png";
13
18
  } | {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.videoImageFormatOption = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const image_format_1 = require("../image-format");
6
+ const is_audio_codec_1 = require("../is-audio-codec");
6
7
  let currentVideoImageFormat = null;
7
8
  const cliFlag = 'image-format';
8
9
  exports.videoImageFormatOption = {
@@ -13,23 +14,64 @@ exports.videoImageFormatOption = {
13
14
  ssrName: 'imageFormat',
14
15
  docLink: 'https://www.remotion.dev/docs/renderer/render-media#imageformat',
15
16
  type: null,
16
- getValue: ({ commandLine }) => {
17
+ getValue: ({ commandLine }, options) => {
18
+ if (options === null || options === void 0 ? void 0 : options.uiVideoImageFormat) {
19
+ return {
20
+ source: 'via UI',
21
+ value: options.uiVideoImageFormat,
22
+ };
23
+ }
17
24
  if (commandLine[cliFlag] !== undefined) {
18
25
  const value = commandLine[cliFlag];
19
26
  if (!image_format_1.validVideoImageFormats.includes(value)) {
20
27
  throw new Error(`Invalid video image format: ${value}. Must be one of: ${image_format_1.validVideoImageFormats.join(', ')}`);
21
28
  }
22
29
  return {
23
- source: 'cli',
30
+ source: 'from --image-format flag',
24
31
  value,
25
32
  };
26
33
  }
34
+ if (options && options.compositionDefaultVideoImageFormat !== null) {
35
+ return {
36
+ source: 'via calculateMetadata',
37
+ value: options.compositionDefaultVideoImageFormat,
38
+ };
39
+ }
27
40
  if (currentVideoImageFormat !== null) {
28
41
  return {
29
- source: 'config',
42
+ source: 'Config file',
30
43
  value: currentVideoImageFormat,
31
44
  };
32
45
  }
46
+ if (options) {
47
+ if ((0, is_audio_codec_1.isAudioCodec)(options.codec)) {
48
+ return {
49
+ source: 'default',
50
+ value: 'none',
51
+ };
52
+ }
53
+ if (options.codec === 'h264' ||
54
+ options.codec === 'h264-mkv' ||
55
+ options.codec === 'h264-ts' ||
56
+ options.codec === 'h265' ||
57
+ options.codec === 'av1' ||
58
+ options.codec === 'vp8' ||
59
+ options.codec === 'vp9' ||
60
+ options.codec === 'prores' ||
61
+ options.codec === 'gif') {
62
+ return {
63
+ source: 'default',
64
+ value: 'jpeg',
65
+ };
66
+ }
67
+ if (options.codec === undefined) {
68
+ return {
69
+ source: 'default',
70
+ value: 'png',
71
+ };
72
+ }
73
+ throw new Error('Unrecognized codec ' + options.codec);
74
+ }
33
75
  return {
34
76
  source: 'default',
35
77
  value: null,
@@ -8,6 +8,7 @@ const get_prores_profile_name_1 = require("./get-prores-profile-name");
8
8
  const logger_1 = require("./logger");
9
9
  const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
10
10
  const pixel_format_1 = require("./pixel-format");
11
+ const probe_encoder_1 = require("./probe-encoder");
11
12
  const validate_1 = require("./validate");
12
13
  const validate_even_dimensions_with_codec_1 = require("./validate-even-dimensions-with-codec");
13
14
  const prespawnFfmpeg = (options) => {
@@ -29,6 +30,16 @@ const prespawnFfmpeg = (options) => {
29
30
  const pixelFormat = (_c = options.pixelFormat) !== null && _c !== void 0 ? _c : pixel_format_1.DEFAULT_PIXEL_FORMAT;
30
31
  const proResProfileName = (0, get_prores_profile_name_1.getProResProfileName)(codec, options.proResProfile);
31
32
  (0, pixel_format_1.validateSelectedPixelFormatAndCodecCombination)(pixelFormat, codec);
33
+ const resolvedHardwareAcceleration = (0, probe_encoder_1.resolveHardwareAcceleration)({
34
+ codec,
35
+ hardwareAcceleration: options.hardwareAcceleration,
36
+ binariesDirectory: options.binariesDirectory,
37
+ indent: options.indent,
38
+ logLevel: options.logLevel,
39
+ crf: options.crf,
40
+ encodingMaxRate: options.encodingMaxRate,
41
+ encodingBufferSize: options.encodingBufferSize,
42
+ });
32
43
  const ffmpegArgs = [
33
44
  ['-r', options.fps],
34
45
  ...[
@@ -51,7 +62,7 @@ const prespawnFfmpeg = (options) => {
51
62
  encodingMaxRate: options.encodingMaxRate,
52
63
  encodingBufferSize: options.encodingBufferSize,
53
64
  colorSpace: options.colorSpace,
54
- hardwareAcceleration: options.hardwareAcceleration,
65
+ hardwareAcceleration: resolvedHardwareAcceleration,
55
66
  indent: options.indent,
56
67
  logLevel: options.logLevel,
57
68
  }),
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Probes FFmpeg to check if a specific encoder is available.
3
+ * Returns true if the encoder is supported, false otherwise.
4
+ */
5
+ export declare const probeEncoderAvailability: ({ encoderName, binariesDirectory, indent, logLevel, }: {
6
+ encoderName: string;
7
+ binariesDirectory: string | null;
8
+ indent: boolean;
9
+ logLevel: "error" | "info" | "trace" | "verbose" | "warn";
10
+ }) => boolean;
11
+ /**
12
+ * Resolves the effective hardware acceleration setting by probing FFmpeg
13
+ * for encoder availability. If the preferred hw encoder is not available:
14
+ * - `if-possible` mode: falls back to software encoding
15
+ * - `required` mode: throws a clear error
16
+ */
17
+ export declare const resolveHardwareAcceleration: ({ codec, hardwareAcceleration, binariesDirectory, indent, logLevel, crf, encodingMaxRate, encodingBufferSize, }: {
18
+ codec: "aac" | "av1" | "gif" | "h264" | "h264-mkv" | "h264-ts" | "h265" | "mp3" | "prores" | "vp8" | "vp9" | "wav";
19
+ hardwareAcceleration: "disable" | "if-possible" | "required";
20
+ binariesDirectory: string | null;
21
+ indent: boolean;
22
+ logLevel: "error" | "info" | "trace" | "verbose" | "warn";
23
+ crf: unknown;
24
+ encodingMaxRate: string | null;
25
+ encodingBufferSize: string | null;
26
+ }) => "disable" | "if-possible" | "required";
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveHardwareAcceleration = exports.probeEncoderAvailability = void 0;
7
+ const node_child_process_1 = require("node:child_process");
8
+ const path_1 = __importDefault(require("path"));
9
+ const get_executable_path_1 = require("./compositor/get-executable-path");
10
+ const get_explicit_env_1 = require("./compositor/get-explicit-env");
11
+ const make_file_executable_1 = require("./compositor/make-file-executable");
12
+ const get_codec_name_1 = require("./get-codec-name");
13
+ const logger_1 = require("./logger");
14
+ /**
15
+ * Probes FFmpeg to check if a specific encoder is available.
16
+ * Returns true if the encoder is supported, false otherwise.
17
+ */
18
+ const probeEncoderAvailability = ({ encoderName, binariesDirectory, indent, logLevel, }) => {
19
+ try {
20
+ const executablePath = (0, get_executable_path_1.getExecutablePath)({
21
+ type: 'ffmpeg',
22
+ indent,
23
+ logLevel,
24
+ binariesDirectory,
25
+ });
26
+ (0, make_file_executable_1.makeFileExecutableIfItIsNot)(executablePath);
27
+ const cwd = path_1.default.dirname(executablePath);
28
+ const result = (0, node_child_process_1.execFileSync)(executablePath, ['-encoders'], {
29
+ encoding: 'utf-8',
30
+ env: (0, get_explicit_env_1.getExplicitEnv)(cwd),
31
+ stdio: ['pipe', 'pipe', 'pipe'],
32
+ timeout: 10000,
33
+ });
34
+ // FFmpeg -encoders output format: " V..... encoder_name"
35
+ // Escape regex metacharacters and use word-boundary to avoid false positives
36
+ const escaped = encoderName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
37
+ const encoderRegex = new RegExp(`\\b${escaped}\\b`);
38
+ const hasEncoder = encoderRegex.test(result);
39
+ if (!hasEncoder) {
40
+ logger_1.Log.verbose({ indent, logLevel, tag: 'probeEncoderAvailability()' }, `Encoder "${encoderName}" not found in FFmpeg build. Falling back to software encoding.`);
41
+ }
42
+ return hasEncoder;
43
+ }
44
+ catch (err) {
45
+ logger_1.Log.verbose({ indent, logLevel, tag: 'probeEncoderAvailability()' }, `Failed to probe FFmpeg for encoder "${encoderName}": ${err}. Falling back to software encoding.`);
46
+ return false;
47
+ }
48
+ };
49
+ exports.probeEncoderAvailability = probeEncoderAvailability;
50
+ /**
51
+ * Resolves the effective hardware acceleration setting by probing FFmpeg
52
+ * for encoder availability. If the preferred hw encoder is not available:
53
+ * - `if-possible` mode: falls back to software encoding
54
+ * - `required` mode: throws a clear error
55
+ */
56
+ const resolveHardwareAcceleration = ({ codec, hardwareAcceleration, binariesDirectory, indent, logLevel, crf, encodingMaxRate, encodingBufferSize, }) => {
57
+ if (hardwareAcceleration === 'disable') {
58
+ return 'disable';
59
+ }
60
+ const preferred = (0, get_codec_name_1.getCodecName)({
61
+ codec,
62
+ hardwareAcceleration,
63
+ crf,
64
+ encodingMaxRate,
65
+ encodingBufferSize,
66
+ logLevel,
67
+ indent,
68
+ });
69
+ // Audio codecs return null, no probing needed
70
+ if (preferred === null) {
71
+ return hardwareAcceleration;
72
+ }
73
+ // Only probe if getCodecName selected a hardware-accelerated encoder
74
+ if (!preferred.hardwareAccelerated) {
75
+ return hardwareAcceleration;
76
+ }
77
+ const encoderAvailable = (0, exports.probeEncoderAvailability)({
78
+ encoderName: preferred.encoderName,
79
+ binariesDirectory,
80
+ indent,
81
+ logLevel,
82
+ });
83
+ if (encoderAvailable) {
84
+ return hardwareAcceleration;
85
+ }
86
+ // Encoder not available in FFmpeg
87
+ if (hardwareAcceleration === 'if-possible') {
88
+ logger_1.Log.verbose({ indent, logLevel, tag: 'resolveHardwareAcceleration()' }, `Hardware encoder "${preferred.encoderName}" not available. Falling back to software encoding.`);
89
+ return 'disable';
90
+ }
91
+ // hardwareAcceleration === 'required'
92
+ throw new Error(`Hardware encoder "${preferred.encoderName}" is not available in your FFmpeg build. ` +
93
+ `Install an FFmpeg with ${preferred.encoderName} support, or use "if-possible" mode instead of "required".`);
94
+ };
95
+ exports.resolveHardwareAcceleration = resolveHardwareAcceleration;
@@ -78,6 +78,7 @@ const internalRenderMediaRaw = ({ proResProfile, x264Preset, gopSize, crf, compo
78
78
  encodingMaxRate,
79
79
  encodingBufferSize,
80
80
  hardwareAcceleration,
81
+ hardwareAccelerated: false,
81
82
  });
82
83
  (0, gop_size_1.validateGopSize)(gopSize);
83
84
  (0, validate_videobitrate_1.validateBitrate)(audioBitrate, 'audioBitrate');
@@ -26,6 +26,7 @@ const color_space_1 = require("./options/color-space");
26
26
  const gop_size_1 = require("./options/gop-size");
27
27
  const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
28
28
  const pixel_format_1 = require("./pixel-format");
29
+ const probe_encoder_1 = require("./probe-encoder");
29
30
  const prores_profile_1 = require("./prores-profile");
30
31
  const render_has_audio_1 = require("./render-has-audio");
31
32
  const validate_1 = require("./validate");
@@ -113,6 +114,7 @@ const innerStitchFramesToVideo = async ({ assetsInfo, audioBitrate, audioCodec:
113
114
  encodingMaxRate: maxRate,
114
115
  encodingBufferSize: bufferSize,
115
116
  hardwareAcceleration,
117
+ hardwareAccelerated: false,
116
118
  });
117
119
  (0, pixel_format_1.validateSelectedPixelFormatAndCodecCombination)(pixelFormat, codec);
118
120
  const updateProgress = (muxProgress) => {
@@ -176,6 +178,16 @@ const innerStitchFramesToVideo = async ({ assetsInfo, audioBitrate, audioCodec:
176
178
  assetsInfo.downloadMap.allowCleanup();
177
179
  return Promise.resolve(file);
178
180
  }
181
+ const resolvedHardwareAcceleration = (0, probe_encoder_1.resolveHardwareAcceleration)({
182
+ codec,
183
+ hardwareAcceleration,
184
+ binariesDirectory,
185
+ indent: indent !== null && indent !== void 0 ? indent : false,
186
+ logLevel,
187
+ crf,
188
+ encodingMaxRate: maxRate,
189
+ encodingBufferSize: bufferSize,
190
+ });
179
191
  const ffmpegArgs = [
180
192
  ...(preEncodedFileLocation
181
193
  ? [['-i', preEncodedFileLocation]]
@@ -205,7 +217,7 @@ const innerStitchFramesToVideo = async ({ assetsInfo, audioBitrate, audioCodec:
205
217
  x264Preset,
206
218
  gopSize,
207
219
  colorSpace,
208
- hardwareAcceleration,
220
+ hardwareAcceleration: resolvedHardwareAcceleration,
209
221
  indent,
210
222
  logLevel,
211
223
  }),
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/renderer"
4
4
  },
5
5
  "name": "@remotion/renderer",
6
- "version": "4.0.482",
6
+ "version": "4.0.484",
7
7
  "description": "Render Remotion videos using Node.js or Bun",
8
8
  "main": "dist/index.js",
9
9
  "types": "dist/index.d.ts",
@@ -22,11 +22,11 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "execa": "5.1.1",
25
- "remotion": "4.0.482",
26
- "@remotion/streaming": "4.0.482",
25
+ "remotion": "4.0.484",
26
+ "@remotion/streaming": "4.0.484",
27
27
  "source-map": "0.8.0-beta.0",
28
28
  "ws": "8.21.0",
29
- "@remotion/licensing": "4.0.482"
29
+ "@remotion/licensing": "4.0.484"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "react": ">=16.8.0",
@@ -40,19 +40,19 @@
40
40
  "react-dom": "19.2.3",
41
41
  "@typescript/native-preview": "7.0.0-dev.20260217.1",
42
42
  "@types/ws": "8.5.10",
43
- "@remotion/example-videos": "4.0.482",
44
- "@remotion/eslint-config-internal": "4.0.482",
43
+ "@remotion/example-videos": "4.0.484",
44
+ "@remotion/eslint-config-internal": "4.0.484",
45
45
  "eslint": "9.19.0",
46
46
  "@types/node": "20.12.14"
47
47
  },
48
48
  "optionalDependencies": {
49
- "@remotion/compositor-darwin-arm64": "4.0.482",
50
- "@remotion/compositor-darwin-x64": "4.0.482",
51
- "@remotion/compositor-linux-arm64-gnu": "4.0.482",
52
- "@remotion/compositor-linux-arm64-musl": "4.0.482",
53
- "@remotion/compositor-linux-x64-gnu": "4.0.482",
54
- "@remotion/compositor-linux-x64-musl": "4.0.482",
55
- "@remotion/compositor-win32-x64-msvc": "4.0.482"
49
+ "@remotion/compositor-darwin-arm64": "4.0.484",
50
+ "@remotion/compositor-darwin-x64": "4.0.484",
51
+ "@remotion/compositor-linux-arm64-gnu": "4.0.484",
52
+ "@remotion/compositor-linux-arm64-musl": "4.0.484",
53
+ "@remotion/compositor-linux-x64-gnu": "4.0.484",
54
+ "@remotion/compositor-linux-x64-musl": "4.0.484",
55
+ "@remotion/compositor-win32-x64-msvc": "4.0.484"
56
56
  },
57
57
  "keywords": [
58
58
  "remotion",