Kaynağa Gözat

split operation works

master
Jonathan Cobb 4 yıl önce
ebeveyn
işleme
fe039ae46d
8 değiştirilmiş dosya ile 48 ekleme ve 30 silme
  1. +1
    -1
      .gitignore
  2. +6
    -10
      README.md
  3. +1
    -1
      src/main/java/jvcl/main/JvclOptions.java
  4. +3
    -1
      src/main/java/jvcl/model/JAsset.java
  5. +10
    -9
      src/main/java/jvcl/op/SplitOperation.java
  6. +14
    -6
      src/main/java/jvcl/service/AssetManager.java
  7. +9
    -0
      src/main/java/jvcl/service/Toolbox.java
  8. +4
    -2
      src/test/resources/tests/test_split.json

+ 1
- 1
.gitignore Dosyayı Görüntüle

@@ -6,4 +6,4 @@ logs
dependency-reduced-pom.xml
*~
*.log
*.mp4
AssetManager.output.*

+ 6
- 10
README.md Dosyayı Görüntüle

@@ -1,18 +1,14 @@
# Javicle - a JSON Video Composition Language

Javicle is not a replacement for Final Cut Pro or even iMovie.
Javicle is a JSON DSL for ffmpeg transformations.

Javicle might be right for you if your video composition and manipulation needs are relatively simple
and you enjoy doing things with the command line and some JSON config instead of a GUI.
Describe your input assets and transformations in a JSON spec file, then run `jvcl spec-file` to
perform the transformations and produce output files.

This also give you the ability to track more of your workflow in source control - if you commit all
the original assets and the .jvcl files that describe how to create the output assets, you don't need
to save/archive the output assets anywhere.
If you like GUIs, Javicle is not for you. Javicle is not a replacement for Final Cut Pro or even iMovie.

Note, for those who want truly 100% re-creatable builds, you would also need to record the versions
of the various tools used (ffmpeg, etc) and reuse those same versions when recreating a build. This is
generally overkill though, since the options we use on the different tools have been stable for a while
and I see a low likelihood of a significant change in behavior, or a bug being introduced.
If you like CLIs, Javicle might be for you. If your video composition needs are relatively simple
and you enjoy capturing repeatable stuff in source control.

In JVCL there are two main concepts: assets and operations.



+ 1
- 1
src/main/java/jvcl/main/JvclOptions.java Dosyayı Görüntüle

@@ -38,6 +38,6 @@ public class JvclOptions extends BaseMainOptions {
public static final String OPT_SCRATCH_DIR = "-t";
public static final String LONGOPT_SCRATCH_DIR = "--temp-dir";
@Option(name=OPT_SCRATCH_DIR, aliases=LONGOPT_SCRATCH_DIR, usage=USAGE_SCRATCH_DIR)
@Getter @Setter private File scratchDir;
@Getter @Setter private File scratchDir = new File("/tmp");

}

+ 3
- 1
src/main/java/jvcl/model/JAsset.java Dosyayı Görüntüle

