@remotion/renderer 3.3.41 → 3.3.42

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/client.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/client';
package/client.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./dist/client');
@@ -0,0 +1,27 @@
1
+ import type { Codec } from './codec';
2
+ export declare const validAudioCodecs: readonly ["pcm-16", "aac", "mp3", "opus"];
3
+ export declare type AudioCodec = typeof validAudioCodecs[number];
4
+ export declare const supportedAudioCodecs: {
5
+ readonly h264: readonly ["aac", "pcm-16"];
6
+ readonly 'h264-mkv': readonly ["pcm-16"];
7
+ readonly aac: readonly ["aac", "pcm-16"];
8
+ readonly gif: readonly [];
9
+ readonly h265: readonly ["aac", "pcm-16"];
10
+ readonly mp3: readonly ["mp3", "pcm-16"];
11
+ readonly prores: readonly ["aac", "pcm-16"];
12
+ readonly vp8: readonly ["opus", "pcm-16"];
13
+ readonly vp9: readonly ["opus", "pcm-16"];
14
+ readonly wav: readonly ["pcm-16"];
15
+ };
16
+ export declare const audioCodecNames: readonly ["pcm_s16le", "aac", "libmp3lame", "libopus"];
17
+ export declare type FfmpegAudioCodecName = typeof audioCodecNames[number];
18
+ export declare const mapAudioCodecToFfmpegAudioCodecName: (audioCodec: AudioCodec) => FfmpegAudioCodecName;
19
+ export declare const defaultAudioCodecs: {
20
+ [key in Codec]: {
21
+ [k in 'compressed' | 'lossless']: typeof supportedAudioCodecs[key][number] | null;
22
+ };
23
+ };
24
+ export declare const getDefaultAudioCodec: ({ codec, preferLossless, }: {
25
+ codec: Codec;
26
+ preferLossless: boolean;
27
+ }) => AudioCodec | null;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDefaultAudioCodec = exports.defaultAudioCodecs = exports.mapAudioCodecToFfmpegAudioCodecName = exports.audioCodecNames = exports.supportedAudioCodecs = exports.validAudioCodecs = void 0;
4
+ exports.validAudioCodecs = ['pcm-16', 'aac', 'mp3', 'opus'];
5
+ exports.supportedAudioCodecs = {
6
+ h264: ['aac', 'pcm-16'],
7
+ 'h264-mkv': ['pcm-16'],
8
+ aac: ['aac', 'pcm-16'],
9
+ gif: [],
10
+ h265: ['aac', 'pcm-16'],
11
+ mp3: ['mp3', 'pcm-16'],
12
+ prores: ['aac', 'pcm-16'],
13
+ vp8: ['opus', 'pcm-16'],
14
+ vp9: ['opus', 'pcm-16'],
15
+ wav: ['pcm-16'],
16
+ };
17
+ const _satisfies = exports.supportedAudioCodecs;
18
+ if (_satisfies) {
19
+ // Just for type checking
20
+ }
21
+ exports.audioCodecNames = [
22
+ 'pcm_s16le',
23
+ 'aac',
24
+ 'libmp3lame',
25
+ 'libopus',
26
+ ];
27
+ const mapAudioCodecToFfmpegAudioCodecName = (audioCodec) => {
28
+ if (audioCodec === 'aac') {
29
+ return 'aac';
30
+ }
31
+ if (audioCodec === 'mp3') {
32
+ return 'libmp3lame';
33
+ }
34
+ if (audioCodec === 'opus') {
35
+ return 'libopus';
36
+ }
37
+ if (audioCodec === 'pcm-16') {
38
+ return 'pcm_s16le';
39
+ }
40
+ throw new Error('unknown audio codec: ' + audioCodec);
41
+ };
42
+ exports.mapAudioCodecToFfmpegAudioCodecName = mapAudioCodecToFfmpegAudioCodecName;
43
+ exports.defaultAudioCodecs = {
44
+ 'h264-mkv': {
45
+ lossless: 'pcm-16',
46
+ compressed: 'pcm-16',
47
+ },
48
+ aac: {
49
+ lossless: 'pcm-16',
50
+ compressed: 'aac',
51
+ },
52
+ gif: {
53
+ lossless: null,
54
+ compressed: null,
55
+ },
56
+ h264: {
57
+ lossless: 'pcm-16',
58
+ compressed: 'aac',
59
+ },
60
+ h265: {
61
+ lossless: 'pcm-16',
62
+ compressed: 'aac',
63
+ },
64
+ mp3: {
65
+ lossless: 'pcm-16',
66
+ compressed: 'mp3',
67
+ },
68
+ prores: {
69
+ lossless: 'pcm-16',
70
+ // V4.0: Make pcm the default
71
+ compressed: 'aac',
72
+ },
73
+ vp8: {
74
+ lossless: 'pcm-16',
75
+ compressed: 'opus',
76
+ },
77
+ vp9: {
78
+ lossless: 'pcm-16',
79
+ compressed: 'opus',
80
+ },
81
+ wav: {
82
+ lossless: 'pcm-16',
83
+ compressed: 'pcm-16',
84
+ },
85
+ };
86
+ const getDefaultAudioCodec = ({ codec, preferLossless, }) => {
87
+ return exports.defaultAudioCodecs[codec][preferLossless ? 'lossless' : 'compressed'];
88
+ };
89
+ exports.getDefaultAudioCodec = getDefaultAudioCodec;
package/dist/client.d.ts CHANGED
@@ -1,10 +1,178 @@
1
1
  export declare const BrowserSafeApis: {
2
- getFileExtensionFromCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => "mp3" | "aac" | "wav" | "gif" | "mp4" | "mkv" | "mov" | "webm";
2
+ getFileExtensionFromCodec: <T extends "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif">(codec: T, audioCodec: "mp3" | "aac" | "pcm-16" | "opus" | null) => import("./file-extensions").FileExtension;
3
3
  validCodecs: readonly ["h264", "h265", "vp8", "vp9", "mp3", "aac", "wav", "prores", "h264-mkv", "gif"];
4
4
  getDefaultCrfForCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => number;
5
5
  getValidCrfRanges: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => [number, number];
6
6
  isAudioCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif" | undefined) => boolean;
7
- proResProfileOptions: readonly ["4444-xq", "4444", "hq", "standard", "light", "proxy"];
8
7
  validPixelFormats: readonly ["yuv420p", "yuva420p", "yuv422p", "yuv444p", "yuv420p10le", "yuv422p10le", "yuv444p10le", "yuva444p10le"];
9
8
  DEFAULT_PIXEL_FORMAT: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le";
9
+ supportedAudioCodecs: {
10
+ readonly h264: readonly ["aac", "pcm-16"];
11
+ readonly 'h264-mkv': readonly ["pcm-16"];
12
+ readonly aac: readonly ["aac", "pcm-16"];
13
+ readonly gif: readonly [];
14
+ readonly h265: readonly ["aac", "pcm-16"];
15
+ readonly mp3: readonly ["mp3", "pcm-16"];
16
+ readonly prores: readonly ["pcm-16", "aac"];
17
+ readonly vp8: readonly ["opus", "pcm-16"];
18
+ readonly vp9: readonly ["opus", "pcm-16"];
19
+ readonly wav: readonly ["pcm-16"];
20
+ };
21
+ defaultFileExtensionMap: {
22
+ h264: {
23
+ default: import("./file-extensions").FileExtension;
24
+ forAudioCodec: {
25
+ aac: {
26
+ possible: import("./file-extensions").FileExtension[];
27
+ default: import("./file-extensions").FileExtension;
28
+ };
29
+ "pcm-16": {
30
+ possible: import("./file-extensions").FileExtension[];
31
+ default: import("./file-extensions").FileExtension;
32
+ };
33
+ };
34
+ };
35
+ h265: {
36
+ default: import("./file-extensions").FileExtension;
37
+ forAudioCodec: {
38
+ aac: {
39
+ possible: import("./file-extensions").FileExtension[];
40
+ default: import("./file-extensions").FileExtension;
41
+ };
42
+ "pcm-16": {
43
+ possible: import("./file-extensions").FileExtension[];
44
+ default: import("./file-extensions").FileExtension;
45
+ };
46
+ };
47
+ };
48
+ vp8: {
49
+ default: import("./file-extensions").FileExtension;
50
+ forAudioCodec: {
51
+ "pcm-16": {
52
+ possible: import("./file-extensions").FileExtension[];
53
+ default: import("./file-extensions").FileExtension;
54
+ };
55
+ opus: {
56
+ possible: import("./file-extensions").FileExtension[];
57
+ default: import("./file-extensions").FileExtension;
58
+ };
59
+ };
60
+ };
61
+ vp9: {
62
+ default: import("./file-extensions").FileExtension;
63
+ forAudioCodec: {
64
+ "pcm-16": {
65
+ possible: import("./file-extensions").FileExtension[];
66
+ default: import("./file-extensions").FileExtension;
67
+ };
68
+ opus: {
69
+ possible: import("./file-extensions").FileExtension[];
70
+ default: import("./file-extensions").FileExtension;
71
+ };
72
+ };
73
+ };
74
+ mp3: {
75
+ default: import("./file-extensions").FileExtension;
76
+ forAudioCodec: {
77
+ mp3: {
78
+ possible: import("./file-extensions").FileExtension[];
79
+ default: import("./file-extensions").FileExtension;
80
+ };
81
+ "pcm-16": {
82
+ possible: import("./file-extensions").FileExtension[];
83
+ default: import("./file-extensions").FileExtension;
84
+ };
85
+ };
86
+ };
87
+ aac: {
88
+ default: import("./file-extensions").FileExtension;
89
+ forAudioCodec: {
90
+ aac: {
91
+ possible: import("./file-extensions").FileExtension[];
92
+ default: import("./file-extensions").FileExtension;
93
+ };
94
+ "pcm-16": {
95
+ possible: import("./file-extensions").FileExtension[];
96
+ default: import("./file-extensions").FileExtension;
97
+ };
98
+ };
99
+ };
100
+ wav: {
101
+ default: import("./file-extensions").FileExtension;
102
+ forAudioCodec: {
103
+ "pcm-16": {
104
+ possible: import("./file-extensions").FileExtension[];
105
+ default: import("./file-extensions").FileExtension;
106
+ };
107
+ };
108
+ };
109
+ prores: {
110
+ default: import("./file-extensions").FileExtension;
111
+ forAudioCodec: {
112
+ aac: {
113
+ possible: import("./file-extensions").FileExtension[];
114
+ default: import("./file-extensions").FileExtension;
115
+ };
116
+ "pcm-16": {
117
+ possible: import("./file-extensions").FileExtension[];
118
+ default: import("./file-extensions").FileExtension;
119
+ };
120
+ };
121
+ };
122
+ "h264-mkv": {
123
+ default: import("./file-extensions").FileExtension;
124
+ forAudioCodec: {
125
+ "pcm-16": {
126
+ possible: import("./file-extensions").FileExtension[];
127
+ default: import("./file-extensions").FileExtension;
128
+ };
129
+ };
130
+ };
131
+ gif: {
132
+ default: import("./file-extensions").FileExtension;
133
+ forAudioCodec: {};
134
+ };
135
+ };
136
+ defaultAudioCodecs: {
137
+ h264: {
138
+ compressed: "aac" | "pcm-16" | null;
139
+ lossless: "aac" | "pcm-16" | null;
140
+ };
141
+ h265: {
142
+ compressed: "aac" | "pcm-16" | null;
143
+ lossless: "aac" | "pcm-16" | null;
144
+ };
145
+ vp8: {
146
+ compressed: "pcm-16" | "opus" | null;
147
+ lossless: "pcm-16" | "opus" | null;
148
+ };
149
+ vp9: {
150
+ compressed: "pcm-16" | "opus" | null;
151
+ lossless: "pcm-16" | "opus" | null;
152
+ };
153
+ mp3: {
154
+ compressed: "mp3" | "pcm-16" | null;
155
+ lossless: "mp3" | "pcm-16" | null;
156
+ };
157
+ aac: {
158
+ compressed: "aac" | "pcm-16" | null;
159
+ lossless: "aac" | "pcm-16" | null;
160
+ };
161
+ wav: {
162
+ compressed: "pcm-16" | null;
163
+ lossless: "pcm-16" | null;
164
+ };
165
+ prores: {
166
+ compressed: "aac" | "pcm-16" | null;
167
+ lossless: "aac" | "pcm-16" | null;
168
+ };
169
+ "h264-mkv": {
170
+ compressed: "pcm-16" | null;
171
+ lossless: "pcm-16" | null;
172
+ };
173
+ gif: {
174
+ compressed: null;
175
+ lossless: null;
176
+ };
177
+ };
10
178
  };
