@remotion/renderer 4.0.339 → 4.0.341

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/LICENSE.md CHANGED
@@ -6,10 +6,10 @@ In Remotion 5.0, the license will slightly change. [View the changes here](https
6
6
 
7
7
  Depending on the type of your legal entity, you are granted permission to use Remotion for your project. Individuals and small companies are allowed to use Remotion to create videos for free (even commercial), while a company license is required for for-profit organizations of a certain size. This two-tier system was designed to ensure funding for this project while still allowing the source code to be available and the program to be free for most. Read below for the exact terms of use.
8
8
 
9
- - [Free license](#free-license)
10
- - [Company license](#company-license)
9
+ - [Free License](#free-license)
10
+ - [Company License](#company-license)
11
11
 
12
- ## Free license
12
+ ## Free License
13
13
 
14
14
  Copyright © 2025 [Remotion](https://www.remotion.dev)
15
15
 
@@ -24,7 +24,7 @@ You are eligible to use Remotion for free if you are:
24
24
 
25
25
  ### Allowed use cases
26
26
 
27
- Permission is hereby granted, free of charge, to any person eligible for the "Free license", to use the software non-commercially or commercially for the purpose of creating videos and images and to modify the software to their own liking, for the purpose of fulfilling their custom use case or to contribute bug fixes or improvements back to Remotion.
27
+ Permission is hereby granted, free of charge, to any person eligible for the "Free License", to use the software non-commercially or commercially for the purpose of creating videos and images and to modify the software to their own liking, for the purpose of fulfilling their custom use case or to contribute bug fixes or improvements back to Remotion.
28
28
 
29
29
  ### Disallowed use cases
30
30
 
@@ -38,12 +38,12 @@ The software is provided "as is", without warranty of any kind, express or impli
38
38
 
39
39
  Support is provided on a best-we-can-do basis via GitHub Issues and Discord.
40
40
 
41
- ## Company license
41
+ ## Company License
42
42
 
43
- You are required to obtain a company license to use Remotion if you are not within the group of entities eligible for a free license. This license will enable you to use Remotion for the allowed use cases specified in the free license, and give you access to prioritized support (read the [Support Policy](https://www.remotion.dev/docs/support)).
43
+ You are required to obtain a Company License to use Remotion if you are not within the group of entities eligible for a Free License. This license will enable you to use Remotion for the allowed use cases specified in the Free License, and give you access to prioritized support (read the [Support Policy](https://www.remotion.dev/docs/support)).
44
44
 
45
45
  Visit [remotion.pro](https://www.remotion.pro/license) for pricing and to buy a license.
46
46
 
47
47
  ### FAQs
48
48
 
49
- Are you not sure whether you need a company license because of an edge case? Here are some [frequently asked questions](https://www.remotion.pro/faq).
49
+ Are you not sure whether you need a Company License because of an edge case? Here are some [frequently asked questions](https://www.remotion.pro/faq).
@@ -47,6 +47,7 @@ const calculateAssetPositions = (frames) => {
47
47
  playbackRate: asset.playbackRate,
48
48
  toneFrequency: asset.toneFrequency,
49
49
  audioStartFrame: asset.audioStartFrame,
50
+ audioStreamIndex: asset.audioStreamIndex,
50
51
  });
51
52
  }
52
53
  const found = assets.find((a) => a.duration === null && areEqual(a, asset));
@@ -7,7 +7,7 @@ export type RenderMediaOnDownload = (src: string) => ((progress: {
7
7
  downloaded: number;
8
8
  totalSize: number | null;
9
9
  }) => void) | undefined | void;
10
- export declare const downloadAsset: ({ src, downloadMap, indent, logLevel, shouldAnalyzeAudioImmediately, binariesDirectory, cancelSignalForAudioAnalysis, }: {
10
+ export declare const downloadAsset: ({ src, downloadMap, indent, logLevel, shouldAnalyzeAudioImmediately, binariesDirectory, cancelSignalForAudioAnalysis, audioStreamIndex, }: {
11
11
  src: string;
12
12
  downloadMap: DownloadMap;
13
13
  indent: boolean;
@@ -15,6 +15,7 @@ export declare const downloadAsset: ({ src, downloadMap, indent, logLevel, shoul
15
15
  shouldAnalyzeAudioImmediately: boolean;
16
16
  binariesDirectory: string | null;
17
17
  cancelSignalForAudioAnalysis: CancelSignal | undefined;
18
+ audioStreamIndex: number | undefined;
18
19
  }) => Promise<string>;
19
20
  export declare const markAllAssetsAsDownloaded: (downloadMap: DownloadMap) => void;
20
21
  export declare const getSanitizedFilenameForAssetUrl: ({ src, downloadDir, contentDisposition, contentType, }: {
@@ -123,7 +123,7 @@ function validateBufferEncoding(potentialEncoding, dataUrl) {
123
123
  throw new TypeError(errMessage);
124
124
  }
125
125
  }
126
- const downloadAsset = async ({ src, downloadMap, indent, logLevel, shouldAnalyzeAudioImmediately, binariesDirectory, cancelSignalForAudioAnalysis, }) => {
126
+ const downloadAsset = async ({ src, downloadMap, indent, logLevel, shouldAnalyzeAudioImmediately, binariesDirectory, cancelSignalForAudioAnalysis, audioStreamIndex, }) => {
127
127
  var _a, _b, _c;
128
128
  if ((0, compress_assets_1.isAssetCompressed)(src)) {
129
129
  return src;
@@ -198,6 +198,7 @@ const downloadAsset = async ({ src, downloadMap, indent, logLevel, shouldAnalyze
198
198
  indent,
199
199
  logLevel,
200
200
  cancelSignal: cancelSignalForAudioAnalysis,
201
+ audioStreamIndex,
201
202
  });
202
203
  }
203
204
  return to;
@@ -266,6 +267,7 @@ const downloadAndMapAssetsToFileUrl = async ({ renderAsset, onDownload, download
266
267
  shouldAnalyzeAudioImmediately,
267
268
  binariesDirectory,
268
269
  cancelSignalForAudioAnalysis,
270
+ audioStreamIndex: renderAsset.audioStreamIndex,
269
271
  });
270
272
  cleanup();
271
273
  return {
@@ -1,18 +1,20 @@
1
1
  import type { LogLevel } from '../log-level';
2
2
  import type { CancelSignal } from '../make-cancel-signal';
3
3
  import type { AudioChannelsAndDurationResultCache, DownloadMap } from './download-map';
4
- export declare const getAudioChannelsAndDurationWithoutCache: ({ src, indent, logLevel, binariesDirectory, cancelSignal, }: {
4
+ export declare const getAudioChannelsAndDurationWithoutCache: ({ src, indent, logLevel, binariesDirectory, cancelSignal, audioStreamIndex, }: {
5
5
  src: string;
6
6
  indent: boolean;
7
7
  logLevel: LogLevel;
8
8
  binariesDirectory: string | null;
9
9
  cancelSignal: CancelSignal | undefined;
10
+ audioStreamIndex: number | undefined;
10
11
  }) => Promise<AudioChannelsAndDurationResultCache>;
11
- export declare const getAudioChannelsAndDuration: ({ downloadMap, src, indent, logLevel, binariesDirectory, cancelSignal, }: {
12
+ export declare const getAudioChannelsAndDuration: ({ downloadMap, src, indent, logLevel, binariesDirectory, cancelSignal, audioStreamIndex, }: {
12
13
  downloadMap: DownloadMap;
13
14
  src: string;
14
15
  indent: boolean;
15
16
  logLevel: LogLevel;
16
17
  binariesDirectory: string | null;
17
18
  cancelSignal: CancelSignal | undefined;
19
+ audioStreamIndex: number | undefined;
18
20
  }) => Promise<AudioChannelsAndDurationResultCache>;
@@ -4,10 +4,10 @@ exports.getAudioChannelsAndDuration = exports.getAudioChannelsAndDurationWithout
4
4
  const call_ffmpeg_1 = require("../call-ffmpeg");
5
5
  const p_limit_1 = require("../p-limit");
6
6
  const limit = (0, p_limit_1.pLimit)(1);
7
- const getAudioChannelsAndDurationWithoutCache = async ({ src, indent, logLevel, binariesDirectory, cancelSignal, }) => {
7
+ const getAudioChannelsAndDurationWithoutCache = async ({ src, indent, logLevel, binariesDirectory, cancelSignal, audioStreamIndex, }) => {
8
8
  const args = [
9
9
  ['-v', 'error'],
10
- ['-select_streams', 'a:0'],
10
+ ['-select_streams', audioStreamIndex ? `a:${audioStreamIndex}` : 'a:0'],
11
11
  [
12
12
  '-show_entries',
13
13
  'stream=channels:stream=start_time:format=duration:format=format_name',
@@ -48,9 +48,10 @@ const getAudioChannelsAndDurationWithoutCache = async ({ src, indent, logLevel,
48
48
  }
49
49
  };
50
50
  exports.getAudioChannelsAndDurationWithoutCache = getAudioChannelsAndDurationWithoutCache;
51
- async function getAudioChannelsAndDurationUnlimited({ downloadMap, src, indent, logLevel, binariesDirectory, cancelSignal, }) {
52
- if (downloadMap.durationOfAssetCache[src]) {
53
- return downloadMap.durationOfAssetCache[src];
51
+ async function getAudioChannelsAndDurationUnlimited({ downloadMap, src, indent, logLevel, binariesDirectory, cancelSignal, audioStreamIndex, }) {
52
+ const cacheKey = audioStreamIndex ? `${src}-${audioStreamIndex}` : src;
53
+ if (downloadMap.durationOfAssetCache[cacheKey]) {
54
+ return downloadMap.durationOfAssetCache[cacheKey];
54
55
  }
55
56
  const result = await (0, exports.getAudioChannelsAndDurationWithoutCache)({
56
57
  src,
@@ -58,11 +59,12 @@ async function getAudioChannelsAndDurationUnlimited({ downloadMap, src, indent,
58
59
  logLevel,
59
60
  binariesDirectory,
60
61
  cancelSignal,
62
+ audioStreamIndex,
61
63
  });
62
- downloadMap.durationOfAssetCache[src] = result;
64
+ downloadMap.durationOfAssetCache[cacheKey] = result;
63
65
  return result;
64
66
  }
65
- const getAudioChannelsAndDuration = ({ downloadMap, src, indent, logLevel, binariesDirectory, cancelSignal, }) => {
67
+ const getAudioChannelsAndDuration = ({ downloadMap, src, indent, logLevel, binariesDirectory, cancelSignal, audioStreamIndex, }) => {
66
68
  return limit(() => getAudioChannelsAndDurationUnlimited({
67
69
  downloadMap,
68
70
  src,
@@ -70,6 +72,7 @@ const getAudioChannelsAndDuration = ({ downloadMap, src, indent, logLevel, binar
70
72
  logLevel,
71
73
  binariesDirectory,
72
74
  cancelSignal,
75
+ audioStreamIndex,
73
76
  }));
74
77
  };
75
78
  exports.getAudioChannelsAndDuration = getAudioChannelsAndDuration;
@@ -7,6 +7,7 @@ export type UnsafeAsset = Omit<AudioOrVideoAsset, 'frame' | 'id' | 'volume' | 'm
7
7
  id: string;
8
8
  playbackRate: number;
9
9
  toneFrequency: number | null;
10
+ audioStreamIndex: number;
10
11
  };
11
12
  export type AssetVolume = number | number[];
12
13
  export type MediaAsset = Omit<UnsafeAsset, 'duration' | 'volume'> & {
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.uncompressMediaAsset = void 0;
4
4
  const uncompressMediaAsset = (allRenderAssets, assetToUncompress) => {
5
- const isCompressed = assetToUncompress.src.match(/same-as-(.*)-([0-9]+)$/);
5
+ const isCompressed = assetToUncompress.src.match(/same-as-(.*)-([0-9.]+)$/);
6
6
  if (!isCompressed) {
7
7
  return assetToUncompress;
8
8
  }
@@ -58,6 +58,7 @@ const createAudio = async ({ assets, onDownload, fps, logLevel, onProgress, down
58
58
  trimLeftOffset,
59
59
  trimRightOffset,
60
60
  forSeamlessAacConcatenation,
61
+ audioStreamIndex: asset.audioStreamIndex,
61
62
  });
62
63
  preprocessProgress[index] = 1;
63
64
  updateProgress();
@@ -14178,11 +14178,12 @@ var getAudioChannelsAndDurationWithoutCache = async ({
14178
14178
  indent,
14179
14179
  logLevel,
14180
14180
  binariesDirectory,
14181
- cancelSignal
14181
+ cancelSignal,
14182
+ audioStreamIndex
14182
14183
  }) => {
14183
14184
  const args = [
14184
14185
  ["-v", "error"],
14185
- ["-select_streams", "a:0"],
14186
+ ["-select_streams", audioStreamIndex ? `a:${audioStreamIndex}` : "a:0"],
14186
14187
  [
14187
14188
  "-show_entries",
14188
14189
  "stream=channels:stream=start_time:format=duration:format=format_name"
@@ -14223,19 +14224,22 @@ async function getAudioChannelsAndDurationUnlimited({
14223
14224
  indent,
14224
14225
  logLevel,
14225
14226
  binariesDirectory,
14226
- cancelSignal
14227
+ cancelSignal,
14228
+ audioStreamIndex
14227
14229
  }) {
14228
- if (downloadMap.durationOfAssetCache[src]) {
14229
- return downloadMap.durationOfAssetCache[src];
14230
+ const cacheKey = audioStreamIndex ? `${src}-${audioStreamIndex}` : src;
14231
+ if (downloadMap.durationOfAssetCache[cacheKey]) {
14232
+ return downloadMap.durationOfAssetCache[cacheKey];
14230
14233
  }
14231
14234
  const result = await getAudioChannelsAndDurationWithoutCache({
14232
14235
  src,
14233
14236
  indent,
14234
14237
  logLevel,
14235
14238
  binariesDirectory,
14236
- cancelSignal
14239
+ cancelSignal,
14240
+ audioStreamIndex
14237
14241
  });
14238
- downloadMap.durationOfAssetCache[src] = result;
14242
+ downloadMap.durationOfAssetCache[cacheKey] = result;
14239
14243
  return result;
14240
14244
  }
14241
14245
  var getAudioChannelsAndDuration = ({
@@ -14244,7 +14248,8 @@ var getAudioChannelsAndDuration = ({
14244
14248
  indent,
14245
14249
  logLevel,
14246
14250
  binariesDirectory,
14247
- cancelSignal
14251
+ cancelSignal,
14252
+ audioStreamIndex
14248
14253
  }) => {
14249
14254
  return limit(() => getAudioChannelsAndDurationUnlimited({
14250
14255
  downloadMap,
@@ -14252,7 +14257,8 @@ var getAudioChannelsAndDuration = ({
14252
14257
  indent,
14253
14258
  logLevel,
14254
14259
  binariesDirectory,
14255
- cancelSignal
14260
+ cancelSignal,
14261
+ audioStreamIndex
14256
14262
  }));
14257
14263
  };
14258
14264
 
@@ -14414,7 +14420,8 @@ var downloadAsset = async ({
14414
14420
  logLevel,
14415
14421
  shouldAnalyzeAudioImmediately,
14416
14422
  binariesDirectory,
14417
- cancelSignalForAudioAnalysis
14423
+ cancelSignalForAudioAnalysis,
14424
+ audioStreamIndex
14418
14425
  }) => {
14419
14426
  if (isAssetCompressed(src)) {
14420
14427
  return src;
@@ -14487,7 +14494,8 @@ var downloadAsset = async ({
14487
14494
  src: to,
14488
14495
  indent,
14489
14496
  logLevel,
14490
- cancelSignal: cancelSignalForAudioAnalysis
14497
+ cancelSignal: cancelSignalForAudioAnalysis,
14498
+ audioStreamIndex
14491
14499
  });
14492
14500
  }
14493
14501
  return to;
@@ -14567,7 +14575,8 @@ var downloadAndMapAssetsToFileUrl = async ({
14567
14575
  logLevel,
14568
14576
  shouldAnalyzeAudioImmediately,
14569
14577
  binariesDirectory,
14570
- cancelSignalForAudioAnalysis
14578
+ cancelSignalForAudioAnalysis,
14579
+ audioStreamIndex: renderAsset.audioStreamIndex
14571
14580
  });
14572
14581
  cleanup();
14573
14582
  return {
@@ -14915,7 +14924,8 @@ var startOffthreadVideoServer = ({
14915
14924
  logLevel,
14916
14925
  binariesDirectory,
14917
14926
  cancelSignalForAudioAnalysis: undefined,
14918
- shouldAnalyzeAudioImmediately: true
14927
+ shouldAnalyzeAudioImmediately: true,
14928
+ audioStreamIndex: undefined
14919
14929
  }).then((to) => {
14920
14930
  return new Promise((resolve2, reject) => {
14921
14931
  if (closed) {
@@ -19683,7 +19693,7 @@ var convertAssetToFlattenedVolume = (asset) => {
19683
19693
 
19684
19694
  // src/assets/types.ts
19685
19695
  var uncompressMediaAsset = (allRenderAssets, assetToUncompress) => {
19686
- const isCompressed = assetToUncompress.src.match(/same-as-(.*)-([0-9]+)$/);
19696
+ const isCompressed = assetToUncompress.src.match(/same-as-(.*)-([0-9.]+)$/);
19687
19697
  if (!isCompressed) {
19688
19698
  return assetToUncompress;
19689
19699
  }
@@ -19741,7 +19751,8 @@ var calculateAssetPositions = (frames) => {
19741
19751
  volume: [],
19742
19752
  playbackRate: asset.playbackRate,
19743
19753
  toneFrequency: asset.toneFrequency,
19744
- audioStartFrame: asset.audioStartFrame
19754
+ audioStartFrame: asset.audioStartFrame,
19755
+ audioStreamIndex: asset.audioStreamIndex
19745
19756
  });
19746
19757
  }
19747
19758
  const found = assets.find((a) => a.duration === null && areEqual(a, asset));
@@ -20306,7 +20317,7 @@ var stringifyFfmpegFilter = ({
20306
20317
  const padAtEnd = chunkLengthInSeconds - audibleDuration - startInVideoSeconds;
20307
20318
  const padStart = startInVideoSeconds + (asset.trimLeft === 0 ? presentationTimeOffsetInSeconds : 0);
20308
20319
  return {
20309
- filter: `[0:a]` + [
20320
+ filter: "[0:a]" + [
20310
20321
  `aformat=sample_fmts=s32:sample_rates=${DEFAULT_SAMPLE_RATE}`,
20311
20322
  ...trimAndTempoFilter,
20312
20323
  volumeFilter.value === "1" ? null : `volume=${volumeFilter.value}:eval=${volumeFilter.eval}`,
@@ -20332,7 +20343,8 @@ var preprocessAudioTrackUnlimited = async ({
20332
20343
  chunkLengthInSeconds,
20333
20344
  trimLeftOffset,
20334
20345
  trimRightOffset,
20335
- forSeamlessAacConcatenation
20346
+ forSeamlessAacConcatenation,
20347
+ audioStreamIndex
20336
20348
  }) => {
20337
20349
  const { channels, duration, startTime } = await getAudioChannelsAndDuration({
20338
20350
  downloadMap,
@@ -20340,7 +20352,8 @@ var preprocessAudioTrackUnlimited = async ({
20340
20352
  indent,
20341
20353
  logLevel,
20342
20354
  binariesDirectory,
20343
- cancelSignal
20355
+ cancelSignal,
20356
+ audioStreamIndex
20344
20357
  });
20345
20358
  const filter = stringifyFfmpegFilter({
20346
20359
  asset,
@@ -20363,6 +20376,7 @@ var preprocessAudioTrackUnlimited = async ({
20363
20376
  const args = [
20364
20377
  ["-hide_banner"],
20365
20378
  ["-i", resolveAssetSrc(asset.src)],
20379
+ audioStreamIndex ? ["-map", `0:a:${audioStreamIndex}`] : [],
20366
20380
  ["-ac", "2"],
20367
20381
  ["-filter_script:a", file],
20368
20382
  ["-c:a", "pcm_s16le"],
@@ -20454,7 +20468,8 @@ var createAudio = async ({
20454
20468
  chunkLengthInSeconds,
20455
20469
  trimLeftOffset,
20456
20470
  trimRightOffset,
20457
- forSeamlessAacConcatenation
20471
+ forSeamlessAacConcatenation,
20472
+ audioStreamIndex: asset.audioStreamIndex
20458
20473
  });
20459
20474
  preprocessProgress[index] = 1;
20460
20475
  updateProgress();
@@ -21026,7 +21041,7 @@ var internalRenderMediaRaw = ({
21026
21041
  proResProfile,
21027
21042
  x264Preset,
21028
21043
  crf,
21029
- composition,
21044
+ composition: compositionWithPossibleUnevenDimensions,
21030
21045
  serializedInputPropsWithCustomSchema,
21031
21046
  pixelFormat: userPixelFormat,
21032
21047
  codec,
@@ -21080,11 +21095,11 @@ var internalRenderMediaRaw = ({
21080
21095
  chromeMode,
21081
21096
  offthreadVideoThreads
21082
21097
  }) => {
21083
- const pixelFormat = userPixelFormat ?? composition.defaultPixelFormat ?? DEFAULT_PIXEL_FORMAT;
21098
+ const pixelFormat = userPixelFormat ?? compositionWithPossibleUnevenDimensions.defaultPixelFormat ?? DEFAULT_PIXEL_FORMAT;
21084
21099
  if (repro) {
21085
21100
  enableRepro({
21086
21101
  serveUrl,
21087
- compositionName: composition.id,
21102
+ compositionName: compositionWithPossibleUnevenDimensions.id,
21088
21103
  serializedInputPropsWithCustomSchema,
21089
21104
  serializedResolvedPropsWithCustomSchema
21090
21105
  });
@@ -21140,8 +21155,8 @@ var internalRenderMediaRaw = ({
21140
21155
  const recentFrameTimings = [];
21141
21156
  const renderStart = Date.now();
21142
21157
  const { estimatedUsage, freeMemory, hasEnoughMemory } = shouldUseParallelEncoding({
21143
- height: composition.height,
21144
- width: composition.width,
21158
+ height: compositionWithPossibleUnevenDimensions.height,
21159
+ width: compositionWithPossibleUnevenDimensions.width,
21145
21160
  logLevel
21146
21161
  });
21147
21162
  const parallelEncoding = !disallowParallelEncoding && hasEnoughMemory && canUseParallelEncoding(codec);
@@ -21186,22 +21201,36 @@ var internalRenderMediaRaw = ({
21186
21201
  tag: "renderMedia()"
21187
21202
  }, "Parallel encoding is disabled.");
21188
21203
  }
21189
- const imageFormat = isAudioCodec(codec) ? "none" : provisionalImageFormat ?? composition.defaultVideoImageFormat ?? DEFAULT_VIDEO_IMAGE_FORMAT;
21204
+ const imageFormat = isAudioCodec(codec) ? "none" : provisionalImageFormat ?? compositionWithPossibleUnevenDimensions.defaultVideoImageFormat ?? DEFAULT_VIDEO_IMAGE_FORMAT;
21190
21205
  validateSelectedPixelFormatAndImageFormatCombination(pixelFormat, imageFormat);
21191
21206
  const workingDir = fs15.mkdtempSync(path25.join(os6.tmpdir(), "react-motion-render"));
21192
21207
  const preEncodedFileLocation = parallelEncoding ? path25.join(workingDir, "pre-encode." + getFileExtensionFromCodec(codec, audioCodec)) : null;
21193
21208
  if (onCtrlCExit && workingDir) {
21194
21209
  onCtrlCExit(`Delete ${workingDir}`, () => deleteDirectory(workingDir));
21195
21210
  }
21211
+ const { actualWidth: widthEvenDimensions, actualHeight: heightEvenDimensions } = validateEvenDimensionsWithCodec({
21212
+ codec,
21213
+ height: compositionWithPossibleUnevenDimensions.height,
21214
+ scale: 1,
21215
+ width: compositionWithPossibleUnevenDimensions.width,
21216
+ wantsImageSequence: false,
21217
+ indent,
21218
+ logLevel
21219
+ });
21196
21220
  const { actualWidth, actualHeight } = validateEvenDimensionsWithCodec({
21197
21221
  codec,
21198
- height: composition.height,
21222
+ height: compositionWithPossibleUnevenDimensions.height,
21199
21223
  scale,
21200
- width: composition.width,
21224
+ width: compositionWithPossibleUnevenDimensions.width,
21201
21225
  wantsImageSequence: false,
21202
21226
  indent,
21203
21227
  logLevel
21204
21228
  });
21229
+ const composition = {
21230
+ ...compositionWithPossibleUnevenDimensions,
21231
+ height: heightEvenDimensions,
21232
+ width: widthEvenDimensions
21233
+ };
21205
21234
  const realFrameRange = getRealFrameRange(composition.durationInFrames, frameRange);
21206
21235
  const totalFramesToRender = getFramesToRender(realFrameRange, everyNthFrame).length;
21207
21236
  Log.verbose({ indent, logLevel, tag: "renderMedia()" }, `Rendering frames ${realFrameRange.join("-")}`);
@@ -101,6 +101,7 @@ const startOffthreadVideoServer = ({ downloadMap, logLevel, indent, offthreadVid
101
101
  binariesDirectory,
102
102
  cancelSignalForAudioAnalysis: undefined,
103
103
  shouldAnalyzeAudioImmediately: true,
104
+ audioStreamIndex: undefined,
104
105
  })
105
106
  .then((to) => {
106
107
  return new Promise((resolve, reject) => {
@@ -17,6 +17,7 @@ type Options = {
17
17
  trimRightOffset: number;
18
18
  forSeamlessAacConcatenation: boolean;
19
19
  onProgress: (progress: number) => void;
20
+ audioStreamIndex: number;
20
21
  };
21
22
  export type PreprocessedAudioTrack = {
22
23
  outName: string;
@@ -11,7 +11,7 @@ const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
11
11
  const resolve_asset_src_1 = require("./resolve-asset-src");
12
12
  const sample_rate_1 = require("./sample-rate");
13
13
  const stringify_ffmpeg_filter_1 = require("./stringify-ffmpeg-filter");
14
- const preprocessAudioTrackUnlimited = async ({ outName, asset, fps, downloadMap, indent, logLevel, binariesDirectory, cancelSignal, onProgress, chunkLengthInSeconds, trimLeftOffset, trimRightOffset, forSeamlessAacConcatenation, }) => {
14
+ const preprocessAudioTrackUnlimited = async ({ outName, asset, fps, downloadMap, indent, logLevel, binariesDirectory, cancelSignal, onProgress, chunkLengthInSeconds, trimLeftOffset, trimRightOffset, forSeamlessAacConcatenation, audioStreamIndex, }) => {
15
15
  var _a;
16
16
  const { channels, duration, startTime } = await (0, get_audio_channels_1.getAudioChannelsAndDuration)({
17
17
  downloadMap,
@@ -20,6 +20,7 @@ const preprocessAudioTrackUnlimited = async ({ outName, asset, fps, downloadMap,
20
20
  logLevel,
21
21
  binariesDirectory,
22
22
  cancelSignal,
23
+ audioStreamIndex,
23
24
  });
24
25
  const filter = (0, stringify_ffmpeg_filter_1.stringifyFfmpegFilter)({
25
26
  asset,
@@ -42,6 +43,7 @@ const preprocessAudioTrackUnlimited = async ({ outName, asset, fps, downloadMap,
42
43
  const args = [
43
44
  ['-hide_banner'],
44
45
  ['-i', (0, resolve_asset_src_1.resolveAssetSrc)(asset.src)],
46
+ audioStreamIndex ? ['-map', `0:a:${audioStreamIndex}`] : [],
45
47
  ['-ac', '2'],
46
48
  ['-filter_script:a', file],
47
49
  ['-c:a', 'pcm_s16le'],
@@ -52,13 +52,13 @@ const validate_videobitrate_1 = require("./validate-videobitrate");
52
52
  const wrap_with_error_handling_1 = require("./wrap-with-error-handling");
53
53
  const SLOWEST_FRAME_COUNT = 10;
54
54
  const MAX_RECENT_FRAME_TIMINGS = 50;
55
- const internalRenderMediaRaw = ({ proResProfile, x264Preset, crf, composition, serializedInputPropsWithCustomSchema, pixelFormat: userPixelFormat, codec, envVariables, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, muted, enforceAudioTrack, ffmpegOverride, audioBitrate, videoBitrate, encodingMaxRate, encodingBufferSize, audioCodec, concurrency, disallowParallelEncoding, everyNthFrame, imageFormat: provisionalImageFormat, indent, jpegQuality, numberOfGifLoops, onCtrlCExit, preferLossless, serveUrl, server: reusedServer, logLevel, serializedResolvedPropsWithCustomSchema, offthreadVideoCacheSizeInBytes, colorSpace, repro, binariesDirectory, separateAudioTo, forSeamlessAacConcatenation, compositionStart, onBrowserDownload, onArtifact, metadata, hardwareAcceleration, chromeMode, offthreadVideoThreads, }) => {
55
+ const internalRenderMediaRaw = ({ proResProfile, x264Preset, crf, composition: compositionWithPossibleUnevenDimensions, serializedInputPropsWithCustomSchema, pixelFormat: userPixelFormat, codec, envVariables, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, muted, enforceAudioTrack, ffmpegOverride, audioBitrate, videoBitrate, encodingMaxRate, encodingBufferSize, audioCodec, concurrency, disallowParallelEncoding, everyNthFrame, imageFormat: provisionalImageFormat, indent, jpegQuality, numberOfGifLoops, onCtrlCExit, preferLossless, serveUrl, server: reusedServer, logLevel, serializedResolvedPropsWithCustomSchema, offthreadVideoCacheSizeInBytes, colorSpace, repro, binariesDirectory, separateAudioTo, forSeamlessAacConcatenation, compositionStart, onBrowserDownload, onArtifact, metadata, hardwareAcceleration, chromeMode, offthreadVideoThreads, }) => {
56
56
  var _a, _b;
57
- const pixelFormat = (_a = userPixelFormat !== null && userPixelFormat !== void 0 ? userPixelFormat : composition.defaultPixelFormat) !== null && _a !== void 0 ? _a : pixel_format_1.DEFAULT_PIXEL_FORMAT;
57
+ const pixelFormat = (_a = userPixelFormat !== null && userPixelFormat !== void 0 ? userPixelFormat : compositionWithPossibleUnevenDimensions.defaultPixelFormat) !== null && _a !== void 0 ? _a : pixel_format_1.DEFAULT_PIXEL_FORMAT;
58
58
  if (repro) {
59
59
  (0, repro_1.enableRepro)({
60
60
  serveUrl,
61
- compositionName: composition.id,
61
+ compositionName: compositionWithPossibleUnevenDimensions.id,
62
62
  serializedInputPropsWithCustomSchema,
63
63
  serializedResolvedPropsWithCustomSchema,
64
64
  });
@@ -117,8 +117,8 @@ const internalRenderMediaRaw = ({ proResProfile, x264Preset, crf, composition, s
117
117
  const recentFrameTimings = [];
118
118
  const renderStart = Date.now();
119
119
  const { estimatedUsage, freeMemory, hasEnoughMemory } = (0, prestitcher_memory_usage_1.shouldUseParallelEncoding)({
120
- height: composition.height,
121
- width: composition.width,
120
+ height: compositionWithPossibleUnevenDimensions.height,
121
+ width: compositionWithPossibleUnevenDimensions.width,
122
122
  logLevel,
123
123
  });
124
124
  const parallelEncoding = !disallowParallelEncoding &&
@@ -168,7 +168,7 @@ const internalRenderMediaRaw = ({ proResProfile, x264Preset, crf, composition, s
168
168
  }
169
169
  const imageFormat = (0, is_audio_codec_1.isAudioCodec)(codec)
170
170
  ? 'none'
171
- : ((_b = provisionalImageFormat !== null && provisionalImageFormat !== void 0 ? provisionalImageFormat : composition.defaultVideoImageFormat) !== null && _b !== void 0 ? _b : image_format_1.DEFAULT_VIDEO_IMAGE_FORMAT);
171
+ : ((_b = provisionalImageFormat !== null && provisionalImageFormat !== void 0 ? provisionalImageFormat : compositionWithPossibleUnevenDimensions.defaultVideoImageFormat) !== null && _b !== void 0 ? _b : image_format_1.DEFAULT_VIDEO_IMAGE_FORMAT);
172
172
  (0, image_format_1.validateSelectedPixelFormatAndImageFormatCombination)(pixelFormat, imageFormat);
173
173
  const workingDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'react-motion-render'));
174
174
  const preEncodedFileLocation = parallelEncoding
@@ -177,15 +177,30 @@ const internalRenderMediaRaw = ({ proResProfile, x264Preset, crf, composition, s
177
177
  if (onCtrlCExit && workingDir) {
178
178
  onCtrlCExit(`Delete ${workingDir}`, () => (0, delete_directory_1.deleteDirectory)(workingDir));
179
179
  }
180
+ const { actualWidth: widthEvenDimensions, actualHeight: heightEvenDimensions } = (0, validate_even_dimensions_with_codec_1.validateEvenDimensionsWithCodec)({
181
+ codec,
182
+ height: compositionWithPossibleUnevenDimensions.height,
183
+ // Don't apply scale yet, only ensure even dimensions
184
+ scale: 1,
185
+ width: compositionWithPossibleUnevenDimensions.width,
186
+ wantsImageSequence: false,
187
+ indent,
188
+ logLevel,
189
+ });
180
190
  const { actualWidth, actualHeight } = (0, validate_even_dimensions_with_codec_1.validateEvenDimensionsWithCodec)({
181
191
  codec,
182
- height: composition.height,
192
+ height: compositionWithPossibleUnevenDimensions.height,
183
193
  scale,
184
- width: composition.width,
194
+ width: compositionWithPossibleUnevenDimensions.width,
185
195
  wantsImageSequence: false,
186
196
  indent,
187
197
  logLevel,
188
198
  });
199
+ const composition = {
200
+ ...compositionWithPossibleUnevenDimensions,
201
+ height: heightEvenDimensions,
202
+ width: widthEvenDimensions,
203
+ };
189
204
  const realFrameRange = (0, get_frame_to_render_1.getRealFrameRange)(composition.durationInFrames, frameRange);
190
205
  const totalFramesToRender = (0, get_duration_from_frame_range_1.getFramesToRender)(realFrameRange, everyNthFrame).length;
191
206
  logger_1.Log.verbose({ indent, logLevel, tag: 'renderMedia()' }, `Rendering frames ${realFrameRange.join('-')}`);
@@ -118,7 +118,7 @@ const stringifyFfmpegFilter = ({ channels, volume, fps, assetDuration, chunkLeng
118
118
  (asset.trimLeft === 0 ? presentationTimeOffsetInSeconds : 0);
119
119
  // Set as few filters as possible, as combining them can create noise
120
120
  return {
121
- filter: `[0:a]` +
121
+ filter: '[0:a]' +
122
122
  [
123
123
  `aformat=sample_fmts=s32:sample_rates=${sample_rate_1.DEFAULT_SAMPLE_RATE}`,
124
124
  // The order matters here! For speed and correctness, we first trim the audio
@@ -18,7 +18,7 @@ var __toESM = (mod, isNodeMode, target) => {
18
18
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
19
19
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
20
20
 
21
- // ../../node_modules/.pnpm/ms@2.1.3/node_modules/ms/index.js
21
+ // ../../node_modules/.pnpm/ms@2.1.2/node_modules/ms/index.js
22
22
  var require_ms = __commonJS((exports, module) => {
23
23
  var s = 1000;
24
24
  var m = s * 60;
@@ -128,7 +128,7 @@ var require_ms = __commonJS((exports, module) => {
128
128
  }
129
129
  });
130
130
 
131
- // ../../node_modules/.pnpm/debug@4.4.0/node_modules/debug/src/common.js
131
+ // ../../node_modules/.pnpm/debug@4.3.4/node_modules/debug/src/common.js
132
132
  var require_common = __commonJS((exports, module) => {
133
133
  function setup(env) {
134
134
  createDebug.debug = createDebug;
@@ -230,64 +230,50 @@ var require_common = __commonJS((exports, module) => {
230
230
  createDebug.namespaces = namespaces;
231
231
  createDebug.names = [];
232
232
  createDebug.skips = [];
233
- const split = (typeof namespaces === "string" ? namespaces : "").trim().replace(" ", ",").split(",").filter(Boolean);
234
- for (const ns of split) {
235
- if (ns[0] === "-") {
236
- createDebug.skips.push(ns.slice(1));
237
- } else {
238
- createDebug.names.push(ns);
233
+ let i;
234
+ const split = (typeof namespaces === "string" ? namespaces : "").split(/[\s,]+/);
235
+ const len = split.length;
236
+ for (i = 0;i < len; i++) {
237
+ if (!split[i]) {
238
+ continue;
239
239
  }
240
- }
241
- }
242
- function matchesTemplate(search, template) {
243
- let searchIndex = 0;
244
- let templateIndex = 0;
245
- let starIndex = -1;
246
- let matchIndex = 0;
247
- while (searchIndex < search.length) {
248
- if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === "*")) {
249
- if (template[templateIndex] === "*") {
250
- starIndex = templateIndex;
251
- matchIndex = searchIndex;
252
- templateIndex++;
253
- } else {
254
- searchIndex++;
255
- templateIndex++;
256
- }
257
- } else if (starIndex !== -1) {
258
- templateIndex = starIndex + 1;
259
- matchIndex++;
260
- searchIndex = matchIndex;
240
+ namespaces = split[i].replace(/\*/g, ".*?");
241
+ if (namespaces[0] === "-") {
242
+ createDebug.skips.push(new RegExp("^" + namespaces.slice(1) + "$"));
261
243
  } else {
262
- return false;
244
+ createDebug.names.push(new RegExp("^" + namespaces + "$"));
263
245
  }
264
246
  }
265
- while (templateIndex < template.length && template[templateIndex] === "*") {
266
- templateIndex++;
267
- }
268
- return templateIndex === template.length;
269
247
  }
270
248
  function disable() {
271
249
  const namespaces = [
272
- ...createDebug.names,
273
- ...createDebug.skips.map((namespace) => "-" + namespace)
250
+ ...createDebug.names.map(toNamespace),
251
+ ...createDebug.skips.map(toNamespace).map((namespace) => "-" + namespace)
274
252
  ].join(",");
275
253
  createDebug.enable("");
276
254
  return namespaces;
277
255
  }
278
256
  function enabled(name) {
279
- for (const skip of createDebug.skips) {
280
- if (matchesTemplate(name, skip)) {
257
+ if (name[name.length - 1] === "*") {
258
+ return true;
259
+ }
260
+ let i;
261
+ let len;
262
+ for (i = 0, len = createDebug.skips.length;i < len; i++) {
263
+ if (createDebug.skips[i].test(name)) {
281
264
  return false;
282
265
  }
283
266
  }
284
- for (const ns of createDebug.names) {
285
- if (matchesTemplate(name, ns)) {
267
+ for (i = 0, len = createDebug.names.length;i < len; i++) {
268
+ if (createDebug.names[i].test(name)) {
286
269
  return true;
287
270
  }
288
271
  }
289
272
  return false;
290
273
  }
274
+ function toNamespace(regexp) {
275
+ return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, "*");
276
+ }
291
277
  function coerce(val) {
292
278
  if (val instanceof Error) {
293
279
  return val.stack || val.message;
@@ -303,7 +289,7 @@ var require_common = __commonJS((exports, module) => {
303
289
  module.exports = setup;
304
290
  });
305
291
 
306
- // ../../node_modules/.pnpm/debug@4.4.0/node_modules/debug/src/browser.js
292
+ // ../../node_modules/.pnpm/debug@4.3.4/node_modules/debug/src/browser.js
307
293
  var require_browser = __commonJS((exports, module) => {
308
294
  exports.formatArgs = formatArgs;
309
295
  exports.save = save;
@@ -404,8 +390,7 @@ var require_browser = __commonJS((exports, module) => {
404
390
  if (typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
405
391
  return false;
406
392
  }
407
- let m;
408
- return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || typeof navigator !== "undefined" && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31 || typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
393
+ return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 || typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
409
394
  }
410
395
  function formatArgs(args) {
411
396
  args[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args[0] + (this.useColors ? "%c " : " ") + "+" + module.exports.humanize(this.diff);
@@ -572,7 +557,7 @@ var require_supports_color = __commonJS((exports, module) => {
572
557
  };
573
558
  });
574
559
 
575
- // ../../node_modules/.pnpm/debug@4.4.0/node_modules/debug/src/node.js
560
+ // ../../node_modules/.pnpm/debug@4.3.4/node_modules/debug/src/node.js
576
561
  var require_node = __commonJS((exports, module) => {
577
562
  var tty = __require("tty");
578
563
  var util = __require("util");
@@ -710,7 +695,7 @@ var require_node = __commonJS((exports, module) => {
710
695
  return new Date().toISOString() + " ";
711
696
  }
712
697
  function log(...args) {
713
- return process.stderr.write(util.formatWithOptions(exports.inspectOpts, ...args) + `
698
+ return process.stderr.write(util.format(...args) + `
714
699
  `);
715
700
  }
716
701
  function save(namespaces) {
@@ -743,7 +728,7 @@ var require_node = __commonJS((exports, module) => {
743
728
  };
744
729
  });
745
730
 
746
- // ../../node_modules/.pnpm/debug@4.4.0/node_modules/debug/src/index.js
731
+ // ../../node_modules/.pnpm/debug@4.3.4/node_modules/debug/src/index.js
747
732
  var require_src = __commonJS((exports, module) => {
748
733
  if (typeof process === "undefined" || process.type === "renderer" || false || process.__nwjs) {
749
734
  module.exports = require_browser();
@@ -3086,17 +3071,14 @@ var getDownloadsCacheDir = () => {
3086
3071
  };
3087
3072
 
3088
3073
  // src/browser/BrowserFetcher.ts
3089
- var TESTED_VERSION = "134.0.6998.35";
3090
- var PLAYWRIGHT_VERSION = "1161";
3074
+ var TESTED_VERSION = "133.0.6943.0";
3075
+ var PLAYWRIGHT_VERSION = "1155";
3091
3076
  function getChromeDownloadUrl({
3092
3077
  platform: platform2,
3093
3078
  version,
3094
3079
  chromeMode
3095
3080
  }) {
3096
3081
  if (platform2 === "linux-arm64") {
3097
- if (chromeMode === "chrome-for-testing") {
3098
- return `https://playwright.azureedge.net/builds/chromium/${version ?? PLAYWRIGHT_VERSION}/chromium-linux-arm64.zip`;
3099
- }
3100
3082
  return `https://playwright.azureedge.net/builds/chromium/${version ?? PLAYWRIGHT_VERSION}/chromium-headless-shell-linux-arm64.zip`;
3101
3083
  }
3102
3084
  if (chromeMode === "headless-shell") {
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.339",
6
+ "version": "4.0.341",
7
7
  "description": "Render Remotion videos using Node.js or Bun",
8
8
  "main": "dist/index.js",
9
9
  "types": "dist/index.d.ts",
@@ -18,8 +18,8 @@
18
18
  "extract-zip": "2.0.1",
19
19
  "source-map": "^0.8.0-beta.0",
20
20
  "ws": "8.17.1",
21
- "remotion": "4.0.339",
22
- "@remotion/streaming": "4.0.339"
21
+ "remotion": "4.0.341",
22
+ "@remotion/streaming": "4.0.341"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "react": ">=16.8.0",
@@ -33,17 +33,17 @@
33
33
  "react-dom": "19.0.0",
34
34
  "@types/ws": "8.5.10",
35
35
  "eslint": "9.19.0",
36
- "@remotion/example-videos": "4.0.339",
37
- "@remotion/eslint-config-internal": "4.0.339"
36
+ "@remotion/example-videos": "4.0.341",
37
+ "@remotion/eslint-config-internal": "4.0.341"
38
38
  },
39
39
  "optionalDependencies": {
40
- "@remotion/compositor-darwin-arm64": "4.0.339",
41
- "@remotion/compositor-linux-arm64-musl": "4.0.339",
42
- "@remotion/compositor-darwin-x64": "4.0.339",
43
- "@remotion/compositor-linux-x64-gnu": "4.0.339",
44
- "@remotion/compositor-linux-x64-musl": "4.0.339",
45
- "@remotion/compositor-win32-x64-msvc": "4.0.339",
46
- "@remotion/compositor-linux-arm64-gnu": "4.0.339"
40
+ "@remotion/compositor-darwin-arm64": "4.0.341",
41
+ "@remotion/compositor-linux-arm64-musl": "4.0.341",
42
+ "@remotion/compositor-linux-x64-gnu": "4.0.341",
43
+ "@remotion/compositor-linux-x64-musl": "4.0.341",
44
+ "@remotion/compositor-win32-x64-msvc": "4.0.341",
45
+ "@remotion/compositor-linux-arm64-gnu": "4.0.341",
46
+ "@remotion/compositor-darwin-x64": "4.0.341"
47
47
  },
48
48
  "keywords": [
49
49
  "remotion",