@@ -30,7 +30,9 @@ public class JAsset {
public static final JAsset NULL_ASSET = new JAsset().setName("~null asset~").setPath("/dev/null");
public static final String PREFIX_CLASSPATH = "classpath:";

public JAsset(JAsset other) { copy(this, other); }
public static final String[] COPY_EXCLUDE_FIELDS = {"list"};

public JAsset(JAsset other) { copy(this, other, null, COPY_EXCLUDE_FIELDS); }

@Getter @Setter private String name;
@Getter @Setter private String path;


+ 10
- 9
src/main/java/jvcl/op/SplitOperation.java Dosyayı Görüntüle

@@ -14,17 +14,15 @@ import org.cobbzilla.util.handlebars.HandlebarsUtil;

import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;

import static jvcl.model.JAsset.json2asset;
import static org.cobbzilla.util.daemon.ZillaRuntime.big;
import static jvcl.service.Toolbox.getDuration;
import static org.cobbzilla.util.daemon.ZillaRuntime.empty;
import static org.cobbzilla.util.io.FileUtil.abs;
import static org.cobbzilla.util.json.JsonUtil.json;
import static org.cobbzilla.util.system.CommandShell.execScript;
import static org.cobbzilla.util.time.TimeUtil.parseDuration;

@Slf4j
public class SplitOperation implements JOperator {
@@ -42,6 +40,7 @@ public class SplitOperation implements JOperator {

// if any format settings are missing, use settings from source
output.mergeFormat(source.getFormat());
assetManager.addOperationArrayAsset(output);

// get format type
final JFileExtension formatType = output.getFormat().getFileExtension();
@@ -50,8 +49,9 @@ public class SplitOperation implements JOperator {
ctx.put("ffmpeg", toolbox.getFfmpeg());
ctx.put("source", source);
final BigDecimal incr = config.getIntervalIncr();
final BigDecimal endTime = config.getEndTime(source);
for (BigDecimal i = config.getStartTime();
i.compareTo(config.getEndTime(source)) < 0;
i.compareTo(endTime) < 0;
i = i.add(incr)) {

final File outfile = assetManager.assetPath(op, source, formatType, new Object[]{i, incr});
@@ -66,12 +66,13 @@ public class SplitOperation implements JOperator {
ctx.put("startSeconds", i);
ctx.put("endSeconds", i.add(incr));
final String script = HandlebarsUtil.apply(toolbox.getHandlebars(), SPLIT_TEMPLATE, ctx);

log.debug("operate: running script: "+script);
final String scriptOutput = execScript(script);
log.debug("operate: command output: "+scriptOutput);
output.addAsset(slice);
assetManager.addOperationAssetSlice(output, slice);
}
assetManager.addOperationAsset(output);
log.info("operate: completed");
}

@NoArgsConstructor
@@ -80,13 +81,13 @@ public class SplitOperation implements JOperator {
@Getter @Setter private String split;

@Getter @Setter private String interval;
public BigDecimal getIntervalIncr() { return big(parseDuration(interval)).divide(big(1000), RoundingMode.UNNECESSARY); }
public BigDecimal getIntervalIncr() { return getDuration(interval); }

@Getter @Setter private String start;
public BigDecimal getStartTime() { return empty(start) ? BigDecimal.ZERO : big(start); }
public BigDecimal getStartTime() { return empty(start) ? BigDecimal.ZERO : getDuration(start); }

@Getter @Setter private String end;
public BigDecimal getEndTime(JAsset source) { return empty(end) ? source.duration() : big(end); }
public BigDecimal getEndTime(JAsset source) { return empty(end) ? source.duration() : getDuration(end); }
}

}

+ 14
- 6
src/main/java/jvcl/service/AssetManager.java Dosyayı Görüntüle

@@ -68,12 +68,20 @@ public class AssetManager {

public void addOperationAsset(JAsset asset) {
if (asset == null || asset == NULL_ASSET) return;
if (asset.hasList()) {
for (JAsset a : asset.getList()) addOperationAsset(a);
} else {
final String name = checkName(asset);
assets.put(name, asset.init(this, toolbox));
}
final String name = checkName(asset);
assets.put(name, asset.init(this, toolbox));
}

public void addOperationArrayAsset(JAsset asset) {
if (asset == null || asset == NULL_ASSET) return;
final String name = checkName(asset);
assets.put(name, asset);
}

public void addOperationAssetSlice(JAsset asset, JAsset slice) {
if (!assets.containsKey(asset.getName())) die("asset not found: "+asset.getName());
final JAsset found = assets.get(asset.getName());
found.addAsset(slice.init(this, toolbox));
}

public JAsset[] resolve(String[] assets) {


+ 9
- 0
src/main/java/jvcl/service/Toolbox.java Dosyayı Görüntüle

@@ -10,15 +10,19 @@ import org.cobbzilla.util.io.FileUtil;
import org.cobbzilla.util.javascript.StandardJsEngine;

import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static org.cobbzilla.util.daemon.ZillaRuntime.*;
import static org.cobbzilla.util.daemon.ZillaRuntime.big;
import static org.cobbzilla.util.io.FileUtil.abs;
import static org.cobbzilla.util.io.FileUtil.replaceExt;
import static org.cobbzilla.util.json.JsonUtil.FULL_MAPPER_ALLOW_UNKNOWN_FIELDS;
import static org.cobbzilla.util.json.JsonUtil.json;
import static org.cobbzilla.util.system.CommandShell.execScript;
import static org.cobbzilla.util.time.TimeUtil.parseDuration;

@Slf4j
public class Toolbox {
@@ -26,6 +30,11 @@ public class Toolbox {
public static final Toolbox DEFAULT_TOOLBOX = new Toolbox();

@Getter(lazy=true) private final Handlebars handlebars = initHandlebars();

public static BigDecimal getDuration(String t) {
return big(parseDuration(t)).divide(big(1000), RoundingMode.UNNECESSARY);
}

private Handlebars initHandlebars() {
final Handlebars hbs = new Handlebars(new HandlebarsUtil(Toolbox.class.getSimpleName()));
HandlebarsUtil.registerUtilityHelpers(hbs);


+ 4
- 2
src/test/resources/tests/test_split.json Dosyayı Görüntüle

@@ -14,10 +14,12 @@
"operations": [
{
"operation": "split", // name of the operation
"creates": "vid1_split_%", // assets it creates, the '%' will be replaced with a counter
"creates": "vid1_splits", // assets it creates, will be an array asset
"perform": {
"split": "vid1", // split this source asset
"interval": "10s" // split every ten seconds
"interval": "10s", // split every ten seconds
"start": "65s", // start one minute and five seconds into the video
"end": "100s" // end 100 seconds into the video
}
}
]


Yükleniyor…
İptal
Kaydet