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

Posted

技术标签:

【中文标题】如何从 java 读取 ffmpeg 响应并使用它来创建进度条?【英文标题】:How to read ffmpeg response from java and use it to create a progress bar? 【发布时间】:2012-06-11 06:22:50 【问题描述】:

我正在为 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);

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

【问题讨论】:

我得到像frame= 2822 fps=493 q=19.1 Lsize=4082kB time=117.66 bitrate= 284.2kbits/s 这样的输出,你打算如何提取%-progress? 首先看到它在开始时显示了持续时间......我希望先读取它并将其转换为秒并存储然后所有行像frame= 2822 fps=493 q=19.1 Lsize=4082kB time=117.66 bitrate= 284.2kbits/s和从这一行开始提取时间=价值,通过这个我会发现公式(time/duration)*100 我明白了。让我试一试:-) 嗨,我也在工作 FFMPEG Java 的说唱歌手 JJPEG。我想问一下,在这里我有两个选择,要么通过 Java 应用程序使用 FFMPEG CMD 命令,要么直接使用 JJMPEG。你能指导我哪些选择对我更好? 【参考方案1】:

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

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();

  

输出:

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%

您还可以考虑为 ffmpeg 使用某种 Java 绑定,例如 jjmpeg,这可能会以更强大的方式提供您需要的内容。

编辑

使用 ffmpeg 2.0,时间输出为 HH:mm:ss.S,因此 timePattern 需要合并 :

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

此外,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;
//...

【讨论】:

请注意,如果文件存在,整个程序会冻结File 'out.mp4' already exists. Overwrite ? [y/N]这个问题。 是的...我知道...这就是我发出命令的原因:String[] command = "gnome-terminal", "-x", "/bin/sh", "-c","ffmpeg -i /home/tmp/F.webm /home/tmp/converted1.mp4"; 这样它在执行此过程时会打开一个终端但是当我这样做时我不能获取ffmpeg进程的错误流......那是我被卡住甚至现在被卡住的地方......我只用你在上面的代码中给出的ffmpeg命令得到了输出......但是如果文件它会冻结已经存在... 啊哈。好吧,如果 ffmpeg 在 gnome 终端中运行,您将无法获得它的输出。在启动ffmpeg 之前使用File.exists 检查文件是否存在。 看起来 ffmpeg 输出已更改为这些模式不再起作用的位置。 @JohnGiotta,请随时将更正的模式附加到我的答案中。【参考方案2】:

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

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

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

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

【讨论】:

嘿,这不需要....我在做什么是正确的?我的方法是获取输入视频的持续时间,当你执行这个 ffmpeg 命令时将显示它......它甚至会使用 time=value 显示进度,而那个时间是告诉有多少视频已被转换的时间。 ..例如,如果我的输入视频持续时间为 00:04:30.00 并且在特定时间点我得到frame= 2822 fps=493 q=19.1 Lsize=4082kB time=117.66 bitrate= 284.2kbits/s 那么这意味着我的 00:01:57.66 视频已被转换......所以通过这个我可以得到进展.. 但问题是我将如何从我的 java 程序中读取它......并且 ffmpeg 将它发送到 stderr......我可以将它重定向到一个文件......但我需要立即取得进展这正在发生..因为我想在进程进行时制作一个进度条.. 获取你做的进程的错误流p.getErrorStream().【参考方案3】:

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

  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();
            

【讨论】:

以上是关于如何从 java 读取 ffmpeg 响应并使用它来创建进度条?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用ffmpeg库把从摄像头传回的bmp图像不断保存成视频?

能够使用 ffmpeg 和 python 读取当前音频帧

如何从 FFMPEG 生成 SDP 文件

有没有办法从 Java 中的响应对象中读取 cookie?

创建一个读取文本并使用它的 API

FFmpeg命令读取RTMP流如何设置超时时间