java 如何从java读取ffmpeg响应并使用它来创建进度条?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10927718/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 03:06:58  来源:igfitidea点击:

How to read ffmpeg response from java and use it to create a progress bar?

javaffmpegprogress-bar

提问by shalki

I am working on creating a progress bar for ffmpeg in java. So for that I need to execute a command, then read all of the progress:

我正在为 java 中的 ffmpeg 创建一个进度条。为此,我需要执行一个命令,然后读取所有进度:

String[] command = {"gnome-terminal", "-x", "/bin/sh", "-c","ffmpeg -i /home/tmp/F.webm /home/tmp/converted1.mp4"};

Process process = Runtime.getRuntime().exec(command);

This runs perfectly. However, I need to capture the all of the progress to make a progress bar. So how can I read that data from java?

这运行完美。但是,我需要捕获所有进度才能制作进度条。那么我怎样才能从java中读取这些数据呢?

回答by aioobe

Here's a complete example for you which should get you started

这是一个完整的示例,应该可以帮助您入门

import java.io.*;
import java.util.Scanner;
import java.util.regex.Pattern;

class Test {
  public static void main(String[] args) throws IOException {
    ProcessBuilder pb = new ProcessBuilder("ffmpeg","-i","in.webm","out.mp4");
    final Process p = pb.start();

    new Thread() {
      public void run() {

        Scanner sc = new Scanner(p.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(":");
        double totalSecs = Integer.parseInt(hms[0]) * 3600
                         + Integer.parseInt(hms[1]) *   60
                         + Double.parseDouble(hms[2]);
        System.out.println("Total duration: " + totalSecs + " seconds.");

        // Find time as long as possible.
        Pattern timePattern = Pattern.compile("(?<=time=)[\d.]*");
        String match;
        while (null != (match = sc.findWithinHorizon(timePattern, 0))) {
          double progress = Double.parseDouble(match) / totalSecs;
          System.out.printf("Progress: %.2f%%%n", progress * 100);
        }
      }
    }.start();

  }
}

Output:

输出:

Total duration: 117.7 seconds.
Progress: 7.71%
Progress: 16.40%
Progress: 25.00%
Progress: 33.16%
Progress: 42.67%
Progress: 51.35%
Progress: 60.57%
Progress: 69.07%
Progress: 78.02%
Progress: 86.49%
Progress: 95.94%
Progress: 99.97%

You may also consider using some kind of Java bindings for ffmpeg such as jjmpegwhich may provide what you need in a more robust way.

您还可以考虑为 ffmpeg 使用某种 Java 绑定,例如jjmpeg,它可以以更健壮的方式提供您需要的内容。

EDIT

编辑

With ffmpeg 2.0, time output is HH:mm:ss.Sso the timePatternneeds a to incorporate a :

使用 ffmpeg 2.0,时间输出是HH:mm:ss.S如此timePattern需要一个合并一个:

Pattern timePattern = Pattern.compile("(?<=time=)[\d:.]*");

In addition, the durwill need to be split on :and summed together

此外,dur将需要拆分:并汇总在一起

String[] matchSplit;
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]) / totalSecs;
//...

回答by AlexR

You can try to parse ffmpegoutput and somehow understand what work is already done. But this is hard and not stable anyway. Neither we (the ffmpeg users) nor ffmpeg itself does not know and cannot know in terms of time how long the processing will take.

您可以尝试解析ffmpeg输出并以某种方式了解已经完成的工作。但这很难而且无论如何都不稳定。我们(ffmpeg 用户)和 ffmpeg 本身都不知道也不知道处理需要多长时间。

According to my experience the easiest way is to implement a kind of heuristics. Assume that the time of processing linearly depends on the file size. This approach is "wrong" but good enough and very simple. Now run your processing with exactly the same options you are using in real life with several files of different size. Create mapping of size-to-time. Do statistical analysis and create formula like time = something + coef * size.

根据我的经验,最简单的方法是实现一种启发式方法。假设处理时间线性取决于文件大小。这种方法是“错误的”,但足够好且非常简单。现在使用您在现实生活中使用的几个不同大小的文件完全相同的选项运行您的处理。创建大小到时间的映射。进行统计分析并创建公式,如time = something + coef * size.

Now you can create you process bar. As most process bars it should arrive to ~95% and then wait for realtermination of the process.

现在您可以创建您的流程栏。由于大多数进程条它应该到达~95%,然后等待进程的真正终止。

It is very simple and works not worse than any other more sophisticated solution.

它非常简单,并且不会比任何其他更复杂的解决方案差。

回答by Chirag Darji

*I have Successfully Display ProgressBar for ffmpeg command using Following Code.

*我已使用以下代码成功显示 ffmpeg 命令的进度条。

  try {
                Scanner sc = new Scanner(process.getErrorStream());

                // Find duration
                Pattern durPattern = Pattern.compile("(?<=Duration: )[^,]*");
                String dur = sc.findWithinHorizon(durPattern, 0);
                Log.e("duration"+dur);
                String givenDateString = dur;
                SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.S");
                sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
                try {
                    Date mDate = sdf.parse(givenDateString);
                    totalDuration = mDate.getTime();
                    System.out.println("Duration in milli :: " + totalDuration);
                } catch (ParseException e) {
                    e.printStackTrace();
                }

                // Find time as long as possible.
                Pattern timePattern = Pattern.compile("(?<=time=)[\d:.]*");
                String match;
                String[] matchSplit;
                while (null != (match = sc.findWithinHorizon(timePattern, 0))) {
                    if (isCancelled()) {
                        return;
                    }
                    Log.e("match"+match);
                    String givenDateString1 = match;
                    SimpleDateFormat sdf1 = new SimpleDateFormat("HH:mm:ss.S");
                    sdf1.setTimeZone(TimeZone.getTimeZone("GMT"));
                    try {
                        Date mDate = sdf1.parse(givenDateString1);
                        currentDuration = mDate.getTime();
                        System.out.println("Time in milli :: " + currentDuration);
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                    Double percentage = (double) 0;

                    long currentSeconds = (int) (currentDuration);
                    long totalSeconds = (int) (totalDuration);

                    // calculating percentage
                     percentage =(((double)currentSeconds)/totalSeconds)*100;


                    Log.e("Progress"+percentage);
                    publishProgress(""+percentage);
                }
            }catch (Exception e){
                e.printStackTrace();
            }