package/dist/client.js CHANGED
@@ -1,19 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BrowserSafeApis = void 0;
4
+ const audio_codec_1 = require("./audio-codec");
4
5
  const codec_1 = require("./codec");
5
6
  const crf_1 = require("./crf");
7
+ const file_extensions_1 = require("./file-extensions");
6
8
  const get_extension_from_codec_1 = require("./get-extension-from-codec");
7
9
  const is_audio_codec_1 = require("./is-audio-codec");
8
10
  const pixel_format_1 = require("./pixel-format");
9
- const prores_profile_1 = require("./prores-profile");
10
11
  exports.BrowserSafeApis = {
11
12
  getFileExtensionFromCodec: get_extension_from_codec_1.getFileExtensionFromCodec,
12
13
  validCodecs: codec_1.validCodecs,
13
14
  getDefaultCrfForCodec: crf_1.getDefaultCrfForCodec,
14
15
  getValidCrfRanges: crf_1.getValidCrfRanges,
15
16
  isAudioCodec: is_audio_codec_1.isAudioCodec,
16
- proResProfileOptions: prores_profile_1.proResProfileOptions,
17
17
  validPixelFormats: pixel_format_1.validPixelFormats,
18
18
  DEFAULT_PIXEL_FORMAT: pixel_format_1.DEFAULT_PIXEL_FORMAT,
19
+ supportedAudioCodecs: file_extensions_1.supportedAudioCodecs,
20
+ defaultFileExtensionMap: file_extensions_1.defaultFileExtensionMap,
21
+ defaultAudioCodecs: audio_codec_1.defaultAudioCodecs,
19
22
  };
@@ -1,3 +1,4 @@
1
+ import type { AudioCodec } from './audio-codec';
1
2
  import type { Codec } from './codec';
2
3
  import type { FfmpegExecutable } from './ffmpeg-executable';
