All Downloads are FREE. Search and download functionalities are using the official Maven repository.

au.edu.jcu.v4l4j.AbstractGrabber Maven / Gradle / Ivy

Go to download

This is mavenized and customized fork of V4L4J by Gilles Gigan. It contains only Java source code (no natives) and *.so objects precompiled for several architectures.

The newest version!
/*
 * Copyright (C) 2007-2008 Gilles Gigan ([email protected])
 * eResearch Centre, James Cook University (eresearch.jcu.edu.au)
 *
 * This program was developed as part of the ARCHER project
 * (Australian Research Enabling Environment) funded by a   
 * Systemic Infrastructure Initiative (SII) grant and supported by the Australian
 * Department of Innovation, Industry, Science and Research
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public  License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 *
 */
package au.edu.jcu.v4l4j;

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.Vector;

import au.edu.jcu.v4l4j.FrameInterval.DiscreteInterval;
import au.edu.jcu.v4l4j.exceptions.CaptureChannelException;
import au.edu.jcu.v4l4j.exceptions.ImageFormatException;
import au.edu.jcu.v4l4j.exceptions.InitialisationException;
import au.edu.jcu.v4l4j.exceptions.InvalidValue;
import au.edu.jcu.v4l4j.exceptions.NoTunerException;
import au.edu.jcu.v4l4j.exceptions.StateException;
import au.edu.jcu.v4l4j.exceptions.UnsupportedMethod;
import au.edu.jcu.v4l4j.exceptions.V4L4JException;
import au.edu.jcu.v4l4j.exceptions.VideoStandardException;


