From 1103c97eeabba3beb562189e8acc68474419bda1 Mon Sep 17 00:00:00 2001 From: Jonathan Cobb Date: Mon, 28 Dec 2020 01:26:09 -0500 Subject: [PATCH] improve support for input videos with no audio track --- docs/jvc_js.md | 3 ++ src/main/java/jvc/model/JStreamType.java | 2 +- src/main/java/jvc/model/info/JTrack.java | 6 +++- src/main/java/jvc/model/js/JAssetJs.java | 2 ++ .../java/jvc/operation/exec/ExecBase.java | 1 + .../jvc/operation/exec/MergeAudioExec.java | 28 ++++++++++----- .../java/jvc/operation/exec/OverlayExec.java | 36 ++++++++++++++++--- 7 files changed, 63 insertions(+), 15 deletions(-) diff --git a/docs/jvc_js.md b/docs/jvc_js.md index 78bf08d..e1fc4fb 100644 --- a/docs/jvc_js.md +++ b/docs/jvc_js.md @@ -67,6 +67,9 @@ Ratio of width / height (video or image). #### `samplingRate` Sampling rate in Hz (audio). +#### `channelLayout` +Channel layout (audio). + #### `tracks` An array of the A/V tracks in a video. Only includes audio and video tracks. diff --git a/src/main/java/jvc/model/JStreamType.java b/src/main/java/jvc/model/JStreamType.java index 3a39719..f1f9fc3 100644 --- a/src/main/java/jvc/model/JStreamType.java +++ b/src/main/java/jvc/model/JStreamType.java @@ -18,7 +18,7 @@ public enum JStreamType { mkv (".mkv", video), mp3 (".mp3", audio), mpeg_audio (".mp3", audio), - aac (".aac", audio), + aac (".m4a", audio), flac (".flac", audio), png (".png", image), jpg (".jpg", image), diff --git a/src/main/java/jvc/model/info/JTrack.java b/src/main/java/jvc/model/info/JTrack.java index bb373d9..350b081 100644 --- a/src/main/java/jvc/model/info/JTrack.java +++ b/src/main/java/jvc/model/info/JTrack.java @@ -45,7 +45,11 @@ public class JTrack { public String channelLayout () { if (!empty(channelLayout)) { - return channelLayout.equals("L R") ? "stereo": channelLayout; + return channelLayout.equals("L R") + ? "stereo" + : channelLayout.equals("C") + ? "mono" + : channelLayout; } if (!empty(channels)) { if (isOnlyDigits(channels)) { diff --git a/src/main/java/jvc/model/js/JAssetJs.java b/src/main/java/jvc/model/js/JAssetJs.java index 5d4cc3b..2efc8e1 100644 --- a/src/main/java/jvc/model/js/JAssetJs.java +++ b/src/main/java/jvc/model/js/JAssetJs.java @@ -20,6 +20,7 @@ public class JAssetJs { @Getter public final Integer width; @Getter public final Integer height; @Getter public final Double aspectRatio; + @Getter public final String channelLayout; @Getter public final Integer samplingRate; @Getter public JTrackJs[] allTracks = EMPTY_TRACKS; @Getter public JTrackJs[] tracks = EMPTY_TRACKS; @@ -42,6 +43,7 @@ public class JAssetJs { this.height = h == null ? null : h.intValue(); this.aspectRatio = asset.aspectRatio() == null ? Double.NaN : asset.aspectRatio().doubleValue(); + this.channelLayout = asset.hasChannelLayout() ? asset.channelLayout() : null; this.samplingRate = asset.hasSamplingRate() ? asset.samplingRate().intValue() : 0; if (asset.hasInfo()) { diff --git a/src/main/java/jvc/operation/exec/ExecBase.java b/src/main/java/jvc/operation/exec/ExecBase.java index 9db62a9..5abc948 100644 --- a/src/main/java/jvc/operation/exec/ExecBase.java +++ b/src/main/java/jvc/operation/exec/ExecBase.java @@ -78,6 +78,7 @@ public abstract class ExecBase { System.out.println(script); return ""; } else { + log.info("exec: "+script); return execScript(script); } } diff --git a/src/main/java/jvc/operation/exec/MergeAudioExec.java b/src/main/java/jvc/operation/exec/MergeAudioExec.java index df5c6d8..7313111 100644 --- a/src/main/java/jvc/operation/exec/MergeAudioExec.java +++ b/src/main/java/jvc/operation/exec/MergeAudioExec.java @@ -27,21 +27,30 @@ public class MergeAudioExec extends SingleOrMultiSourceExecBase ctx) { + @Override protected void addCommandContext(MergeAudioOperation op, + JSingleOperationContext opCtx, + Map ctx) { final JAsset audio = opCtx.assetManager.resolve(op.getInsert()); final JsEngine js = opCtx.toolbox.getJs(); final BigDecimal insertAt = op.getAt(ctx, js); @@ -64,7 +73,7 @@ public class MergeAudioExec extends SingleOrMultiSourceExecBase ctx = new HashMap<>(); ctx.put("ffmpeg", toolbox.getFfmpeg()); - final JStreamType streamType = audio.getFormat().getStreamType(); + final JStreamType streamType = audio.audioExtension(); final JAsset padded = new JAsset().setPath(abs(assetManager.assetPath(op, audio, streamType))); final String paddedName = basename(padded.getPath()); ctx.put("padded", paddedName); @@ -98,6 +107,9 @@ public class MergeAudioExec extends SingleOrMultiSourceExecBase { public static final String OVERLAY_TEMPLATE - = "ffmpeg -i {{{source.path}}} -i {{{overlay.path}}} " + = "ffmpeg -i {{{source.path}}} " + "{{#if hasMainStart}}-ss {{mainStart}} {{/if}}" + "{{#if hasMainEnd}}-t {{mainDuration}} {{/if}}" + + "-i {{{overlay.path}}} " + + "-filter_complex \"" - + "[1:v] setpts=PTS-STARTPTS+(1/TB){{#exists width}}, scale={{width}}x{{height}}{{/exists}} [1v]; " - + "[0:v][1v] overlay={{{overlayFilterConfig}}} " - + "\" -y {{{output.path}}}"; + + "[1:v] setpts=PTS-STARTPTS" + + "{{#exists width}}, scale={{width}}x{{height}}{{/exists}}" + + " [1v]; " + + "[0:v][1v] overlay={{{overlayFilterConfig}}} [v]; " + + + "{{#if source.hasAudio}}" + // source has audio -- mix with overlay + + "[1:a] setpts=PTS-STARTPTS{{#if hasOverlayStart}}-({{overlayStart}}/TB){{/if}} [1a]; " + + "[0:a][1a] amix=inputs=2 [merged]" + + "{{else}}" + // source has no audio -- mix null source with overlay + + "anullsrc=channel_layout={{overlay.channelLayout}}:sample_rate={{overlay.samplingRate}}:duration={{source.duration}} [silence]; " + // + "[1:a] setpts=PTS-STARTPTS{{#if hasOverlayStart}}-({{overlayStart}}/TB){{/if}} [1a]; " + + "[silence][1:a] amix=inputs=2 [merged]" + + "{{/if}}" + + + "\" " + + "-map \"[v]\" -map \"[merged]\" " + + " -y {{{output.path}}}"; @Override public Map operate(OverlayOperation op, Toolbox toolbox, AssetManager assetManager) { @@ -81,7 +100,14 @@ public class OverlayExec extends ExecBase { final StringBuilder b = new StringBuilder(); final BigDecimal startTime = overlay.getStartTime(ctx, js); final BigDecimal endTime = overlay.hasEndTime() ? overlay.getEndTime(ctx, js) : overlaySource.duration(); - b.append("enable=between(t\\,").append(startTime).append("\\,").append(endTime).append(")"); + ctx.put("overlayStart", startTime.doubleValue()); + ctx.put("hasOverlayStart", !startTime.equals(ZERO)); + ctx.put("overlayEnd", endTime.doubleValue()); + b.append("enable=between(t\\,") + .append(startTime.doubleValue()) + .append("\\,") + .append(endTime.doubleValue()) + .append(")"); if (overlay.hasX() && overlay.hasY()) { b.append(":x=").append(overlay.getX(ctx, js).intValue())