瀏覽代碼

add remove-track operation, refactor a bit

master
Jonathan Cobb 3 年之前
父節點
當前提交
9bfc55abe2
共有 18 個檔案被更改,包括 353 行新增39 行删除
  1. +67
    -4
      README.md
  2. +9
    -0
      src/main/java/jvcl/model/JAsset.java
  3. +24
    -2
      src/main/java/jvcl/model/JFileExtension.java
  4. +34
    -0
      src/main/java/jvcl/model/JTrackId.java
  5. +33
    -4
      src/main/java/jvcl/model/info/JMediaInfo.java
  6. +2
    -0
      src/main/java/jvcl/model/info/JTrack.java
  7. +11
    -6
      src/main/java/jvcl/model/info/JTrackType.java
  8. +4
    -0
      src/main/java/jvcl/model/operation/JOperation.java
  9. +3
    -3
      src/main/java/jvcl/model/operation/JSingleSourceOperation.java
  10. +58
    -0
      src/main/java/jvcl/operation/RemoveTrackOperation.java
  11. +0
    -6
      src/main/java/jvcl/operation/exec/LetterboxExec.java
  12. +60
    -0
      src/main/java/jvcl/operation/exec/RemoveTrackExec.java
  13. +0
    -5
      src/main/java/jvcl/operation/exec/ScaleExec.java
  14. +2
    -1
      src/main/java/jvcl/operation/exec/SingleOrMultiSourceExecBase.java
  15. +0
    -5
      src/main/java/jvcl/operation/exec/TrimExec.java
  16. +3
    -2
      src/test/java/javicle/test/BasicTest.java
  17. +1
    -1
      src/test/resources/tests/test_concat.jvcl
  18. +42
    -0
      src/test/resources/tests/test_remove_track.jvcl

+ 67
- 4
README.md 查看文件

@@ -123,7 +123,7 @@ Most of the operation settings can be JavaScript expressions, for example:
The above would set the `start` value to ten seconds before the end of `someAsset`.

### Supported Operations
Today, JVCL supports seven basic operations.
Today, JVCL supports several basic operations.

For each operation listed below, the header links to an example from the JVCL test suite.

@@ -149,26 +149,38 @@ For transforming still images into video via a fade-pan (aka Ken Burns) effect.
Transform a video in one size to another size using black letterboxes on the sides or top/bottom.
Handy for embedding mobile videos into other screen formats.

### [remove-track](src/test/resources/tests/test_remove_track.jvcl)
For transforming still images into video via a fade-pan (aka Ken Burns) effect.

# Complex Example
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.

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.

