Saturday, May 22, 2010

Get first and last thumb of a video using Java and FFMpeg

In the post Java Video to FLV (Flash Video) converter using FFmpeg we saw how almost any video format can be easily converted to FLV video file using FFMpeg.

FFMpeg can do much more. It can also grab a thumb at any size from any point of time in the video. We will see how it can be used to get the first and the last thumbs of a video. Getting the first thumb of a video is not so hard. You simply have to take the thumb at zero or first second of the video.

In order to achieve that, we will first construct a Java class that can take a thumb from a given video file for a given second minute and hour:"

package com.bashan.blog.video;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
/**
 * @author Bashan
 */
public class VideoThumbTaker
{
  protected String ffmpegApp;
  public VideoThumbTaker(String ffmpegApp)
  {
    this.ffmpegApp = ffmpegApp;
  }
  public void getThumb(String videoFilename, String thumbFilename, int width, int height,int hour, int min, float sec)
      throws IOException, InterruptedException
  {
    ProcessBuilder processBuilder = new ProcessBuilder(ffmpegApp, "-y", "-i", videoFilename, "-vframes", "1",
        "-ss", hour + ":" + min + ":" + sec, "-f", "mjpeg", "-s", width + "*" + height, "-an", thumbFilename);
    Process process = processBuilder.start();
    InputStream stderr = process.getErrorStream();
    InputStreamReader isr = new InputStreamReader(stderr);
    BufferedReader br = new BufferedReader(isr);
    String line;
    while ((line = br.readLine()) != null);
    process.waitFor();
  }
  public static void main(String[] args)
  {
    VideoThumbTaker videoThumbTaker = new VideoThumbTaker("C:\\ffmpeg.exe");
    try
    {
      videoThumbTaker.getThumb("C:\\someVideo.flv", "C:\\thumbTest.png", 120, 100, 0, 0, 10);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
}


Take a look at the test program, showing how a thumb is being taken from the 10th second of a video named: “someVideo.flv”. Note that we pass to the constructor the class the location of your FFMpeg tool. Also note, that we pass the width and height of the thumb.



After we have a class that knows to grab the thumb of any size from any video on a specific point in time, we can easily construct a class that grabs the first thumb of a video:



package com.bashan.blog.video;
import java.io.IOException;
/**
 * @author Bashan
 */
public class VideoFirstThumbTaker extends VideoThumbTaker
{
  public VideoFirstThumbTaker(String ffmpegApp)
  {
    super(ffmpegApp);
  }
  public void getThumb(String videoFilename, String thumbFilename, int width, int height)
      throws IOException, InterruptedException
  {
    super.getThumb(videoFilename, thumbFilename, width, height, 0, 0, 1);
  }
}


In that class we simply extend our “VideoThumbTaker” to grab the first second of the video. Grabbing the zero second of the video is not a good idea, since the first frame in many videos usually black.



In order to get the last frame of a video, we first have to know what is it’s length in hours, minutes and seconds. FFMpeg can also help us with that. We will take a dummy thumb from the video, by doing it, FFMpeg will write to the standard output the video information. We will use simple regular expression to extract the length of the video in term of hours, minutes and second. and finally, we will delete the dummy thumb that was created:



package com.bashan.blog.video;
import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * @author Bashan
 */
public class VideoInfo {
  private String ffmpegApp;
  private int hours;
  private int minutes;
  private float seconds;
  public VideoInfo(String ffmpegApp) {
    this.ffmpegApp = ffmpegApp;
  }
  public void getInfo(String videoFilename)
      throws IOException, InterruptedException {
    String tmpFile = videoFilename + ".tmp.png";
    ProcessBuilder processBuilder = new ProcessBuilder(ffmpegApp, "-y", "-i", videoFilename, "-vframes", "1",
        "-ss", "0:0:0", "-an", "-vcodec", "png", "-f", "rawvideo", "-s", "100*100", tmpFile);
    Process process = processBuilder.start();
    InputStream stderr = process.getErrorStream();
    InputStreamReader isr = new InputStreamReader(stderr);
    BufferedReader br = new BufferedReader(isr);
    String line;
    StringBuffer sb = new StringBuffer();
    while ((line = br.readLine()) != null) {
      sb.append(line);
    }
    new File(tmpFile).delete();
    Pattern pattern = Pattern.compile("Duration: (.*?),");
    Matcher matcher = pattern.matcher(sb);
    if (matcher.find()) {
      String time = matcher.group(1);
      calcTime(time);
    }
    process.waitFor();
  }
  private void calcTime(String timeStr) {
    String[] parts = timeStr.split(":");
    hours = Integer.parseInt(parts[0]);
    minutes = Integer.parseInt(parts[1]);
    seconds = Float.parseFloat(parts[2]);
  }
  public int getHours() {
    return hours;
  }
  public int getMinutes() {
    return minutes;
  }
  public float getSeconds() {
    return seconds;
  }
  public static void main(String[] args) {
    VideoInfo videoInfo = new VideoInfo("C:\\ffmpeg.exe");
    try {
      videoInfo.getInfo("C:\\testVideo.wmv");
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}


We will now use our “VideoInfo” class to construct: “VideoLastThumbTaker”. We will take the duration of the video, and subtract from it’s seconds part a small value of: 0.2 second , in order to take almost the last thumb of the video. Note: This class doesn’t handle properly a case in which the video seconds part length is a bit less than 0.2 seconds. I assume you won’t have much terrible handling this case yourself. Let’s have a look at the class:



package com.bashan.blog.video;
import java.io.IOException;
/**
 * @author Bashan
 */
public class VideoLastThumbTaker extends VideoThumbTaker
{
  public VideoLastThumbTaker(String ffmpegApp)
  {
    super(ffmpegApp);
  }
  public void getThumb(String videoFilename, String thumbFilename, int width, int height)
      throws IOException, InterruptedException
  {
    VideoInfo videoInfo = new VideoInfo(ffmpegApp);
    videoInfo.getInfo(videoFilename);
    super.getThumb(videoFilename, thumbFilename, width, height, videoInfo.getHours(),
                                                                videoInfo.getMinutes(), videoInfo.getSeconds() - 0.2f);
  }
}

You can download the source of that classes here.

2 comments:

  1. how it works in linux operating system. i con't get ffmpeg.exe location help me

    ReplyDelete
  2. You will have to install ffmpeg for linux.

    ReplyDelete