/**
 * This abstract class implements the core functionalities found in all frame 
 * grabbers, initialisation, starting the capture, capturing frames, stopping
 * the capture and releasing resources. It must be subclassed.
* Subclasses must implement {@link FrameGrabber#getImageFormat()} to return the * correct image format used for capture. {@link #init()} may also be overridden * if required. * @author gilles */ abstract class AbstractGrabber implements FrameGrabber { protected final static int RAW_GRABBER = 0; protected final static int JPEG_GRABBER = 1; protected final static int RGB24_GRABBER = 2; protected final static int BGR24_GRABBER = 3; protected final static int YUV_GRABBER = 4; protected final static int YVU_GRABBER = 5; protected DeviceInfo dInfo; private int width; private int height; private int channel; private int standard; protected int nbV4LBuffers; protected Vector videoFrames; private Vector availableVideoFrames; protected State state; protected int format; private Tuner tuner; private int type; private long lastCapturedFrameSequence; // update v4l4j_FrameGrabber.c if private long lastCapturedFrameTimeuSec; // these three names are changed private int lastCapturedFrameBufferIndex;// private PushSource pushSource; private ThreadFactory threadFactory; /* * JNI returns a long (which is really a pointer) when a device is allocated * for use. This field is read-only (!!!) */ protected long object; static { try { System.loadLibrary("v4l4j"); } catch (UnsatisfiedLinkError e) { System.err.println("Cant load v4l4j JNI library"); throw e; } } private native int doInit(long o, int numBuffers, int w, int h, int ch, int std, int requestedFormat, int output) throws V4L4JException; private native void start(long o) throws V4L4JException; /** * This method sets a new value for the JPEG quality * @param o the struct v4l4_device * @param i the new value * @throws V4L4JException if the JPEG quality is disabled because of the * type of this frame grabber (not {@link #JPEG_GRABBER}). */ protected native void setQuality(long o, int i); private native int getBufferSize(long o); private native int enqueueBuffer(long o, int index); private native int fillBuffer(long o, byte array[]) throws V4L4JException; private native void stop(long o); private native void doRelease(long o); private native void doSetFrameIntv(long o, int n, int d) throws InvalidValue; private native int doGetFrameIntv(long o, int what); private native void doSetVideoInputNStandard(long o, int input, int std); private native int doGetVideoInput(long o); private native int doGetVideoStandard(long o); /** * This constructor builds a FrameGrabber object used to capture frames from * a video source. * @param di the DeviceInfo object associated with the video device who * created this grabber * @param o a JNI pointer to the v4l4j_device structure * @param w the requested frame width * @param h the requested frame height * @param ch the input index, as returned by * InputInfo.getIndex() * @param std the video standard, as returned by * InputInfo.getSupportedStandards() (see V4L4JConstants) * @param t the {@link Tuner} associated with this frame grabber or * null. * @param imf the image format frames should be captured in * @param ty the output image format, ie the type of this frame grabber: * {@link #RAW_GRABBER}, {@link #JPEG_GRABBER}, {@link #RGB24_GRABBER}, * {@link #BGR24_GRABBER}, {@link #YUV_GRABBER}, {@link #YVU_GRABBER} * @param factory the {@link ThreadFactory} to use when creating new threads * @throw {@link ImageFormatException} if the image format is null and * type = {@link #RAW_GRABBER} */ protected AbstractGrabber(DeviceInfo di, long o, int w, int h, int ch, int std , Tuner t,ImageFormat imf, int ty, ThreadFactory factory) throws ImageFormatException{ if(imf==null) throw new ImageFormatException("The image format can not be null"); state= new State(); dInfo = di; object = o; width = w; height = h; channel = ch; standard= std; format = imf.getIndex(); tuner = t; type = ty; // Check property for user-specified number of buffers - otherwise use 4. nbV4LBuffers = (System.getProperty("v4l4j.num_driver_buffers") != null) ? Integer.parseInt(System.getProperty("v4l4j.num_driver_buffers")) : 4; videoFrames = new Vector(); availableVideoFrames = new Vector(); pushSource = null; threadFactory = factory; } /** * This method initialises the capture, and apply the capture parameters. * V4L may either adjust the height and width parameters to the closest * valid values or reject them altogether. If the values were adjusted, * they can be retrieved after calling {@link #init()} using * {@link #getWidth()} and {@link #getHeight()}. * @throws VideoStandardException if the chosen video standard is not * supported * @throws ImageFormatException for a raw frame grabber, this exception is * thrown if the chosen Image format is unsupported. * @throws CaptureChannelException if the given channel number value is not * valid * @throws ImageDimensionException if the given image dimensions are not * supported * @throws InitialisationException if the video device file can not be * initialised * @throws StateException if the frame grabber is already initialised * @throws V4L4JException if there is an error applying capture parameters */ void init() throws V4L4JException{ state.init(); // Initialise libvideo and setup capture parameters // Return value is the number of buffers mmaped into the driver's memory nbV4LBuffers = doInit(object, nbV4LBuffers, width, height, channel, standard, format, type); int bufferSize = getBufferSize(object); // Create the V4L4J data buffer objects createBuffers(bufferSize); state.commit(); } /** * This abstract method is called when {@link #init()} succeeds and is * responsible for populating the {@link #videoFrames} member (vector of * {@link #nbV4LBuffers} {@link BaseVideoFrame}s). * @param bufferSize the size of each buffer */ protected abstract void createBuffers(int bufferSize); /* (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#getNumberOfBuffers() */ @Override public int getNumberOfVideoFrames() { return nbV4LBuffers; } /* * (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#getNumberOfRecycledVideoFrames() */ @Override public int getNumberOfRecycledVideoFrames(){ synchronized(availableVideoFrames) { return availableVideoFrames.size(); } } /* (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#setFrameInterval() */ @Override public void setFrameInterval(int num, int denom) throws InvalidValue{ synchronized(state){ if(state.isStarted()) throw new StateException("Invalid method call"); doSetFrameIntv(object, num, denom); } } /* (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#getFrameInterval() */ @Override public DiscreteInterval getFrameInterval() { synchronized(state){ //TODO: not sure if the following if statement is required //ie, it might be possible to get the current frame intv //while capturing... to be tested if(state.isStarted()) throw new StateException("Invalid method call: cannot get the" +" frame interval while capturing."); return new DiscreteInterval( doGetFrameIntv(object, 0), doGetFrameIntv(object, 1) ); } } /* (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#setVideoInputNStandard() */ @Override public void setVideoInputNStandard(int input, int standard) throws VideoStandardException, CaptureChannelException { state.checkReleased(); doSetVideoInputNStandard(object, input, standard); } /* (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#getVideoInput() */ @Override public int getVideoInput() { state.checkReleased(); return doGetVideoInput(object); } /* (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#getVideoStandard() */ @Override public int getVideoStandard() { state.checkReleased(); return doGetVideoStandard(object); } /* (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#getTuner() */ @Override public final Tuner getTuner() throws NoTunerException{ if(tuner==null) throw new NoTunerException("This input does not have a tuner"); state.checkReleased(); return tuner; } /* (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#setCaptureCallback() */ @Override public final void setCaptureCallback(CaptureCallback callback) { if (callback == null) throw new NullPointerException("The callback object cannot be null"); synchronized (state){ // make sure we are in the right state. if (state.isStarted()) throw new StateException("This frame grabber is already started"); // create the push source object pushSource = new PushSource(this, callback, threadFactory); } } /* (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#startCapture() */ @Override public final void startCapture() throws V4L4JException { state.start(); // make sure we have a push source if(pushSource == null) { state.rollback(); throw new V4L4JException("setCaptureCallback() must be called with a valid " + "callback object before startCapture()"); } // start the push source and wait until it's blocked on getVideoFrame() pushSource.startCapture(); state.waitForAtLeastOneUser(); try { // start video capture and enqueue all buffers start(object); } catch (V4L4JException e) { // Error starting the capture... // stop the push source thread pushSource.stopCapture(); // return to previous state state.rollback(); // propagate exception throw e; } // change state to STARTED state.commit(); // put all frames into the available queue and wake up push source thread synchronized(availableVideoFrames){ availableVideoFrames.addAll(videoFrames); availableVideoFrames.notifyAll(); } } /** * This method is called as part of {@link #getVideoFrame()}. It retrieves a video * frame marked as available (recycled). if no frame is available, this method * will block. If interrupted while waiting, a {@link StateException} will * be thrown * @return an available video frame. * @thrown {@link StateException} if interrupted while waiting. */ private BaseVideoFrame getAvailableVideoFrame() { BaseVideoFrame frame = null; // block until a video frame is available synchronized (availableVideoFrames) { while (availableVideoFrames.size() == 0) try { availableVideoFrames.wait(); } catch (InterruptedException e) { throw new StateException("Interrupted while waiting for a video frame", e); } // get the video frame frame = availableVideoFrames.remove(0); } return frame; } final VideoFrame getNextVideoFrame() throws V4L4JException { int frameSize; BaseVideoFrame nextFrame; state.get(); try { // get next available video frame object nextFrame = getAvailableVideoFrame(); // get the latest frame and store it in the video frame frameSize = fillBuffer(object, nextFrame.getByteArray()); // mark the video frame as available for use nextFrame.prepareForDelivery(frameSize, lastCapturedFrameBufferIndex, lastCapturedFrameSequence, lastCapturedFrameTimeuSec); } finally { state.put(); } return nextFrame; } synchronized static void Log(String s){ System.out.println(Thread.currentThread().getName()+": "+s); System.out.flush(); } /** * This method is called by a video frame, when it is being recycled. * @param frame the frame being recycled. */ final void recycleVideoBuffer(BaseVideoFrame frame) { // Make sure we are in started state if (state.isStarted()) { enqueueBuffer(object, frame.getBufferInex()); synchronized(availableVideoFrames){ availableVideoFrames.add(frame); availableVideoFrames.notify(); } } } /* (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#stopCapture() */ @Override public final void stopCapture(){ state.stop(); // At this stage, further calls to getVideoFrame() will throw an exception.. // However, the push thread might still be blocked in getVideoFrame(). // The push thread blocked in getVideoFrame() can be blocked either: // 1) in getAvailableVideoFrame(), waiting for an available frame // OR // 2) in fillBuffer(), waiting for a V4L buffer // If the push thread is blocked in 1), we can wake it up by interrupting it. // If the push thread is blocked in 2), tell the JNI layer to stop the capture // which will wake up the push thread blocked in fillBuffer() with an error. // unblock thread in 1): stop the push source pushSource.stopCapture(); // unblock thread in 2): tell libvideo to stop capture stop(object); // wait for thread blocked in 2) to return state.waitTillNoMoreUsers(); // at this stage, we know that no one is waiting in getVideoFrame() anymore, // and further calls to it will throw a StateException. // Make sure all video frames are recycled for(VideoFrame frame: videoFrames) frame.recycle(); // remove all frames from available queue synchronized(availableVideoFrames) { availableVideoFrames.removeAllElements(); } // commit new state state.commit(); } /** * This method releases resources used by the FrameCapture object. * @throws StateException if if this * FrameGrabber has been already released, and therefore must * not be used anymore. */ final void release(){ try {stopCapture();} catch (StateException se) { //capture already stopped } state.release(); doRelease(object); state.commit(); } /* (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#getHeight() */ @Override public final int getHeight(){ state.checkReleased(); return height; } /* (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#getWidth() */ @Override public final int getWidth(){ state.checkReleased(); return width; } /* (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#getChannel() */ @Override public final int getChannel(){ state.checkReleased(); return channel; } /* (non-Javadoc) * @see au.edu.jcu.v4l4j.FrameGrabber#getStandard() */ @Override public final int getStandard(){ state.checkReleased(); return standard; } /** * be careful when using this method to not introduce race conditions * @return if this grabber is started */ final boolean isStarted(){ return state.isStarted(); } protected static class State { private int state; private int temp; private int users; private static int UNINIT=0; private static int INIT=1; private static int STARTED=2; private static int STOPPED=3; private static int RELEASED=4; /** * Start the state machine with state == temp == UNINIT */ public State() { state = UNINIT; temp = UNINIT; users = 0; } /** * From state = UNINIT and not about to move to INIT, move to INIT state * otherwise throw StateException */ public synchronized void init(){ if(state==UNINIT && temp!=INIT) { temp=INIT; } else throw new StateException("This FrameGrabber can not be " +"initialised again"); } /** * From state = INIT or (state = STOPPED and not about to start), * move to state = STARTED */ public synchronized void start(){ if(state==INIT || state==STOPPED && temp!=STARTED) { temp=STARTED; } else throw new StateException("This FrameGrabber is not initialised" +" or stopped and can not be started"); } /** * Must be called with state object lock held. * @throws StateException if released * @return */ public boolean isStarted(){ checkReleased(); return state==STARTED && temp!=STOPPED; } /** * Must be called with state object lock held * @return */ public void checkReleased(){ if(state==RELEASED || temp==RELEASED) throw new StateException("This FrameGrabber has been released"); } /** * Increase number of users by one. */ public synchronized void get(){ // If there were no user, now there is one, // notify any thread waiting for at least one user. if (users == 0) notify(); users++; } /** * Block until there is at least one user */ public synchronized void waitForAtLeastOneUser() { while (users == 0) { try { wait(); } catch (InterruptedException e) { System.err.println("Interrupted while waiting for " +"push thread to start"); e.printStackTrace(); throw new StateException("Interrupted while waiting for" +"push thread to start"); } } } /** * Decrease the number of users by one */ public synchronized void put(){ // decrement the number of users and notify any blocked // thread if there are no more users if (--users==0) notify(); // if we are about to stop, throw a state exception so // the captured frame is not returned if(temp==STOPPED) throw new StateException("This framegrabber was stopped"); } public synchronized void stop(){ if(state==STARTED && temp!=STOPPED) { temp=STOPPED; } else throw new StateException("This FrameGrabber is not started and " +"can not be stopped"); } /** * This method unblocks when there are no more users. * It is the caller's responsibility to ensure before the call that the * current state does not allow any more users to join, but only to exit */ public synchronized void waitTillNoMoreUsers(){ while(users!=0) try { wait(); } catch (InterruptedException e) { // a thread called stopCapture() while another was // blocked in getVideoFrame() // System.err.println("Interrupted while waiting for " // +"FrameGrabber users to complete"); // e.printStackTrace(); // throw new StateException("There are remaining users of " // +"this FrameGrabber and it can not be stopped"); } } public synchronized void release(){ if(state==INIT || state==STOPPED && temp!=RELEASED) { temp=RELEASED; } else throw new StateException("This FrameGrabber is neither " +"initialised nor stopped and can not be released"); } public synchronized void commit(){ state = temp; } public synchronized void rollback(){ temp = state; } } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((dInfo == null) ? 0 : dInfo.hashCode()); return result; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof AbstractGrabber)) { return false; } AbstractGrabber other = (AbstractGrabber) obj; if (dInfo == null) { if (other.dInfo != null) { return false; } } else if (!dInfo.equals(other.dInfo)) { return false; } return true; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy