polotno-node 2.9.37 → 2.9.39
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/package.json +1 -1
- package/video-parallel.js +67 -39
package/package.json
CHANGED
package/video-parallel.js
CHANGED
|
@@ -276,47 +276,75 @@ module.exports.jsonToVideo = async function jsonToVideo(
|
|
|
276
276
|
.inputFPS(fps)
|
|
277
277
|
.videoCodec('libx264')
|
|
278
278
|
.outputOptions('-pix_fmt yuv420p')
|
|
279
|
-
.videoFilters('scale=trunc(iw/2)*2:trunc(ih/2)*2');
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
279
|
+
.videoFilters('scale=trunc(iw/2)*2:trunc(ih/2)*2');
|
|
280
|
+
|
|
281
|
+
const audioInputs = [];
|
|
282
|
+
|
|
283
|
+
// Use Promise.all to handle async ffprobe calls
|
|
284
|
+
Promise.all(
|
|
285
|
+
inputs.map((input, index) => {
|
|
286
|
+
return new Promise((resolveProbe) => {
|
|
287
|
+
const inputStartSec = (input.inputStartTime / 1000).toFixed(3);
|
|
288
|
+
const inputDurationSec = (
|
|
289
|
+
(input.inputEndTime - input.inputStartTime) /
|
|
290
|
+
1000
|
|
291
|
+
).toFixed(3);
|
|
292
|
+
const outputOffsetMs = Math.round(input.outputStartTime);
|
|
293
|
+
const volume = input.volume !== undefined ? input.volume : 1;
|
|
294
|
+
|
|
295
|
+
ffmpegCmd
|
|
296
|
+
.input(input.mp4File)
|
|
297
|
+
.inputOptions([`-ss ${inputStartSec}`, `-t ${inputDurationSec}`]);
|
|
298
|
+
|
|
299
|
+
ffmpeg.ffprobe(input.mp4File, (err, metadata) => {
|
|
300
|
+
if (
|
|
301
|
+
!err &&
|
|
302
|
+
metadata.streams.some((stream) => stream.codec_type === 'audio')
|
|
303
|
+
) {
|
|
304
|
+
audioInputs.push({
|
|
305
|
+
index: index + 1,
|
|
306
|
+
delay: outputOffsetMs,
|
|
307
|
+
volume: volume,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
resolveProbe();
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
})
|
|
314
|
+
).then(() => {
|
|
315
|
+
// Map the video stream from the image sequence
|
|
316
|
+
ffmpegCmd.outputOptions(['-map 0:v']);
|
|
317
|
+
|
|
318
|
+
// Prepare audio filter complex only if there are audio inputs
|
|
319
|
+
if (audioInputs.length > 0) {
|
|
320
|
+
const audioFilters = audioInputs.map(
|
|
321
|
+
(input, i) =>
|
|
322
|
+
`[${input.index}:a]adelay=${input.delay}|${input.delay},volume=${input.volume}[a${i}]`
|
|
323
|
+
);
|
|
324
|
+
const audioMerge = audioInputs.map((_, i) => `[a${i}]`).join('');
|
|
325
|
+
const filterComplex = `${audioFilters.join(
|
|
326
|
+
';'
|
|
327
|
+
)};${audioMerge}amix=inputs=${
|
|
328
|
+
audioInputs.length
|
|
329
|
+
}:duration=longest[aout]`;
|
|
330
|
+
|
|
331
|
+
ffmpegCmd.complexFilter(filterComplex);
|
|
332
|
+
ffmpegCmd.outputOptions(['-map [aout]']);
|
|
333
|
+
}
|
|
303
334
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
335
|
+
ffmpegCmd
|
|
336
|
+
.format(format)
|
|
337
|
+
.audioCodec('aac')
|
|
338
|
+
.on('start', (command) => console.log('FFmpeg command:', command))
|
|
339
|
+
.on('end', () => resolve())
|
|
340
|
+
.on('error', (err, stdout, stderr) => {
|
|
341
|
+
console.error('Error:', err.message);
|
|
342
|
+
console.error('stdout:', stdout);
|
|
343
|
+
console.error('stderr:', stderr);
|
|
344
|
+
reject(err);
|
|
345
|
+
})
|
|
346
|
+
.save(attrs.out);
|
|
307
347
|
});
|
|
308
|
-
|
|
309
|
-
ffmpegCmd
|
|
310
|
-
.format(format)
|
|
311
|
-
.audioCodec('aac')
|
|
312
|
-
.on('end', () => resolve())
|
|
313
|
-
.on('error', (err, stdout, stderr) => {
|
|
314
|
-
console.log(err.message);
|
|
315
|
-
console.log('stdout:\n' + stdout);
|
|
316
|
-
console.log('stderr:\n' + stderr);
|
|
317
|
-
reject(err);
|
|
318
|
-
})
|
|
319
|
-
.save(attrs.out);
|
|
320
348
|
});
|
|
321
349
|
} catch (e) {
|
|
322
350
|
throw e;
|