Skip to content

Commit 4506377

Browse files
authored
Feature/318 input builder (#339)
* feat: Implement AbstractFFmepgInputBuilder * test: Add precondition checks for two pass builder * feat: Extract input and output specifier into their own method * docs: Update javadoc * docs(README): Update README to include the new InputBuilder syntax * feat: Add -stream_loop parameter to AbstractFFmpegInputBuilder
1 parent b3b21a9 commit 4506377

22 files changed

+1302
-60
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ FFprobe ffprobe = new FFprobe("/path/to/ffprobe");
3939
FFmpegBuilder builder = new FFmpegBuilder()
4040

4141
.setInput("input.mp4") // Filename, or a FFmpegProbeResult
42+
.done()
4243
.overrideOutputFiles(true) // Override the output if it exists
4344

4445
.addOutput("output.mp4") // Filename for the destination
@@ -100,6 +101,7 @@ FFmpegProbeResult in = ffprobe.probe("input.flv");
100101

101102
FFmpegBuilder builder = new FFmpegBuilder()
102103
.setInput(in) // Or filename
104+
.done()
103105
.addOutput("output.mp4")
104106
.done();
105107

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package net.bramp.ffmpeg.builder;
2+
3+
import com.google.common.collect.ImmutableList;
4+
import net.bramp.ffmpeg.options.EncodingOptions;
5+
import net.bramp.ffmpeg.probe.FFmpegProbeResult;
6+
7+
import javax.annotation.CheckReturnValue;
8+
9+
public abstract class AbstractFFmpegInputBuilder<T extends AbstractFFmpegInputBuilder<T>> extends AbstractFFmpegStreamBuilder<T> {
10+
private final FFmpegProbeResult probeResult;
11+
12+
private boolean readAtNativeFrameRate;
13+
/**
14+
* Number of times input stream shall be looped. Loop 0 means no loop, loop -1 means infinite loop.
15+
*/
16+
private int streamLoop;
17+
18+
protected AbstractFFmpegInputBuilder(FFmpegBuilder parent, String filename) {
19+
this(parent, null, filename);
20+
}
21+
22+
protected AbstractFFmpegInputBuilder(FFmpegBuilder parent, FFmpegProbeResult probeResult, String filename) {
23+
super(parent, filename);
24+
this.probeResult = probeResult;
25+
}
26+
27+
public T readAtNativeFrameRate() {
28+
this.readAtNativeFrameRate = true;
29+
return getThis();
30+
}
31+
32+
/**
33+
* Sets number of times input stream shall be looped. Loop 0 means no loop, loop -1 means infinite loop.
34+
* @param streamLoop loop counter
35+
* @return this
36+
*/
37+
public T setStreamLoop(int streamLoop) {
38+
this.streamLoop = streamLoop;
39+
40+
return getThis();
41+
}
42+
43+
public FFmpegProbeResult getProbeResult() {
44+
return probeResult;
45+
}
46+
47+
@Override
48+
@CheckReturnValue
49+
@SuppressWarnings("unchecked")
50+
protected T getThis() {
51+
return (T) this;
52+
}
53+
54+
@Override
55+
public EncodingOptions buildOptions() {
56+
return null;
57+
}
58+
59+
@Override
60+
protected void addGlobalFlags(FFmpegBuilder parent, ImmutableList.Builder<String> args) {
61+
if (this.readAtNativeFrameRate) {
62+
args.add("-re");
63+
}
64+
65+
if (this.streamLoop != 0) {
66+
args.add("-stream_loop", Integer.toString(this.streamLoop));
67+
}
68+
69+
super.addGlobalFlags(parent, args);
70+
}
71+
72+
public int getStreamLoop() {
73+
return streamLoop;
74+
}
75+
}

src/main/java/net/bramp/ffmpeg/builder/AbstractFFmpegOutputBuilder.java

+41-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import net.bramp.ffmpeg.probe.FFmpegProbeResult;
2020

2121
/** Builds a representation of a single output/encoding setting */
22-
@SuppressWarnings({"DeprecatedIsStillUsed", "deprecation","unchecked"})
22+
@SuppressWarnings({"DeprecatedIsStillUsed", "unchecked"})
2323
public abstract class AbstractFFmpegOutputBuilder<T extends AbstractFFmpegOutputBuilder<T>> extends AbstractFFmpegStreamBuilder<T> {
2424

2525
static final Pattern trailingZero = Pattern.compile("\\.0*$");
@@ -67,6 +67,8 @@ public abstract class AbstractFFmpegOutputBuilder<T extends AbstractFFmpegOutput
6767
@Deprecated
6868
public String video_bit_stream_filter;
6969

70+
protected String complexFilter;
71+
7072
public AbstractFFmpegOutputBuilder() {
7173
super();
7274
}
@@ -194,6 +196,12 @@ public T setAudioBitStreamFilter(String filter) {
194196
return (T) this;
195197
}
196198

199+
public T setComplexFilter(String filter) {
200+
this.complexFilter = checkNotEmpty(filter, "filter must not be empty");
201+
202+
return (T) this;
203+
}
204+
197205
/**
198206
* Sets Audio Filter
199207
*
@@ -247,6 +255,7 @@ public EncodingOptions buildOptions() {
247255
@Override
248256
protected List<String> build(int pass) {
249257
Preconditions.checkState(parent != null, "Can not build without parent being set");
258+
250259
return build(parent, pass);
251260
}
252261

@@ -265,15 +274,18 @@ protected List<String> build(FFmpegBuilder parent, int pass) {
265274
checkArgument(
266275
targetSize != 0 || video_bit_rate != 0,
267276
"Target size, or video bitrate must be specified when using two-pass");
277+
278+
checkArgument(format != null, "Format must be specified when using two-pass");
268279
}
280+
269281
if (targetSize > 0) {
270282
checkState(parent.inputs.size() == 1, "Target size does not support multiple inputs");
271283

272284
checkArgument(
273285
constantRateFactor == null, "Target size can not be used with constantRateFactor");
274286

275-
String firstInput = parent.inputs.iterator().next();
276-
FFmpegProbeResult input = parent.inputProbes.get(firstInput);
287+
AbstractFFmpegInputBuilder<?> firstInput = parent.inputs.iterator().next();
288+
FFmpegProbeResult input = firstInput.getProbeResult();
277289

278290
checkState(input != null, "Target size must be used with setInput(FFmpegProbeResult)");
279291

@@ -316,6 +328,10 @@ protected void addGlobalFlags(FFmpegBuilder parent, ImmutableList.Builder<String
316328
if (constantRateFactor != null) {
317329
args.add("-crf", formatDecimalInteger(constantRateFactor));
318330
}
331+
332+
if (complexFilter != null) {
333+
args.add("-filter_complex", complexFilter);
334+
}
319335
}
320336

321337
@Override
@@ -381,6 +397,24 @@ protected void addAudioFlags(ImmutableList.Builder<String> args) {
381397
}
382398
}
383399

400+
@Override
401+
protected void addSourceTarget(int pass, ImmutableList.Builder<String> args) {
402+
if (filename != null && uri != null) {
403+
throw new IllegalStateException("Only one of filename and uri can be set");
404+
}
405+
406+
// Output
407+
if (pass == 1) {
408+
args.add(DEVNULL);
409+
} else if (filename != null) {
410+
args.add(filename);
411+
} else if (uri != null) {
412+
args.add(uri.toString());
413+
} else {
414+
assert false;
415+
}
416+
}
417+
384418
@CheckReturnValue
385419
@Override
386420
protected T getThis() {
@@ -431,4 +465,8 @@ public String getVideoFilter() {
431465
public String getVideoBitStreamFilter() {
432466
return video_bit_stream_filter;
433467
}
468+
469+
public String getComplexFilter() {
470+
return complexFilter;
471+
}
434472
}

src/main/java/net/bramp/ffmpeg/builder/AbstractFFmpegStreamBuilder.java

+4-20
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
*/
5656
public abstract class AbstractFFmpegStreamBuilder<T extends AbstractFFmpegStreamBuilder<T>> {
5757

58-
private static final String DEVNULL = SystemUtils.IS_OS_WINDOWS ? "NUL" : "/dev/null";
58+
protected static final String DEVNULL = SystemUtils.IS_OS_WINDOWS ? "NUL" : "/dev/null";
5959

6060
final FFmpegBuilder parent;
6161

@@ -551,11 +551,6 @@ protected List<String> build(int pass) {
551551
protected List<String> build(FFmpegBuilder parent, int pass) {
552552
checkNotNull(parent);
553553

554-
if (pass > 0) {
555-
// TODO Write a test for this:
556-
checkArgument(format != null, "Format must be specified when using two-pass");
557-
}
558-
559554
ImmutableList.Builder<String> args = new ImmutableList.Builder<>();
560555

561556
addGlobalFlags(parent, args);
@@ -589,24 +584,13 @@ protected List<String> build(FFmpegBuilder parent, int pass) {
589584

590585
args.addAll(extra_args);
591586

592-
if (filename != null && uri != null) {
593-
throw new IllegalStateException("Only one of filename and uri can be set");
594-
}
595-
596-
// Output
597-
if (pass == 1) {
598-
args.add(DEVNULL);
599-
} else if (filename != null) {
600-
args.add(filename);
601-
} else if (uri != null) {
602-
args.add(uri.toString());
603-
} else {
604-
assert false;
605-
}
587+
addSourceTarget(pass, args);
606588

607589
return args.build();
608590
}
609591

592+
protected abstract void addSourceTarget(int pass, ImmutableList.Builder<String> args);
593+
610594
protected void addGlobalFlags(FFmpegBuilder parent, ImmutableList.Builder<String> args) {
611595
if (strict != FFmpegBuilder.Strict.NORMAL) {
612596
args.add("-strict", strict.toString());

0 commit comments

Comments
 (0)