Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect order of arguments for input #124

Closed
kresic-ivan-nsoft opened this issue Sep 12, 2017 · 6 comments
Closed

Incorrect order of arguments for input #124

kresic-ivan-nsoft opened this issue Sep 12, 2017 · 6 comments

Comments

@kresic-ivan-nsoft
Copy link

kresic-ivan-nsoft commented Sep 12, 2017

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?

@bobmarks
Copy link

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).

@kresic-ivan-nsoft
Copy link
Author

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.

@bobmarks
Copy link

bobmarks commented Sep 13, 2017

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).

@kresic-ivan-nsoft
Copy link
Author

Awesome @bobmarks, I'll take a look. Thanks a bunch!

@bobmarks
Copy link

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.

@Euklios
Copy link
Collaborator

Euklios commented Aug 21, 2024

Closed thanks to #339

@Euklios Euklios closed this as completed Aug 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants