📄 framegrabber.java
字号:
/**
* JMF/Webcam Frame Grabber Demo
*
* @author S.Ritter 24.01.2002
* @version 1.0
*
* ALL EXAMPLES OF CODE AND/OR COMMAND-LINE INSTRUCTIONS ARE BEING
* PROVIDED BY SUN AS A COURTESY, "AS IS," AND SUN DISCLAIMS ANY AND
* ALL WARRANTIES PERTAINING THERETO, INCLUDING ANY WARRANTIES OF
* MERCHANTABILTY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
* SUN IS NOT LICENSING THIS EXAMPLE FOR ANY USE OTHER THAN FOR THE
* EDUCATIONAL PURPOSE OF SHOWING THE FUNCTIONALITY CONTAINED
* THEREIN, BY WAY OF EXAMPLE.
**/
package jmfdemo;
import java.io.*;
import java.awt.*;
import java.awt.image.*;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;
import javax.media.*;
import javax.media.control.*;
import javax.media.protocol.*;
import javax.media.util.*;
import javax.media.format.RGBFormat;
import javax.media.format.VideoFormat;
/**
* Frame grabber class
**/
public class FrameGrabber extends Thread implements ControllerListener {
/* Default device name and format parameters to use if no properties file
* is provided
*/
private final static String DEFAULT_DEV_NAME =
"vfw:Microsoft WDM Image Capture (Win32):0";
private final static String DEFAULT_X_RES = "160";
private final static String DEFAULT_Y_RES = "120";
private final static String DEFAULT_DEPTH = "24";
private Properties videoProperties;
/* These objects are used for controlling access via wait and notify to
* ensure that the processor has been realised and the second thread has
* completed it's startup
*/
private Object stateLock = new Object();
private Object runLock = new Object();
private Processor deviceProc = null;
private PushBufferStream camStream;
private PushBufferDataSource source = null;
private BufferToImage converter;
private Image currentImage;
private boolean threadRunning = false;
/**
* Constructor
*
*
* @throws FrameGrabberException If we can't start up the camera
**/
public FrameGrabber() throws FrameGrabberException {
/* If the user chooses the no parameter form of the constructor we
* try to get the video property file name from the properties
* passed on the command line
*/
String videoPropFile =
System.getProperty("video.properties", "video.properties");
setup(videoPropFile);
}
/**
* Constructor
*
* @param videoPropFile The name of the video properties file
* @throws FrameGrabberException If we can't start up the camera
**/
public FrameGrabber(String videoPropFile) throws FrameGrabberException {
setup(videoPropFile);
}
/**
* Setup method. Configures webcam and JMF ready to get images
*
* @param videoPropFile The name of the video properties file
* @throws FrameGrabberException If we can't start up the camera
**/
private void setup(String videoPropFile) throws FrameGrabberException {
videoProperties = new Properties();
if (videoPropFile != null) {
try {
FileInputStream fis = new FileInputStream(new File(videoPropFile));
videoProperties.load(fis);
} catch (IOException ioe) {
System.out.println("Unable to access video properties");
System.out.println(ioe.getMessage());
}
}
Dimension viewSize = null;
int viewDepth = 0;
String cameraDevice =
videoProperties.getProperty("device-name", DEFAULT_DEV_NAME);
/* Get the parameters for the video capture device from the properties
* file. If not defined use default values
*/
try {
String pValue =
videoProperties.getProperty("resolution-x", DEFAULT_X_RES);
int xRes = Integer.parseInt(pValue);
pValue = videoProperties.getProperty("resolution-y", DEFAULT_Y_RES);
int yRes = Integer.parseInt(pValue);
viewSize = new Dimension(xRes, yRes);
pValue = videoProperties.getProperty("colour-depth", DEFAULT_DEPTH);
viewDepth = Integer.parseInt(pValue);
} catch (NumberFormatException nfe) {
System.out.println("Bad numeric value in video properties file");
System.exit(1);
}
/* Try to get the CaptureDevice that matches the name supplied by the
* user
*/
CaptureDeviceInfo device = CaptureDeviceManager.getDevice(cameraDevice);
if (device == null)
throw new FrameGrabberException("No device found [ " +
cameraDevice + "]");
RGBFormat userFormat = null;
Format[] cfmt = device.getFormats();
/* Find the format that the user has requested (if available) */
for (int i = 0; i < cfmt.length; i++) {
if (cfmt[i] instanceof RGBFormat) {
userFormat = (RGBFormat)cfmt[i];
Dimension d = userFormat.getSize();
int bitsPerPixel = userFormat.getBitsPerPixel();
if (viewSize.equals(d) && bitsPerPixel == viewDepth)
break;
userFormat = null;
}
}
/* Throw an exception if we can't find a format that matches the
* user's criteria
*/
if (userFormat == null)
throw new FrameGrabberException("Requested format not supported");
/* To use this device we need a MediaLocator */
MediaLocator loc = device.getLocator();
if (loc == null)
throw new FrameGrabberException("Unable to get MediaLocator for device");
DataSource formattedSource = null;
/* Now create a dataSource for this device and set the format to
* the one chosen by the user.
*/
try {
formattedSource = Manager.createDataSource(loc);
} catch (IOException ioe) {
throw new FrameGrabberException("IO Error creating dataSource");
} catch (NoDataSourceException ndse) {
throw new FrameGrabberException("Unable to create dataSource");
}
/* Setting the format is rather complicated. Firstly we need to get
* the format controls from the dataSource we just created. In order
* to do this we need a reference to an object implementing the
* CaptureDevice interface (which DataSource objects can).
*/
if (!(formattedSource instanceof CaptureDevice))
throw new FrameGrabberException("DataSource not a CaptureDevice");
FormatControl[] fmtControls =
((CaptureDevice)formattedSource).getFormatControls();
if (fmtControls == null || fmtControls.length == 0)
throw new FrameGrabberException("No FormatControl available");
Format setFormat = null;
/* Now we need to loop through the available FormatControls and try
* to set the format to the one we want. According to the documentation
* even though this may appear to work, it may fail later on. Since
* we know that the format is supported we hope that this won't happen
*/
for (int i = 0; i < fmtControls.length; i++) {
if (fmtControls[i] == null)
continue;
if ((setFormat = fmtControls[i].setFormat(userFormat)) != null)
break;
}
/* Throw an exception if we couldn't set the format */
if (setFormat == null)
throw new FrameGrabberException("Failed to set camera format");
/* Connect to the DataSource */
try {
formattedSource.connect();
} catch (IOException ioe) {
throw new FrameGrabberException("Unable to connect to DataSource");
}
/* Since we don't want to display the output to the user at this stage
* we use a processor rather than a player to get frame access
*/
try {
deviceProc = Manager.createProcessor(formattedSource);
} catch (IOException ioe) {
throw new FrameGrabberException("Unable to get Processor for device: " +
ioe.getMessage());
} catch (NoProcessorException npe) {
throw new FrameGrabberException("Unable to get Processor for device: " +
npe.getMessage());
}
/* In order to use the controller we have to put it in the realized
* state. We do this by calling the realize method, but this will
* return immediately so we must register a listener (this class) to
* be notified when the controller is ready.
*/
deviceProc.addControllerListener(this);
deviceProc.realize();
/* Wait for the device to send an event telling us that it has
* reached the realized state
*/
while (deviceProc.getState() != Controller.Realized) {
synchronized (stateLock) {
try {
stateLock.wait();
} catch (InterruptedException ie) {
throw new FrameGrabberException("Failed to get to realized state");
}
}
}
deviceProc.start();
/* Get access to the PushBufferDataSource which will provide us with
* a means to get at the frame grabber
*/
try {
source = (PushBufferDataSource)deviceProc.getDataOutput();
} catch (NotRealizedError nre) {
/* Should never happen */
throw new FrameGrabberException("Processor not realized");
}
/* Now we can retrieve the PushBufferStreams that will enable us to
* access the data from the camera
*/
PushBufferStream[] streams = source.getStreams();
camStream = null;
for (int i = 0; i < streams.length; i++) {
/* Use the first Stream that is RGBFormat (there should be only one */
if (streams[i].getFormat() instanceof RGBFormat) {
camStream = streams[i];
RGBFormat rgbf = (RGBFormat)streams[i].getFormat();
converter = new BufferToImage(rgbf);
break;
}
}
}
/**
* Get an image from the camera as an AWT Image object
*
* @returns The current image from the camera
**/
public Image getImage() {
/* Since we are using a second thread to grab the images from the webcam
* we need to ensure that an image has been aquired.
* We do this by using a flag which will be set to true in the run()
* method. If this is false we wait until the run method notifies us
* that there is an image to collect
*/
while (threadRunning == false) {
synchronized (runLock) {
try {
runLock.wait();
} catch (InterruptedException ie) {
// Ignore
}
}
}
return accessInternalImage(null);
}
/**
* Get an image from the camera as a BufferedImage
*
* @returns The current image from the camera
**/
public BufferedImage getBufferedImage() {
return (BufferedImage)getImage();
}
/**
* Run method for Thread class
**/
public void run() {
Buffer b = new Buffer();
/* Simply loop forever grabbing images from the web cam and storing
* them so that the user can retrieve them when required.
*/
while (true) {
try {
camStream.read(b);
} catch (Exception e) {
// Ignore. Nothing we can really do about this
}
Image i = converter.createImage(b);
accessInternalImage(i);
/* If this is the first image we've collected we need to advertise
* to the main thread that there is an image ready and then notify
* the main thread in case it is waiting on the image
*/
if (!threadRunning) {
threadRunning = true;
synchronized (runLock) {
runLock.notifyAll();
}
}
}
}
/**
* Method called when a controller event is received (implements
* ControllerListener interface)
*
* @param ce The controller event
**/
public void controllerUpdate(ControllerEvent ce) {
if (ce instanceof RealizeCompleteEvent) {
synchronized (stateLock) {
stateLock.notifyAll();
}
}
}
/**
* Method that controls access to the global image variable. This ensures
* that there is no confusion over one thread reading an image whilst
* another is writing to it
*
* @param image The image to store (null indicates retrieval of the image)
* @return The image (if the parameter was null)
**/
private synchronized Image accessInternalImage(Image image) {
if (image == null) {
return currentImage;
}
currentImage = image;
return null;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -