Skip to content

Commit

Permalink
Feature/318 input builder (#339)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Euklios authored Aug 21, 2024
1 parent b3b21a9 commit 4506377
Show file tree
Hide file tree
Showing 22 changed files with 1,302 additions and 60 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ FFprobe ffprobe = new FFprobe("/path/to/ffprobe");
FFmpegBuilder builder = new FFmpegBuilder()

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

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

FFmpegBuilder builder = new FFmpegBuilder()
.setInput(in) // Or filename
.done()
.addOutput("output.mp4")
.done();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package net.bramp.ffmpeg.builder;

import com.google.common.collect.ImmutableList;
import net.bramp.ffmpeg.options.EncodingOptions;
import net.bramp.ffmpeg.probe.FFmpegProbeResult;

import javax.annotation.CheckReturnValue;

public abstract class AbstractFFmpegInputBuilder<T extends AbstractFFmpegInputBuilder<T>> extends AbstractFFmpegStreamBuilder<T> {
private final FFmpegProbeResult probeResult;

private boolean readAtNativeFrameRate;
/**
* Number of times input stream shall be looped. Loop 0 means no loop, loop -1 means infinite loop.
*/
private int streamLoop;

protected AbstractFFmpegInputBuilder(FFmpegBuilder parent, String filename) {
this(parent, null, filename);
}

protected AbstractFFmpegInputBuilder(FFmpegBuilder parent, FFmpegProbeResult probeResult, String filename) {
super(parent, filename);
this.probeResult = probeResult;
}

public T readAtNativeFrameRate() {
this.readAtNativeFrameRate = true;
return getThis();
}

/**
* Sets number of times input stream shall be looped. Loop 0 means no loop, loop -1 means infinite loop.
* @param streamLoop loop counter
* @return this
*/
public T setStreamLoop(int streamLoop) {
this.streamLoop = streamLoop;

return getThis();
}

public FFmpegProbeResult getProbeResult() {
return probeResult;
}

@Override
@CheckReturnValue
@SuppressWarnings("unchecked")
protected T getThis() {
return (T) this;
}

@Override
public EncodingOptions buildOptions() {
return null;
}

@Override
protected void addGlobalFlags(FFmpegBuilder parent, ImmutableList.Builder<String> args) {
if (this.readAtNativeFrameRate) {
args.add("-re");
}

if (this.streamLoop != 0) {
args.add("-stream_loop", Integer.toString(this.streamLoop));
}

super.addGlobalFlags(parent, args);
}

public int getStreamLoop() {
return streamLoop;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import net.bramp.ffmpeg.probe.FFmpegProbeResult;

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

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

protected String complexFilter;

public AbstractFFmpegOutputBuilder() {
super();
}
Expand Down Expand Up @@ -194,6 +196,12 @@ public T setAudioBitStreamFilter(String filter) {
return (T) this;
}

public T setComplexFilter(String filter) {
this.complexFilter = checkNotEmpty(filter, "filter must not be empty");

return (T) this;
}

/**
* Sets Audio Filter
*
Expand Down Expand Up @@ -247,6 +255,7 @@ public EncodingOptions buildOptions() {
@Override
protected List<String> build(int pass) {
Preconditions.checkState(parent != null, "Can not build without parent being set");

return build(parent, pass);
}

Expand All @@ -265,15 +274,18 @@ protected List<String> build(FFmpegBuilder parent, int pass) {
checkArgument(
targetSize != 0 || video_bit_rate != 0,
"Target size, or video bitrate must be specified when using two-pass");

checkArgument(format != null, "Format must be specified when using two-pass");
}

if (targetSize > 0) {
checkState(parent.inputs.size() == 1, "Target size does not support multiple inputs");

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

String firstInput = parent.inputs.iterator().next();
FFmpegProbeResult input = parent.inputProbes.get(firstInput);
AbstractFFmpegInputBuilder<?> firstInput = parent.inputs.iterator().next();
FFmpegProbeResult input = firstInput.getProbeResult();

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

Expand Down Expand Up @@ -316,6 +328,10 @@ protected void addGlobalFlags(FFmpegBuilder parent, ImmutableList.Builder<String
if (constantRateFactor != null) {
args.add("-crf", formatDecimalInteger(constantRateFactor));
}

if (complexFilter != null) {
args.add("-filter_complex", complexFilter);
}
}

@Override
Expand Down Expand Up @@ -381,6 +397,24 @@ protected void addAudioFlags(ImmutableList.Builder<String> args) {
}
}

@Override
protected void addSourceTarget(int pass, ImmutableList.Builder<String> args) {
if (filename != null && uri != null) {
throw new IllegalStateException("Only one of filename and uri can be set");
}

// Output
if (pass == 1) {
args.add(DEVNULL);
} else if (filename != null) {
args.add(filename);
} else if (uri != null) {
args.add(uri.toString());
} else {
assert false;
}
}

@CheckReturnValue
@Override
protected T getThis() {
Expand Down Expand Up @@ -431,4 +465,8 @@ public String getVideoFilter() {
public String getVideoBitStreamFilter() {
return video_bit_stream_filter;
}

public String getComplexFilter() {
return complexFilter;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
*/
public abstract class AbstractFFmpegStreamBuilder<T extends AbstractFFmpegStreamBuilder<T>> {

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

final FFmpegBuilder parent;

Expand Down Expand Up @@ -551,11 +551,6 @@ protected List<String> build(int pass) {
protected List<String> build(FFmpegBuilder parent, int pass) {
checkNotNull(parent);

if (pass > 0) {
// TODO Write a test for this:
checkArgument(format != null, "Format must be specified when using two-pass");
}

ImmutableList.Builder<String> args = new ImmutableList.Builder<>();

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

args.addAll(extra_args);

if (filename != null && uri != null) {
throw new IllegalStateException("Only one of filename and uri can be set");
}

// Output
if (pass == 1) {
args.add(DEVNULL);
} else if (filename != null) {
args.add(filename);
} else if (uri != null) {
args.add(uri.toString());
} else {
assert false;
}
addSourceTarget(pass, args);

return args.build();
}

protected abstract void addSourceTarget(int pass, ImmutableList.Builder<String> args);

protected void addGlobalFlags(FFmpegBuilder parent, ImmutableList.Builder<String> args) {
if (strict != FFmpegBuilder.Strict.NORMAL) {
args.add("-strict", strict.toString());
Expand Down
Loading

0 comments on commit 4506377

Please sign in to comment.