ffmpeg-cli-wrapper
ffmpeg-cli-wrapper copied to clipboard
Incorrect order of arguments for input
Hi there,
I'm trying to add some text to an input video for a new output video. However, I'm getting errors no matter what order of commands I try.
The command need to be something like ffmpeg -i input.mp4 -vf "drawtext=...", however, I cannot accomplish that order of arguments, which needs to be respected.
I've tried
- FFmpegBuilder().addExtraArgs("-vf", "drawtext="..."").addInput().addOutput()
- FFmpegBuilder().addInput().addExtraArgs("-vf", "drawtext"=..."").addOutput()
- FFmpegBuilder().addInput().addOutput().addExtraArgs("-vf", ""drawtext=...""),
however, the result is always incorrect order of the arguments (either -vf -i -f -r..., or -i -f -r -vf, both of which cause errors because -vf isn't immediately following the -i).
Any help here? Is there a way to define the order or the args or explicitly say the args should be applied after the input?
I had the same issue with extra args. The extra args is a great idea as the wrapper will always be playing catch up with ffmpeg. Unfortunately it isn't well implemented currently and this wrapper can only do 80 of the 80 / 20. Really the extra args should be added at any stage with the order preserved to mimic ANY scenario of ffmpeg. This will then cover both the 80 (via fluid API methods) and 20 scenarios (via manually strings in extra args).
Thanks @bobmarks for the comment. I somehow managed to hack it to work losing a few days of coding unfortunately. Now I need to add smaller images on top of the large one, which is the video background. The same issue with arguments order, again.
If you look at https://github.com/bramp/ffmpeg-cli-wrapper/blob/master/src/main/java/net/bramp/ffmpeg/builder/FFmpegBuilder.java you'll see: -
...
final List<String> inputs = new ArrayList<>();
final Map<String, FFmpegProbeResult> inputProbes = new TreeMap<>();
final List<String> extra_args = new ArrayList<>();
// Output
final List<FFmpegOutputBuilder> outputs = new ArrayList<>();
// Filters
String complexFilter;
String audioFilter;
String videoFilter;
...
I think the problem is really that EVERY object should have the ability to have extra_args
so that ANY order of ffmpeg
arguments can be constructed. This could be achieved by refactoring ffmpeg commands from e.g. String
to a base object e.g. FfmpegCommand
which has an extra_args
list as a parameter.
In the end I simply wrote my own tiny ffmpeg
wrapper.
I wanted 2 things (1) parameters which this wrapper didn't support (even via extra_args
) and (2) progress updater (which this wrapper does very nicely). The wrapper wasn't hard and I used this article for inspiration:
https://codedump.io/share/JXwbHVigF2SB/1/how-to-read-ffmpeg-response-from-java-and-use-it-to-create-a-progress-bar
e.g.
private String ffmpeg; // location of ffmpeg (e.g. inject in using Spring etc).
...
String [] ffmpegArgs = {
ffmpeg,
"-y", // Override file
"-i", input.getAbsolutePath(), // input video
"-c:v", "libvpx-vp9", // Copy video using `libvpx-vp9` video encoder - see https://trac.ffmpeg.org/wiki/Encode/VP9
"-crf", "20", // "constant quality" of 20 (see above URL)
"-b:v", "0", // Video bit-rate limit of 0 (see above URL)
"-c:a", "libvorbis", // Copy audio using `libvorbis` audio encoder (see https://xiph.org/vorbis/)
"-threads", "4", // Use 4 threads.
output.getAbsolutePath()
};
ProcessBuilder pb = new ProcessBuilder(ffmpegArgs);
Process process = pb.start();
FfmpegProgressExtractor progressExtractor = new FfmpegProgressExtractor (
process, progressService); // progressService is an interface which is called when progress is updated
Thread progressThread = new Thread(progressExtractor);
progressThread.setName(output.getName() + "-" + progressSection);
progressThread.start();
int retcode = process.waitFor();
and
public class FfmpegProgressExtractor implements Runnable {
// Injected fields
private Process process;
private ProgressService progressService; // simple interface
// Other fields
private double totalDurationSeconds = -1;
public FfmpegProgressExtractor (Process process, ProgressService progressService) {
this.process = process;
this.progressService = progressService;
}
@Override
public void run() {
Scanner sc = new Scanner(process.getErrorStream());
// Find duration
Pattern durPattern = Pattern.compile("(?<=Duration: )[^,]*");
String dur = sc.findWithinHorizon(durPattern, 0);
if (dur == null)
throw new RuntimeException("Could not parse duration.");
String[] hms = dur.split(":");
this.totalDurationSeconds = Integer.parseInt(hms[0]) * 3600
+ Integer.parseInt(hms[1]) * 60
+ Double.parseDouble(hms[2]);
// Start progress of this section
this.progressService.update(totalDurationSeconds, 0);
// Find time as long as possible.
Pattern timePattern = Pattern.compile("(?<=time=)[\\d:.]*");
String[] matchSplit;
String match;
while (null != (match = sc.findWithinHorizon(timePattern, 0))) {
matchSplit = match.split(":");
double progress = Integer.parseInt(matchSplit[0]) * 3600 +
Integer.parseInt(matchSplit[1]) * 60 +
Double.parseDouble(matchSplit[2]) / totalDurationSeconds;
double percentage = progress * 100;
// Update section progress
this.progressService.update(-1, percentage);
}
this.progressService.update(-1, 100); // 100 percent finished
}
}
and
public interface ProgressService {
void update(double durationSeconds, double percentage);
}
Obviously the implementation the ProgressService
will vary from app to app so no point in putting my implementation in here (which talks to a DAO
i.e. updates a database).
Awesome @bobmarks, I'll take a look. Thanks a bunch!
No worries. This is easily the best wrapper I've found and it actually helped me learn FFmpeg - if they crack the extra_args
it could also future proof it into future version of ffmpeg as well.