```json
{
"assets": [
// file -- will be referenced directory
{
"comment": "first video, already local",
"name": "vid1",
"path": "/tmp/path/to/video1.mp4"
},

// URL -- will be downloaded to scratch directory and referenced from there
{
"comment": "second video, will be downloaded",
"name": "vid2",
"path": "https://archive.org/download/gov.archives.arc.1257628/gov.archives.arc.1257628_512kb.mp4"
},

// URL -- will be downloaded to `dest` directory and referenced from there
{
"comment": "third video, will be downloaded",
"name": "vid3",
"path": "https://archive.org/download/gov.archives.arc.49442/gov.archives.arc.49442_512kb.mp4",
"dest": "src/test/resources/sources/"
@@ -176,13 +188,16 @@ Here is a complex example using multiple assets and operations.

// Image URL
{
"comment": "JPEG image, will be downloaded",
"name": "img1",
"path": "https://live.staticflickr.com/65535/48159911972_01efa0e5ea_b.jpg",
"dest": "src/test/resources/sources/"
}
],
"operations": [
// scale examples
{
"comment": "scale using explicity height x width",
"operation": "scale", // name of the operation
"creates": "vid2_scaled", // asset it creates
"source": "vid2", // source asset
@@ -190,33 +205,45 @@ Here is a complex example using multiple assets and operations.
"height": "768" // height of scaled asset. if omitted and width is present, height will be proportional
},
{
"comment": "scale proportionally by a scale factor",
"operation": "scale", // name of the operation
"creates": "vid2_big", // asset it creates
"source": "vid2", // source asset
"factor": "2.2" // scale factor. if factor is set, width and height are ignored.
},

// split example
{
"comment": "split one asset into many",
"operation": "split", // name of the operation
"creates": "vid1_split_%", // assets it creates, the '%' will be replaced with a counter
"source": "vid1", // split this source asset
"interval": "10" // split every ten seconds
},

// concat examples
{
"comment": "re-combine previously split assets back together",
"operation": "concat", // name of the operation
"creates": "recombined_vid1", // assets it creates, the '%' will be replaced with a counter
"source": ["vid1_split"] // recombine all split assets
},
{
"comment": "append vid2 to the end of vid1 and create a new asset",
"operation": "concat", // name of the operation
"creates": "combined_vid", // asset it creates, can be referenced later
"creates": "combined_vid2", // asset it creates, can be referenced later
"source": ["vid1", "vid2"] // operation-specific: this says, concatenate these named assets
},
{
"comment": "re-combine only some of the previously split assets",
"operation": "concat", // name of the operation
"creates": "combined_vid", // the asset it creates, can be referenced later
"source": ["vid1", "vid2"] // operation-specific: this says, concatenate these named assets
"sources": ["vid1_splits[1..2]"],// concatentate these sources -- the 2nd and 3rd files only
"creates": "combined_vid3" // name of the output asset, will be written to scratch directory
},

// trim example
{
"comment": "trim all of the assets that were split above",
"operation": "trim", // name of the operation
"creates": { // create multiple files, will be prefixed with `name`, store them in `dest`
"name": "vid1_trims",
@@ -226,7 +253,10 @@ Here is a complex example using multiple assets and operations.
"start": "1", // cropped region starts here, default is zero
"end": "6" // cropped region ends here, default is end of video
},

// overlay example
{
"comment": "overlay one video onto another",
"operation": "overlay", // name of the operation
"creates": "overlay1", // asset it creates
"source": "combined_vid1", // main video asset
@@ -242,7 +272,10 @@ Here is a complex example using multiple assets and operations.
"y": "source.height / 2" // vertical overlay position on main video. default is 0
}
},

// ken-burns example
{
"comment": "apply zoom-pan effect to image, creates video",
"operation": "ken-burns", // name of the operation
"creates": "ken1", // asset it creates
"source": "img1", // source image
@@ -256,13 +289,43 @@ Here is a complex example using multiple assets and operations.
"width": "1024", // width of output video
"height": "768" // height of output video
},

// letterbox example
{
"comment": "increase video size without scaling, add letterboxes as needed",
"operation": "letterbox", // name of the operation
"creates": "boxed1", // asset it creates
"source": "ken1", // source asset
"width": "source.width * 1.5", // make it wider
"height": "source.height * 0.9", // and shorter
"color": "AliceBlue" // default is black. can be a hex value (0xff0000 for red) or a color name from here: https://ffmpeg.org/ffmpeg-utils.html#color-syntax
},

// remove-track examples
{
"comment": "remove all audio tracks",
"operation": "remove-track", // name of the operation
"creates": "vid2_video_only", // name of the output asset
"source": "vid2", // main video asset
"track": "audio" // remove all audio tracks
},
{
"comment": "remove all video tracks",
"operation": "remove-track", // name of the operation
"creates": "vid2_audio_only", // name of the output asset
"source": "vid2", // main video asset
"track": "video" // remove all video tracks
},
{
"comment": "remove a specific audio track",
"operation": "remove-track", // name of the operation
"creates": "vid2_video_only2", // name of the output asset
"source": "vid2", // main video asset
"track": {
// only remove the first audio track
"type": "audio", // track type to remove
"number": "0" // track number to remove
}
}
]
}


+ 9
- 0
src/main/java/jvcl/model/JAsset.java 查看文件

@@ -3,6 +3,8 @@ package jvcl.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.JsonNode;
import jvcl.model.info.JMediaInfo;
import jvcl.model.info.JTrack;
import jvcl.model.info.JTrackType;
import jvcl.service.AssetManager;
import jvcl.service.Toolbox;
import lombok.*;
@@ -113,6 +115,9 @@ public class JAsset implements JsObjectView {
}
public boolean hasInfo() { return info != null; }

@Getter @Setter private String comment;
public boolean hasComment () { return !empty(comment); }

@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -153,6 +158,10 @@ public class JAsset implements JsObjectView {
public BigDecimal height() { return hasInfo() ? getInfo().height() : null; }
@JsonIgnore public BigDecimal getHeight () { return height(); }

public int numTracks(JTrackType type) { return hasInfo() ? getInfo().numTracks(type) : 0; }

public JTrack firstTrack(JTrackType type) { return hasInfo() ? getInfo().firstTrack(type) : null; }

public BigDecimal aspectRatio() {
final BigDecimal width = width();
final BigDecimal height = height();


+ 24
- 2
src/main/java/jvcl/model/JFileExtension.java 查看文件

@@ -1,12 +1,14 @@
package jvcl.model;

import com.fasterxml.jackson.annotation.JsonCreator;
import jvcl.model.info.JTrack;
import jvcl.model.info.JTrackType;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import static jvcl.model.info.JTrackType.*;

@AllArgsConstructor
@AllArgsConstructor @Slf4j
public enum JFileExtension {

mp4 (".mp4", video),
@@ -16,7 +18,9 @@ public enum JFileExtension {
flac (".flac", audio),
png (".png", image),
jpg (".jpg", image),
jpeg (".jpeg", image);
jpeg (".jpeg", image),
sub (".sub", subtitle),
dat (".dat", data);

@JsonCreator public static JFileExtension fromString(String v) { return valueOf(v.toLowerCase()); }

@@ -31,4 +35,22 @@ public enum JFileExtension {
private final JTrackType mediaType;
public JTrackType mediaType() { return mediaType; }

public static JFileExtension fromTrack(JTrack track) {
if (track.hasFileExtension()) {
try {
return fromString(track.getFileExtension());
} catch (Exception e) {
log.warn("fromTrack: unrecognized file extension: "+track.getFileExtension());
}
}
if (track.hasFormat()) {
try {
return fromString(track.getFormat());
} catch (Exception e) {
log.warn("fromTrack: unrecognized format: "+track.getFormat());
}
}
return null;
}

}

+ 34
- 0
src/main/java/jvcl/model/JTrackId.java 查看文件

@@ -0,0 +1,34 @@
package jvcl.model;

import com.fasterxml.jackson.databind.JsonNode;
import jvcl.model.info.JTrackType;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

import static org.cobbzilla.util.daemon.ZillaRuntime.die;
import static org.cobbzilla.util.json.JsonUtil.json;

@Accessors(chain=true)
public class JTrackId {

@Getter @Setter private JTrackType type;
@Getter @Setter private Integer number;
public boolean hasNumber () { return number != null; }

public static JTrackId createTrackId (JsonNode val) {
if (val == null) return die("createTrackId: constructor val was null");
if (val.isObject()) {
return json(json(val), JTrackId.class);
} else {
final JTrackType trackType;
try {
trackType = JTrackType.fromString(val.asText());
} catch (Exception e) {
return die("createTrackId: not a valid track type: "+val.asText());
}
return new JTrackId().setType(trackType);
}
}

}

+ 33
- 4
src/main/java/jvcl/model/info/JMediaInfo.java 查看文件

@@ -1,22 +1,34 @@
package jvcl.model.info;

import jvcl.model.JFormat;
import jvcl.model.JFileExtension;
import jvcl.model.JFormat;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.concurrent.atomic.AtomicReference;

import static java.math.BigDecimal.ZERO;
import static org.cobbzilla.util.daemon.ZillaRuntime.*;

@Slf4j
@NoArgsConstructor @Slf4j
public class JMediaInfo {

public JMediaInfo (JMediaInfo other, JFormat format) {
this.media = other.getMedia();
this.formatRef.set(format);
}

@Getter @Setter private JMedia media;
@Getter(lazy=true) private final JFormat format = initFormat();
public boolean hasFormat () { return getFormat() != null; }

private final AtomicReference<JFormat> formatRef = new AtomicReference<>();

public JFormat getFormat () {
if (formatRef.get() == null) formatRef.set(initFormat());
return formatRef.get();
}

private JFormat initFormat () {
if (media == null || empty(media.getTrack())) return null;
@@ -106,4 +118,21 @@ public class JMediaInfo {
return null;
}

public int numTracks(JTrackType type) {
if (media == null || empty(media.getTrack())) return 0;
int count = 0;
for (JTrack t : media.getTrack()) {
if (t.type() == type) count++;
}
return count;
}

public JTrack firstTrack(JTrackType type) {
if (media == null || empty(media.getTrack())) return null;
for (JTrack t : media.getTrack()) {
if (t.type() == type) return t;
}
return null;
}

}

+ 2
- 0
src/main/java/jvcl/model/info/JTrack.java 查看文件

@@ -33,6 +33,8 @@ public class JTrack {
public boolean hasFileExtension () { return !empty(fileExtension); }

@JsonProperty("Format") @Getter @Setter private String format;
public boolean hasFormat () { return !empty(format); }

@JsonProperty("Format_AdditionalFeatures") @Getter @Setter private String formatAdditionalFeatures;
@JsonProperty("Format_Profile") @Getter @Setter private String formatProfile;
@JsonProperty("Format_Level") @Getter @Setter private String formatLevel;


+ 11
- 6
src/main/java/jvcl/model/info/JTrackType.java 查看文件

@@ -7,16 +7,21 @@ import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum JTrackType {

general (null),
audio (JFileExtension.flac),
video (JFileExtension.mp4),
image (JFileExtension.png),
other (null);
general (null, null),
audio (JFileExtension.flac, "a"),
video (JFileExtension.mp4, "v"),
image (JFileExtension.png, null),
subtitle(JFileExtension.png, "s"),
data (JFileExtension.png, "d"),
other (null, null);

@JsonCreator public static JTrackType fromString(String val) { return valueOf(val.toLowerCase()); }

private final JFileExtension ext;

public JFileExtension ext() { return ext; }

private final String ffmpegType;
public String ffmpegType() { return ffmpegType; }
public boolean hasFfmpegType() { return ffmpegType != null; }

}

+ 4
- 0
src/main/java/jvcl/model/operation/JOperation.java 查看文件

@@ -14,6 +14,7 @@ import java.util.HashMap;
import java.util.Map;

import static jvcl.service.json.JOperationFactory.getOperationExecClass;
import static org.cobbzilla.util.daemon.ZillaRuntime.empty;
import static org.cobbzilla.util.daemon.ZillaRuntime.hashOf;
import static org.cobbzilla.util.json.JsonUtil.json;
import static org.cobbzilla.util.reflect.ReflectionUtil.instantiate;
@@ -33,6 +34,9 @@ public abstract class JOperation {
@Getter @Setter private JsonNode creates;
@Getter @Setter private boolean noExec = false;

@Getter @Setter private String comment;
public boolean hasComment () { return !empty(comment); }

public String hash(JAsset[] sources) { return hash(sources, null); }

public String hash(JAsset[] sources, Object[] args) {


+ 3
- 3
src/main/java/jvcl/model/operation/JSingleSourceOperation.java 查看文件

@@ -23,7 +23,7 @@ public class JSingleSourceOperation extends JOperation {
final JAsset output = json2asset(getCreates());
output.mergeFormat(source.getFormat());

// ensure output is in the correct fprmat
// ensure output is in the correct format
final JFormat format = output.getFormat();
final JTrackType type = outputMediaType();
if (!format.hasFileExtension() || format.getFileExtension().mediaType() != type) {
@@ -33,12 +33,12 @@ public class JSingleSourceOperation extends JOperation {
}
format.setFileExtension(ext);
}
final JFileExtension formatType = getFileExtension(output);
final JFileExtension formatType = getFileExtension(source, output);

return new JSingleOperationContext(source, output, formatType);
}

protected JFileExtension getFileExtension(JAsset output) {
protected JFileExtension getFileExtension(JAsset source, JAsset output) {
return output.getFormat().getFileExtension();
}



+ 58
- 0
src/main/java/jvcl/operation/RemoveTrackOperation.java 查看文件

@@ -0,0 +1,58 @@
package jvcl.operation;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.JsonNode;
import jvcl.model.JAsset;
import jvcl.model.JFileExtension;
import jvcl.model.JFormat;
import jvcl.model.JTrackId;
import jvcl.model.info.JMediaInfo;
import jvcl.model.info.JTrack;
import jvcl.model.info.JTrackType;
import jvcl.model.operation.JSingleSourceOperation;
import lombok.Getter;
import lombok.Setter;

import static jvcl.model.JTrackId.createTrackId;
import static org.cobbzilla.util.daemon.ZillaRuntime.die;

public class RemoveTrackOperation extends JSingleSourceOperation {

@Getter @Setter private JsonNode track;

@JsonIgnore @Getter(lazy=true) private final JTrackId trackId = initTrackId();
private JTrackId initTrackId() {
final JTrackId trackId = createTrackId(getTrack());
final JTrackType trackType = trackId.getType();
if (!trackType.hasFfmpegType()) die("initTrackType: cannot remove tracks of type "+ trackType);
return trackId;
}

@Override protected JFileExtension getFileExtension(JAsset source, JAsset output) {

final JTrackId trackId = getTrackId();
final JTrackType trackType = trackId.getType();

// if we are removing all video tracks, the output will be an audio asset
final int trackCount = source.numTracks(trackType);
if (trackCount == 0) return die("getFileExtension: no tracks of type "+ trackType +" found in source: "+source);

if (wouldRemoveAllVideoTracks(trackId, trackCount)) {
// find the format of the first audio track
final JTrack audio = source.firstTrack(JTrackType.audio);
if (audio == null) return die("getFileExtension: no audio tracks found!");
final JFileExtension ext = JFileExtension.fromTrack(audio);
source.setInfo(new JMediaInfo(source.getInfo(), new JFormat().setFileExtension(ext)));
return ext;
}

return super.getFileExtension(source, output);
}

private boolean wouldRemoveAllVideoTracks(JTrackId trackId, int trackCount) {
if (trackId.getType() != JTrackType.video) return false;
if (!trackId.hasNumber()) return true;
return trackCount == 1;
}

}

+ 0
- 6
src/main/java/jvcl/operation/exec/LetterboxExec.java 查看文件

@@ -64,7 +64,6 @@ public class LetterboxExec extends SingleOrMultiSourceExecBase<LetterboxOperatio
JAsset subOutput,
Toolbox toolbox,
AssetManager assetManager) {

ctx.put("source", source);
ctx.put("output", subOutput);
final String script = renderScript(toolbox, ctx, LETTERBOX_TEMPLATE);
@@ -72,11 +71,6 @@ public class LetterboxExec extends SingleOrMultiSourceExecBase<LetterboxOperatio
log.debug("operate: running script: "+script);
final String scriptOutput = exec(script, op.isNoExec());
log.debug("operate: command output: "+scriptOutput);
if (output == subOutput) {
assetManager.addOperationAsset(output);
} else {
assetManager.addOperationAssetSlice(output, subOutput);
}
}

}

+ 60
- 0
src/main/java/jvcl/operation/exec/RemoveTrackExec.java 查看文件

@@ -0,0 +1,60 @@
package jvcl.operation.exec;

import jvcl.model.JAsset;
import jvcl.model.JFileExtension;
import jvcl.model.JTrackId;
import jvcl.model.info.JTrackType;
import jvcl.model.operation.JSingleOperationContext;
import jvcl.operation.RemoveTrackOperation;
import jvcl.service.AssetManager;
import jvcl.service.Toolbox;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;

@Slf4j
public class RemoveTrackExec extends SingleOrMultiSourceExecBase<RemoveTrackOperation> {

public static final String REMOVE_TRACK_TEMPLATE
= "{{{ffmpeg}}} -i {{{source.path}}} "
+ "-map 0 -map -0:{{trackType}}{{#exists trackNumber}}:{{trackNumber}}{{/exists}} "
+ "-c copy "
+ "-y {{{output.path}}}";

@Override public void operate(RemoveTrackOperation op, Toolbox toolbox, AssetManager assetManager) {

final JSingleOperationContext opCtx = op.getSingleInputContext(assetManager);
final JAsset source = opCtx.source;
final JAsset output = opCtx.output;
final JFileExtension formatType = opCtx.formatType;

final Map<String, Object> ctx = new HashMap<>();
ctx.put("ffmpeg", toolbox.getFfmpeg());
ctx.put("source", source);

final JTrackId trackId = op.getTrackId();
final JTrackType trackType = trackId.getType();
ctx.put("trackType", trackType.ffmpegType());
if (trackId.hasNumber()) ctx.put("trackNumber", trackId.getNumber());

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);
}

}

+ 0
- 5
src/main/java/jvcl/operation/exec/ScaleExec.java 查看文件

@@ -58,11 +58,6 @@ public class ScaleExec extends SingleOrMultiSourceExecBase<ScaleOperation> {
log.debug("operate: running script: "+script);
final String scriptOutput = exec(script, op.isNoExec());
log.debug("operate: command output: "+scriptOutput);
if (output == subOutput) {
assetManager.addOperationAsset(output);
} else {
assetManager.addOperationAssetSlice(output, subOutput);
}
}

}

+ 2
- 1
src/main/java/jvcl/operation/exec/SingleOrMultiSourceExecBase.java 查看文件

@@ -37,6 +37,7 @@ public abstract class SingleOrMultiSourceExecBase<OP extends JOperation> extends
}
subOutput.setPath(abs(outfile));
process(ctx, op, asset, output, subOutput, toolbox, assetManager);
assetManager.addOperationAssetSlice(output, subOutput);
}
} else {
final File defaultOutfile = assetManager.assetPath(op, source, formatType);
@@ -44,6 +45,7 @@ public abstract class SingleOrMultiSourceExecBase<OP extends JOperation> extends
if (path == null) return;
output.setPath(abs(path));
process(ctx, op, source, output, output, toolbox, assetManager);
assetManager.addOperationAsset(output);
}
}

@@ -55,5 +57,4 @@ public abstract class SingleOrMultiSourceExecBase<OP extends JOperation> extends
Toolbox toolbox,
AssetManager assetManager);


}

+ 0
- 5
src/main/java/jvcl/operation/exec/TrimExec.java 查看文件

@@ -55,11 +55,6 @@ public class TrimExec extends SingleOrMultiSourceExecBase<TrimOperation> {
log.debug("operate: running script: "+script);
final String scriptOutput = exec(script, op.isNoExec());
log.debug("operate: command output: "+scriptOutput);
if (output == subOutput) {
assetManager.addOperationAsset(output);
} else {
assetManager.addOperationAssetSlice(output, subOutput);
}
}

}

+ 3
- 2
src/test/java/javicle/test/BasicTest.java 查看文件

@@ -26,8 +26,9 @@ public class BasicTest {
runSpec("tests/test_letterbox.jvcl");
}

@Test public void testOverlay() { runSpec("tests/test_overlay.jvcl"); }
@Test public void testKenBurns() { runSpec("tests/test_ken_burns.jvcl"); }
@Test public void testOverlay () { runSpec("tests/test_overlay.jvcl"); }
@Test public void testKenBurns () { runSpec("tests/test_ken_burns.jvcl"); }
@Test public void testRemoveTrack () { runSpec("tests/test_remove_track.jvcl"); }

private void runSpec(String specPath) {
try {


+ 1
- 1
src/test/resources/tests/test_concat.jvcl 查看文件

@@ -15,7 +15,7 @@
{
"operation": "concat", // name of the operation
"sources": ["vid1_splits[1..2]"], // concatentate these sources -- the 2nd and 3rd files only
"creates": "combined_vid2", // name of the output asset, will be written to scratch directory
"creates": "combined_vid2" // name of the output asset, will be written to scratch directory
}
]
}

+ 42
- 0
src/test/resources/tests/test_remove_track.jvcl 查看文件

@@ -0,0 +1,42 @@
{
"assets": [
// this is a US government video not covered by copyright
{
"name": "vid2",
"path": "https://archive.org/download/gov.archives.arc.49442/gov.archives.arc.49442_512kb.mp4",
"dest": "src/test/resources/sources/"
}
],
"operations": [
// trim video first, so test runs faster
{
"operation": "trim",
"creates": "v2",
"source": "vid2",
"start": "0",
"end": "20"
},
{
"operation": "remove-track", // name of the operation
"creates": "vid2_video_only", // name of the output asset
"source": "v2", // main video asset
"track": "audio" // remove all audio tracks
},
{
"operation": "remove-track", // name of the operation
"creates": "vid2_audio_only", // name of the output asset
"source": "v2", // main video asset
"track": "video" // remove all video tracks
},
{
"operation": "remove-track", // name of the operation
"creates": "vid2_video_only2", // name of the output asset
"source": "v2", // main video asset
"track": {
// only remove the first audio track
"type": "audio", // track type to remove
"number": "0" // track number to remove
}
}
]
}

Loading…
取消
儲存