blob: 99b84c201a203cde36592f6c867ab0819f940e43 [file] [log] [blame]
/*
* 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 2 of the License, or
* (at your option) any later version.
*
*/
#define LOG_TAG "V4L2Camera"
#include <utils/Log.h>
extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/select.h>
#include "uvc_compat.h"
#include "v4l2_formats.h"
};
#include "V4L2Camera.h"
#include "Utils.h"
#include "Converter.h"
#define HEADERFRAME1 0xaf
//#define DEBUG_FRAME 0
#ifdef DEBUG_FRAME
#define LOG_FRAME ALOGD
#else
#define LOG_FRAME ALOGV
#endif
namespace android {
V4L2Camera::V4L2Camera ()
: fd(-1), nQueued(0), nDequeued(0)
{
videoIn = (struct vdIn *) calloc (1, sizeof (struct vdIn));
}
V4L2Camera::~V4L2Camera()
{
Close();
free(videoIn);
}
int V4L2Camera::Open (const char *device)
{
int ret;
/* Close the previous instance, if any */
Close();
memset(videoIn, 0, sizeof (struct vdIn));
if ((fd = open(device, O_RDWR)) == -1) {
ALOGE("ERROR opening V4L interface %s: %s", device, strerror(errno));
return -1;
}
ALOGD("Open %s OK", device);
ret = ioctl (fd, VIDIOC_QUERYCAP, &videoIn->cap);
if (ret < 0) {
ALOGE("Error opening device: unable to query device.");
return -1;
}
if ((videoIn->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
ALOGE("Error opening device: video capture not supported.");
return -1;
}
if (!(videoIn->cap.capabilities & V4L2_CAP_STREAMING)) {
ALOGE("Capture device does not support streaming i/o");
return -1;
}
/* Enumerate all available frame formats */
EnumFrameFormats();
return ret;
}
void V4L2Camera::Close ()
{
/* Release the temporary buffer, if any */
if (videoIn->tmpBuffer)
free(videoIn->tmpBuffer);
videoIn->tmpBuffer = NULL;
/* Close the file descriptor */
if (fd > 0)
close(fd);
fd = -1;
}
static int my_abs(int x)
{
return (x < 0) ? -x : x;
}
int V4L2Camera::Init(int width, int height, int fps)
{
ALOGD("V4L2Camera::Init(%d, %d, %d)", width, height, fps);
/* Initialize the capture to the specified width and height */
static const struct {
int fmt; /* PixelFormat */
int bpp; /* bytes per pixel */
int isplanar; /* If format is planar or not */
int allowscrop; /* If we support cropping with this pixel format */
} pixFmtsOrder[] = {
{V4L2_PIX_FMT_YUYV, 2,0,1},
{V4L2_PIX_FMT_YVYU, 2,0,1},
{V4L2_PIX_FMT_UYVY, 2,0,1},
{V4L2_PIX_FMT_YYUV, 2,0,1},
{V4L2_PIX_FMT_SPCA501, 2,0,0},
{V4L2_PIX_FMT_SPCA505, 2,0,0},
{V4L2_PIX_FMT_SPCA508, 2,0,0},
{V4L2_PIX_FMT_YUV420, 0,1,0},
{V4L2_PIX_FMT_YVU420, 0,1,0},
{V4L2_PIX_FMT_NV12, 0,1,0},
{V4L2_PIX_FMT_NV21, 0,1,0},
{V4L2_PIX_FMT_NV16, 0,1,0},
{V4L2_PIX_FMT_NV61, 0,1,0},
{V4L2_PIX_FMT_Y41P, 0,0,0},
{V4L2_PIX_FMT_SGBRG8, 0,0,0},
{V4L2_PIX_FMT_SGRBG8, 0,0,0},
{V4L2_PIX_FMT_SBGGR8, 0,0,0},
{V4L2_PIX_FMT_SRGGB8, 0,0,0},
{V4L2_PIX_FMT_BGR24, 3,0,1},
{V4L2_PIX_FMT_RGB24, 3,0,1},
{V4L2_PIX_FMT_MJPEG, 0,1,0},
{V4L2_PIX_FMT_JPEG, 0,1,0},
{V4L2_PIX_FMT_GREY, 1,0,1},
{V4L2_PIX_FMT_Y16, 2,0,1},
};
int ret;
// If no formats, break here
if (m_AllFmts.isEmpty()) {
ALOGE("No video formats available");
return -1;
}
// Try to get the closest match ...
SurfaceDesc closest;
int closestDArea = -1;
int closestDFps = -1;
unsigned int i;
int area = width * height;
for (i = 0; i < m_AllFmts.size(); i++) {
SurfaceDesc sd = m_AllFmts[i];
// Always choose a bigger or equal surface
if (sd.getWidth() >= width &&
sd.getHeight() >= height) {
int difArea = sd.getArea() - area;
int difFps = my_abs(sd.getFps() - fps);
ALOGD("Trying format: (%d x %d), Fps: %d [difArea:%d, difFps:%d, cDifArea:%d, cDifFps:%d]",sd.getWidth(),sd.getHeight(),sd.getFps(), difArea, difFps, closestDArea, closestDFps);
if (closestDArea < 0 ||
difArea < closestDArea ||
(difArea == closestDArea && difFps < closestDFps)) {
// Store approximation
closestDArea = difArea;
closestDFps = difFps;
// And the new surface descriptor
closest = sd;
}
}
}
if (closestDArea == -1) {
ALOGE("Size not available: (%d x %d)",width,height);
return -1;
}
ALOGD("Selected format: (%d x %d), Fps: %d",closest.getWidth(),closest.getHeight(),closest.getFps());
// Check if we will have to crop the captured image
bool crop = width != closest.getWidth() || height != closest.getHeight();
// Iterate through pixel formats from best to worst
ret = -1;
for (i=0; i < (sizeof(pixFmtsOrder) / sizeof(pixFmtsOrder[0])); i++) {
// If we will need to crop, make sure to only select formats we can crop...
if (!crop || pixFmtsOrder[i].allowscrop) {
memset(&videoIn->format,0,sizeof(videoIn->format));
videoIn->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
videoIn->format.fmt.pix.width = closest.getWidth();
videoIn->format.fmt.pix.height = closest.getHeight();
videoIn->format.fmt.pix.pixelformat = pixFmtsOrder[i].fmt;
ret = ioctl(fd, VIDIOC_TRY_FMT, &videoIn->format);
if (ret >= 0 &&
videoIn->format.fmt.pix.width == (uint)closest.getWidth() &&
videoIn->format.fmt.pix.height == (uint)closest.getHeight()) {
break;
}
}
}
if (ret < 0) {
ALOGE("Open: VIDIOC_TRY_FMT Failed: %s", strerror(errno));
return ret;
}
/* Set the format */
memset(&videoIn->format,0,sizeof(videoIn->format));
videoIn->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
videoIn->format.fmt.pix.width = closest.getWidth();
videoIn->format.fmt.pix.height = closest.getHeight();
videoIn->format.fmt.pix.pixelformat = pixFmtsOrder[i].fmt;
ret = ioctl(fd, VIDIOC_S_FMT, &videoIn->format);
if (ret < 0) {
ALOGE("Open: VIDIOC_S_FMT Failed: %s", strerror(errno));
return ret;
}
/* Query for the effective video format used */
memset(&videoIn->format,0,sizeof(videoIn->format));
videoIn->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_G_FMT, &videoIn->format);
if (ret < 0) {
ALOGE("Open: VIDIOC_G_FMT Failed: %s", strerror(errno));
return ret;
}
/* Note VIDIOC_S_FMT may change width and height. */
/* Buggy driver paranoia. */
unsigned int min = videoIn->format.fmt.pix.width * 2;
if (videoIn->format.fmt.pix.bytesperline < min)
videoIn->format.fmt.pix.bytesperline = min;
min = videoIn->format.fmt.pix.bytesperline * videoIn->format.fmt.pix.height;
if (videoIn->format.fmt.pix.sizeimage < min)
videoIn->format.fmt.pix.sizeimage = min;
/* Store the pixel formats we will use */
videoIn->outWidth = width;
videoIn->outHeight = height;
videoIn->outFrameSize = width * height << 1; // Calculate the expected output framesize in YUYV
videoIn->capBytesPerPixel = pixFmtsOrder[i].bpp;
/* Now calculate cropping margins, if needed, rounding to even */
int startX = ((closest.getWidth() - width) >> 1) & (-2);
int startY = ((closest.getHeight() - height) >> 1) & (-2);
/* Avoid crashing if the mode found is smaller than the requested */
if (startX < 0) {
videoIn->outWidth += startX;
startX = 0;
}
if (startY < 0) {
videoIn->outHeight += startY;
startY = 0;
}
/* Calculate the starting offset into each captured frame */
videoIn->capCropOffset = (startX * videoIn->capBytesPerPixel) +
(videoIn->format.fmt.pix.bytesperline * startY);
ALOGI("Cropping from origin: %dx%d - size: %dx%d (offset:%d)",
startX,startY,
videoIn->outWidth,videoIn->outHeight,
videoIn->capCropOffset);
/* sets video device frame rate */
memset(&videoIn->params,0,sizeof(videoIn->params));
videoIn->params.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
videoIn->params.parm.capture.timeperframe.numerator = 1;
videoIn->params.parm.capture.timeperframe.denominator = closest.getFps();
/* Set the framerate. If it fails, it wont be fatal */
if (ioctl(fd,VIDIOC_S_PARM,&videoIn->params) < 0) {
ALOGE("VIDIOC_S_PARM error: Unable to set %d fps", closest.getFps());
}
/* Gets video device defined frame rate (not real - consider it a maximum value) */
if (ioctl(fd,VIDIOC_G_PARM,&videoIn->params) < 0) {
ALOGE("VIDIOC_G_PARM - Unable to get timeperframe");
}
ALOGI("Actual format: (%d x %d), Fps: %d, pixfmt: '%c%c%c%c', bytesperline: %d",
videoIn->format.fmt.pix.width,
videoIn->format.fmt.pix.height,
videoIn->params.parm.capture.timeperframe.denominator,
videoIn->format.fmt.pix.pixelformat & 0xFF, (videoIn->format.fmt.pix.pixelformat >> 8) & 0xFF,
(videoIn->format.fmt.pix.pixelformat >> 16) & 0xFF, (videoIn->format.fmt.pix.pixelformat >> 24) & 0xFF,
videoIn->format.fmt.pix.bytesperline);
/* Configure JPEG quality, if dealing with those formats */
if (videoIn->format.fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG ||
videoIn->format.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
/* Get the compression format */
ioctl(fd,VIDIOC_G_JPEGCOMP, &videoIn->jpegcomp);
/* Set to maximum */
videoIn->jpegcomp.quality = 100;
/* Try to set it */
if(ioctl(fd,VIDIOC_S_JPEGCOMP, &videoIn->jpegcomp) >= 0)
{
ALOGE("VIDIOC_S_COMP:");
if(errno == EINVAL)
{
videoIn->jpegcomp.quality = -1; //not supported
ALOGE(" compression control not supported\n");
}
}
/* gets video stream jpeg compression parameters */
if(ioctl(fd,VIDIOC_G_JPEGCOMP, &videoIn->jpegcomp) >= 0) {
ALOGD("VIDIOC_G_COMP:\n");
ALOGD(" quality: %i\n", videoIn->jpegcomp.quality);
ALOGD(" APPn: %i\n", videoIn->jpegcomp.APPn);
ALOGD(" APP_len: %i\n", videoIn->jpegcomp.APP_len);
ALOGD(" APP_data: %s\n", videoIn->jpegcomp.APP_data);
ALOGD(" COM_len: %i\n", videoIn->jpegcomp.COM_len);
ALOGD(" COM_data: %s\n", videoIn->jpegcomp.COM_data);
ALOGD(" jpeg_markers: 0x%x\n", videoIn->jpegcomp.jpeg_markers);
} else {
ALOGE("VIDIOC_G_COMP:");
if(errno == EINVAL) {
videoIn->jpegcomp.quality = -1; //not supported
ALOGE(" compression control not supported\n");
}
}
}
/* Check if camera can handle NB_BUFFER buffers */
memset(&videoIn->rb,0,sizeof(videoIn->rb));
videoIn->rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
videoIn->rb.memory = V4L2_MEMORY_MMAP;
videoIn->rb.count = NB_BUFFER;
ret = ioctl(fd, VIDIOC_REQBUFS, &videoIn->rb);
if (ret < 0) {
ALOGE("Init: VIDIOC_REQBUFS failed: %s", strerror(errno));
return ret;
}
for (int i = 0; i < NB_BUFFER; i++) {
memset (&videoIn->buf, 0, sizeof (struct v4l2_buffer));
videoIn->buf.index = i;
videoIn->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
videoIn->buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl (fd, VIDIOC_QUERYBUF, &videoIn->buf);
if (ret < 0) {
ALOGE("Init: Unable to query buffer (%s)", strerror(errno));
return ret;
}
videoIn->mem[i] = mmap (0,
videoIn->buf.length,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
videoIn->buf.m.offset);
if (videoIn->mem[i] == MAP_FAILED) {
ALOGE("Init: Unable to map buffer (%s)", strerror(errno));
return -1;
}
ret = ioctl(fd, VIDIOC_QBUF, &videoIn->buf);
if (ret < 0) {
ALOGE("Init: VIDIOC_QBUF Failed");
return -1;
}
nQueued++;
}
// Reserve temporary buffers, if they will be needed
size_t tmpbuf_size=0;
switch (videoIn->format.fmt.pix.pixelformat)
{
case V4L2_PIX_FMT_JPEG:
case V4L2_PIX_FMT_MJPEG:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_YVYU:
case V4L2_PIX_FMT_YYUV:
case V4L2_PIX_FMT_YUV420: // only needs 3/2 bytes per pixel but we alloc 2 bytes per pixel
case V4L2_PIX_FMT_YVU420: // only needs 3/2 bytes per pixel but we alloc 2 bytes per pixel
case V4L2_PIX_FMT_Y41P: // only needs 3/2 bytes per pixel but we alloc 2 bytes per pixel
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_SPCA501:
case V4L2_PIX_FMT_SPCA505:
case V4L2_PIX_FMT_SPCA508:
case V4L2_PIX_FMT_GREY:
case V4L2_PIX_FMT_Y16:
case V4L2_PIX_FMT_YUYV:
// YUYV doesn't need a temp buffer but we will set it if/when
// video processing disable control is checked (bayer processing).
// (logitech cameras only)
break;
case V4L2_PIX_FMT_SGBRG8: //0
case V4L2_PIX_FMT_SGRBG8: //1
case V4L2_PIX_FMT_SBGGR8: //2
case V4L2_PIX_FMT_SRGGB8: //3
// Raw 8 bit bayer
// when grabbing use:
// bayer_to_rgb24(bayer_data, RGB24_data, width, height, 0..3)
// rgb2yuyv(RGB24_data, pFrameBuffer, width, height)
// alloc a temp buffer for converting to YUYV
// rgb buffer for decoding bayer data
tmpbuf_size = videoIn->format.fmt.pix.width * videoIn->format.fmt.pix.height * 3;
if (videoIn->tmpBuffer)
free(videoIn->tmpBuffer);
videoIn->tmpBuffer = (uint8_t*)calloc(1, tmpbuf_size);
if (!videoIn->tmpBuffer) {
ALOGE("couldn't calloc %lu bytes of memory for frame buffer\n",
(unsigned long) tmpbuf_size);
return -ENOMEM;
}
break;
case V4L2_PIX_FMT_RGB24: //rgb or bgr (8-8-8)
case V4L2_PIX_FMT_BGR24:
break;
default:
ALOGE("Should never arrive (1)- exit fatal !!\n");
return -1;
}
return 0;
}
void V4L2Camera::Uninit ()
{
int ret;
memset(&videoIn->buf,0,sizeof(videoIn->buf));
videoIn->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
videoIn->buf.memory = V4L2_MEMORY_MMAP;
/* Dequeue everything */
int DQcount = nQueued - nDequeued;
for (int i = 0; i < DQcount-1; i++) {
ret = ioctl(fd, VIDIOC_DQBUF, &videoIn->buf);
ALOGE_IF(ret < 0, "Uninit: VIDIOC_DQBUF Failed");
}
nQueued = 0;
nDequeued = 0;
/* Unmap buffers */
for (int i = 0; i < NB_BUFFER; i++)
if (videoIn->mem[i] != NULL) {
ret = munmap(videoIn->mem[i], videoIn->buf.length);
ALOGE_IF(ret < 0, "Uninit: Unmap failed");
videoIn->mem[i] = NULL;
}
if (videoIn->tmpBuffer)
free(videoIn->tmpBuffer);
videoIn->tmpBuffer = NULL;
}
int V4L2Camera::StartStreaming ()
{
enum v4l2_buf_type type;
int ret;
if (!videoIn->isStreaming) {
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl (fd, VIDIOC_STREAMON, &type);
if (ret < 0) {
ALOGE("StartStreaming: Unable to start capture: %s", strerror(errno));
return ret;
}
videoIn->isStreaming = true;
}
return 0;
}
int V4L2Camera::StopStreaming ()
{
enum v4l2_buf_type type;
int ret;
if (videoIn->isStreaming) {
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl (fd, VIDIOC_STREAMOFF, &type);
if (ret < 0) {
ALOGE("StopStreaming: Unable to stop capture: %s", strerror(errno));
return ret;
}
videoIn->isStreaming = false;
}
return 0;
}
/* Returns the effective capture size */
void V4L2Camera::getSize(int& width, int& height) const
{
width = videoIn->outWidth;
height = videoIn->outHeight;
}
/* Returns the effective fps */
int V4L2Camera::getFps() const
{
return videoIn->params.parm.capture.timeperframe.denominator;
}
/* Grab frame in YUYV mode */
void V4L2Camera::GrabRawFrame (void *frameBuffer, int maxSize)
{
LOG_FRAME("V4L2Camera::GrabRawFrame: frameBuffer:%p, len:%d",frameBuffer,maxSize);
int ret;
/* DQ */
memset(&videoIn->buf,0,sizeof(videoIn->buf));
videoIn->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
videoIn->buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd, VIDIOC_DQBUF, &videoIn->buf);
if (ret < 0) {
ALOGE("GrabPreviewFrame: VIDIOC_DQBUF Failed");
return;
}
nDequeued++;
// Calculate the stride of the output image (YUYV) in bytes
int strideOut = videoIn->outWidth << 1;
// And the pointer to the start of the image
uint8_t* src = (uint8_t*)videoIn->mem[videoIn->buf.index] + videoIn->capCropOffset;
LOG_FRAME("V4L2Camera::GrabRawFrame - Got Raw frame (%dx%d) (buf:%d@0x%p, len:%d)",videoIn->format.fmt.pix.width,videoIn->format.fmt.pix.height,videoIn->buf.index,src,videoIn->buf.bytesused);
/* Avoid crashing! - Make sure there is enough room in the output buffer! */
if (maxSize < videoIn->outFrameSize) {
ALOGE("V4L2Camera::GrabRawFrame: Insufficient space in output buffer: Required: %d, Got %d - DROPPING FRAME",videoIn->outFrameSize,maxSize);
} else {
switch (videoIn->format.fmt.pix.pixelformat)
{
case V4L2_PIX_FMT_JPEG:
case V4L2_PIX_FMT_MJPEG:
if(videoIn->buf.bytesused <= HEADERFRAME1) {
// Prevent crash on empty image
ALOGE("Ignoring empty buffer ...\n");
break;
}
if (jpeg_decode((uint8_t*)frameBuffer, strideOut, src, videoIn->outWidth, videoIn->outHeight) < 0) {
ALOGE("jpeg decode errors\n");
break;
}
break;
case V4L2_PIX_FMT_UYVY:
uyvy_to_yuyv((uint8_t*)frameBuffer, strideOut,
src, videoIn->format.fmt.pix.bytesperline, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_YVYU:
yvyu_to_yuyv((uint8_t*)frameBuffer, strideOut,
src, videoIn->format.fmt.pix.bytesperline, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_YYUV:
yyuv_to_yuyv((uint8_t*)frameBuffer, strideOut,
src, videoIn->format.fmt.pix.bytesperline, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_YUV420:
yuv420_to_yuyv((uint8_t*)frameBuffer, strideOut, src, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_YVU420:
yvu420_to_yuyv((uint8_t*)frameBuffer, strideOut, src, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_NV12:
nv12_to_yuyv((uint8_t*)frameBuffer, strideOut, src, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_NV21:
nv21_to_yuyv((uint8_t*)frameBuffer, strideOut, src, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_NV16:
nv16_to_yuyv((uint8_t*)frameBuffer, strideOut, src, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_NV61:
nv61_to_yuyv((uint8_t*)frameBuffer, strideOut, src, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_Y41P:
y41p_to_yuyv((uint8_t*)frameBuffer, strideOut, src, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_GREY:
grey_to_yuyv((uint8_t*)frameBuffer, strideOut,
src, videoIn->format.fmt.pix.bytesperline, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_Y16:
y16_to_yuyv((uint8_t*)frameBuffer, strideOut,
src, videoIn->format.fmt.pix.bytesperline, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_SPCA501:
s501_to_yuyv((uint8_t*)frameBuffer, strideOut, src, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_SPCA505:
s505_to_yuyv((uint8_t*)frameBuffer, strideOut, src, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_SPCA508:
s508_to_yuyv((uint8_t*)frameBuffer, strideOut, src, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_YUYV:
{
int h;
uint8_t* pdst = (uint8_t*)frameBuffer;
uint8_t* psrc = src;
int ss = videoIn->outWidth << 1;
for (h = 0; h < videoIn->outHeight; h++) {
memcpy(pdst,psrc,ss);
pdst += strideOut;
psrc += videoIn->format.fmt.pix.bytesperline;
}
}
break;
case V4L2_PIX_FMT_SGBRG8: //0
bayer_to_rgb24 (src,(uint8_t*) videoIn->tmpBuffer, videoIn->outWidth, videoIn->outHeight, 0);
rgb_to_yuyv ((uint8_t*) frameBuffer, strideOut,
(uint8_t*)videoIn->tmpBuffer, videoIn->outWidth*3, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_SGRBG8: //1
bayer_to_rgb24 (src,(uint8_t*) videoIn->tmpBuffer, videoIn->outWidth, videoIn->outHeight, 1);
rgb_to_yuyv ((uint8_t*) frameBuffer, strideOut,
(uint8_t*)videoIn->tmpBuffer, videoIn->outWidth*3, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_SBGGR8: //2
bayer_to_rgb24 (src,(uint8_t*) videoIn->tmpBuffer, videoIn->outWidth, videoIn->outHeight, 2);
rgb_to_yuyv ((uint8_t*) frameBuffer, strideOut,
(uint8_t*)videoIn->tmpBuffer, videoIn->outWidth*3, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_SRGGB8: //3
bayer_to_rgb24 (src,(uint8_t*) videoIn->tmpBuffer, videoIn->outWidth, videoIn->outHeight, 3);
rgb_to_yuyv ((uint8_t*) frameBuffer, strideOut,
(uint8_t*)videoIn->tmpBuffer, videoIn->outWidth*3, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_RGB24:
rgb_to_yuyv((uint8_t*) frameBuffer, strideOut,
src, videoIn->format.fmt.pix.bytesperline, videoIn->outWidth, videoIn->outHeight);
break;
case V4L2_PIX_FMT_BGR24:
bgr_to_yuyv((uint8_t*) frameBuffer, strideOut,
src, videoIn->format.fmt.pix.bytesperline, videoIn->outWidth, videoIn->outHeight);
break;
default:
ALOGE("error grabbing: unknown format: %i\n", videoIn->format.fmt.pix.pixelformat);
break;
}
LOG_FRAME("V4L2Camera::GrabRawFrame - Copied frame to destination 0x%p",frameBuffer);
}
/* And Queue the buffer again */
ret = ioctl(fd, VIDIOC_QBUF, &videoIn->buf);
if (ret < 0) {
ALOGE("GrabPreviewFrame: VIDIOC_QBUF Failed");
return;
}
nQueued++;
LOG_FRAME("V4L2Camera::GrabRawFrame - Queued buffer");
}
/* enumerate frame intervals (fps)
* args:
* pixfmt: v4l2 pixel format that we want to list frame intervals for
* width: video width that we want to list frame intervals for
* height: video height that we want to list frame intervals for
*
* returns 0 if enumeration succeded or errno otherwise */
bool V4L2Camera::EnumFrameIntervals(int pixfmt, int width, int height)
{
ALOGD("V4L2Camera::EnumFrameIntervals: pixfmt: 0x%08x, w:%d, h:%d",pixfmt,width,height);
struct v4l2_frmivalenum fival;
int list_fps=0;
memset(&fival, 0, sizeof(fival));
fival.index = 0;
fival.pixel_format = pixfmt;
fival.width = width;
fival.height = height;
ALOGD("\tTime interval between frame: ");
while (ioctl(fd,VIDIOC_ENUM_FRAMEINTERVALS, &fival) >= 0)
{
fival.index++;
if (fival.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
ALOGD("%u/%u", fival.discrete.numerator, fival.discrete.denominator);
m_AllFmts.add( SurfaceDesc( width, height, fival.discrete.denominator ) );
list_fps++;
} else if (fival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS) {
ALOGD("{min { %u/%u } .. max { %u/%u } }",
fival.stepwise.min.numerator, fival.stepwise.min.numerator,
fival.stepwise.max.denominator, fival.stepwise.max.denominator);
break;
} else if (fival.type == V4L2_FRMIVAL_TYPE_STEPWISE) {
ALOGD("{min { %u/%u } .. max { %u/%u } / "
"stepsize { %u/%u } }",
fival.stepwise.min.numerator, fival.stepwise.min.denominator,
fival.stepwise.max.numerator, fival.stepwise.max.denominator,
fival.stepwise.step.numerator, fival.stepwise.step.denominator);
break;
}
}
// Assume at least 1fps
if (list_fps == 0) {
m_AllFmts.add( SurfaceDesc( width, height, 1 ) );
}
return true;
}
/* enumerate frame sizes
* pixfmt: v4l2 pixel format that we want to list frame sizes for
*
* returns 0 if enumeration succeded or errno otherwise */
bool V4L2Camera::EnumFrameSizes(int pixfmt)
{
ALOGD("V4L2Camera::EnumFrameSizes: pixfmt: 0x%08x",pixfmt);
int fsizeind = 0;
struct v4l2_frmsizeenum fsize;
memset(&fsize, 0, sizeof(fsize));
fsize.index = 0;
fsize.pixel_format = pixfmt;
while (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fsize) >= 0) {
fsize.index++;
if (fsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
ALOGD("{ discrete: width = %u, height = %u }",
fsize.discrete.width, fsize.discrete.height);
fsizeind++;
if (!EnumFrameIntervals(pixfmt,fsize.discrete.width, fsize.discrete.height))
ALOGD(" Unable to enumerate frame intervals");
} else if (fsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) {
ALOGD("{ continuous: min { width = %u, height = %u } .. "
"max { width = %u, height = %u } }",
fsize.stepwise.min_width, fsize.stepwise.min_height,
fsize.stepwise.max_width, fsize.stepwise.max_height);
ALOGD(" will not enumerate frame intervals.\n");
} else if (fsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
ALOGD("{ stepwise: min { width = %u, height = %u } .. "
"max { width = %u, height = %u } / "
"stepsize { width = %u, height = %u } }",
fsize.stepwise.min_width, fsize.stepwise.min_height,
fsize.stepwise.max_width, fsize.stepwise.max_height,
fsize.stepwise.step_width, fsize.stepwise.step_height);
ALOGD(" will not enumerate frame intervals.");
} else {
ALOGE(" fsize.type not supported: %d\n", fsize.type);
ALOGE(" (Discrete: %d Continuous: %d Stepwise: %d)",
V4L2_FRMSIZE_TYPE_DISCRETE,
V4L2_FRMSIZE_TYPE_CONTINUOUS,
V4L2_FRMSIZE_TYPE_STEPWISE);
}
}
if (fsizeind == 0) {
/* ------ gspca doesn't enumerate frame sizes ------ */
/* negotiate with VIDIOC_TRY_FMT instead */
static const struct {
int w,h;
} defMode[] = {
{800,600},
{768,576},
{768,480},
{720,576},
{720,480},
{704,576},
{704,480},
{640,480},
{352,288},
{320,240}
};
unsigned int i;
for (i = 0 ; i < (sizeof(defMode) / sizeof(defMode[0])); i++) {
fsizeind++;
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = defMode[i].w;
fmt.fmt.pix.height = defMode[i].h;
fmt.fmt.pix.pixelformat = pixfmt;
fmt.fmt.pix.field = V4L2_FIELD_ANY;
if (ioctl(fd,VIDIOC_TRY_FMT, &fmt) >= 0) {
ALOGD("{ ?GSPCA? : width = %u, height = %u }\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
// Add the mode descriptor
m_AllFmts.add( SurfaceDesc( fmt.fmt.pix.width, fmt.fmt.pix.height, 25 ) );
}
}
}
return true;
}
/* enumerate frames (formats, sizes and fps)
* args:
* width: current selected width
* height: current selected height
*
* returns: pointer to LFormats struct containing list of available frame formats */
bool V4L2Camera::EnumFrameFormats()
{
ALOGD("V4L2Camera::EnumFrameFormats");
struct v4l2_fmtdesc fmt;
// Start with no modes
m_AllFmts.clear();
memset(&fmt, 0, sizeof(fmt));
fmt.index = 0;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while (ioctl(fd,VIDIOC_ENUM_FMT, &fmt) >= 0) {
fmt.index++;
ALOGD("{ pixelformat = '%c%c%c%c', description = '%s' }",
fmt.pixelformat & 0xFF, (fmt.pixelformat >> 8) & 0xFF,
(fmt.pixelformat >> 16) & 0xFF, (fmt.pixelformat >> 24) & 0xFF,
fmt.description);
//enumerate frame sizes for this pixel format
if (!EnumFrameSizes(fmt.pixelformat)) {
ALOGE(" Unable to enumerate frame sizes.");
}
};
// Now, select the best preview format and the best PictureFormat
m_BestPreviewFmt = SurfaceDesc();
m_BestPictureFmt = SurfaceDesc();
unsigned int i;
for (i=0; i<m_AllFmts.size(); i++) {
SurfaceDesc s = m_AllFmts[i];
// Prioritize size over everything else when taking pictures. use the
// least fps possible, as that usually means better quality
if ((s.getSize() > m_BestPictureFmt.getSize()) ||
(s.getSize() == m_BestPictureFmt.getSize() && s.getFps() < m_BestPictureFmt.getFps() )
) {
m_BestPictureFmt = s;
}
// Prioritize fps, then size when doing preview
if ((s.getFps() > m_BestPreviewFmt.getFps()) ||
(s.getFps() == m_BestPreviewFmt.getFps() && s.getSize() > m_BestPreviewFmt.getSize() )
) {
m_BestPreviewFmt = s;
}
}
return true;
}
SortedVector<SurfaceSize> V4L2Camera::getAvailableSizes() const
{
ALOGD("V4L2Camera::getAvailableSizes");
SortedVector<SurfaceSize> ret;
// Iterate through the list. All duplicated entries will be removed
unsigned int i;
for (i = 0; i< m_AllFmts.size() ; i++) {
ret.add(m_AllFmts[i].getSize());
}
return ret;
}
SortedVector<int> V4L2Camera::getAvailableFps() const
{
ALOGD("V4L2Camera::getAvailableFps");
SortedVector<int> ret;
// Iterate through the list. All duplicated entries will be removed
unsigned int i;
for (i = 0; i< m_AllFmts.size() ; i++) {
ret.add(m_AllFmts[i].getFps());
}
return ret;
}
const SurfaceDesc& V4L2Camera::getBestPreviewFmt() const
{
return m_BestPreviewFmt;
}
const SurfaceDesc& V4L2Camera::getBestPictureFmt() const
{
return m_BestPictureFmt;
}
}; // namespace android