simple-ffmpegjs 0.3.4 → 0.3.5
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/src/ffmpeg/audio_builder.js +12 -3
- package/src/simpleffmpeg.js +12 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simple-ffmpegjs",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Declarative video composition for Node.js — define clips, transitions, text, and audio as simple objects, and let FFmpeg handle the rest.",
|
|
5
5
|
"author": "Brayden Blackwell <braydenblackwell21@gmail.com> (https://github.com/Fats403)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Build audio filter chain for video clips.
|
|
3
|
+
*
|
|
4
|
+
* @param {Object} project - The SIMPLEFFMPEG project instance
|
|
5
|
+
* @param {Array} videoClips - Array of video clip objects
|
|
6
|
+
* @param {Map} [transitionOffsets] - Map of clip -> cumulative transition offset in seconds
|
|
7
|
+
*/
|
|
8
|
+
function buildAudioForVideoClips(project, videoClips, transitionOffsets) {
|
|
2
9
|
let audioFilter = "";
|
|
3
10
|
const labels = [];
|
|
4
11
|
|
|
@@ -15,9 +22,11 @@ function buildAudioForVideoClips(project, videoClips) {
|
|
|
15
22
|
: requestedDuration;
|
|
16
23
|
const clipDuration = Math.max(0, Math.min(requestedDuration, maxAvailable));
|
|
17
24
|
|
|
18
|
-
const
|
|
25
|
+
const offset = transitionOffsets ? (transitionOffsets.get(clip) || 0) : 0;
|
|
26
|
+
const adelayMs = Math.round(Math.max(0, (clip.position || 0) - offset) * 1000);
|
|
27
|
+
const vol = clip.volume != null ? clip.volume : 1;
|
|
19
28
|
const out = `[va${inputIndex}]`;
|
|
20
|
-
audioFilter += `[${inputIndex}:a]atrim=start=${clip.cutFrom}:duration=${clipDuration},asetpts=PTS-STARTPTS,adelay=${adelayMs}|${adelayMs}${out};`;
|
|
29
|
+
audioFilter += `[${inputIndex}:a]volume=${vol},atrim=start=${clip.cutFrom}:duration=${clipDuration},asetpts=PTS-STARTPTS,adelay=${adelayMs}|${adelayMs}${out};`;
|
|
21
30
|
labels.push(out);
|
|
22
31
|
});
|
|
23
32
|
|
package/src/simpleffmpeg.js
CHANGED
|
@@ -253,7 +253,7 @@ class SIMPLEFFMPEG {
|
|
|
253
253
|
await Promise.all(
|
|
254
254
|
resolvedClips.map((clipObj) => {
|
|
255
255
|
if (clipObj.type === "video" || clipObj.type === "audio") {
|
|
256
|
-
clipObj.volume = clipObj.volume
|
|
256
|
+
clipObj.volume = clipObj.volume != null ? clipObj.volume : 1;
|
|
257
257
|
clipObj.cutFrom = clipObj.cutFrom || 0;
|
|
258
258
|
if (clipObj.type === "video" && clipObj.transition) {
|
|
259
259
|
clipObj.transition = {
|
|
@@ -439,8 +439,18 @@ class SIMPLEFFMPEG {
|
|
|
439
439
|
}
|
|
440
440
|
|
|
441
441
|
// Audio for video clips (aligned amix)
|
|
442
|
+
// Compute cumulative transition offsets so audio adelay values
|
|
443
|
+
// match the xfade-compressed video timeline.
|
|
442
444
|
if (videoClips.length > 0) {
|
|
443
|
-
const
|
|
445
|
+
const transitionOffsets = new Map();
|
|
446
|
+
let cumOffset = 0;
|
|
447
|
+
for (let i = 0; i < videoClips.length; i++) {
|
|
448
|
+
if (i > 0 && videoClips[i].transition) {
|
|
449
|
+
cumOffset += videoClips[i].transition.duration || 0;
|
|
450
|
+
}
|
|
451
|
+
transitionOffsets.set(videoClips[i], cumOffset);
|
|
452
|
+
}
|
|
453
|
+
const ares = buildAudioForVideoClips(this, videoClips, transitionOffsets);
|
|
444
454
|
filterComplex += ares.filter;
|
|
445
455
|
finalAudioLabel = ares.finalAudioLabel || finalAudioLabel;
|
|
446
456
|
hasAudio = hasAudio || ares.hasAudio;
|