3
4
  declare type Options = {
@@ -11,6 +12,7 @@ declare type Options = {
11
12
  numberOfGifLoops: number | null;
12
13
  remotionRoot: string;
13
14
  ffmpegExecutable: FfmpegExecutable;
15
+ audioCodec: AudioCodec | null;
14
16
  };
15
17
  export declare const combineVideos: (options: Options) => Promise<void>;
16
18
  export {};
@@ -8,17 +8,18 @@ exports.combineVideos = void 0;
8
8
  const execa_1 = __importDefault(require("execa"));
9
9
  const fs_1 = require("fs");
10
10
  const path_1 = require("path");
11
+ const audio_codec_1 = require("./audio-codec");
11
12
  const ffmpeg_flags_1 = require("./ffmpeg-flags");
12
- const get_audio_codec_name_1 = require("./get-audio-codec-name");
13
13
  const is_audio_codec_1 = require("./is-audio-codec");
14
14
  const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
15
15
  const truthy_1 = require("./truthy");
16
16
  const combineVideos = async (options) => {
17
17
  var _a;
18
- const { files, filelistDir, output, onProgress, numberOfFrames, codec, fps, numberOfGifLoops, ffmpegExecutable, remotionRoot, } = options;
18
+ const { files, filelistDir, output, onProgress, numberOfFrames, codec, fps, numberOfGifLoops, ffmpegExecutable, remotionRoot, audioCodec, } = options;
19
19
  const fileList = files.map((p) => `file '${p}'`).join('\n');
20
20
  const fileListTxt = (0, path_1.join)(filelistDir, 'files.txt');
21
21
  (0, fs_1.writeFileSync)(fileListTxt, fileList);
22
+ const resolvedAudioCodec = audioCodec !== null && audioCodec !== void 0 ? audioCodec : (0, audio_codec_1.getDefaultAudioCodec)({ codec, preferLossless: false });
22
23
  try {
23
24
  const task = (0, execa_1.default)(await (0, ffmpeg_flags_1.getExecutableBinary)(ffmpegExecutable, remotionRoot, 'ffmpeg'), [
24
25
  (0, is_audio_codec_1.isAudioCodec)(codec) ? null : '-r',
@@ -37,8 +38,10 @@ const combineVideos = async (options) => {
37
38
  : '-1',
38
39
  (0, is_audio_codec_1.isAudioCodec)(codec) ? null : '-c:v',
39
40
  (0, is_audio_codec_1.isAudioCodec)(codec) ? null : codec === 'gif' ? 'gif' : 'copy',
40
- '-c:a',
41
- (0, get_audio_codec_name_1.getAudioCodecName)(codec),
41
+ resolvedAudioCodec ? '-c:a' : null,
42
+ resolvedAudioCodec
43
+ ? (0, audio_codec_1.mapAudioCodecToFfmpegAudioCodecName)(resolvedAudioCodec)
44
+ : null,
42
45
  // Set max bitrate up to 512kbps, will choose lower if that's too much
43
46
  '-b:a',
44
47
  '512K',
@@ -0,0 +1,25 @@
1
+ import type { Codec } from './codec';
2
+ export declare const supportedAudioCodecs: {
3
+ readonly h264: readonly ["aac", "pcm-16"];
4
+ readonly 'h264-mkv': readonly ["pcm-16"];
5
+ readonly aac: readonly ["aac", "pcm-16"];
6
+ readonly gif: readonly [];
7
+ readonly h265: readonly ["aac", "pcm-16"];
8
+ readonly mp3: readonly ["mp3", "pcm-16"];
9
+ readonly prores: readonly ["pcm-16", "aac"];
10
+ readonly vp8: readonly ["opus", "pcm-16"];
11
+ readonly vp9: readonly ["opus", "pcm-16"];
12
+ readonly wav: readonly ["pcm-16"];
13
+ };
14
+ export declare type FileExtension = 'aac' | '3gp' | 'm4a' | 'm4b' | 'mpg' | 'mpeg' | 'mkv' | 'mp4' | 'gif' | 'hevc' | 'mp3' | 'mov' | 'mxf' | 'wav' | 'webm';
15
+ export declare const defaultFileExtensionMap: {
16
+ [key in Codec]: {
17
+ default: FileExtension;
18
+ forAudioCodec: {
19
+ [k in typeof supportedAudioCodecs[key][number]]: {
20
+ possible: FileExtension[];
21
+ default: FileExtension;
22
+ };
23
+ };
24
+ };
25
+ };
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultFileExtensionMap = exports.supportedAudioCodecs = void 0;
4
+ exports.supportedAudioCodecs = {
5
+ h264: ['aac', 'pcm-16'],
6
+ 'h264-mkv': ['pcm-16'],
7
+ aac: ['aac', 'pcm-16'],
8
+ gif: [],
9
+ h265: ['aac', 'pcm-16'],
10
+ mp3: ['mp3', 'pcm-16'],
11
+ prores: ['pcm-16', 'aac'],
12
+ vp8: ['opus', 'pcm-16'],
13
+ vp9: ['opus', 'pcm-16'],
14
+ wav: ['pcm-16'],
15
+ };
16
+ exports.defaultFileExtensionMap = {
17
+ 'h264-mkv': {
18
+ default: 'mkv',
19
+ forAudioCodec: {
20
+ 'pcm-16': { possible: ['mkv'], default: 'mkv' },
21
+ },
22
+ },
23
+ aac: {
24
+ default: 'aac',
25
+ forAudioCodec: {
26
+ aac: {
27
+ possible: ['aac', '3gp', 'm4a', 'm4b', 'mpg', 'mpeg'],
28
+ default: 'aac',
29
+ },
30
+ 'pcm-16': {
31
+ possible: ['wav'],
32
+ default: 'wav',
33
+ },
34
+ },
35
+ },
36
+ gif: {
37
+ default: 'gif',
38
+ forAudioCodec: {},
39
+ },
40
+ h264: {
41
+ default: 'mp4',
42
+ forAudioCodec: {
43
+ 'pcm-16': { possible: ['mkv'], default: 'mkv' },
44
+ aac: { possible: ['mp4', 'mkv'], default: 'mp4' },
45
+ },
46
+ },
47
+ h265: {
48
+ default: 'mp4',
49
+ forAudioCodec: {
50
+ aac: { possible: ['mp4', 'mkv', 'hevc'], default: 'mp4' },
51
+ 'pcm-16': { possible: ['mkv'], default: 'mkv' },
52
+ },
53
+ },
54
+ mp3: {
55
+ default: 'mp3',
56
+ forAudioCodec: {
57
+ mp3: { possible: ['mp3'], default: 'mp3' },
58
+ 'pcm-16': { possible: ['wav'], default: 'wav' },
59
+ },
60
+ },
61
+ prores: {
62
+ default: 'mov',
63
+ forAudioCodec: {
64
+ aac: { possible: ['mov', 'mkv', 'mxf'], default: 'mov' },
65
+ 'pcm-16': { possible: ['mov', 'mkv', 'mxf'], default: 'mov' },
66
+ },
67
+ },
68
+ vp8: {
69
+ default: 'webm',
70
+ forAudioCodec: {
71
+ 'pcm-16': { possible: ['mkv'], default: 'mkv' },
72
+ opus: { possible: ['webm'], default: 'webm' },
73
+ },
74
+ },
75
+ vp9: {
76
+ default: 'webm',
77
+ forAudioCodec: {
78
+ 'pcm-16': { possible: ['mkv'], default: 'mkv' },
79
+ opus: { possible: ['webm'], default: 'webm' },
80
+ },
81
+ },
82
+ wav: {
83
+ default: 'wav',
84
+ forAudioCodec: {
85
+ 'pcm-16': { possible: ['wav'], default: 'wav' },
86
+ },
87
+ },
88
+ };
@@ -1,2 +1,6 @@
1
+ import type { AudioCodec } from './audio-codec';
1
2
  import type { Codec } from './codec';
2
- export declare const getFileExtensionFromCodec: (codec: Codec) => "mp3" | "aac" | "wav" | "gif" | "webm" | "mp4" | "mov" | "mkv";
3
+ import type { FileExtension } from './file-extensions';
4
+ export declare const getFileExtensionFromCodec: <T extends "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif">(codec: T, audioCodec: AudioCodec | null) => FileExtension;
5
+ export declare const makeFileExtensionMap: () => Record<string, ("h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif")[]>;
6
+ export declare const defaultCodecsForFileExtension: Record<FileExtension, Codec>;
@@ -1,35 +1,61 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getFileExtensionFromCodec = void 0;
3
+ exports.defaultCodecsForFileExtension = exports.makeFileExtensionMap = exports.getFileExtensionFromCodec = void 0;
4
4
  const codec_1 = require("./codec");
5
- const getFileExtensionFromCodec = (codec) => {
5
+ const file_extensions_1 = require("./file-extensions");
6
+ const getFileExtensionFromCodec = (codec, audioCodec) => {
6
7
  if (!codec_1.validCodecs.includes(codec)) {
7
8
  throw new Error(`Codec must be one of the following: ${codec_1.validCodecs.join(', ')}, but got ${codec}`);
8
9
  }
9
- switch (codec) {
10
- case 'aac':
11
- return 'aac';
12
- case 'h264':
13
- return 'mp4';
14
- // The chunks will be rendered as mkv, but the final output will still be MP4
15
- case 'h264-mkv':
16
- return 'mkv';
17
- case 'h265':
18
- return 'mp4';
19
- case 'mp3':
20
- return 'mp3';
21
- case 'prores':
22
- return 'mov';
23
- case 'vp8':
24
- return 'webm';
25
- case 'vp9':
26
- return 'webm';
27
- case 'gif':
28
- return 'gif';
29
- case 'wav':
30
- return 'wav';
31
- default:
32
- throw new Error("Don't know which file extension to use for codec " + codec);
10
+ const map = file_extensions_1.defaultFileExtensionMap[codec];
11
+ if (audioCodec === null) {
12
+ return map.default;
33
13
  }
14
+ const typedAudioCodec = audioCodec;
15
+ if (!(typedAudioCodec in map.forAudioCodec)) {
16
+ throw new Error(`Audio codec ${typedAudioCodec} is not supported for codec ${codec}`);
17
+ }
18
+ return map.forAudioCodec[audioCodec]
19
+ .default;
34
20
  };
35
21
  exports.getFileExtensionFromCodec = getFileExtensionFromCodec;
22
+ const makeFileExtensionMap = () => {
23
+ const map = {};
24
+ Object.keys(file_extensions_1.defaultFileExtensionMap).forEach((_codec) => {
25
+ const codec = _codec;
26
+ const fileExtMap = file_extensions_1.defaultFileExtensionMap[codec];
27
+ const audioCodecs = Object.keys(fileExtMap.forAudioCodec);
28
+ const possibleExtensionsForAudioCodec = audioCodecs.map((audioCodec) => fileExtMap.forAudioCodec[audioCodec].possible);
29
+ const allPossibleExtensions = [
30
+ fileExtMap.default,
31
+ ...possibleExtensionsForAudioCodec.flat(1),
32
+ ];
33
+ for (const extension of allPossibleExtensions) {
34
+ if (!map[extension]) {
35
+ map[extension] = [];
36
+ }
37
+ if (!map[extension].includes(codec)) {
38
+ map[extension].push(codec);
39
+ }
40
+ }
41
+ });
42
+ return map;
43
+ };
44
+ exports.makeFileExtensionMap = makeFileExtensionMap;
45
+ exports.defaultCodecsForFileExtension = {
46
+ '3gp': 'aac',
47
+ aac: 'aac',
48
+ gif: 'gif',
49
+ hevc: 'h265',
50
+ m4a: 'aac',
51
+ m4b: 'aac',
52
+ mkv: 'h264-mkv',
53
+ mov: 'prores',
54
+ mp3: 'mp3',
55
+ mp4: 'h264',
56
+ mpeg: 'aac',
57
+ mpg: 'aac',
58
+ mxf: 'prores',
59
+ wav: 'wav',
60
+ webm: 'vp8',
61
+ };
@@ -2,4 +2,4 @@ export declare const guessExtensionForVideo: ({ src, remotionRoot, ffprobeBinary
2
2
  src: string;
3
3
  remotionRoot: string;
4
4
  ffprobeBinary: string | null;
5
- }) => Promise<"mp3" | "wav" | "webm" | "mp4">;
5
+ }) => Promise<"mp3" | "wav" | "mp4" | "webm">;
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ import { mimeContentType, mimeLookup } from './mime-types';
5
5
  import * as perf from './perf';
6
6
  export type { RenderMediaOnDownload } from './assets/download-and-map-assets-to-file';
7
7
  export type { DownloadMap } from './assets/download-map';
8
+ export { AudioCodec } from './audio-codec';
8
9
  export { Browser } from './browser';
9
10
  export { BrowserExecutable } from './browser-executable';
10
11
  export { BrowserLog } from './browser-log';
@@ -15,6 +16,7 @@ export { ErrorWithStackFrame } from './error-handling/handle-javascript-exceptio
15
16
  export { FfmpegExecutable } from './ffmpeg-executable';
16
17
  export { FfmpegVersion } from './ffmpeg-flags';
17
18
  export type { FfmpegOverrideFn } from './ffmpeg-override';
19
+ export { FileExtension } from './file-extensions';
18
20
  export { FrameRange } from './frame-range';
19
21
  export { getCanExtractFramesFast } from './get-can-extract-frames-fast';
20
22
  export { getCompositions } from './get-compositions';
@@ -60,7 +62,7 @@ export declare const RenderInternals: {
60
62
  scale: number;
61
63
  codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif";
62
64
  }) => void;
63
- getFileExtensionFromCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => "mp3" | "aac" | "wav" | "gif" | "webm" | "mp4" | "mov" | "mkv";
65
+ getFileExtensionFromCodec: <T extends "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif">(codec: T, audioCodec: "mp3" | "aac" | "pcm-16" | "opus" | null) => import("./file-extensions").FileExtension;
64
66
  tmpDir: (str: string) => string;
65
67
  deleteDirectory: (directory: string) => Promise<void>;
66
68
  isServeUrl: (potentialUrl: string) => boolean;
@@ -155,5 +157,140 @@ export declare const RenderInternals: {
155
157
  numberOfGifLoops: number | null;
156
158
  remotionRoot: string;
157
159
  ffmpegExecutable: import("./ffmpeg-executable").FfmpegExecutable;
160
+ audioCodec: "mp3" | "aac" | "pcm-16" | "opus" | null;
158
161
  }) => Promise<void>;
162
+ getDefaultAudioCodec: ({ codec, preferLossless, }: {
163
+ codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif";
164
+ preferLossless: boolean;
165
+ }) => "mp3" | "aac" | "pcm-16" | "opus" | null;
166
+ validAudioCodecs: readonly ["pcm-16", "aac", "mp3", "opus"];
167
+ defaultFileExtensionMap: {
168
+ h264: {
169
+ default: import("./file-extensions").FileExtension;
170
+ forAudioCodec: {
171
+ aac: {
172
+ possible: import("./file-extensions").FileExtension[];
173
+ default: import("./file-extensions").FileExtension;
174
+ };
175
+ "pcm-16": {
176
+ possible: import("./file-extensions").FileExtension[];
177
+ default: import("./file-extensions").FileExtension;
178
+ };
179
+ };
180
+ };
181
+ h265: {
182
+ default: import("./file-extensions").FileExtension;
183
+ forAudioCodec: {
184
+ aac: {
185
+ possible: import("./file-extensions").FileExtension[];
186
+ default: import("./file-extensions").FileExtension;
187
+ };
188
+ "pcm-16": {
189
+ possible: import("./file-extensions").FileExtension[];
190
+ default: import("./file-extensions").FileExtension;
191
+ };
192
+ };
193
+ };
194
+ vp8: {
195
+ default: import("./file-extensions").FileExtension;
196
+ forAudioCodec: {
197
+ "pcm-16": {
198
+ possible: import("./file-extensions").FileExtension[];
199
+ default: import("./file-extensions").FileExtension;
200
+ };
201
+ opus: {
202
+ possible: import("./file-extensions").FileExtension[];
203
+ default: import("./file-extensions").FileExtension;
204
+ };
205
+ };
206
+ };
207
+ vp9: {
208
+ default: import("./file-extensions").FileExtension;
209
+ forAudioCodec: {
210
+ "pcm-16": {
211
+ possible: import("./file-extensions").FileExtension[];
212
+ default: import("./file-extensions").FileExtension;
213
+ };
214
+ opus: {
215
+ possible: import("./file-extensions").FileExtension[];
216
+ default: import("./file-extensions").FileExtension;
217
+ };
218
+ };
219
+ };
220
+ mp3: {
221
+ default: import("./file-extensions").FileExtension;
222
+ forAudioCodec: {
223
+ mp3: {
224
+ possible: import("./file-extensions").FileExtension[];
225
+ default: import("./file-extensions").FileExtension;
226
+ };
227
+ "pcm-16": {
228
+ possible: import("./file-extensions").FileExtension[];
229
+ default: import("./file-extensions").FileExtension;
230
+ };
231
+ };
232
+ };
233
+ aac: {
234
+ default: import("./file-extensions").FileExtension;
235
+ forAudioCodec: {
236
+ aac: {
237
+ possible: import("./file-extensions").FileExtension[];
238
+ default: import("./file-extensions").FileExtension;
239
+ };
240
+ "pcm-16": {
241
+ possible: import("./file-extensions").FileExtension[];
242
+ default: import("./file-extensions").FileExtension;
243
+ };
244
+ };
245
+ };
246
+ wav: {
247
+ default: import("./file-extensions").FileExtension;
248
+ forAudioCodec: {
249
+ "pcm-16": {
250
+ possible: import("./file-extensions").FileExtension[];
251
+ default: import("./file-extensions").FileExtension;
252
+ };
253
+ };
254
+ };
255
+ prores: {
256
+ default: import("./file-extensions").FileExtension;
257
+ forAudioCodec: {
258
+ aac: {
259
+ possible: import("./file-extensions").FileExtension[];
260
+ default: import("./file-extensions").FileExtension;
261
+ };
262
+ "pcm-16": {
263
+ possible: import("./file-extensions").FileExtension[];
264
+ default: import("./file-extensions").FileExtension;
265
+ };
266
+ };
267
+ };
268
+ "h264-mkv": {
269
+ default: import("./file-extensions").FileExtension;
270
+ forAudioCodec: {
271
+ "pcm-16": {
272
+ possible: import("./file-extensions").FileExtension[];
273
+ default: import("./file-extensions").FileExtension;
274
+ };
275
+ };
276
+ };
277
+ gif: {
278
+ default: import("./file-extensions").FileExtension;
279
+ forAudioCodec: {};
280
+ };
281
+ };
282
+ supportedAudioCodecs: {
283
+ readonly h264: readonly ["aac", "pcm-16"];
284
+ readonly 'h264-mkv': readonly ["pcm-16"];
285
+ readonly aac: readonly ["aac", "pcm-16"];
286
+ readonly gif: readonly [];
287
+ readonly h265: readonly ["aac", "pcm-16"];
288
+ readonly mp3: readonly ["mp3", "pcm-16"];
289
+ readonly prores: readonly ["pcm-16", "aac"];
290
+ readonly vp8: readonly ["opus", "pcm-16"];
291
+ readonly vp9: readonly ["opus", "pcm-16"];
292
+ readonly wav: readonly ["pcm-16"];
293
+ };
294
+ makeFileExtensionMap: () => Record<string, ("h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif")[]>;
295
+ defaultCodecsForFileExtension: Record<import("./file-extensions").FileExtension, "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif">;
159
296
  };
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ exports.RenderInternals = exports.validateOutputFilename = exports.stitchFramesT
30
30
  const execa_1 = __importDefault(require("execa"));
31
31
  const download_file_1 = require("./assets/download-file");
32
32
  const download_map_1 = require("./assets/download-map");
33
+ const audio_codec_1 = require("./audio-codec");
33
34
  const browser_1 = require("./browser");
34
35
  const TimeoutSettings_1 = require("./browser/TimeoutSettings");
35
36
  const can_use_parallel_encoding_1 = require("./can-use-parallel-encoding");
@@ -42,6 +43,7 @@ const ensure_output_directory_1 = require("./ensure-output-directory");
42
43
  const symbolicate_error_1 = require("./error-handling/symbolicate-error");
43
44
  const symbolicateable_error_1 = require("./error-handling/symbolicateable-error");
44
45
  const ffmpeg_flags_1 = require("./ffmpeg-flags");
46
+ const file_extensions_1 = require("./file-extensions");
45
47
  const find_closest_package_json_1 = require("./find-closest-package-json");
46
48
  const frame_range_1 = require("./frame-range");
47
49
  const get_concurrency_1 = require("./get-concurrency");
@@ -153,6 +155,12 @@ exports.RenderInternals = {
153
155
  validateBitrate: validate_videobitrate_1.validateBitrate,
154
156
  getFfmpegVersion: ffmpeg_flags_1.getFfmpegVersion,
155
157
  combineVideos: combine_videos_1.combineVideos,
158
+ getDefaultAudioCodec: audio_codec_1.getDefaultAudioCodec,
159
+ validAudioCodecs: audio_codec_1.validAudioCodecs,
160
+ defaultFileExtensionMap: file_extensions_1.defaultFileExtensionMap,
161
+ supportedAudioCodecs: file_extensions_1.supportedAudioCodecs,
162
+ makeFileExtensionMap: get_extension_from_codec_1.makeFileExtensionMap,
163
+ defaultCodecsForFileExtension: get_extension_from_codec_1.defaultCodecsForFileExtension,
156
164
  };
157
165
  // Warn of potential performance issues with Apple Silicon (M1 chip under Rosetta)
158
166
  (0, check_apple_silicon_1.checkNodeVersionAndWarnAboutRosetta)();
@@ -2,6 +2,7 @@
2
2
  import type { SmallTCompMetadata } from 'remotion';
3
3
  import type { RenderMediaOnDownload } from './assets/download-and-map-assets-to-file';
4
4
  import type { DownloadMap } from './assets/download-map';
5
+ import type { AudioCodec } from './audio-codec';
5
6
  import type { BrowserExecutable } from './browser-executable';
6
7
  import type { BrowserLog } from './browser-log';
7
8
  import type { Browser as PuppeteerBrowser } from './browser/Browser';
@@ -63,6 +64,10 @@ export declare type RenderMediaOptions = {
63
64
  * @deprecated Only for Remotion internal usage
64
65
  */
65
66
  downloadMap?: DownloadMap;
67
+ /**
68
+ * @deprecated Only for Remotion internal usage
69
+ */
70
+ preferLossless?: boolean;
66
71
  muted?: boolean;
67
72
  enforceAudioTrack?: boolean;
68
73
  ffmpegOverride?: FfmpegOverrideFn;
@@ -70,6 +75,7 @@ export declare type RenderMediaOptions = {
70
75
  videoBitrate?: string | null;
71
76
  onSlowestFrames?: OnSlowestFrames;
72
77
  disallowParallelEncoding?: boolean;
78
+ audioCodec?: AudioCodec | null;
73
79
  } & ServeUrlOrWebpackBundle & ConcurrencyOrParallelism;
74
80
  declare type ConcurrencyOrParallelism = {
75
81
  concurrency?: number | string | null;
@@ -84,5 +90,5 @@ declare type ConcurrencyOrParallelism = {
84
90
  * @description Render a video from a composition
85
91
  * @link https://www.remotion.dev/docs/renderer/render-media
86
92
  */
87
- export declare const renderMedia: ({ proResProfile, crf, composition, ffmpegExecutable, ffprobeExecutable, inputProps, pixelFormat, codec, envVariables, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, muted, enforceAudioTrack, ffmpegOverride, audioBitrate, videoBitrate, onSlowestFrames, ...options }: RenderMediaOptions) => Promise<Buffer | null>;
93
+ export declare const renderMedia: ({ proResProfile, crf, composition, ffmpegExecutable, ffprobeExecutable, inputProps, pixelFormat, codec, envVariables, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, muted, enforceAudioTrack, ffmpegOverride, audioBitrate, videoBitrate, onSlowestFrames, audioCodec, ...options }: RenderMediaOptions) => Promise<Buffer | null>;
88
94
  export {};
@@ -55,8 +55,8 @@ const getConcurrency = (others) => {
55
55
  * @description Render a video from a composition
56
56
  * @link https://www.remotion.dev/docs/renderer/render-media
57
57
  */
58
- const renderMedia = ({ proResProfile, crf, composition, ffmpegExecutable, ffprobeExecutable, inputProps, pixelFormat, codec, envVariables, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, muted, enforceAudioTrack, ffmpegOverride, audioBitrate, videoBitrate, onSlowestFrames, ...options }) => {
59
- var _a, _b, _c, _d;
58
+ const renderMedia = ({ proResProfile, crf, composition, ffmpegExecutable, ffprobeExecutable, inputProps, pixelFormat, codec, envVariables, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, muted, enforceAudioTrack, ffmpegOverride, audioBitrate, videoBitrate, onSlowestFrames, audioCodec, ...options }) => {
59
+ var _a, _b, _c, _d, _e;
60
60
  const remotionRoot = (0, find_closest_package_json_1.findRemotionRoot)();
61
61
  (0, validate_ffmpeg_1.validateFfmpeg)(ffmpegExecutable !== null && ffmpegExecutable !== void 0 ? ffmpegExecutable : null, remotionRoot, 'ffmpeg');
62
62
  (0, quality_1.validateQuality)(options.quality);
@@ -69,7 +69,12 @@ const renderMedia = ({ proResProfile, crf, composition, ffmpegExecutable, ffprob
69
69
  });
70
70
  (0, pixel_format_1.validateSelectedPixelFormatAndCodecCombination)(pixelFormat, codec);
71
71
  if (outputLocation) {
72
- (0, validate_output_filename_1.validateOutputFilename)(codec, (0, get_extension_of_filename_1.getExtensionOfFilename)(outputLocation));
72
+ (0, validate_output_filename_1.validateOutputFilename)({
73
+ codec,
74
+ audioCodec: audioCodec !== null && audioCodec !== void 0 ? audioCodec : null,
75
+ extension: (0, get_extension_of_filename_1.getExtensionOfFilename)(outputLocation),
76
+ preferLossless: (_a = options.preferLossless) !== null && _a !== void 0 ? _a : false,
77
+ });
73
78
  }
74
79
  const absoluteOutputLocation = outputLocation
75
80
  ? path_1.default.resolve(process.cwd(), outputLocation)
@@ -77,9 +82,9 @@ const renderMedia = ({ proResProfile, crf, composition, ffmpegExecutable, ffprob
77
82
  (0, validate_scale_1.validateScale)(scale);
78
83
  const concurrency = getConcurrency(options);
79
84
  (0, validate_ffmpeg_override_1.validateFfmpegOverride)(ffmpegOverride);
80
- const everyNthFrame = (_a = options.everyNthFrame) !== null && _a !== void 0 ? _a : 1;
85
+ const everyNthFrame = (_b = options.everyNthFrame) !== null && _b !== void 0 ? _b : 1;
81
86
  (0, validate_every_nth_frame_1.validateEveryNthFrame)(everyNthFrame, codec);
82
- const numberOfGifLoops = (_b = options.numberOfGifLoops) !== null && _b !== void 0 ? _b : null;
87
+ const numberOfGifLoops = (_c = options.numberOfGifLoops) !== null && _c !== void 0 ? _c : null;
83
88
  const serveUrl = (0, legacy_webpack_config_1.getServeUrlWithFallback)(options);
84
89
  let stitchStage = 'encoding';
85
90
  let stitcherFfmpeg;
@@ -90,7 +95,7 @@ const renderMedia = ({ proResProfile, crf, composition, ffmpegExecutable, ffprob
90
95
  let encodedDoneIn = null;
91
96
  let cancelled = false;
92
97
  const renderStart = Date.now();
93
- const downloadMap = (_c = options.downloadMap) !== null && _c !== void 0 ? _c : (0, download_map_1.makeDownloadMap)();
98
+ const downloadMap = (_d = options.downloadMap) !== null && _d !== void 0 ? _d : (0, download_map_1.makeDownloadMap)();
94
99
  const { estimatedUsage, freeMemory, hasEnoughMemory } = (0, prestitcher_memory_usage_1.shouldUseParallelEncoding)({
95
100
  height: composition.height,
96
101
  width: composition.width,
@@ -111,11 +116,11 @@ const renderMedia = ({ proResProfile, crf, composition, ffmpegExecutable, ffprob
111
116
  }
112
117
  const imageFormat = (0, is_audio_codec_1.isAudioCodec)(codec)
113
118
  ? 'none'
114
- : (_d = options.imageFormat) !== null && _d !== void 0 ? _d : 'jpeg';
119
+ : (_e = options.imageFormat) !== null && _e !== void 0 ? _e : 'jpeg';
115
120
  const quality = imageFormat === 'jpeg' ? options.quality : undefined;
116
121
  (0, image_format_1.validateSelectedPixelFormatAndImageFormatCombination)(pixelFormat, imageFormat);
117
122
  const preEncodedFileLocation = parallelEncoding
118
- ? path_1.default.join(downloadMap.preEncode, 'pre-encode.' + (0, get_extension_from_codec_1.getFileExtensionFromCodec)(codec))
123
+ ? path_1.default.join(downloadMap.preEncode, 'pre-encode.' + (0, get_extension_from_codec_1.getFileExtensionFromCodec)(codec, audioCodec !== null && audioCodec !== void 0 ? audioCodec : null))
119
124
  : null;
120
125
  const outputDir = parallelEncoding
121
126
  ? null
@@ -270,6 +275,7 @@ const renderMedia = ({ proResProfile, crf, composition, ffmpegExecutable, ffprob
270
275
  return Promise.all([renderFramesReturn, waitForPrestitcherIfNecessary()]);
271
276
  })
272
277
  .then(([{ assetsInfo }]) => {
278
+ var _a;
273
279
  renderedDoneIn = Date.now() - renderStart;
274
280
  callUpdate();
275
281
  if (absoluteOutputLocation) {
@@ -285,6 +291,7 @@ const renderMedia = ({ proResProfile, crf, composition, ffmpegExecutable, ffprob
285
291
  internalOptions: {
286
292
  preEncodedFileLocation,
287
293
  imageFormat,
294
+ preferLossless: (_a = options.preferLossless) !== null && _a !== void 0 ? _a : false,
288
295
  },
289
296
  force: overwrite !== null && overwrite !== void 0 ? overwrite : overwrite_1.DEFAULT_OVERWRITE,
290
297
  pixelFormat,
@@ -309,6 +316,7 @@ const renderMedia = ({ proResProfile, crf, composition, ffmpegExecutable, ffprob
309
316
  ffmpegOverride,
310
317
  audioBitrate,
311
318
  videoBitrate,
319
+ audioCodec: audioCodec !== null && audioCodec !== void 0 ? audioCodec : null,
312
320
  }),
313
321
  stitchStart,
314
322
  ]);
@@ -1,6 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import type { RenderMediaOnDownload } from './assets/download-and-map-assets-to-file';
3
3
  import type { RenderAssetInfo } from './assets/download-map';
4
+ import type { AudioCodec } from './audio-codec';
4
5
  import type { Codec } from './codec';
5
6
  import type { FfmpegExecutable } from './ffmpeg-executable';
6
7
  import type { FfmpegOverrideFn } from './ffmpeg-override';
@@ -20,6 +21,7 @@ export declare type StitcherOptions = {
20
21
  pixelFormat?: PixelFormat;
21
22
  numberOfGifLoops?: number | null;
22
23
  codec?: Codec;
24
+ audioCodec?: AudioCodec | null;
23
25
  crf?: number | null;
24
26
  onProgress?: (progress: number) => void;
25
27
  onDownload?: RenderMediaOnDownload;
@@ -32,6 +34,7 @@ export declare type StitcherOptions = {
32
34
  internalOptions?: {
33
35
  preEncodedFileLocation: string | null;
34
36
  imageFormat: ImageFormat;
37
+ preferLossless: boolean;
35
38
  };
36
39
  muted?: boolean;
37
40
  enforceAudioTrack?: boolean;
@@ -34,6 +34,7 @@ const remotion_1 = require("remotion");
34
34
  const calculate_asset_positions_1 = require("./assets/calculate-asset-positions");
35
35
  const convert_assets_to_file_urls_1 = require("./assets/convert-assets-to-file-urls");
36
36
  const download_and_map_assets_to_file_1 = require("./assets/download-and-map-assets-to-file");
37
+ const audio_codec_1 = require("./audio-codec");
37
38
  const codec_1 = require("./codec");
38
39
  const codec_supports_media_1 = require("./codec-supports-media");
39
40
  const convert_number_of_gif_loops_to_ffmpeg_1 = require("./convert-number-of-gif-loops-to-ffmpeg");
@@ -41,7 +42,6 @@ const crf_1 = require("./crf");
41
42
  const delete_directory_1 = require("./delete-directory");
42
43
  const ffmpeg_flags_1 = require("./ffmpeg-flags");
43
44
  const find_closest_package_json_1 = require("./find-closest-package-json");
44
- const get_audio_codec_name_1 = require("./get-audio-codec-name");
45
45
  const get_codec_name_1 = require("./get-codec-name");
46
46
  const get_extension_from_codec_1 = require("./get-extension-from-codec");
47
47
  const get_prores_profile_name_1 = require("./get-prores-profile-name");
@@ -109,7 +109,7 @@ const getAssetsData = async ({ assets, onDownload, fps, expectedFrames, verbose,
109
109
  return outName;
110
110
  };
111
111
  const spawnFfmpeg = async (options, remotionRoot) => {
112
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
112
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
113
113
  remotion_1.Internals.validateDimension(options.height, 'height', 'passed to `stitchFramesToVideo()`');
114
114
  remotion_1.Internals.validateDimension(options.width, 'width', 'passed to `stitchFramesToVideo()`');
115
115
  const codec = (_a = options.codec) !== null && _a !== void 0 ? _a : codec_1.DEFAULT_CODEC;
@@ -129,12 +129,8 @@ const spawnFfmpeg = async (options, remotionRoot) => {
129
129
  const pixelFormat = (_b = options.pixelFormat) !== null && _b !== void 0 ? _b : pixel_format_1.DEFAULT_PIXEL_FORMAT;
130
130
  await (0, validate_ffmpeg_1.validateFfmpeg)((_c = options.ffmpegExecutable) !== null && _c !== void 0 ? _c : null, remotionRoot, 'ffmpeg');
131
131
  const encoderName = (0, get_codec_name_1.getCodecName)(codec);
132
- const audioCodecName = (0, get_audio_codec_name_1.getAudioCodecName)(codec);
133
132
  const proResProfileName = (0, get_prores_profile_name_1.getProResProfileName)(codec, options.proResProfile);
134
133
  const mediaSupport = (0, codec_supports_media_1.codecSupportsMedia)(codec);
135
- const tempFile = options.outputLocation
136
- ? null
137
- : path_1.default.join(options.assetsInfo.downloadMap.stitchFrames, `out.${(0, get_extension_from_codec_1.getFileExtensionFromCodec)(codec)}`);
138
134
  const shouldRenderAudio = mediaSupport.audio &&
139
135
  (options.assetsInfo.assets.flat(1).length > 0 ||
140
136
  options.enforceAudioTrack) &&
@@ -143,10 +139,17 @@ const spawnFfmpeg = async (options, remotionRoot) => {
143
139
  if (!shouldRenderAudio && !shouldRenderVideo) {
144
140
  throw new Error('The output format has neither audio nor video. This can happen if you are rendering an audio codec and the output file has no audio or the muted flag was passed.');
145
141
  }
142
+ // Explanation: https://github.com/remotion-dev/remotion/issues/1647
143
+ const resolvedAudioCodec = ((_d = options.internalOptions) === null || _d === void 0 ? void 0 : _d.preferLossless)
144
+ ? (0, audio_codec_1.getDefaultAudioCodec)({ codec, preferLossless: true })
145
+ : (_e = options.audioCodec) !== null && _e !== void 0 ? _e : (0, audio_codec_1.getDefaultAudioCodec)({ codec, preferLossless: false });
146
+ const tempFile = options.outputLocation
147
+ ? null
148
+ : path_1.default.join(options.assetsInfo.downloadMap.stitchFrames, `out.${(0, get_extension_from_codec_1.getFileExtensionFromCodec)(codec, resolvedAudioCodec)}`);
146
149
  if (options.verbose) {
147
- console.log('[verbose] ffmpeg', (_d = options.ffmpegExecutable) !== null && _d !== void 0 ? _d : 'ffmpeg in PATH');
150
+ console.log('[verbose] ffmpeg', (_f = options.ffmpegExecutable) !== null && _f !== void 0 ? _f : 'ffmpeg in PATH');
148
151
  console.log('[verbose] encoder', encoderName);
149
- console.log('[verbose] audioCodec', audioCodecName);
152
+ console.log('[verbose] audioCodec', resolvedAudioCodec);
150
153
  console.log('[verbose] pixelFormat', pixelFormat);
151
154
  if (options.ffmpegOverride) {
152
155
  console.log('[verbose] ffmpegOverride', options.ffmpegOverride);
@@ -174,34 +177,34 @@ const spawnFfmpeg = async (options, remotionRoot) => {
174
177
  onDownload: options.onDownload,
175
178
  fps: options.fps,
176
179
  expectedFrames,
177
- verbose: (_e = options.verbose) !== null && _e !== void 0 ? _e : false,
178
- ffmpegExecutable: (_f = options.ffmpegExecutable) !== null && _f !== void 0 ? _f : null,
179
- ffprobeExecutable: (_g = options.ffprobeExecutable) !== null && _g !== void 0 ? _g : null,
180
+ verbose: (_g = options.verbose) !== null && _g !== void 0 ? _g : false,
181
+ ffmpegExecutable: (_h = options.ffmpegExecutable) !== null && _h !== void 0 ? _h : null,
182
+ ffprobeExecutable: (_j = options.ffprobeExecutable) !== null && _j !== void 0 ? _j : null,
180
183
  onProgress: (prog) => updateProgress(prog, 0),
181
184
  downloadMap: options.assetsInfo.downloadMap,
182
185
  remotionRoot,
183
186
  })
184
187
  : null;
185
188
  if (mediaSupport.audio && !mediaSupport.video) {
186
- if (!audioCodecName) {
189
+ if (!resolvedAudioCodec) {
187
190
  throw new TypeError('exporting audio but has no audio codec name. Report this in the Remotion repo.');
188
191
  }
189
- const ffmpegTask = (0, execa_1.default)(await (0, ffmpeg_flags_1.getExecutableBinary)((_h = options.ffmpegExecutable) !== null && _h !== void 0 ? _h : null, remotionRoot, 'ffmpeg'), [
192
+ const ffmpegTask = (0, execa_1.default)(await (0, ffmpeg_flags_1.getExecutableBinary)((_k = options.ffmpegExecutable) !== null && _k !== void 0 ? _k : null, remotionRoot, 'ffmpeg'), [
190
193
  '-i',
191
194
  audio,
192
195
  '-c:a',
193
- audioCodecName,
196
+ (0, audio_codec_1.mapAudioCodecToFfmpegAudioCodecName)(resolvedAudioCodec),
194
197
  // Set bitrate up to 320k, for aac it might effectively be lower
195
198
  '-b:a',
196
- (_j = options.audioBitrate) !== null && _j !== void 0 ? _j : '320k',
199
+ (_l = options.audioBitrate) !== null && _l !== void 0 ? _l : '320k',
197
200
  options.force ? '-y' : null,
198
- (_k = options.outputLocation) !== null && _k !== void 0 ? _k : tempFile,
201
+ (_m = options.outputLocation) !== null && _m !== void 0 ? _m : tempFile,
199
202
  ].filter(remotion_1.Internals.truthy));
200
- (_l = options.cancelSignal) === null || _l === void 0 ? void 0 : _l.call(options, () => {
203
+ (_o = options.cancelSignal) === null || _o === void 0 ? void 0 : _o.call(options, () => {
201
204
  ffmpegTask.kill();
202
205
  });
203
206
  await ffmpegTask;
204
- (_m = options.onProgress) === null || _m === void 0 ? void 0 : _m.call(options, expectedFrames);
207
+ (_p = options.onProgress) === null || _p === void 0 ? void 0 : _p.call(options, expectedFrames);
205
208
  if (audio) {
206
209
  await (0, delete_directory_1.deleteDirectory)(path_1.default.dirname(audio));
207
210
  }
@@ -226,8 +229,8 @@ const spawnFfmpeg = async (options, remotionRoot) => {
226
229
  }
227
230
  const ffmpegArgs = [
228
231
  ['-r', String(options.fps)],
229
- ...(((_o = options.internalOptions) === null || _o === void 0 ? void 0 : _o.preEncodedFileLocation)
230
- ? [['-i', (_p = options.internalOptions) === null || _p === void 0 ? void 0 : _p.preEncodedFileLocation]]
232
+ ...(((_q = options.internalOptions) === null || _q === void 0 ? void 0 : _q.preEncodedFileLocation)
233
+ ? [['-i', (_r = options.internalOptions) === null || _r === void 0 ? void 0 : _r.preEncodedFileLocation]]
231
234
  : [
232
235
  ['-f', 'image2'],
233
236
  ['-s', `${options.width}x${options.height}`],
@@ -235,16 +238,16 @@ const spawnFfmpeg = async (options, remotionRoot) => {
235
238
  ['-i', options.assetsInfo.imageSequenceName],
236
239
  ]),
237
240
  audio ? ['-i', audio] : null,
238
- ((_q = options.numberOfGifLoops) !== null && _q !== void 0 ? _q : null) === null
241
+ ((_s = options.numberOfGifLoops) !== null && _s !== void 0 ? _s : null) === null
239
242
  ? null
240
243
  : [
241
244
  '-loop',
242
- (0, convert_number_of_gif_loops_to_ffmpeg_1.convertNumberOfGifLoopsToFfmpegSyntax)((_r = options.numberOfGifLoops) !== null && _r !== void 0 ? _r : null),
245
+ (0, convert_number_of_gif_loops_to_ffmpeg_1.convertNumberOfGifLoopsToFfmpegSyntax)((_t = options.numberOfGifLoops) !== null && _t !== void 0 ? _t : null),
243
246
  ],
244
247
  // -c:v is the same as -vcodec as -codec:video
245
248
  // and specified the video codec.
246
249
  ['-c:v', encoderName],
247
- ...(((_s = options.internalOptions) === null || _s === void 0 ? void 0 : _s.preEncodedFileLocation)
250
+ ...(((_u = options.internalOptions) === null || _u === void 0 ? void 0 : _u.preEncodedFileLocation)
248
251
  ? []
249
252
  : [
250
253
  proResProfileName ? ['-profile:v', proResProfileName] : null,
@@ -259,9 +262,11 @@ const spawnFfmpeg = async (options, remotionRoot) => {
259
262
  }),
260
263
  ]),
261
264
  codec === 'h264' ? ['-movflags', 'faststart'] : null,
262
- audioCodecName ? ['-c:a', audioCodecName] : null,
265
+ resolvedAudioCodec
266
+ ? ['-c:a', (0, audio_codec_1.mapAudioCodecToFfmpegAudioCodecName)(resolvedAudioCodec)]
267
+ : null,
263
268
  // Set max bitrate up to 1024kbps, will choose lower if that's too much
264
- audioCodecName ? ['-b:a', options.audioBitrate || '512K'] : null,
269
+ resolvedAudioCodec ? ['-b:a', options.audioBitrate || '512K'] : null,
265
270
  // Ignore metadata that may come from remote media
266
271
  ['-map_metadata', '-1'],
267
272
  [
@@ -270,7 +275,7 @@ const spawnFfmpeg = async (options, remotionRoot) => {
270
275
  [`Made with Remotion`, packageJson ? packageJson.version : null].join(' '),
271
276
  ],
272
277
  options.force ? '-y' : null,
273
- (_t = options.outputLocation) !== null && _t !== void 0 ? _t : tempFile,
278
+ (_v = options.outputLocation) !== null && _v !== void 0 ? _v : tempFile,
274
279
  ];
275
280
  if (options.verbose) {
276
281
  console.log('Generated FFMPEG command:');
@@ -284,15 +289,15 @@ const spawnFfmpeg = async (options, remotionRoot) => {
284
289
  console.log('Generated final FFMPEG command:');
285
290
  console.log(finalFfmpegString);
286
291
  }
287
- const task = (0, execa_1.default)(await (0, ffmpeg_flags_1.getExecutableBinary)((_u = options.ffmpegExecutable) !== null && _u !== void 0 ? _u : null, remotionRoot, 'ffmpeg'), finalFfmpegString, {
292
+ const task = (0, execa_1.default)(await (0, ffmpeg_flags_1.getExecutableBinary)((_w = options.ffmpegExecutable) !== null && _w !== void 0 ? _w : null, remotionRoot, 'ffmpeg'), finalFfmpegString, {
288
293
  cwd: options.dir,
289
294
  });
290
- (_v = options.cancelSignal) === null || _v === void 0 ? void 0 : _v.call(options, () => {
295
+ (_x = options.cancelSignal) === null || _x === void 0 ? void 0 : _x.call(options, () => {
291
296
  task.kill();
292
297
  });
293
298
  let ffmpegOutput = '';
294
299
  let isFinished = false;
295
- (_w = task.stderr) === null || _w === void 0 ? void 0 : _w.on('data', (data) => {
300
+ (_y = task.stderr) === null || _y === void 0 ? void 0 : _y.on('data', (data) => {
296
301
  var _a;
297
302
  const str = data.toString();
298
303
  ffmpegOutput += str;
@@ -1,2 +1,7 @@
1
- import type { Codec } from './codec';
2
- export declare const validateOutputFilename: (codec: Codec, extension: string | null) => void;
1
+ import type { AudioCodec } from './audio-codec';
2
+ export declare const validateOutputFilename: <T extends "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif">({ codec, audioCodec, extension, preferLossless, }: {
3
+ codec: T;
4
+ audioCodec: AudioCodec | null;
5
+ extension: string;
6
+ preferLossless: boolean;
7
+ }) => void;
@@ -1,57 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validateOutputFilename = void 0;
4
- const validateOutputFilename = (codec, extension) => {
5
- if (codec === 'h264') {
6
- if (extension !== 'mp4' && extension !== 'mkv') {
7
- throw new TypeError('When using the H264 codec, the output filename must end in .mp4 or .mkv.');
8
- }
9
- }
10
- if (codec === 'h264-mkv') {
11
- if (extension !== 'mkv') {
12
- throw new TypeError('When using the "h264-mkv" codec, the output filename must end in ".mkv".');
13
- }
14
- }
15
- if (codec === 'h265') {
16
- if (extension !== 'mp4' && extension !== 'hevc') {
17
- throw new TypeError('When using H265 codec, the output filename must end in .mp4 or .hevc.');
18
- }
19
- }
20
- if (codec === 'vp8' || codec === 'vp9') {
21
- if (extension !== 'webm') {
22
- throw new TypeError(`When using the ${codec.toUpperCase()} codec, the output filename must end in .webm.`);
23
- }
24
- }
25
- if (codec === 'prores') {
26
- const allowedProResExtensions = ['mov', 'mkv', 'mxf'];
27
- if (!extension || !allowedProResExtensions.includes(extension)) {
28
- throw new TypeError(`When using the 'prores' codec, the output must end in one of those extensions: ${allowedProResExtensions
29
- .map((a) => `.${a}`)
30
- .join(', ')}`);
31
- }
32
- }
33
- if (codec === 'mp3') {
34
- if (extension !== 'mp3') {
35
- throw new TypeError("When using the 'mp3' codec, the output must end in .mp3");
36
- }
37
- }
38
- if (codec === 'aac') {
39
- const allowedAacExtensions = ['aac', '3gp', 'm4a', 'm4b', 'mpg', 'mpeg'];
40
- if (!extension || !allowedAacExtensions.includes(extension)) {
41
- throw new TypeError(`When using the 'aac' codec, the output must end in one of those extensions: ${allowedAacExtensions
42
- .map((a) => `.${a}`)
43
- .join(', ')}`);
44
- }
45
- }
46
- if (codec === 'wav') {
47
- if (extension !== 'wav') {
48
- throw new TypeError("When using the 'wav' codec, the output location must end in .wav.");
49
- }
50
- }
51
- if (codec === 'gif') {
52
- if (extension !== 'gif') {
53
- throw new TypeError('When using the GIF codec, the output filename must end in .gif.');
54
- }
4
+ const audio_codec_1 = require("./audio-codec");
5
+ const file_extensions_1 = require("./file-extensions");
6
+ const validateOutputFilename = ({ codec, audioCodec, extension, preferLossless, }) => {
7
+ if (!file_extensions_1.defaultFileExtensionMap[codec]) {
8
+ throw new TypeError(`The codec "${codec}" is not supported. Supported codecs are: ${Object.keys(file_extensions_1.defaultFileExtensionMap).join(', ')}`);
9
+ }
10
+ const map = file_extensions_1.defaultFileExtensionMap[codec];
11
+ const resolvedAudioCodec = audioCodec !== null && audioCodec !== void 0 ? audioCodec : (0, audio_codec_1.getDefaultAudioCodec)({ codec, preferLossless });
12
+ if (resolvedAudioCodec === null) {
13
+ if (extension !== map.default) {
14
+ throw new TypeError(`When using the ${codec} codec, the output filename must end in .${map.default}.`);
15
+ }
16
+ return;
17
+ }
18
+ if (!(resolvedAudioCodec in map.forAudioCodec)) {
19
+ throw new Error(`Audio codec ${resolvedAudioCodec} is not supported for codec ${codec}`);
20
+ }
21
+ const acceptableExtensions = map.forAudioCodec[resolvedAudioCodec].possible;
22
+ if (!acceptableExtensions.includes(extension)) {
23
+ throw new TypeError(`When using the ${codec} codec with the ${resolvedAudioCodec} audio codec, the output filename must end in one of the following: ${acceptableExtensions.join(', ')}.`);
55
24
  }
56
25
  };
57
26
  exports.validateOutputFilename = validateOutputFilename;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/renderer",
3
- "version": "3.3.41",
3
+ "version": "3.3.42",
4
4
  "description": "Renderer for Remotion",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -24,7 +24,7 @@
24
24
  "dependencies": {
25
25
  "execa": "5.1.1",
26
26
  "extract-zip": "2.0.1",
27
- "remotion": "3.3.41",
27
+ "remotion": "3.3.42",
28
28
  "source-map": "^0.8.0-beta.0",
29
29
  "ws": "8.7.0"
30
30
  },
@@ -49,13 +49,13 @@
49
49
  "vitest": "0.24.3"
50
50
  },
51
51
  "optionalDependencies": {
52
- "@remotion/compositor-darwin-arm64": "3.3.41",
53
- "@remotion/compositor-darwin-x64": "3.3.41",
54
- "@remotion/compositor-linux-arm64-gnu": "3.3.41",
55
- "@remotion/compositor-linux-arm64-musl": "3.3.41",
56
- "@remotion/compositor-linux-x64-gnu": "3.3.41",
57
- "@remotion/compositor-linux-x64-musl": "3.3.41",
58
- "@remotion/compositor-win32-x64-msvc": "3.3.41"
52
+ "@remotion/compositor-darwin-arm64": "3.3.42",
53
+ "@remotion/compositor-darwin-x64": "3.3.42",
54
+ "@remotion/compositor-linux-arm64-gnu": "3.3.42",
55
+ "@remotion/compositor-linux-arm64-musl": "3.3.42",
56
+ "@remotion/compositor-linux-x64-gnu": "3.3.42",
57
+ "@remotion/compositor-linux-x64-musl": "3.3.42",
58
+ "@remotion/compositor-win32-x64-msvc": "3.3.42"
59
59
  },
60
60
  "keywords": [
61
61
  "remotion",
@@ -67,5 +67,5 @@
67
67
  "publishConfig": {
68
68
  "access": "public"
69
69
  },
70
- "gitHead": "8e408247d151ac824864acf357782f10c89ed5f5"
70
+ "gitHead": "bfb8098300fb75d4c27220eead954c2dc8e5b4ce"
71
71
  }