Sunday, February 19, 2012

Capturing webcam image with Java Media Framework

I have used Java Media Framework (JMF) quite a lot in the past, and recently I strarted to experiment again with some of my old algorithms. Having changed my computers during these years, I noticed that the old code did not work correctly. Especially, setting the webcam video capture format did not respond. This is the problem I noticed many people were still having, based on the question on the stackoverflow etc.

After some hours of debugging, I found out that the format needs to be set before the javax.media.Player is started. After the player has been started, the FormatControl.setFormat() has no effect.

Here is also a full source code minimal webcam image grabber. Please, let me know if you have problems with it. Please note that the initialization of the video capture device may take some seconds. If you are grabbing images as fast as possible, the first image captures will fail (the images returned have dimension 1x1) until the video capture device is ready.

Use class like this:

    FrameGrabber grabber = new FrameGrabber();
    if (grabber.init()==true) {
        BufferedImage frame = grabber.grab();
    }

And the class source code:
import java.util.Vector;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.media.*;
import javax.media.control.FormatControl;
import javax.media.control.FrameGrabbingControl;
import javax.media.format.VideoFormat;
import javax.media.format.YUVFormat;
import javax.media.util.BufferToImage;

//
// source code by tejopa / popscan.blogspot.com, 2012
//

public class FrameGrabber {
 
    CaptureDeviceInfo    _cdi;
    Player               _player;
    FrameGrabbingControl _frameGrabber;
    FormatControl        _formatControl;

    public FrameGrabber() { }
  
    public boolean init() {
     Vector devices = CaptureDeviceManager.getDeviceList(new YUVFormat());
     if (devices.size()<1) {
      System.out.println("No capture devices available!");
     } else {
      _cdi = (CaptureDeviceInfo)devices.elementAt(0);
     }
     try {
            _player = Manager.createRealizedPlayer(_cdi.getLocator());
         _frameGrabber = (FrameGrabbingControl)_player.getControl("javax.media.control.FrameGrabbingControl");
         _formatControl = (FormatControl)_player.getControl("javax.media.control.FormatControl");
         _formatControl.setFormat(_cdi.getFormats()[0]);
         _player.start();
         return true;
     } catch (Exception e) {
      e.printStackTrace();
  }
     return false;
    }
    
    public BufferedImage grab() {
        if (_frameGrabber==null) {
            System.out.println("FrameGrabber is not initialized!");
            return null;
        }
     // Grab a frame from the capture device
        Buffer buf = _frameGrabber.grabFrame();
        // check that buf actually exists!
        if (buf!=null) {
            Image img = new BufferToImage((VideoFormat)buf.getFormat()).createImage(buf);
            // image creation may also fail!
            if (img!=null) {
         BufferedImage bi = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
  Graphics2D g = bi.createGraphics();
  g.drawImage(img, 0,0, null);
  return bi;
            }
        } 
        // grab failed, still return image, but make it empty and small
        BufferedImage bi = new BufferedImage(1,1, BufferedImage.TYPE_INT_ARGB);
        return bi;
    }
}