Skip to content

Commit

Permalink
Add support for RasPi camera module in v4l4j driver
Browse files Browse the repository at this point in the history
refs #286
refs #315
refs #244
refs #219
refs #194
refs #192
refs #189
refs #184
refs #176
refs #153
refs #152
  • Loading branch information
sarxos committed Feb 26, 2015
1 parent 2219579 commit e90f760
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,50 @@
import au.edu.jcu.v4l4j.ImageFormatList;
import au.edu.jcu.v4l4j.ResolutionInfo;
import au.edu.jcu.v4l4j.ResolutionInfo.DiscreteResolution;
import au.edu.jcu.v4l4j.ResolutionInfo.StepwiseResolution;
import au.edu.jcu.v4l4j.VideoDevice;
import au.edu.jcu.v4l4j.VideoFrame;
import au.edu.jcu.v4l4j.exceptions.StateException;
import au.edu.jcu.v4l4j.exceptions.V4L4JException;

import com.github.sarxos.webcam.WebcamDevice;
import com.github.sarxos.webcam.WebcamException;
import com.github.sarxos.webcam.WebcamResolution;


public class V4l4jDevice implements WebcamDevice, CaptureCallback, WebcamDevice.FPSSource {

private static final Logger LOG = LoggerFactory.getLogger(V4l4jDevice.class);

private final File vfile;
private static final String[] BEST_FORMATS = new String[] {

// MJPEG and JPEG are the best match because there is no need to
// recalculate too much, hardware will deliver what we need

"MJPEG", "JPEG",

// next are YUV formats where every 2 pixels can be written in 4 bytes

"YU", "UY", "YV", "UV",

// 24-bit formats where every pixel can be stored in 3 bytes

"BGR24", "RGB24",

// 32-bit formats where every pixel can be stored in 4 bytes

"BGR32", "RGB32"
};

private final File videoFile;
private final VideoDevice videoDevice;
// private final DeviceInfo videoDeviceInfo;
private final ImageFormat videoBestImageFormat;

private VideoDevice vd = null;
private DeviceInfo di = null;
private ImageFormatList ifl = null;
private FrameGrabber grabber = null;
private final List<Dimension> videoResolutions;

private List<ImageFormat> formats = null;
private List<Dimension> resolutions = new ArrayList<Dimension>();
// private final List<ImageFormat> formats;
private Dimension resolution = null;

private AtomicBoolean open = new AtomicBoolean(false);
Expand All @@ -56,61 +78,185 @@ public class V4l4jDevice implements WebcamDevice, CaptureCallback, WebcamDevice.

private volatile double fps = 0;

public V4l4jDevice(File vfile) {
public V4l4jDevice(File file) {

if (file == null) {
throw new IllegalArgumentException("Video file cannot be null!");
}

videoFile = file;
videoDevice = getVideoDevice(file);
videoBestImageFormat = getVideoBestImageFormat(videoDevice);
videoResolutions = getVideoResolutions(videoBestImageFormat);
}

this.vfile = vfile;
/**
* Create video device from file.
*
* @param file the video descriptor file
* @return The {@link VideoDevice}
*/
private static VideoDevice getVideoDevice(File file) {

LOG.debug("Creating V4L4J devuce");
LOG.debug("Creating V4L4J device from file {}", file);

try {
vd = new VideoDevice(vfile.getAbsolutePath());
return new VideoDevice(file.getAbsolutePath());
} catch (V4L4JException e) {
throw new WebcamException(String.format("Cannot instantiate V4L4J device from %s", vfile), e);
throw new WebcamException("Cannot instantiate V4L4J device from " + file, e);
}
}

private static DeviceInfo getVideoDeviceInfo(VideoDevice device) {

LOG.trace("Get video device info");

DeviceInfo info = null;
try {
di = vd.getDeviceInfo();
info = device.getDeviceInfo();
} catch (V4L4JException e) {
throw new WebcamException(String.format("Cannot get V4L4J device info from %s", vfile), e);
throw new WebcamException("Cannot get V4L4J device info from " + device, e);
}

ifl = di.getFormatList();
formats = ifl.getYUVEncodableFormats();
if (info == null) {
throw new WebcamException("Cannot get device info from device");
}

return info;
}

private static ImageFormat getVideoBestImageFormat(VideoDevice device) {

if (device == null) {
throw new IllegalArgumentException("Device must not be null!");
}

ImageFormatList formatsList = getVideoDeviceInfo(device).getFormatList();
List<ImageFormat> formats = formatsList.getJPEGEncodableFormats();

int min = Integer.MAX_VALUE;
ImageFormat bestFormat = null;

for (ImageFormat format : formats) {

ResolutionInfo info = format.getResolutionInfo();
ResolutionInfo.Type type = info.getType();
String name = format.getName();
LOG.debug("Found format {}", name);

if (name.startsWith("YU")) {
// skip unsupported resolution type

switch (type) {
case UNSUPPORTED:
LOG.trace("Ignore {}, resolution type not supported", name);
continue;
case DISCRETE:
case STEPWISE:
break;
default:
throw new WebcamException("Unknown resolution type " + type);
}

ResolutionInfo ri = format.getResolutionInfo();
LOG.debug("Resolution info {} {}", name, ri);
LOG.trace("Testing {}", name);

for (DiscreteResolution dr : ri.getDiscreteResolutions()) {
resolutions.add(new Dimension(dr.getWidth(), dr.getHeight()));
for (int i = 0; i < BEST_FORMATS.length; i++) {
if (name.startsWith(BEST_FORMATS[i]) && i < min) {
min = i;
bestFormat = format;
}
}
}

LOG.debug("Best image format match {}", bestFormat);

if (bestFormat == null) {
throw new WebcamException("No suitable image format detected");
}

return bestFormat;
}

private static List<Dimension> getResolutionsDiscrete(ResolutionInfo info) {
List<Dimension> resolutions = new ArrayList<Dimension>();
for (DiscreteResolution resolution : info.getDiscreteResolutions()) {
resolutions.add(new Dimension(resolution.getWidth(), resolution.getHeight()));
}
return resolutions;
}

private static List<Dimension> getResolutionsStepwise(ResolutionInfo info) {

List<Dimension> resolutions = new ArrayList<Dimension>();
StepwiseResolution resolution = info.getStepwiseResolution();

int minW = resolution.getMinWidth();
int minH = resolution.getMinHeight();
int maxW = resolution.getMaxWidth();
int maxH = resolution.getMaxHeight();
int stepW = resolution.getWidthStep();
int stepH = resolution.getHeightStep();

for (WebcamResolution r : WebcamResolution.values()) {

Dimension size = r.getSize();

int w = size.width;
int h = size.height;

boolean wok = w <= maxW && w >= minW;
boolean hok = h <= maxH && h >= minH;
boolean sok = w % stepW == 0 && h % stepH == 0;

if (wok && hok && sok) {
resolutions.add(size);
}
}

return resolutions;
}

/**
* Get video resolutions from {@link ImageFormat}.
*
* @param format the {@link ImageFormat} to test
* @return List of resolutions supported by given format
*/
private static List<Dimension> getVideoResolutions(ImageFormat format) {

if (format == null) {
throw new IllegalArgumentException("Image format cannot be null!");
}

ResolutionInfo info = format.getResolutionInfo();
ResolutionInfo.Type type = info.getType();

switch (type) {
case DISCRETE:
return getResolutionsDiscrete(info);
case STEPWISE:
return getResolutionsStepwise(info);
case UNSUPPORTED:
default:
throw new WebcamException("Unsupported resolution type " + type);
}
}

@Override
public String getName() {
return vfile.getAbsolutePath();
return videoFile.getAbsolutePath();
}

@Override
public Dimension[] getResolutions() {
return resolutions.toArray(new Dimension[resolutions.size()]);
return videoResolutions.toArray(new Dimension[videoResolutions.size()]);
}

@Override
public Dimension getResolution() {
if (resolution == null) {
if (resolutions.isEmpty()) {
throw new WebcamException("No valid resolution detected for " + vfile);
if (videoResolutions.isEmpty()) {
throw new WebcamException("No valid resolution detected for " + videoFile);
}
resolution = resolutions.get(0);
resolution = videoResolutions.get(0);
}
return resolution;
}
Expand Down Expand Up @@ -153,14 +299,18 @@ public synchronized void open() {
return;
}

LOG.debug("Opening V4L4J device {}", vfile);
if (!videoDevice.supportJPEGConversion()) {
throw new WebcamException("Video device does not support JPEG conversion");
}

LOG.debug("Opening V4L4J device {}", videoFile);

Dimension d = getResolution();

LOG.debug("Constructing V4L4J frame grabber");

try {
grabber = vd.getJPEGFrameGrabber(d.width, d.height, 0, 0, 80);
grabber = videoDevice.getJPEGFrameGrabber(d.width, d.height, 0, 0, 80, videoBestImageFormat);
} catch (V4L4JException e) {
throw new WebcamException(e);
}
Expand Down Expand Up @@ -195,7 +345,7 @@ public synchronized void close() {
return;
}

LOG.debug("Closing V4L4J device {}", vfile);
LOG.debug("Closing V4L4J device {}", videoFile);

try {
grabber.stopCapture();
Expand All @@ -207,9 +357,9 @@ public synchronized void close() {
}

grabber = null;
vd.releaseFrameGrabber();
videoDevice.releaseFrameGrabber();

LOG.debug("V4L4J device {} has been closed", vfile);
LOG.debug("V4L4J device {} has been closed", videoFile);

}

Expand All @@ -220,16 +370,16 @@ public void dispose() {
return;
}

LOG.debug("Disposing V4L4J device {}", vfile);
LOG.debug("Disposing V4L4J device {}", videoFile);

if (open.get()) {
close();
}

vd.releaseControlList();
vd.release();
videoDevice.releaseControlList();
videoDevice.release();

LOG.debug("V4L4J device {} has been disposed", vfile);
LOG.debug("V4L4J device {} has been disposed", videoFile);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,19 @@ public enum WebcamResolution {
/**
* Size 2048x1536
*/
QXGA(2048, 1536);
QXGA(2048, 1536),

/**
* Size 2560x1440
*/
WQHD(2560, 1440),

/**
* Size 2560x1600
*/
WQXGA(2560, 1600),

;

/**
* Resolution size.
Expand Down

0 comments on commit e90f760

Please sign in to comment.