Преглед изворни кода

WIP. simplify param names, work on overlay test

master
Jonathan Cobb пре 3 година
родитељ
комит
5ff4d39014
6 измењених фајлова са 86 додато и 86 уклоњено
  1. +6
    -6
      README.md
  2. +36
    -47
      src/main/java/jvcl/operation/OverlayOperation.java
  3. +19
    -19
      src/main/java/jvcl/operation/exec/OverlayExec.java
  4. +7
    -0
      src/main/java/jvcl/service/Toolbox.java
  5. +1
    -1
      src/test/java/javicle/test/BasicTest.java
  6. +17
    -13
      src/test/resources/tests/test_overlay.jvcl

+ 6
- 6
README.md Прегледај датотеку

@@ -116,9 +116,9 @@ later operations.

Most of the operation settings can be JavaScript expressions, for example:

"startTime": "someAsset.duration - 10"
"start": "someAsset.duration - 10"

The above would set the `startTime` value to ten seconds before the end of `someAsset`.
The above would set the `start` value to ten seconds before the end of `someAsset`.

### Supported Operations
Today, JVCL supports these operations:
@@ -199,12 +199,12 @@ Here is a complex example using multiple assets and operations.
"height": "1024" // output height in pixes. default is source height
},
"main": "combined_vid1", // main video asset
"startTime": "30", // when (on the main video timeline) to begin showing the overlay. default is 0 (beginning)
"endTime": "60", // when (on the main video timeline) to stop showing the overlay. default is to play the entire overlay
"start": "30", // when (on the main video timeline) to begin showing the overlay. default is 0 (beginning)
"end": "60", // when (on the main video timeline) to stop showing the overlay. default is to play the entire overlay
"overlay": {
"source": "vid2", // overlay this video on the main video
"startTime": "0", // when (on the overlay video timeline) to begin playback on the overlay. default is 0 (beginning)
"endTime": "overlay.duration", // when (on the overlay video timeline) to end playback on the overlay. default is to play the entire overlay
"start": "0", // when (on the overlay video timeline) to begin playback on the overlay. default is 0 (beginning)
"end": "overlay.duration", // when (on the overlay video timeline) to end playback on the overlay. default is to play the entire overlay
"width": "overlay.width / 2", // how wide the overlay will be, in pixels. default is the full overlay width, or maintain aspect ratio if height was set
"height": "source.height", // how tall the overlay will be, in pixels. default is the full overlay height, or maintain aspect ratio if width was set
"x": "source.width / 2", // horizontal overlay position on main video. default is 0


+ 36
- 47
src/main/java/jvcl/operation/OverlayOperation.java Прегледај датотеку

@@ -1,79 +1,68 @@
package jvcl.operation;

import jvcl.model.JAsset;
import jvcl.model.JFileExtension;
import jvcl.model.JOperation;
import jvcl.service.AssetManager;
import jvcl.service.Toolbox;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.cobbzilla.util.javascript.JsEngine;
import org.cobbzilla.util.javascript.StandardJsEngine;

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

import static java.math.RoundingMode.HALF_EVEN;
import static jvcl.model.JAsset.json2asset;
import static jvcl.service.Toolbox.eval;
import static jvcl.service.Toolbox.getDuration;
import static org.cobbzilla.util.daemon.ZillaRuntime.big;
import static org.cobbzilla.util.daemon.ZillaRuntime.empty;
import static org.cobbzilla.util.io.FileUtil.abs;
import static org.cobbzilla.util.system.CommandShell.execScript;

@Slf4j
public class OverlayOperation extends JOperation {

@Getter @Setter private String source;
@Getter @Setter private String overlay;
@Getter @Setter private OverlayConfig overlay;

private String eval(String val, Map<String, Object> ctx, JsEngine js) {
final Map<String, Object> jsCtx = Toolbox.jsContext(ctx);
final Object result = js.evaluate(val, jsCtx);
return result == null ? null : result.toString();
@Getter @Setter private String start;
public BigDecimal getStartSeconds(Map<String, Object> ctx, JsEngine js) {
return empty(start) ? BigDecimal.ZERO : getDuration(eval(start, ctx, js));
}

@Getter @Setter private String offset;
public BigDecimal getOffsetSeconds (Map<String, Object> ctx, JsEngine js) {
return empty(offset) ? BigDecimal.ZERO : getDuration(eval(offset, ctx, js));
@Getter @Setter private String end;
public BigDecimal getEndSeconds(Map<String, Object> ctx, JsEngine js) {
return empty(end) ? BigDecimal.ZERO : getDuration(eval(end, ctx, js));
}

@Getter @Setter private String overlayStart;
public BigDecimal getOverlayStartSeconds (Map<String, Object> ctx, JsEngine js) {
return empty(overlayStart) ? BigDecimal.ZERO : getDuration(eval(overlayStart, ctx, js));
}

@Getter @Setter private String overlayEnd;
public boolean hasOverlayEnd () { return !empty(overlayEnd); }
public BigDecimal getOverlayEndSeconds (Map<String, Object> ctx, JsEngine js) {
return getDuration(eval(overlayEnd, ctx, js));
}
public static class OverlayConfig {
@Getter @Setter private String source;

@Getter @Setter private String width;
public boolean hasWidth () { return !empty(width); }
public String getWidth(Map<String, Object> ctx, JsEngine js) { return eval(width, ctx, js); }
@Getter @Setter private String start;
public BigDecimal getStartTime(Map<String, Object> ctx, JsEngine js) {
return empty(start) ? BigDecimal.ZERO : getDuration(eval(start, ctx, js));
}

@Getter @Setter private String height;
public boolean hasHeight () { return !empty(height); }
public String getHeight(Map<String, Object> ctx, JsEngine js) { return eval(height, ctx, js); }
@Getter @Setter private String end;
public boolean hasEndTime () { return !empty(end); }
public BigDecimal getEndTime(Map<String, Object> ctx, JsEngine js) {
return getDuration(eval(end, ctx, js));
}

@Getter @Setter private String x;
public boolean hasX () { return !empty(x); }
public String getX(Map<String, Object> ctx, JsEngine js) { return eval(x, ctx, js); }
@Getter @Setter private String width;
public boolean hasWidth () { return !empty(width); }
public String getWidth(Map<String, Object> ctx, JsEngine js) { return eval(width, ctx, js); }

@Getter @Setter private String y;
public boolean hasY () { return !empty(y); }
public String getY(Map<String, Object> ctx, JsEngine js) { return eval(y, ctx, js); }
@Getter @Setter private String height;
public boolean hasHeight () { return !empty(height); }
public String getHeight(Map<String, Object> ctx, JsEngine js) { return eval(height, ctx, js); }

@Getter @Setter private String outputWidth;
public boolean hasOutputWidth () { return !empty(outputWidth); }
public String getOutputWidth(Map<String, Object> ctx, JsEngine js) { return eval(outputWidth, ctx, js); }
@Getter @Setter private String x;
public boolean hasX () { return !empty(x); }
public String getX(Map<String, Object> ctx, JsEngine js) { return eval(x, ctx, js); }

@Getter @Setter private String outputHeight;
public boolean hasOutputHeight () { return !empty(outputHeight); }
public String getOutputHeight(Map<String, Object> ctx, JsEngine js) { return eval(outputHeight, ctx, js); }
@Getter @Setter private String y;
public boolean hasY () { return !empty(y); }
public String getY(Map<String, Object> ctx, JsEngine js) { return eval(y, ctx, js); }

public BigDecimal aspectRatio () {
return big(getWidth()).divide(big(getHeight()), RoundingMode.HALF_EVEN);
}
}
}

+ 19
- 19
src/main/java/jvcl/operation/exec/OverlayExec.java Прегледај датотеку

@@ -22,16 +22,16 @@ import static org.cobbzilla.util.system.CommandShell.execScript;
public class OverlayExec extends ExecBase<OverlayOperation> {

public static final String OVERLAY_TEMPLATE
= "{{ffmpeg}} -i {{{source.path}}} -filter_complex \"" +
"movie={{{overlay.path}}}{{#exists overlayStart}}:seek_point={{overlayStart}}{{/exists}} [ovl]; " +
"[v:0] setpts=PTS-(STARTPTS+{{#exists offset}}{{offset}}{{else}}0{{/exists}}) [main]; " +
"[ovl] setpts=PTS-STARTPTS{{#exists width}}, scale={{width}}x{{height}}{{/exists}} ; " +
"[main][ovl] overlay=shortest=1{{#exists x}}:x={{x}}{{/exists}}{{#exists y}}:y={{y}}{{/exists}} " +
"\" {{{output.path}}}";
= "ffmpeg -i {{{source.path}}} -vf \""
+ " movie={{{overlay.path}}}:seek_point=0 [ovl]; "
+ " [ovl] setpts=PTS-STARTPTS, scale=160x160 [ovl2] ; "
+ " [main][ovl2] overlay=enable=between(t\\,15\\,20):x=160:y=120\" "
+ "{{{output.path}}}";

@Override public void operate(OverlayOperation op, Toolbox toolbox, AssetManager assetManager) {
final JAsset source = assetManager.resolve(op.getSource());
final JAsset overlay = assetManager.resolve(op.getOverlay());
final OverlayOperation.OverlayConfig overlay = op.getOverlay();
final JAsset overlaySource = assetManager.resolve(overlay.getSource());

final JAsset output = json2asset(op.getCreates());
output.mergeFormat(source.getFormat());
@@ -47,29 +47,29 @@ public class OverlayExec extends ExecBase<OverlayOperation> {
final Map<String, Object> ctx = new HashMap<>();
ctx.put("ffmpeg", toolbox.getFfmpeg());
ctx.put("source", source);
ctx.put("overlay", overlay);
ctx.put("overlay", overlaySource);
ctx.put("output", output);
ctx.put("offset", op.getOffsetSeconds(ctx, js));
ctx.put("overlayStart", op.getOverlayStartSeconds(ctx, js));
if (op.hasOverlayEnd()) ctx.put("overlayEnd", op.getOverlayEndSeconds(ctx, js));
if (op.hasWidth()) {
final String width = op.getWidth(ctx, js);
ctx.put("offset", op.getStartSeconds(ctx, js));
ctx.put("overlayStart", overlay.getStartTime(ctx, js));
if (overlay.hasEndTime()) ctx.put("overlayEnd", overlay.getEndTime(ctx, js));
if (overlay.hasWidth()) {
final String width = overlay.getWidth(ctx, js);
ctx.put("width", width);
if (!op.hasHeight()) {
if (!overlay.hasHeight()) {
final int height = big(width).divide(overlay.aspectRatio(), HALF_EVEN).intValue();
ctx.put("height", height);
}
}
if (op.hasHeight()) {
final String height = op.getHeight(ctx, js);
if (overlay.hasHeight()) {
final String height = overlay.getHeight(ctx, js);
ctx.put("height", height);
if (!op.hasWidth()) {
if (!overlay.hasWidth()) {
final int width = big(height).multiply(overlay.aspectRatio()).intValue();
ctx.put("width", width);
}
}
if (op.hasX()) ctx.put("x", op.getX(ctx, js));
if (op.hasY()) ctx.put("y", op.getY(ctx, js));
if (overlay.hasX()) ctx.put("x", overlay.getX(ctx, js));
if (overlay.hasY()) ctx.put("y", overlay.getY(ctx, js));

final String script = renderScript(toolbox, ctx, OVERLAY_TEMPLATE);



+ 7
- 0
src/main/java/jvcl/service/Toolbox.java Прегледај датотеку

@@ -9,6 +9,7 @@ import jvcl.service.json.JOperationModule;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.cobbzilla.util.handlebars.HandlebarsUtil;
import org.cobbzilla.util.javascript.JsEngine;
import org.cobbzilla.util.javascript.StandardJsEngine;

import java.io.File;
@@ -36,6 +37,12 @@ public class Toolbox {

@Getter(lazy=true) private final StandardJsEngine js = new StandardJsEngine();

public static String eval(String val, Map<String, Object> ctx, JsEngine js) {
final Map<String, Object> jsCtx = Toolbox.jsContext(ctx);
final Object result = js.evaluate(val, jsCtx);
return result == null ? null : result.toString();
}

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


+ 1
- 1
src/test/java/javicle/test/BasicTest.java Прегледај датотеку

@@ -21,7 +21,7 @@ public class BasicTest {
runSpec("tests/test_trim.jvcl");
}

// @Test public void test4Overlay() { runSpec("tests/test_overlay.jvcl"); }
@Test public void testOverlay() { runSpec("tests/test_overlay.jvcl"); }

private void runSpec(String specPath) {
try {


+ 17
- 13
src/test/resources/tests/test_overlay.jvcl Прегледај датотеку

@@ -12,27 +12,31 @@
}
],
"operations": [
{
"operation": "trim",
"creates": "v1",
"trim": "vid1",
"start": "0"
},
{
"operation": "overlay", // name of the operation
"creates": {
"name": "overlay1",
"width": "1920", // output width in pixels. default is source width
"height": "1024", // output height in pixes. default is source height
"dest": "src/test/resources/outputs/overlay/"
},
"source": "vid1", // main video asset
"overlay": "vid2", // overlay this video on the main video
"offset": "30", // when (on the main video timeline) to begin showing the overlay. default is 0 (beginning)
"overlayStart": "0", // when (on the overlay video timeline) to begin playback on the overlay. default is 0 (beginning)
"overlayEnd": "0", // when (on the overlay video timeline) to end playback on the overlay. default is to play the whole overlay
"source": "vid1", // main video asset
"start": "30", // when (on the main video timeline) to begin showing the overlay. default is 0 (beginning)
"end": "30 + overlay.duration", // when (on the main video timeline) to stop showing the overlay. default is to play the entire overlay
"overlay": {
"source": "vid2", // overlay this video on the main video
"start": "0", // when (on the overlay video timeline) to begin playback on the overlay. default is 0 (beginning)
"end": "overlay.duration", // when (on the overlay video timeline) to end playback on the overlay. default is to play the entire overlay
"width": "overlay.width / 2", // how wide the overlay will be, in pixels. default is the full overlay width, or maintain aspect ratio if height was set
"height": "", // how tall the overlay will be, in pixels. default is the full overlay height, or maintain aspect ratio if width was set

"height": "source.height", // how tall the overlay will be, in pixels. default is the full overlay height, or maintain aspect ratio if width was set
"x": "source.width / 2", // horizontal overlay position on main video. default is 0
"y": "source.height / 2", // vertical overlay position on main video. default is 0

"outputWidth": "1920", // output width in pixels. default is source width
"outputHeight": "1024" // output height in pixes. default is source height
"y": "source.height / 2" // vertical overlay position on main video. default is 0
}
}
]


Loading…
Откажи
Сачувај