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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-ffmpegjs",
3
- "version": "0.3.4",
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
- function buildAudioForVideoClips(project, videoClips) {
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 adelayMs = Math.round(Math.max(0, clip.position || 0) * 1000);
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
 
@@ -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 || 1;
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 ares = buildAudioForVideoClips(this, videoClips);
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;