Showing posts with label ffmpeg. Show all posts
Showing posts with label ffmpeg. Show all posts

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.

Wednesday, September 30, 2009

Java Video to FLV (Flash Video) converter using FFmpeg

This post should have been written one or two years ago, with the Exit of YouTube and the big promise of the video content on the internet. But, as with every big buzz, the world has already taken it's next step towards smart phones and social networks.
YouTube and all the other video web sites allow uploading a video file. This video file can be shown on the web using a Flash based video player. The Flash video player can play FLV files. FLV file stands for: Flash Video File, which is a video file quite similar to WMV, MPG, AVI etc'.
So, as you can understand, the basic step towards building your own video site, is knowing to convert uploaded video files from all kinds to FLV. Doing the video conversion is a very complex job, requiring a great knowledge. Luckily for us, there is a great open source project named: FFmpeg, which, among the rest, allows us to convert almost any video from any type to any type. FFmpeg is a cross platform project. Whether you have Linux servers or Windows server (or any other popular operating system), FFmpeg will be available for you.
First, in order to do video conversion, we will have to download FFmpeg. If you are using Windows, you can find on the web compiled version ready for use. I found mine here.
FFmpeg is command line application. It has many options and parameters. In general, in order to convert a video file from any type to FLV we have to run FFmpeg with the following parameters:
ffmpeg -i "C:\filein.mp4" -ar 44100 -s 320x200 -qscale 5 "C:\fileout.flv"

This command takes a file named “filein.mp4” and converts it to FLV video file named: “fileout.flv”. The out video file dimensions is: 320x200. The parameter “qscale” defines the quality of the resulted video. Lower values give better quality. This parameter is not mandatory. Of course that the higher the quality of the video the higher the weight of the resulted file.

We will wrap the FFmpeg command line tool with Java code. We do it by simply executing the above command from Java. This will enable us to convert video to FLV file. These FLV files could be later played by some Flash Video Player (for example , to show the video on the web). A great free Flash based video player is: JW FLV Media Player. It supports many modern features required from a flash player. Some of them are:

  • Full events support allowing to control the video and get notifications from JavaScrip.
  • It allows using plugins.
  • It supports playing a video from any point (just like YouTube).
  • It can play videos directly from YouTube.

These are only some of the features this player has.

Let have a look at out Java Video to FLV converter. It’s code is very simple and straight forward:

package com.bashan.blog.video;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class FLVConverter {
    private String ffmpegApp;
    public FLVConverter(String ffmpegApp) {
        this.ffmpegApp = ffmpegApp;
    }
    public void convert(String filenameIn, String filenameOut, int width, int height) throws IOException, InterruptedException {
        convert(filenameIn, filenameOut, width, height, -1);
    }
    public int convert(String filenameIn, String filenameOut, int width, int height, int quality)
            throws IOException, InterruptedException {
        ProcessBuilder processBuilder;
        if (quality > -1) {
            processBuilder = new ProcessBuilder(ffmpegApp, "-i", filenameIn, "-ar", "44100",
                    "-s", width + "*" + height, "-qscale", quality + "", filenameOut);
        } else {
            processBuilder = new ProcessBuilder(ffmpegApp, "-i", filenameIn, "-ar", "44100",
                    "-s", width + "*" + height, filenameOut);
        }
        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) ;
        {
        }
        return process.waitFor();
    }
}

And here is a small test program that does exactly what the above command line example is doing:

    public static void main(String[] args) throws Exception {
        FLVConverter FLVConverter = new FLVConverter("C:\\Users\\merdok\\IdeaProjects\\dev\\tools\\ffmpeg\\ffmpeg.exe");
        FLVConverter.convert("C:\\filein.mp4", "C:\\fileout.flv", 320, 200, 5);
    }
You can also download the converter here.