@@ -4,10 +4,12 @@ | |||||
# | # | ||||
# Usage: | # Usage: | ||||
# | # | ||||
# jaddsilence in-file out-file | |||||
# jaddsilence in-file out-file [channel-mode] [sampling-rate] | |||||
# | # | ||||
# in-file : input video file | |||||
# out-file : write output file here | |||||
# in-file : input video file | |||||
# out-file : write output file here | |||||
# channel-mode : channel layout, usually 'mono' or 'stereo'. Default is stereo | |||||
# sampling-rate : sampling rate, in Hz. Default is 48000 | |||||
# | # | ||||
SCRIPT="${0}" | SCRIPT="${0}" | ||||
SCRIPT_DIR="$(cd "$(dirname "${SCRIPT}")" && pwd)" | SCRIPT_DIR="$(cd "$(dirname "${SCRIPT}")" && pwd)" | ||||
@@ -15,6 +17,8 @@ SCRIPT_DIR="$(cd "$(dirname "${SCRIPT}")" && pwd)" | |||||
IN_FILE="${1?no video-file provided}" | IN_FILE="${1?no video-file provided}" | ||||
OUT_FILE="${2?no out-file provided}" | OUT_FILE="${2?no out-file provided}" | ||||
CHANNEL_LAYOUT="${3}" | |||||
SAMPLING_RATE="${4}" | |||||
echo " | echo " | ||||
{ | { | ||||
@@ -28,7 +32,9 @@ echo " | |||||
\"name\": \"with_silence\", | \"name\": \"with_silence\", | ||||
\"dest\": \"${OUT_FILE}\" | \"dest\": \"${OUT_FILE}\" | ||||
}, | }, | ||||
\"source\": \"input\" | |||||
\"source\": \"input\"$(if [[ -n "${CHANNEL_LAYOUT}" ]] ; then echo ", | |||||
\"channelLayout\": \"${CHANNEL_LAYOUT}\"" ; fi)$(if [[ -n "${SAMPLING_RATE}" ]] ; then echo ", | |||||
\"samplingRate: \"${SAMPLING_RATE}\"" ; fi) | |||||
} | } | ||||
] | ] | ||||
} | } | ||||
@@ -1,11 +1,15 @@ | |||||
# Complex Example | # Complex Example | ||||
Here is a complex example using multiple assets and operations. | Here is a complex example using multiple assets and operations. | ||||
Note that comments, which are not usually legal in JSON, are allowed in JVCL files. | |||||
Note that comments, which are not usually legal in JSON, are allowed in | |||||
JVCL files. | |||||
If you have other JSON-aware tools that need to read JVLC files, you may not want to | |||||
use this comment syntax. The `asset` and `operation` JSON objects also support a `comment` | |||||
field, which can be used as well. | |||||
If you have other JSON-aware tools that need to read JVLC files, you may not | |||||
want to use this comment syntax. The `asset` and `operation` JSON objects also | |||||
support a `comment` field, which can be used as well. | |||||
<sub><sup>Doug C: I promise these will always be just comments; jvcl will never | |||||
use [comments as parsing directives or otherwise break interoperability](https://web.archive.org/web/20120507155137/https://plus.google.com/118095276221607585885/posts/RK8qyGVaGSr) (note: disable javascript to view this link)</sup></sub> | |||||
```js | ```js | ||||
{ | { | ||||
@@ -188,6 +192,15 @@ field, which can be used as well. | |||||
"source": "vid2", // main video asset | "source": "vid2", // main video asset | ||||
"insert": "bull-roar", // audio asset to insert | "insert": "bull-roar", // audio asset to insert | ||||
"at": "5" // when (on the video timeline) to start playing the audio. default is 0 (beginning) | "at": "5" // when (on the video timeline) to start playing the audio. default is 0 (beginning) | ||||
}, | |||||
// add-silence example | |||||
{ | |||||
"operation": "add-silence", // name of the operation | |||||
"creates": "v2_silent", // output asset name | |||||
"source": "v2", // main video asset | |||||
"channelLayout": "stereo", // optional channel layout, usually 'mono' or 'stereo'. Default is 'stereo' | |||||
"samplingRate": 48000 // optional samping rate, in Hz. default is 48000 | |||||
} | } | ||||
] | ] | ||||
} | } | ||||
@@ -177,6 +177,10 @@ public class JAsset implements JsObjectView { | |||||
@JsonIgnore public String getChannelLayout() { return channelLayout(); } | @JsonIgnore public String getChannelLayout() { return channelLayout(); } | ||||
public boolean hasChannelLayout() { return channelLayout() != null; } | public boolean hasChannelLayout() { return channelLayout() != null; } | ||||
public JFileExtension audioExtension() { return hasInfo() ? getInfo().audioExtension() : null; } | |||||
@JsonIgnore public JFileExtension getAudioExtension() { return audioExtension(); } | |||||
public boolean hasAudioExtension() { return audioExtension() != null; } | |||||
public JAsset init(AssetManager assetManager, Toolbox toolbox) { | public JAsset init(AssetManager assetManager, Toolbox toolbox) { | ||||
final JAsset asset = initPath(assetManager); | final JAsset asset = initPath(assetManager); | ||||
if (!asset.hasListAssets()) { | if (!asset.hasListAssets()) { | ||||
@@ -7,10 +7,12 @@ import lombok.AllArgsConstructor; | |||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import static jvcl.model.info.JTrackType.*; | import static jvcl.model.info.JTrackType.*; | ||||
import static org.cobbzilla.util.daemon.ZillaRuntime.die; | |||||
@AllArgsConstructor @Slf4j | @AllArgsConstructor @Slf4j | ||||
public enum JFileExtension { | public enum JFileExtension { | ||||
avc (".mp4", video), | |||||
mp4 (".mp4", video), | mp4 (".mp4", video), | ||||
mkv (".mkv", video), | mkv (".mkv", video), | ||||
mp3 (".mp3", audio), | mp3 (".mp3", audio), | ||||
@@ -48,11 +50,9 @@ public enum JFileExtension { | |||||
if (track.hasFormat()) { | if (track.hasFormat()) { | ||||
try { | try { | ||||
return fromString(track.getFormat().replace(" ", "_")); | return fromString(track.getFormat().replace(" ", "_")); | ||||
} catch (Exception e) { | |||||
log.warn("fromTrack: unrecognized format: "+track.getFormat()); | |||||
} | |||||
} catch (Exception ignored) { } | |||||
} | } | ||||
return null; | |||||
return die("fromTrack: unrecognized file extension/format: "+track.getFileExtension()+"/"+track.getFormat()); | |||||
} | } | ||||
} | } |
@@ -120,6 +120,11 @@ public class JMediaInfo { | |||||
return null; | return null; | ||||
} | } | ||||
public JFileExtension audioExtension() { | |||||
final JTrack audio = firstTrack(JTrackType.audio); | |||||
return audio == null ? null : JFileExtension.fromTrack(audio); | |||||
} | |||||
public BigDecimal width() { | public BigDecimal width() { | ||||
if (emptyMedia()) return ZERO; | if (emptyMedia()) return ZERO; | ||||
// find the first video track | // find the first video track | ||||
@@ -44,7 +44,9 @@ public class JTrack { | |||||
@JsonProperty("ChannelLayout") @Getter @Setter private String channelLayout; | @JsonProperty("ChannelLayout") @Getter @Setter private String channelLayout; | ||||
public String channelLayout () { | public String channelLayout () { | ||||
if (!empty(channelLayout)) return channelLayout; | |||||
if (!empty(channelLayout)) { | |||||
return channelLayout.equals("L R") ? "stereo": channelLayout; | |||||
} | |||||
if (!empty(channels)) { | if (!empty(channels)) { | ||||
if (isOnlyDigits(channels)) { | if (isOnlyDigits(channels)) { | ||||
switch (parseInt(channels)) { | switch (parseInt(channels)) { | ||||
@@ -1,5 +1,15 @@ | |||||
package jvcl.operation; | package jvcl.operation; | ||||
import jvcl.model.operation.JSingleSourceOperation; | import jvcl.model.operation.JSingleSourceOperation; | ||||
import lombok.Getter; | |||||
import lombok.Setter; | |||||
public class AddSilenceOperation extends JSingleSourceOperation {} | |||||
public class AddSilenceOperation extends JSingleSourceOperation { | |||||
private static final String DEFAULT_CHANNEL_LAYOUT = "stereo"; | |||||
private static final Integer DEFAULT_SAMPLING_RATE = 48000; | |||||
@Getter @Setter private String channelLayout = DEFAULT_CHANNEL_LAYOUT; | |||||
@Getter @Setter private Integer samplingRate = DEFAULT_SAMPLING_RATE; | |||||
} |
@@ -14,7 +14,12 @@ import java.util.Map; | |||||
@Slf4j | @Slf4j | ||||
public class AddSilenceExec extends SingleOrMultiSourceExecBase<AddSilenceOperation> { | public class AddSilenceExec extends SingleOrMultiSourceExecBase<AddSilenceOperation> { | ||||
public static final String ADD_SILENCE_TEMPLATE = ""; | |||||
public static final String ADD_SILENCE_TEMPLATE | |||||
= "{{{ffmpeg}}} -i {{{source.path}}} -i {{{silence.path}}} " | |||||
+ "-map 0:v -map 1:a -c:v copy -shortest " | |||||
+ "-y {{{output.path}}}"; | |||||
@Override protected String getProcessTemplate() { return ADD_SILENCE_TEMPLATE; } | |||||
@Override public void operate(AddSilenceOperation op, Toolbox toolbox, AssetManager assetManager) { | @Override public void operate(AddSilenceOperation op, Toolbox toolbox, AssetManager assetManager) { | ||||
final JSingleOperationContext opCtx = op.getSingleInputContext(assetManager); | final JSingleOperationContext opCtx = op.getSingleInputContext(assetManager); | ||||
@@ -26,11 +31,10 @@ public class AddSilenceExec extends SingleOrMultiSourceExecBase<AddSilenceOperat | |||||
ctx.put("ffmpeg", toolbox.getFfmpeg()); | ctx.put("ffmpeg", toolbox.getFfmpeg()); | ||||
ctx.put("source", source); | ctx.put("source", source); | ||||
operate(op, toolbox, assetManager, source, output, formatType, ctx); | |||||
} | |||||
final JAsset silence = createSilence(op, toolbox, assetManager, source.duration(), source); | |||||
ctx.put("silence", silence); | |||||
@Override protected void process(Map<String, Object> ctx, AddSilenceOperation addSilenceOperation, JAsset source, JAsset output, JAsset asset, Toolbox toolbox, AssetManager assetManager) { | |||||
// todo | |||||
operate(op, toolbox, assetManager, source, output, formatType, ctx); | |||||
} | } | ||||
} | } |
@@ -1,6 +1,7 @@ | |||||
package jvcl.operation.exec; | package jvcl.operation.exec; | ||||
import jvcl.model.JAsset; | import jvcl.model.JAsset; | ||||
import jvcl.model.JFileExtension; | |||||
import jvcl.model.operation.JOperation; | import jvcl.model.operation.JOperation; | ||||
import jvcl.service.AssetManager; | import jvcl.service.AssetManager; | ||||
import jvcl.service.Toolbox; | import jvcl.service.Toolbox; | ||||
@@ -8,8 +9,11 @@ import lombok.extern.slf4j.Slf4j; | |||||
import org.cobbzilla.util.handlebars.HandlebarsUtil; | import org.cobbzilla.util.handlebars.HandlebarsUtil; | ||||
import java.io.File; | import java.io.File; | ||||
import java.math.BigDecimal; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | import java.util.Map; | ||||
import static org.cobbzilla.util.daemon.ZillaRuntime.die; | |||||
import static org.cobbzilla.util.io.FileUtil.abs; | import static org.cobbzilla.util.io.FileUtil.abs; | ||||
import static org.cobbzilla.util.io.FileUtil.basename; | import static org.cobbzilla.util.io.FileUtil.basename; | ||||
import static org.cobbzilla.util.system.CommandShell.execScript; | import static org.cobbzilla.util.system.CommandShell.execScript; | ||||
@@ -46,4 +50,40 @@ public abstract class ExecBase<OP extends JOperation> { | |||||
return execScript(script); | return execScript(script); | ||||
} | } | ||||
} | } | ||||
public static final String CREATE_SILENCE_TEMPLATE | |||||
= "{{{ffmpeg}}} -f lavfi " | |||||
+ "-i anullsrc=channel_layout={{channelLayout}}:sample_rate={{samplingRate}} " | |||||
+ "-t {{duration}} " | |||||
+ "-y {{{silence.path}}}"; | |||||
protected JAsset createSilence(OP op, | |||||
Toolbox toolbox, | |||||
AssetManager assetManager, | |||||
BigDecimal duration, | |||||
JAsset asset) { | |||||
final Map<String, Object> ctx = new HashMap<>(); | |||||
ctx.put("ffmpeg", toolbox.getFfmpeg()); | |||||
ctx.put("duration", duration); | |||||
if (!asset.hasSamplingRate()) return die("createSilence: no sampling rate could be determined: "+asset); | |||||
ctx.put("samplingRate", asset.samplingRate()); | |||||
if (!asset.hasChannelLayout()) return die("createSilence: no channel layout could be determined: "+asset); | |||||
ctx.put("channelLayout", asset.channelLayout()); | |||||
final JFileExtension ext = asset.audioExtension(); | |||||
final File silenceFile = assetManager.assetPath(op, asset, ext, new Object[]{duration}); | |||||
final JAsset silence = new JAsset().setPath(abs(silenceFile)); | |||||
ctx.put("silence", silence); | |||||
final String script = renderScript(toolbox, ctx, CREATE_SILENCE_TEMPLATE); | |||||
log.debug("createSilence: running script: "+script); | |||||
final String scriptOutput = exec(script, op.isNoExec()); | |||||
log.debug("createSilence: command output: "+scriptOutput); | |||||
return silence; | |||||
} | |||||
} | } |
@@ -18,6 +18,8 @@ import static org.cobbzilla.util.string.StringUtil.safeShellArg; | |||||
@Slf4j | @Slf4j | ||||
public class LetterboxExec extends SingleOrMultiSourceExecBase<LetterboxOperation> { | public class LetterboxExec extends SingleOrMultiSourceExecBase<LetterboxOperation> { | ||||
public static final String DEFAULT_LETTERBOX_COLOR = "black"; | |||||
public static final String LETTERBOX_TEMPLATE | public static final String LETTERBOX_TEMPLATE | ||||
= "{{ffmpeg}} -i {{{source.path}}} -filter_complex \"" | = "{{ffmpeg}} -i {{{source.path}}} -filter_complex \"" | ||||
+ "pad=" | + "pad=" | ||||
@@ -28,7 +30,7 @@ public class LetterboxExec extends SingleOrMultiSourceExecBase<LetterboxOperatio | |||||
+ "color={{{color}}}" | + "color={{{color}}}" | ||||
+ "\" -y {{{output.path}}}"; | + "\" -y {{{output.path}}}"; | ||||
public static final String DEFAULT_LETTERBOX_COLOR = "black"; | |||||
@Override protected String getProcessTemplate() { return LETTERBOX_TEMPLATE; } | |||||
@Override public void operate(LetterboxOperation op, Toolbox toolbox, AssetManager assetManager) { | @Override public void operate(LetterboxOperation op, Toolbox toolbox, AssetManager assetManager) { | ||||
@@ -57,20 +59,4 @@ public class LetterboxExec extends SingleOrMultiSourceExecBase<LetterboxOperatio | |||||
operate(op, toolbox, assetManager, source, output, formatType, ctx); | operate(op, toolbox, assetManager, source, output, formatType, ctx); | ||||
} | } | ||||
@Override protected void process(Map<String, Object> ctx, | |||||
LetterboxOperation op, | |||||
JAsset source, | |||||
JAsset output, | |||||
JAsset subOutput, | |||||
Toolbox toolbox, | |||||
AssetManager assetManager) { | |||||
ctx.put("source", source); | |||||
ctx.put("output", subOutput); | |||||
final String script = renderScript(toolbox, ctx, LETTERBOX_TEMPLATE); | |||||
log.debug("operate: running script: "+script); | |||||
final String scriptOutput = exec(script, op.isNoExec()); | |||||
log.debug("operate: command output: "+scriptOutput); | |||||
} | |||||
} | } |
@@ -23,12 +23,6 @@ import static org.cobbzilla.util.io.FileUtil.*; | |||||
@Slf4j | @Slf4j | ||||
public class MergeAudioExec extends SingleOrMultiSourceExecBase<MergeAudioOperation> { | public class MergeAudioExec extends SingleOrMultiSourceExecBase<MergeAudioOperation> { | ||||
public static final String CREATE_SILENCE_TEMPLATE | |||||
= "{{{ffmpeg}}} -f lavfi " | |||||
+ "-i anullsrc=channel_layout={{channelLayout}}:sample_rate={{samplingRate}} " | |||||
+ "-t {{duration}} " | |||||
+ "-y {{{silence.path}}}"; | |||||
public static final String PAD_WITH_SILENCE_TEMPLATE | public static final String PAD_WITH_SILENCE_TEMPLATE | ||||
= "cd {{{tempDir}}} && {{{ffmpeg}}} -f concat -i {{{playlist.path}}} -codec copy -y {{{padded}}}"; | = "cd {{{tempDir}}} && {{{ffmpeg}}} -f concat -i {{{playlist.path}}} -codec copy -y {{{padded}}}"; | ||||
@@ -39,6 +33,8 @@ public class MergeAudioExec extends SingleOrMultiSourceExecBase<MergeAudioOperat | |||||
+ "-map 0:v -map \"[merged]\" -c:v copy " | + "-map 0:v -map \"[merged]\" -c:v copy " | ||||
+ "-y {{{output.path}}}"; | + "-y {{{output.path}}}"; | ||||
@Override protected String getProcessTemplate() { return MERGE_AUDIO_TEMPLATE; } | |||||
@Override public void operate(MergeAudioOperation op, Toolbox toolbox, AssetManager assetManager) { | @Override public void operate(MergeAudioOperation op, Toolbox toolbox, AssetManager assetManager) { | ||||
final JSingleOperationContext opCtx = op.getSingleInputContext(assetManager); | final JSingleOperationContext opCtx = op.getSingleInputContext(assetManager); | ||||
final JAsset source = opCtx.source; | final JAsset source = opCtx.source; | ||||
@@ -65,35 +61,6 @@ public class MergeAudioExec extends SingleOrMultiSourceExecBase<MergeAudioOperat | |||||
operate(op, toolbox, assetManager, source, output, formatType, ctx); | operate(op, toolbox, assetManager, source, output, formatType, ctx); | ||||
} | } | ||||
protected JAsset createSilence(MergeAudioOperation op, | |||||
Toolbox toolbox, | |||||
AssetManager assetManager, | |||||
BigDecimal duration, | |||||
JAsset audio) { | |||||
final Map<String, Object> ctx = new HashMap<>(); | |||||
ctx.put("ffmpeg", toolbox.getFfmpeg()); | |||||
ctx.put("duration", duration); | |||||
if (!audio.hasSamplingRate()) return die("createSilence: no sampling rate could be determined: "+audio); | |||||
ctx.put("samplingRate", audio.samplingRate()); | |||||
if (!audio.hasChannelLayout()) return die("createSilence: no channel layout could be determined: "+audio); | |||||
ctx.put("channelLayout", audio.channelLayout()); | |||||
final JFileExtension ext = audio.getFormat().getFileExtension(); | |||||
final File silenceFile = assetManager.assetPath(op, audio, ext, new Object[]{duration}); | |||||
final JAsset silence = new JAsset().setPath(abs(silenceFile)); | |||||
ctx.put("silence", silence); | |||||
final String script = renderScript(toolbox, ctx, CREATE_SILENCE_TEMPLATE); | |||||
log.debug("operate: running script: "+script); | |||||
final String scriptOutput = exec(script, op.isNoExec()); | |||||
log.debug("operate: command output: "+scriptOutput); | |||||
return silence; | |||||
} | |||||
protected JAsset padWithSilence(MergeAudioOperation op, | protected JAsset padWithSilence(MergeAudioOperation op, | ||||
Toolbox toolbox, | Toolbox toolbox, | ||||
AssetManager assetManager, | AssetManager assetManager, | ||||
@@ -139,20 +106,4 @@ public class MergeAudioExec extends SingleOrMultiSourceExecBase<MergeAudioOperat | |||||
return padded; | return padded; | ||||
} | } | ||||
@Override protected void process(Map<String, Object> ctx, | |||||
MergeAudioOperation op, | |||||
JAsset source, | |||||
JAsset output, | |||||
JAsset subOutput, | |||||
Toolbox toolbox, | |||||
AssetManager assetManager) { | |||||
ctx.put("source", source); | |||||
ctx.put("output", subOutput); | |||||
final String script = renderScript(toolbox, ctx, MERGE_AUDIO_TEMPLATE); | |||||
log.debug("operate: running script: "+script); | |||||
final String scriptOutput = exec(script, op.isNoExec()); | |||||
log.debug("operate: command output: "+scriptOutput); | |||||
} | |||||
} | } |
@@ -22,6 +22,8 @@ public class RemoveTrackExec extends SingleOrMultiSourceExecBase<RemoveTrackOper | |||||
+ "-c copy " | + "-c copy " | ||||
+ "-y {{{output.path}}}"; | + "-y {{{output.path}}}"; | ||||
@Override protected String getProcessTemplate() { return REMOVE_TRACK_TEMPLATE; } | |||||
@Override public void operate(RemoveTrackOperation op, Toolbox toolbox, AssetManager assetManager) { | @Override public void operate(RemoveTrackOperation op, Toolbox toolbox, AssetManager assetManager) { | ||||
final JSingleOperationContext opCtx = op.getSingleInputContext(assetManager); | final JSingleOperationContext opCtx = op.getSingleInputContext(assetManager); | ||||
@@ -41,20 +43,4 @@ public class RemoveTrackExec extends SingleOrMultiSourceExecBase<RemoveTrackOper | |||||
operate(op, toolbox, assetManager, source, output, formatType, ctx); | operate(op, toolbox, assetManager, source, output, formatType, ctx); | ||||
} | } | ||||
@Override protected void process(Map<String, Object> ctx, | |||||
RemoveTrackOperation op, | |||||
JAsset source, | |||||
JAsset output, | |||||
JAsset subOutput, | |||||
Toolbox toolbox, | |||||
AssetManager assetManager) { | |||||
ctx.put("source", source); | |||||
ctx.put("output", subOutput); | |||||
final String script = renderScript(toolbox, ctx, REMOVE_TRACK_TEMPLATE); | |||||
log.debug("operate: running script: "+script); | |||||
final String scriptOutput = exec(script, op.isNoExec()); | |||||
log.debug("operate: command output: "+scriptOutput); | |||||
} | |||||
} | } |
@@ -21,6 +21,8 @@ public class ScaleExec extends SingleOrMultiSourceExecBase<ScaleOperation> { | |||||
+ "scale={{width}}x{{height}}" + | + "scale={{width}}x{{height}}" + | ||||
"\" -y {{{output.path}}}"; | "\" -y {{{output.path}}}"; | ||||
@Override protected String getProcessTemplate() { return SCALE_TEMPLATE; } | |||||
@Override public void operate(ScaleOperation op, Toolbox toolbox, AssetManager assetManager) { | @Override public void operate(ScaleOperation op, Toolbox toolbox, AssetManager assetManager) { | ||||
final JSingleOperationContext opCtx = op.getSingleInputContext(assetManager); | final JSingleOperationContext opCtx = op.getSingleInputContext(assetManager); | ||||
@@ -43,21 +45,4 @@ public class ScaleExec extends SingleOrMultiSourceExecBase<ScaleOperation> { | |||||
operate(op, toolbox, assetManager, source, output, formatType, ctx); | operate(op, toolbox, assetManager, source, output, formatType, ctx); | ||||
} | } | ||||
@Override protected void process(Map<String, Object> ctx, | |||||
ScaleOperation op, | |||||
JAsset source, | |||||
JAsset output, | |||||
JAsset subOutput, | |||||
Toolbox toolbox, | |||||
AssetManager assetManager) { | |||||
ctx.put("source", source); | |||||
ctx.put("output", subOutput); | |||||
final String script = renderScript(toolbox, ctx, SCALE_TEMPLATE); | |||||
log.debug("operate: running script: "+script); | |||||
final String scriptOutput = exec(script, op.isNoExec()); | |||||
log.debug("operate: command output: "+scriptOutput); | |||||
} | |||||
} | } |
@@ -49,12 +49,22 @@ public abstract class SingleOrMultiSourceExecBase<OP extends JOperation> extends | |||||
} | } | ||||
} | } | ||||
protected abstract void process(Map<String, Object> ctx, | |||||
OP op, | |||||
JAsset source, | |||||
JAsset output, | |||||
JAsset asset, | |||||
Toolbox toolbox, | |||||
AssetManager assetManager); | |||||
protected abstract String getProcessTemplate(); | |||||
protected void process(Map<String, Object> ctx, | |||||
OP op, | |||||
JAsset source, | |||||
JAsset output, | |||||
JAsset subOutput, | |||||
Toolbox toolbox, | |||||
AssetManager assetManager) { | |||||
ctx.put("source", source); | |||||
ctx.put("output", subOutput); | |||||
final String script = renderScript(toolbox, ctx, getProcessTemplate()); | |||||
log.debug("operate: running script: "+script); | |||||
final String scriptOutput = exec(script, op.isNoExec()); | |||||
log.debug("operate: command output: "+scriptOutput); | |||||
} | |||||
} | } |
@@ -22,6 +22,8 @@ public class TrimExec extends SingleOrMultiSourceExecBase<TrimOperation> { | |||||
"{{#exists interval}}-t {{interval}} {{/exists}}" + | "{{#exists interval}}-t {{interval}} {{/exists}}" + | ||||
"-y {{{output.path}}}"; | "-y {{{output.path}}}"; | ||||
@Override protected String getProcessTemplate() { return TRIM_TEMPLATE; } | |||||
@Override public void operate(TrimOperation op, Toolbox toolbox, AssetManager assetManager) { | @Override public void operate(TrimOperation op, Toolbox toolbox, AssetManager assetManager) { | ||||
final JSingleOperationContext opCtx = op.getSingleInputContext(assetManager); | final JSingleOperationContext opCtx = op.getSingleInputContext(assetManager); | ||||
@@ -42,19 +44,12 @@ public class TrimExec extends SingleOrMultiSourceExecBase<TrimOperation> { | |||||
JAsset subOutput, | JAsset subOutput, | ||||
Toolbox toolbox, | Toolbox toolbox, | ||||
AssetManager assetManager) { | AssetManager assetManager) { | ||||
ctx.put("source", source); | |||||
ctx.put("output", subOutput); | |||||
final StandardJsEngine js = toolbox.getJs(); | final StandardJsEngine js = toolbox.getJs(); | ||||
final BigDecimal startTime = op.getStartTime(ctx, js); | final BigDecimal startTime = op.getStartTime(ctx, js); | ||||
ctx.put("startSeconds", startTime); | ctx.put("startSeconds", startTime); | ||||
if (op.hasEndTime()) ctx.put("interval", op.getEndTime(ctx, js).subtract(startTime)); | if (op.hasEndTime()) ctx.put("interval", op.getEndTime(ctx, js).subtract(startTime)); | ||||
final String script = renderScript(toolbox, ctx, TRIM_TEMPLATE); | |||||
log.debug("operate: running script: "+script); | |||||
final String scriptOutput = exec(script, op.isNoExec()); | |||||
log.debug("operate: command output: "+scriptOutput); | |||||
super.process(ctx, op, source, output, subOutput, toolbox, assetManager); | |||||
} | } | ||||
} | } |
@@ -19,7 +19,9 @@ | |||||
{ | { | ||||
"operation": "add-silence", // name of the operation | "operation": "add-silence", // name of the operation | ||||
"creates": "v2_silent", // output asset name | "creates": "v2_silent", // output asset name | ||||
"source": "v2" // main video asset | |||||
"source": "v2", // main video asset | |||||
"channelLayout": "stereo", // optional channel layout, usually 'mono' or 'stereo'. Default is 'stereo' | |||||
"samplingRate": 48000 // optional samping rate, in Hz. default is 48000 | |||||
} | } | ||||
] | ] | ||||
} | } |