@remotion/renderer 4.0.516 → 4.0.517

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.
@@ -1,5 +1,10 @@
1
1
  import type { InlineAudioAsset } from 'remotion/no-react';
2
2
  import type { CancelSignal } from '../make-cancel-signal';
3
+ export type InlineAudioTrack = {
4
+ outName: string;
5
+ startInSamples: number;
6
+ durationInSamples: number;
7
+ };
3
8
  export declare const makeInlineAudioMixing: (dir: string, sampleRate: number) => {
4
9
  cleanup: () => void;
5
10
  addAsset: ({ asset, fps, totalNumberOfFrames, firstFrame, trimLeftOffset, trimRightOffset, }: {
@@ -10,7 +15,7 @@ export declare const makeInlineAudioMixing: (dir: string, sampleRate: number) =>
10
15
  trimLeftOffset: number;
11
16
  trimRightOffset: number;
12
17
  }) => void;
13
- getListOfAssets: () => string[];
18
+ getListOfAssets: () => InlineAudioTrack[];
14
19
  finish: ({ binariesDirectory, indent, logLevel, cancelSignal, sampleRate: finishSampleRate, }: {
15
20
  indent: boolean;
16
21
  logLevel: "error" | "info" | "trace" | "verbose" | "warn";
@@ -69,12 +69,14 @@ const correctFloatingPointError = (value) => {
69
69
  const BIT_DEPTH = 16;
70
70
  const BYTES_PER_SAMPLE = BIT_DEPTH / 8;
71
71
  const NUMBER_OF_CHANNELS = 2;
72
+ const WAV_HEADER_SIZE = 44;
72
73
  const makeInlineAudioMixing = (dir, sampleRate) => {
73
74
  const folderToAdd = (0, download_map_1.makeAndReturn)(dir, 'remotion-inline-audio-mixing');
74
- // asset id -> file descriptor
75
75
  const openFiles = {};
76
76
  const writtenHeaders = {};
77
77
  const toneFrequencies = {};
78
+ const startTimesInSamples = {};
79
+ const writtenDataSizes = {};
78
80
  const cleanup = () => {
79
81
  for (const fileName of Object.keys(openFiles)) {
80
82
  try {
@@ -86,42 +88,51 @@ const makeInlineAudioMixing = (dir, sampleRate) => {
86
88
  (0, delete_directory_1.deleteDirectory)(folderToAdd);
87
89
  };
88
90
  const getListOfAssets = () => {
89
- return Object.keys(openFiles);
91
+ return Object.keys(writtenHeaders)
92
+ .map((outName) => ({
93
+ outName,
94
+ startInSamples: startTimesInSamples[outName],
95
+ durationInSamples: writtenDataSizes[outName] / (NUMBER_OF_CHANNELS * BYTES_PER_SAMPLE),
96
+ }))
97
+ .sort((a, b) => a.startInSamples - b.startInSamples);
90
98
  };
91
99
  const getFilePath = (asset) => {
92
100
  return node_path_1.default.join(folderToAdd, `${asset.id}.wav`);
93
101
  };
94
- const ensureAsset = ({ asset, fps, totalNumberOfFrames, trimLeftOffset, trimRightOffset, }) => {
102
+ const ensureAsset = (asset) => {
95
103
  const filePath = getFilePath(asset);
96
- if (!openFiles[filePath]) {
104
+ if (openFiles[filePath] === undefined) {
97
105
  openFiles[filePath] = node_fs_1.default.openSync(filePath, 'w');
98
106
  }
99
107
  if (writtenHeaders[filePath]) {
100
108
  return;
101
109
  }
102
110
  writtenHeaders[filePath] = true;
103
- const expectedDataSize = Math.round((totalNumberOfFrames / fps - trimLeftOffset + trimRightOffset) *
104
- NUMBER_OF_CHANNELS *
105
- sampleRate *
106
- BYTES_PER_SAMPLE);
107
- const expectedSize = 40 + expectedDataSize;
111
+ writtenDataSizes[filePath] = 0;
108
112
  const fd = openFiles[filePath];
109
113
  (0, node_fs_1.writeSync)(fd, new Uint8Array([0x52, 0x49, 0x46, 0x46]), 0, 4, 0); // "RIFF"
110
- (0, node_fs_1.writeSync)(fd, new Uint8Array(numberTo32BiIntLittleEndian(expectedSize)), 0, 4, 4); // Remaining size
114
+ (0, node_fs_1.writeSync)(fd, numberTo32BiIntLittleEndian(36), 0, 4, 4); // Remaining size
111
115
  (0, node_fs_1.writeSync)(fd, new Uint8Array([0x57, 0x41, 0x56, 0x45]), 0, 4, 8); // "WAVE"
112
116
  (0, node_fs_1.writeSync)(fd, new Uint8Array([0x66, 0x6d, 0x74, 0x20]), 0, 4, 12); // "fmt "
113
117
  (0, node_fs_1.writeSync)(fd, new Uint8Array([BIT_DEPTH, 0x00, 0x00, 0x00]), 0, 4, 16); // fmt chunk size = 16
114
118
  (0, node_fs_1.writeSync)(fd, new Uint8Array([0x01, 0x00]), 0, 2, 20); // Audio format (PCM) = 1, set 3 if float32 would be true
115
119
  (0, node_fs_1.writeSync)(fd, new Uint8Array([NUMBER_OF_CHANNELS, 0x00]), 0, 2, 22); // Number of channels
116
- (0, node_fs_1.writeSync)(fd, new Uint8Array(numberTo32BiIntLittleEndian(sampleRate)), 0, 4, 24); // Sample rate
117
- (0, node_fs_1.writeSync)(fd, new Uint8Array(numberTo32BiIntLittleEndian(sampleRate * NUMBER_OF_CHANNELS * BYTES_PER_SAMPLE)), 0, 4, 28); // Byte rate
118
- (0, node_fs_1.writeSync)(fd, new Uint8Array(numberTo16BitLittleEndian(NUMBER_OF_CHANNELS * BYTES_PER_SAMPLE)), 0, 2, 32); // Block align
120
+ (0, node_fs_1.writeSync)(fd, numberTo32BiIntLittleEndian(sampleRate), 0, 4, 24); // Sample rate
121
+ (0, node_fs_1.writeSync)(fd, numberTo32BiIntLittleEndian(sampleRate * NUMBER_OF_CHANNELS * BYTES_PER_SAMPLE), 0, 4, 28); // Byte rate
122
+ (0, node_fs_1.writeSync)(fd, numberTo16BitLittleEndian(NUMBER_OF_CHANNELS * BYTES_PER_SAMPLE), 0, 2, 32); // Block align
119
123
  (0, node_fs_1.writeSync)(fd, numberTo16BitLittleEndian(BIT_DEPTH), 0, 2, 34); // Bits per sample
120
124
  (0, node_fs_1.writeSync)(fd, new Uint8Array([0x64, 0x61, 0x74, 0x61]), 0, 4, 36); // "data"
121
- (0, node_fs_1.writeSync)(fd, new Uint8Array(numberTo32BiIntLittleEndian(expectedDataSize)), 0, 4, 40); // Remaining size
125
+ (0, node_fs_1.writeSync)(fd, numberTo32BiIntLittleEndian(0), 0, 4, 40); // Data size
122
126
  };
123
127
  const finish = async ({ binariesDirectory, indent, logLevel, cancelSignal, sampleRate: finishSampleRate, }) => {
124
- for (const fileName of Object.keys(openFiles)) {
128
+ for (const fileName of Object.keys(writtenHeaders)) {
129
+ const fd = openFiles[fileName];
130
+ const dataSize = Math.max(0, node_fs_1.default.fstatSync(fd).size - WAV_HEADER_SIZE);
131
+ writtenDataSizes[fileName] = dataSize;
132
+ (0, node_fs_1.writeSync)(fd, numberTo32BiIntLittleEndian(36 + dataSize), 0, 4, 4);
133
+ (0, node_fs_1.writeSync)(fd, numberTo32BiIntLittleEndian(dataSize), 0, 4, 40);
134
+ node_fs_1.default.closeSync(fd);
135
+ delete openFiles[fileName];
125
136
  const frequency = toneFrequencies[fileName];
126
137
  if (frequency === 1) {
127
138
  continue;
@@ -137,21 +148,12 @@ const makeInlineAudioMixing = (dir, sampleRate) => {
137
148
  cancelSignal,
138
149
  sampleRate: finishSampleRate,
139
150
  });
140
- try {
141
- node_fs_1.default.closeSync(openFiles[fileName]);
142
- }
143
- catch (_a) { }
144
151
  node_fs_1.default.renameSync(tmpFile, fileName);
145
152
  }
146
153
  };
147
154
  const addAsset = ({ asset, fps, totalNumberOfFrames, firstFrame, trimLeftOffset, trimRightOffset, }) => {
148
- ensureAsset({
149
- asset,
150
- fps,
151
- totalNumberOfFrames,
152
- trimLeftOffset,
153
- trimRightOffset,
154
- });
155
+ var _a;
156
+ ensureAsset(asset);
155
157
  const filePath = getFilePath(asset);
156
158
  if (toneFrequencies[filePath] !== undefined &&
157
159
  toneFrequencies[filePath] !== asset.toneFrequency) {
@@ -159,6 +161,15 @@ const makeInlineAudioMixing = (dir, sampleRate) => {
159
161
  }
160
162
  const fileDescriptor = openFiles[filePath];
161
163
  toneFrequencies[filePath] = asset.toneFrequency;
164
+ const assetStartInVideo = (_a = asset.startInVideo) !== null && _a !== void 0 ? _a : firstFrame;
165
+ const firstFrameForAsset = Math.max(assetStartInVideo, firstFrame);
166
+ const startInSamples = Math.max(0, Math.floor(correctFloatingPointError(((firstFrameForAsset - firstFrame) / fps - trimLeftOffset) *
167
+ sampleRate)));
168
+ if (startTimesInSamples[filePath] !== undefined &&
169
+ startTimesInSamples[filePath] !== startInSamples) {
170
+ throw new Error(`The start time for inline audio asset ${asset.id} changed from ${startTimesInSamples[filePath]} to ${startInSamples} samples`);
171
+ }
172
+ startTimesInSamples[filePath] = startInSamples;
162
173
  let arr = new Int16Array(asset.audio);
163
174
  const isFirst = asset.frame === firstFrame;
164
175
  const isLast = asset.frame === totalNumberOfFrames + firstFrame - 1;
@@ -176,25 +187,17 @@ const makeInlineAudioMixing = (dir, sampleRate) => {
176
187
  Math.ceil(correctFloatingPointError(samplesToShaveFromEnd)) *
177
188
  NUMBER_OF_CHANNELS);
178
189
  }
179
- const positionInSeconds = (asset.frame - firstFrame) / fps - (isFirst ? 0 : trimLeftOffset);
190
+ const positionInRenderInSeconds = (asset.frame - firstFrame) / fps - (isFirst ? 0 : trimLeftOffset);
180
191
  // Always rounding down to ensure there are no gaps when the samples don't align
181
192
  // In @remotion/media, we also round down the sample start timestamp and round up the end timestamp
182
193
  // This might lead to overlapping, hopefully aligning perfectly!
183
194
  // Test case: https://github.com/remotion-dev/remotion/issues/5758
184
- const position = Math.floor(correctFloatingPointError(positionInSeconds * sampleRate)) *
195
+ const position = (Math.floor(correctFloatingPointError(positionInRenderInSeconds * sampleRate)) -
196
+ startInSamples) *
185
197
  NUMBER_OF_CHANNELS *
186
198
  BYTES_PER_SAMPLE;
187
- (0, node_fs_1.writeSync)(
188
- // fs
189
- fileDescriptor,
190
- // data
191
- arr,
192
- // offset of data
193
- 0,
194
- // length
195
- arr.byteLength,
196
- // position
197
- 44 + position);
199
+ (0, node_fs_1.writeSync)(fileDescriptor, arr, 0, arr.byteLength, WAV_HEADER_SIZE + position);
200
+ writtenDataSizes[filePath] = Math.max(writtenDataSizes[filePath], position + arr.byteLength);
198
201
  };
199
202
  return {
200
203
  cleanup,
@@ -18,20 +18,6 @@ const audio_codec_1 = require("./options/audio-codec");
18
18
  const tmp_dir_1 = require("./tmp-dir");
19
19
  const truthy_1 = require("./truthy");
20
20
  const validate_number_of_gif_loops_1 = require("./validate-number-of-gif-loops");
21
- const codecSupportsFastStart = {
22
- 'h264-mkv': false,
23
- 'h264-ts': false,
24
- h264: true,
25
- h265: true,
26
- av1: true,
27
- aac: false,
28
- gif: false,
29
- mp3: false,
30
- prores: false,
31
- vp8: false,
32
- vp9: false,
33
- wav: false,
34
- };
35
21
  const REMOTION_FILELIST_TOKEN = 'remotion-filelist';
36
22
  const internalCombineChunks = async ({ outputLocation: output, onProgress, codec, fps, numberOfGifLoops, audioBitrate, indent, logLevel, binariesDirectory, cancelSignal, metadata, audioFiles, videoFiles, framesPerChunk, audioCodec, preferLossless, everyNthFrame, frameRange, compositionDurationInFrames, sampleRate, }) => {
37
23
  (0, validate_number_of_gif_loops_1.validateNumberOfGifLoops)(numberOfGifLoops, codec);
@@ -136,7 +122,6 @@ const internalCombineChunks = async ({ outputLocation: output, onProgress, codec
136
122
  binariesDirectory,
137
123
  fps,
138
124
  cancelSignal,
139
- addFaststart: codecSupportsFastStart[codec],
140
125
  metadata,
141
126
  numberOfGifLoops,
142
127
  });
@@ -12,6 +12,7 @@ const compress_audio_1 = require("./compress-audio");
12
12
  const delete_directory_1 = require("./delete-directory");
13
13
  const logger_1 = require("./logger");
14
14
  const merge_audio_track_1 = require("./merge-audio-track");
15
+ const merge_inline_audio_tracks_1 = require("./merge-inline-audio-tracks");
15
16
  const audio_codec_1 = require("./options/audio-codec");
16
17
  const preprocess_audio_track_1 = require("./preprocess-audio-track");
17
18
  const truthy_1 = require("./truthy");
@@ -73,16 +74,29 @@ const createAudio = async ({ assets, onDownload, fps, logLevel, onProgress, down
73
74
  sampleRate,
74
75
  });
75
76
  const inlinedAudio = downloadMap.inlineAudioMixing.getListOfAssets();
77
+ const mergedInlineAudio = await (0, merge_inline_audio_tracks_1.mergeInlineAudioTracks)({
78
+ tracks: inlinedAudio,
79
+ downloadMap,
80
+ remotionRoot,
81
+ indent,
82
+ logLevel,
83
+ binariesDirectory,
84
+ cancelSignal,
85
+ fps,
86
+ chunkLengthInSeconds,
87
+ sampleRate,
88
+ });
76
89
  const preprocessed = [
77
90
  ...audioTracks.filter(truthy_1.truthy),
78
- ...inlinedAudio.map((asset) => ({
79
- outName: asset,
80
- filter: {
81
- filter: null,
82
- pad_start: null,
83
- pad_end: null,
84
- },
85
- })),
91
+ ...(mergedInlineAudio
92
+ ? [
93
+ (0, merge_inline_audio_tracks_1.inlineAudioTrackToPreprocessedAudioTrack)({
94
+ track: mergedInlineAudio,
95
+ relativeToInSamples: 0,
96
+ padToDurationInSamples: Math.round(chunkLengthInSeconds * sampleRate),
97
+ }),
98
+ ]
99
+ : []),
86
100
  ];
87
101
  const merged = path_1.default.join(downloadMap.audioPreprocessing, 'merged.wav');
88
102
  const extension = (0, audio_codec_1.getExtensionFromAudioCodec)(audioCodec);