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

I am working on creating a progress bar for ffmpeg in java. Therefore, for this I need to execute the command and then read all the progress:

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 works great. However, I need to record all progress in order to make an indicator of progress. So how can I read this data from java?

+7
source share
3 answers

Here is a complete example for you that should run you

 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 might also consider using some kind of Java bindings for ffmpeg, such as jjmpeg , which can provide what you need in a more reliable way.

EDIT

With ffmpeg 2.0, the time output is HH:mm:ss.S , so timePattern must include a :

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

In addition, dur needs to be broken down into : and summed together

 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; //... 
+16
source

You can try to parse ffmpeg output and somehow understand what work has already been done. But it is difficult and unstable. Neither we (users of ffmpeg) nor ffmpeg ourselves know and cannot know in terms of time how much time the processing will take.

According to my experience, the easiest way is to implement a kind of heuristic. Suppose processing time is linearly dependent on file size. This approach is "wrong", but good enough and very simple. Now start your processing using the same parameters that you use in real life, with several files of different sizes. Create a mapping from time to time. Do a statistical analysis and create a formula, for example time = something + coef * size .

Now you can create a process panel. Like most stages of processing, it should reach ~ 95%, and then wait for the actual completion of the process.

It is very simple and works no worse than any other more complex solution.

+1
source

* I successfully display a ProgressBar for the ffmpeg command using the following code.

  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(); } 
0
source

All Articles