blob: c3e8570803531e9a17e7eb269d9bb99c0ed36294 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Contains code that is used to capture video frames from a camera device
* on Linux. This code uses V4L2 API to work with camera devices, and requires
* Linux kernel version at least 2.5
*/
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include "android/camera/camera-capture.h"
#include "android/camera/camera-format-converters.h"
#include "android/utils/eintr_wrapper.h"
#define E(...) derror(__VA_ARGS__)
#define W(...) dwarning(__VA_ARGS__)
#define D(...) VERBOSE_PRINT(camera,__VA_ARGS__)
#define D_ACTIVE VERBOSE_CHECK(camera)
/* the T(...) macro is used to dump traffic */
#define T_ACTIVE 0
#if T_ACTIVE
#define T(...) VERBOSE_PRINT(camera,__VA_ARGS__)
#else
#define T(...) ((void)0)
#endif
#define CLEAR(x) memset (&(x), 0, sizeof(x))
/* Pixel format descriptor.
* Instances of this descriptor are created during camera device enumeration, and
* an instance of this structure describing pixel format chosen for the camera
* emulation is saved by the camera factory service to represent an emulating
* camera properties.
*/
typedef struct QemuPixelFormat {
/* Pixel format in V4L2_PIX_FMT_XXX form. */
uint32_t format;
/* Frame dimensions supported by this format. */
CameraFrameDim* dims;
/* Number of frame dimensions supported by this format. */
int dim_num;
} QemuPixelFormat;
/* Describes a framebuffer. */
typedef struct CameraFrameBuffer {
/* Framebuffer data. */
uint8_t* data;
/* Framebuffer data size. */
size_t size;
} CameraFrameBuffer;
/* Defines type of the I/O used to obtain frames from the device. */
typedef enum CameraIoType {
/* Framebuffers are shared via memory mapping. */
CAMERA_IO_MEMMAP,
/* Framebuffers are available via user pointers. */
CAMERA_IO_USERPTR,
/* Framebuffers are to be read from the device. */
CAMERA_IO_DIRECT
} CameraIoType;
typedef struct LinuxCameraDevice LinuxCameraDevice;
/*
* Describes a connection to an actual camera device.
*/
struct LinuxCameraDevice {
/* Common header. */
CameraDevice header;
/* Camera device name. (default is /dev/video0) */
char* device_name;
/* Input channel. (default is 0) */
int input_channel;
/*
* Set by the framework after initializing camera connection.
*/
/* Handle to the opened camera device. */
int handle;
/* Device capabilities. */
struct v4l2_capability caps;
/* Actual pixel format reported by the device when capturing is started. */
struct v4l2_pix_format actual_pixel_format;
/* Defines type of the I/O to use to retrieve frames from the device. */
CameraIoType io_type;
/* Allocated framebuffers. */
struct CameraFrameBuffer* framebuffers;
/* Actual number of allocated framebuffers. */
int framebuffer_num;
};
/* Preferred pixel formats arranged from the most to the least desired.
*
* More than anything else this array is defined by an existance of format
* conversion between the camera supported formats, and formats that are
* supported by camera framework in the guest system. Currently, guest supports
* only YV12 pixel format for data, and RGB32 for preview. So, this array should
* contain only those formats, for which converters are implemented. Generally
* speaking, the order in which entries should be arranged in this array matters
* only as far as conversion speed is concerned. So, formats with the fastest
* converters should be put closer to the top of the array, while slower ones
* should be put closer to the bottom. But as far as functionality is concerned,
* the orser doesn't matter, and any format can be placed anywhere in this array,
* as long as conversion for it exists.
*/
static const uint32_t _preferred_formats[] =
{
/* Native format for the emulated camera: no conversion at all. */
V4L2_PIX_FMT_YUV420,
V4L2_PIX_FMT_YVU420,
/* Continue with YCbCr: less math than with RGB */
V4L2_PIX_FMT_NV12,
V4L2_PIX_FMT_NV21,
V4L2_PIX_FMT_YUYV,
/* End with RGB. */
V4L2_PIX_FMT_RGB32,
V4L2_PIX_FMT_RGB24,
V4L2_PIX_FMT_RGB565,
};
/* Number of entries in _preferred_formats array. */
static const int _preferred_format_num =
sizeof(_preferred_formats)/sizeof(*_preferred_formats);
/*******************************************************************************
* Helper routines
******************************************************************************/
/* IOCTL wrapper. */
static int
_xioctl(int fd, int request, void *arg) {
return HANDLE_EINTR(ioctl(fd, request, arg));
}
/* Frees resource allocated for QemuPixelFormat instance, excluding the instance
* itself.
*/
static void _qemu_pixel_format_free(QemuPixelFormat* fmt)
{
if (fmt != NULL) {
if (fmt->dims != NULL)
free(fmt->dims);
}
}
/* Returns an index of the given pixel format in an array containing pixel
* format descriptors.
* This routine is used to choose a pixel format for a camera device. The idea
* is that when the camera service enumerates all pixel formats for all cameras
* connected to the host, we need to choose just one, which would be most
* appropriate for camera emulation. To do that, the camera service will run
* formats, contained in _preferred_formats array against enumerated pixel
* formats to pick the first format that match.
* Param:
* fmt - Pixel format, for which to obtain the index.
* formats - Array containing list of pixel formats, supported by the camera
* device.
* size - Number of elements in the 'formats' array.
* Return:
* Index of the matched entry in the array, or -1 if no entry has been found.
*/
static int
_get_format_index(uint32_t fmt, QemuPixelFormat* formats, int size)
{
int f;
for (f = 0; f < size && formats[f].format != fmt; f++);
return f < size ? f : -1;
}
/*******************************************************************************
* CameraFrameBuffer routines
******************************************************************************/
/* Frees array of framebuffers, depending on the I/O method the array has been
* initialized for.
* Note that this routine doesn't frees the array itself.
* Param:
* fb, num - Array data, and its size.
* io_type - Type of the I/O the array has been initialized for.
*/
static void
_free_framebuffers(CameraFrameBuffer* fb, int num, CameraIoType io_type)
{
if (fb != NULL) {
int n;
switch (io_type) {
case CAMERA_IO_MEMMAP:
/* Unmap framebuffers. */
for (n = 0; n < num; n++) {
if (fb[n].data != NULL) {
munmap(fb[n].data, fb[n].size);
fb[n].data = NULL;
fb[n].size = 0;
}
}
break;
case CAMERA_IO_USERPTR:
case CAMERA_IO_DIRECT:
/* Free framebuffers. */
for (n = 0; n < num; n++) {
if (fb[n].data != NULL) {
free(fb[n].data);
fb[n].data = NULL;
fb[n].size = 0;
}
}
break;
default:
E("%s: Invalid I/O type %d", __FUNCTION__, io_type);
break;
}
}
}
/*******************************************************************************
* CameraDevice routines
******************************************************************************/
/* Allocates an instance of LinuxCameraDevice structure.
* Return:
* Allocated instance of LinuxCameraDevice structure. Note that this routine
* also sets 'opaque' field in the 'header' structure to point back to the
* containing LinuxCameraDevice instance.
*/
static LinuxCameraDevice*
_camera_device_alloc(void)
{
LinuxCameraDevice* cd;
ANEW0(cd);
memset(cd, 0, sizeof(*cd));
cd->header.opaque = cd;
cd->handle = -1;
return cd;
}
/* Uninitializes and frees CameraDevice structure.
*/
static void
_camera_device_free(LinuxCameraDevice* lcd)
{
if (lcd != NULL) {
/* Closing handle will also disconnect from the driver. */
if (lcd->handle >= 0) {
close(lcd->handle);
}
if (lcd->device_name != NULL) {
free(lcd->device_name);
}
if (lcd->framebuffers != NULL) {
_free_framebuffers(lcd->framebuffers, lcd->framebuffer_num,
lcd->io_type);
free(lcd->framebuffers);
}
AFREE(lcd);
} else {
E("%s: No descriptor", __FUNCTION__);
}
}
/* Resets camera device after capturing.
* Since new capture request may require different frame dimensions we must
* reset camera device by reopening its handle. Otherwise attempts to set up new
* frame properties (different from the previous one) may fail. */
static void
_camera_device_reset(LinuxCameraDevice* cd)
{
struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
/* Free capturing framebuffers first. */
if (cd->framebuffers != NULL) {
_free_framebuffers(cd->framebuffers, cd->framebuffer_num, cd->io_type);
free(cd->framebuffers);
cd->framebuffers = NULL;
cd->framebuffer_num = 0;
}
/* Reset device handle. */
close(cd->handle);
cd->handle = open(cd->device_name, O_RDWR | O_NONBLOCK, 0);
if (cd->handle >= 0) {
/* Select video input, video standard and tune here. */
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
_xioctl(cd->handle, VIDIOC_CROPCAP, &cropcap);
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect; /* reset to default */
_xioctl (cd->handle, VIDIOC_S_CROP, &crop);
}
}
/* Memory maps buffers and shares mapped memory with the device.
* Return:
* 0 Framebuffers have been mapped.
* -1 A critical error has ocurred.
* 1 Memory mapping is not available.
*/
static int
_camera_device_mmap_framebuffer(LinuxCameraDevice* cd)
{
struct v4l2_requestbuffers req;
CLEAR(req);
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
/* Request memory mapped buffers. Note that device can return less buffers
* than requested. */
if(_xioctl(cd->handle, VIDIOC_REQBUFS, &req)) {
if (EINVAL == errno) {
D("%s: Device '%s' does not support memory mapping",
__FUNCTION__, cd->device_name);
return 1;
} else {
E("%s: VIDIOC_REQBUFS has failed: %s",
__FUNCTION__, strerror(errno));
return -1;
}
}
/* Allocate framebuffer array. */
cd->framebuffers = calloc(req.count, sizeof(CameraFrameBuffer));
if (cd->framebuffers == NULL) {
E("%s: Not enough memory to allocate framebuffer array", __FUNCTION__);
return -1;
}
/* Map every framebuffer to the shared memory, and queue it
* with the device. */
for(cd->framebuffer_num = 0; cd->framebuffer_num < req.count;
cd->framebuffer_num++) {
/* Map framebuffer. */
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = cd->framebuffer_num;
if(_xioctl(cd->handle, VIDIOC_QUERYBUF, &buf) < 0) {
E("%s: VIDIOC_QUERYBUF has failed: %s",
__FUNCTION__, strerror(errno));
return -1;
}
cd->framebuffers[cd->framebuffer_num].size = buf.length;
cd->framebuffers[cd->framebuffer_num].data =
mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
cd->handle, buf.m.offset);
if (MAP_FAILED == cd->framebuffers[cd->framebuffer_num].data) {
E("%s: Memory mapping has failed: %s",
__FUNCTION__, strerror(errno));
return -1;
}
/* Queue the mapped buffer. */
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = cd->framebuffer_num;
if (_xioctl(cd->handle, VIDIOC_QBUF, &buf) < 0) {
E("%s: VIDIOC_QBUF has failed: %s", __FUNCTION__, strerror(errno));
return -1;
}
}
cd->io_type = CAMERA_IO_MEMMAP;
return 0;
}
/* Allocates frame buffers and registers them with the device.
* Return:
* 0 Framebuffers have been mapped.
* -1 A critical error has ocurred.
* 1 Device doesn't support user pointers.
*/
static int
_camera_device_user_framebuffer(LinuxCameraDevice* cd)
{
struct v4l2_requestbuffers req;
CLEAR (req);
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_USERPTR;
/* Request user buffers. Note that device can return less buffers
* than requested. */
if(_xioctl(cd->handle, VIDIOC_REQBUFS, &req)) {
if (EINVAL == errno) {
D("%s: Device '%s' does not support user pointers",
__FUNCTION__, cd->device_name);
return 1;
} else {
E("%s: VIDIOC_REQBUFS has failed: %s",
__FUNCTION__, strerror(errno));
return -1;
}
}
/* Allocate framebuffer array. */
cd->framebuffers = calloc(req.count, sizeof(CameraFrameBuffer));
if (cd->framebuffers == NULL) {
E("%s: Not enough memory to allocate framebuffer array", __FUNCTION__);
return -1;
}
/* Allocate buffers, queueing them wit the device at the same time */
for(cd->framebuffer_num = 0; cd->framebuffer_num < req.count;
cd->framebuffer_num++) {
cd->framebuffers[cd->framebuffer_num].size =
cd->actual_pixel_format.sizeimage;
cd->framebuffers[cd->framebuffer_num].data =
malloc(cd->framebuffers[cd->framebuffer_num].size);
if (cd->framebuffers[cd->framebuffer_num].data == NULL) {
E("%s: Not enough memory to allocate framebuffer", __FUNCTION__);
return -1;
}
/* Queue the user buffer. */
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_USERPTR;
buf.m.userptr = (unsigned long)cd->framebuffers[cd->framebuffer_num].data;
buf.length = cd->framebuffers[cd->framebuffer_num].size;
if (_xioctl(cd->handle, VIDIOC_QBUF, &buf) < 0) {
E("%s: VIDIOC_QBUF has failed: %s", __FUNCTION__, strerror(errno));
return -1;
}
}
cd->io_type = CAMERA_IO_USERPTR;
return 0;
}
/* Allocate frame buffer for direct read from the device.
* Return:
* 0 Framebuffers have been mapped.
* -1 A critical error has ocurred.
* 1 Memory mapping is not available.
*/
static int
_camera_device_direct_framebuffer(LinuxCameraDevice* cd)
{
/* Allocate framebuffer array. */
cd->framebuffer_num = 1;
cd->framebuffers = malloc(sizeof(CameraFrameBuffer));
if (cd->framebuffers == NULL) {
E("%s: Not enough memory to allocate framebuffer array", __FUNCTION__);
return -1;
}
cd->framebuffers[0].size = cd->actual_pixel_format.sizeimage;
cd->framebuffers[0].data = malloc(cd->framebuffers[0].size);
if (cd->framebuffers[0].data == NULL) {
E("%s: Not enough memory to allocate framebuffer", __FUNCTION__);
return -1;
}
cd->io_type = CAMERA_IO_DIRECT;
return 0;
}
/* Opens camera device.
* Param:
* cd - Camera device descriptor to open the camera for.
* Return:
* 0 on success, != 0 on failure.
*/
static int
_camera_device_open(LinuxCameraDevice* cd)
{
struct stat st;
if (stat(cd->device_name, &st)) {
return -1;
}
if (!S_ISCHR(st.st_mode)) {
E("%s: '%s' is not a device", __FUNCTION__, cd->device_name);
return -1;
}
/* Open handle to the device, and query device capabilities. */
cd->handle = open(cd->device_name, O_RDWR | O_NONBLOCK, 0);
if (cd->handle < 0) {
E("%s: Cannot open camera device '%s': %s",
__FUNCTION__, cd->device_name, strerror(errno));
return -1;
}
if (_xioctl(cd->handle, VIDIOC_QUERYCAP, &cd->caps) < 0) {
if (EINVAL == errno) {
E("%s: Camera '%s' is not a V4L2 device",
__FUNCTION__, cd->device_name);
close(cd->handle);
cd->handle = -1;
return -1;
} else {
E("%s: Unable to query capabilities for camera device '%s'",
__FUNCTION__, cd->device_name);
close(cd->handle);
cd->handle = -1;
return -1;
}
}
/* Make sure that camera supports minimal requirements. */
if (!(cd->caps.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
E("%s: Camera '%s' is not a video capture device",
__FUNCTION__, cd->device_name);
close(cd->handle);
cd->handle = -1;
return -1;
}
return 0;
}
/* Enumerates frame sizes for the given pixel format.
* Param:
* cd - Opened camera device descriptor.
* fmt - Pixel format to enum frame sizes for.
* sizes - Upon success contains an array of supported frame sizes. The size of
* the array is defined by the value, returned from this routine. The caller
* is responsible for freeing memory allocated for this array.
* Return:
* On success returns number of entries in the 'sizes' array. On failure returns
* a negative value.
*/
static int
_camera_device_enum_format_sizes(LinuxCameraDevice* cd,
uint32_t fmt,
CameraFrameDim** sizes)
{
int n;
int sizes_num = 0;
int out_num = 0;
struct v4l2_frmsizeenum size_enum;
CameraFrameDim* arr;
/* Calculate number of supported sizes for the given format. */
for (n = 0; ; n++) {
size_enum.index = n;
size_enum.pixel_format = fmt;
if(_xioctl(cd->handle, VIDIOC_ENUM_FRAMESIZES, &size_enum)) {
break;
}
if (size_enum.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
/* Size is in the simpe width, height form. */
sizes_num++;
} else if (size_enum.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
/* Sizes are represented as min/max width and height with a step for
* each dimension. Since at the end we want to list each supported
* size in the array (that's the only format supported by the guest
* camera framework), we need to calculate how many array entries
* this will generate. */
const uint32_t dif_widths =
(size_enum.stepwise.max_width - size_enum.stepwise.min_width) /
size_enum.stepwise.step_width + 1;
const uint32_t dif_heights =
(size_enum.stepwise.max_height - size_enum.stepwise.min_height) /
size_enum.stepwise.step_height + 1;
sizes_num += dif_widths * dif_heights;
} else if (size_enum.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) {
/* Special stepwise case, when steps are set to 1. We still need to
* flatten this for the guest, but the array may be too big.
* Fortunately, we don't need to be fancy, so three sizes would be
* sufficient here: min, max, and one in the middle. */
sizes_num += 3;
}
}
if (sizes_num == 0) {
return 0;
}
/* Allocate, and initialize the array of supported entries. */
*sizes = (CameraFrameDim*)malloc(sizes_num * sizeof(CameraFrameDim));
if (*sizes == NULL) {
E("%s: Memory allocation failure", __FUNCTION__);
return -1;
}
arr = *sizes;
for (n = 0; out_num < sizes_num; n++) {
size_enum.index = n;
size_enum.pixel_format = fmt;
if(_xioctl(cd->handle, VIDIOC_ENUM_FRAMESIZES, &size_enum)) {
/* Errors are not welcome here anymore. */
E("%s: Unexpected failure while getting pixel dimensions: %s",
__FUNCTION__, strerror(errno));
free(arr);
return -1;
}
if (size_enum.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
arr[out_num].width = size_enum.discrete.width;
arr[out_num].height = size_enum.discrete.height;
out_num++;
} else if (size_enum.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
uint32_t w;
for (w = size_enum.stepwise.min_width;
w <= size_enum.stepwise.max_width;
w += size_enum.stepwise.step_width) {
uint32_t h;
for (h = size_enum.stepwise.min_height;
h <= size_enum.stepwise.max_height;
h += size_enum.stepwise.step_height) {
arr[out_num].width = w;
arr[out_num].height = h;
out_num++;
}
}
} else if (size_enum.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) {
/* min */
arr[out_num].width = size_enum.stepwise.min_width;
arr[out_num].height = size_enum.stepwise.min_height;
out_num++;
/* one in the middle */
arr[out_num].width =
(size_enum.stepwise.min_width + size_enum.stepwise.max_width) / 2;
arr[out_num].height =
(size_enum.stepwise.min_height + size_enum.stepwise.max_height) / 2;
out_num++;
/* max */
arr[out_num].width = size_enum.stepwise.max_width;
arr[out_num].height = size_enum.stepwise.max_height;
out_num++;
}
}
return out_num;
}
/* Enumerates pixel formats, supported by the device.
* Note that this routine will enumerate only raw (uncompressed) formats.
* Param:
* cd - Opened camera device descriptor.
* fmts - Upon success contains an array of supported pixel formats. The size of
* the array is defined by the value, returned from this routine. The caller
* is responsible for freeing memory allocated for this array.
* Return:
* On success returns number of entries in the 'fmts' array. On failure returns
* a negative value.
*/
static int
_camera_device_enum_pixel_formats(LinuxCameraDevice* cd, QemuPixelFormat** fmts)
{
int n, max_fmt;
int fmt_num = 0;
int out_num = 0;
struct v4l2_fmtdesc fmt_enum;
QemuPixelFormat* arr;
/* Calculate number of supported formats. */
for (max_fmt = 0; ; max_fmt++) {
memset(&fmt_enum, 0, sizeof(fmt_enum));
fmt_enum.index = max_fmt;
fmt_enum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(_xioctl(cd->handle, VIDIOC_ENUM_FMT, &fmt_enum)) {
break;
}
/* Skip the compressed ones. */
if ((fmt_enum.flags & V4L2_FMT_FLAG_COMPRESSED) == 0) {
fmt_num++;
}
}
if (fmt_num == 0) {
return 0;
}
/* Allocate, and initialize array for enumerated formats. */
*fmts = (QemuPixelFormat*)malloc(fmt_num * sizeof(QemuPixelFormat));
if (*fmts == NULL) {
E("%s: Memory allocation failure", __FUNCTION__);
return -1;
}
arr = *fmts;
memset(arr, 0, fmt_num * sizeof(QemuPixelFormat));
for (n = 0; n < max_fmt && out_num < fmt_num; n++) {
memset(&fmt_enum, 0, sizeof(fmt_enum));
fmt_enum.index = n;
fmt_enum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(_xioctl(cd->handle, VIDIOC_ENUM_FMT, &fmt_enum)) {
int nn;
/* Errors are not welcome here anymore. */
E("%s: Unexpected failure while getting pixel format: %s",
__FUNCTION__, strerror(errno));
for (nn = 0; nn < out_num; nn++) {
_qemu_pixel_format_free(arr + nn);
}
free(arr);
return -1;
}
/* Skip the compressed ones. */
if ((fmt_enum.flags & V4L2_FMT_FLAG_COMPRESSED) == 0) {
arr[out_num].format = fmt_enum.pixelformat;
/* Enumerate frame dimensions supported for this format. */
arr[out_num].dim_num =
_camera_device_enum_format_sizes(cd, fmt_enum.pixelformat,
&arr[out_num].dims);
if (arr[out_num].dim_num > 0) {
out_num++;
} else if (arr[out_num].dim_num < 0) {
int nn;
E("Unable to enumerate supported dimensions for pixel format %d",
fmt_enum.pixelformat);
for (nn = 0; nn < out_num; nn++) {
_qemu_pixel_format_free(arr + nn);
}
free(arr);
return -1;
}
}
}
return out_num;
}
/* Collects information about an opened camera device.
* The information collected in this routine contains list of pixel formats,
* supported by the device, and list of frame dimensions supported by the camera
* for each pixel format.
* Param:
* cd - Opened camera device descriptor.
* cis - Upon success contains information collected from the camera device.
* Return:
* 0 on success, != 0 on failure.
*/
static int
_camera_device_get_info(LinuxCameraDevice* cd, CameraInfo* cis)
{
int f;
int chosen = -1;
QemuPixelFormat* formats = NULL;
int num_pix_fmts = _camera_device_enum_pixel_formats(cd, &formats);
if (num_pix_fmts <= 0) {
return -1;
}
/* Lets see if camera supports preferred formats */
for (f = 0; f < _preferred_format_num; f++) {
chosen = _get_format_index(_preferred_formats[f], formats, num_pix_fmts);
if (chosen >= 0) {
break;
}
}
if (chosen < 0) {
/* Camera doesn't support any of the chosen formats. Then it doesn't
* matter which one we choose. Lets choose the first one. */
chosen = 0;
}
cis->device_name = ASTRDUP(cd->device_name);
cis->inp_channel = cd->input_channel;
cis->pixel_format = formats[chosen].format;
cis->frame_sizes_num = formats[chosen].dim_num;
/* Swap instead of copy. */
cis->frame_sizes = formats[chosen].dims;
formats[chosen].dims = NULL;
cis->in_use = 0;
for (f = 0; f < num_pix_fmts; f++) {
_qemu_pixel_format_free(formats + f);
}
free(formats);
return 0;
}
/*******************************************************************************
* CameraDevice API
******************************************************************************/
CameraDevice*
camera_device_open(const char* name, int inp_channel)
{
struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
LinuxCameraDevice* cd;
/* Allocate and initialize the descriptor. */
cd = _camera_device_alloc();
cd->device_name = name != NULL ? ASTRDUP(name) : ASTRDUP("/dev/video0");
cd->input_channel = inp_channel;
/* Open the device. */
if (_camera_device_open(cd)) {
_camera_device_free(cd);
return NULL;
}
/* Select video input, video standard and tune here. */
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
_xioctl(cd->handle, VIDIOC_CROPCAP, &cropcap);
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect; /* reset to default */
_xioctl (cd->handle, VIDIOC_S_CROP, &crop);
return &cd->header;
}
int
camera_device_start_capturing(CameraDevice* ccd,
uint32_t pixel_format,
int frame_width,
int frame_height)
{
struct v4l2_format fmt;
LinuxCameraDevice* cd;
char fmt_str[5];
int r;
/* Sanity checks. */
if (ccd == NULL || ccd->opaque == NULL) {
E("%s: Invalid camera device descriptor", __FUNCTION__);
return -1;
}
cd = (LinuxCameraDevice*)ccd->opaque;
if (cd->handle < 0) {
E("%s: Camera device is not opened", __FUNCTION__);
return -1;
}
/* Try to set pixel format with the given dimensions. */
CLEAR(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = frame_width;
fmt.fmt.pix.height = frame_height;
fmt.fmt.pix.pixelformat = pixel_format;
if (_xioctl(cd->handle, VIDIOC_S_FMT, &fmt) < 0) {
memcpy(fmt_str, &pixel_format, 4);
fmt_str[4] = '\0';
E("%s: Camera '%s' does not support pixel format '%s' with dimensions %dx%d",
__FUNCTION__, cd->device_name, fmt_str, frame_width, frame_height);
_camera_device_reset(cd);
return -1;
}
/* VIDIOC_S_FMT may has changed some properties of the structure. Make sure
* that dimensions didn't change. */
if (fmt.fmt.pix.width != frame_width || fmt.fmt.pix.height != frame_height) {
memcpy(fmt_str, &pixel_format, 4);
fmt_str[4] = '\0';
E("%s: Dimensions %dx%d are wrong for pixel format '%s'",
__FUNCTION__, frame_width, frame_height, fmt_str);
_camera_device_reset(cd);
return -1;
}
memcpy(&cd->actual_pixel_format, &fmt.fmt.pix, sizeof(struct v4l2_pix_format));
/*
* Lets initialize frame buffers, and see what kind of I/O we're going to
* use to retrieve frames.
*/
/* First, lets see if we can do mapped I/O (as most performant one). */
r = _camera_device_mmap_framebuffer(cd);
if (r < 0) {
/* Some critical error has ocurred. Bail out. */
_camera_device_reset(cd);
return -1;
} else if (r > 0) {
/* Device doesn't support memory mapping. Retrieve to the next performant
* one: preallocated user buffers. */
r = _camera_device_user_framebuffer(cd);
if (r < 0) {
/* Some critical error has ocurred. Bail out. */
_camera_device_reset(cd);
return -1;
} else if (r > 0) {
/* The only thing left for us is direct reading from the device. */
if (!(cd->caps.capabilities & V4L2_CAP_READWRITE)) {
E("%s: Don't know how to access frames on device '%s'",
__FUNCTION__, cd->device_name);
_camera_device_reset(cd);
return -1;
}
r = _camera_device_direct_framebuffer(cd);
if (r != 0) {
/* Any error at this point is a critical one. */
_camera_device_reset(cd);
return -1;
}
}
}
/* Start capturing from the device. */
if (cd->io_type != CAMERA_IO_DIRECT) {
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (_xioctl (cd->handle, VIDIOC_STREAMON, &type) < 0) {
E("%s: VIDIOC_STREAMON on camera '%s' has failed: %s",
__FUNCTION__, cd->device_name, strerror(errno));
_camera_device_reset(cd);
return -1;
}
}
return 0;
}
int
camera_device_stop_capturing(CameraDevice* ccd)
{
enum v4l2_buf_type type;
LinuxCameraDevice* cd;
/* Sanity checks. */
if (ccd == NULL || ccd->opaque == NULL) {
E("%s: Invalid camera device descriptor", __FUNCTION__);
return -1;
}
cd = (LinuxCameraDevice*)ccd->opaque;
if (cd->handle < 0) {
E("%s: Camera device is not opened", __FUNCTION__);
return -1;
}
switch (cd->io_type) {
case CAMERA_IO_DIRECT:
/* Nothing to do. */
break;
case CAMERA_IO_MEMMAP:
case CAMERA_IO_USERPTR:
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (_xioctl(cd->handle, VIDIOC_STREAMOFF, &type) < 0) {
E("%s: VIDIOC_STREAMOFF on camera '%s' has failed: %s",
__FUNCTION__, cd->device_name, strerror(errno));
return -1;
}
break;
default:
E("%s: Unknown I/O method: %d", __FUNCTION__, cd->io_type);
return -1;
}
/* Reopen the device to reset its internal state. It seems that if we don't
* do that, an attempt to reinit the device with different frame dimensions
* would fail. */
_camera_device_reset(cd);
return 0;
}
int
camera_device_read_frame(CameraDevice* ccd,
ClientFrameBuffer* framebuffers,
int fbs_num,
float r_scale,
float g_scale,
float b_scale,
float exp_comp)
{
LinuxCameraDevice* cd;
/* Sanity checks. */
if (ccd == NULL || ccd->opaque == NULL) {
E("%s: Invalid camera device descriptor", __FUNCTION__);
return -1;
}
cd = (LinuxCameraDevice*)ccd->opaque;
if (cd->handle < 0) {
E("%s: Camera device is not opened", __FUNCTION__);
return -1;
}
if (cd->io_type == CAMERA_IO_DIRECT) {
/* Read directly from the device. */
size_t total_read_bytes = 0;
/* There is one framebuffer allocated for direct read. */
void* buff = cd->framebuffers[0].data;
do {
int read_bytes =
read(cd->handle, buff + total_read_bytes,
cd->actual_pixel_format.sizeimage - total_read_bytes);
if (read_bytes < 0) {
switch (errno) {
case EIO:
case EAGAIN:
continue;
default:
E("%s: Unable to read from the camera device '%s': %s",
__FUNCTION__, cd->device_name, strerror(errno));
return -1;
}
}
total_read_bytes += read_bytes;
} while (total_read_bytes < cd->actual_pixel_format.sizeimage);
/* Convert the read frame into the caller's framebuffers. */
return convert_frame(buff, cd->actual_pixel_format.pixelformat,
cd->actual_pixel_format.sizeimage,
cd->actual_pixel_format.width,
cd->actual_pixel_format.height,
framebuffers, fbs_num,
r_scale, g_scale, b_scale, exp_comp);
} else {
/* Dequeue next buffer from the device. */
struct v4l2_buffer buf;
int res;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = cd->io_type == CAMERA_IO_MEMMAP ? V4L2_MEMORY_MMAP :
V4L2_MEMORY_USERPTR;
for (;;) {
const int res = _xioctl(cd->handle, VIDIOC_DQBUF, &buf);
if (res >= 0) {
break;
} else if (errno == EAGAIN) {
return 1; // Tells the caller to repeat.
} else if (errno != EIO) {
E("%s: VIDIOC_DQBUF on camera '%s' has failed: %s",
__FUNCTION__, cd->device_name, strerror(errno));
return -1;
}
}
/* Convert frame to the receiving buffers. */
res = convert_frame(cd->framebuffers[buf.index].data,
cd->actual_pixel_format.pixelformat,
cd->actual_pixel_format.sizeimage,
cd->actual_pixel_format.width,
cd->actual_pixel_format.height,
framebuffers, fbs_num,
r_scale, g_scale, b_scale, exp_comp);
/* Requeue the buffer back to the device. */
if (_xioctl(cd->handle, VIDIOC_QBUF, &buf) < 0) {
W("%s: VIDIOC_QBUF on camera '%s' has failed: %s",
__FUNCTION__, cd->device_name, strerror(errno));
}
return res;
}
}
void
camera_device_close(CameraDevice* ccd)
{
LinuxCameraDevice* cd;
/* Sanity checks. */
if (ccd != NULL && ccd->opaque != NULL) {
cd = (LinuxCameraDevice*)ccd->opaque;
_camera_device_free(cd);
} else {
E("%s: Invalid camera device descriptor", __FUNCTION__);
}
}
int
enumerate_camera_devices(CameraInfo* cis, int max)
{
char dev_name[24];
int found = 0;
int n;
for (n = 0; n < max; n++) {
CameraDevice* cd;
sprintf(dev_name, "/dev/video%d", n);
cd = camera_device_open(dev_name, 0);
if (cd != NULL) {
LinuxCameraDevice* lcd = (LinuxCameraDevice*)cd->opaque;
if (!_camera_device_get_info(lcd, cis + found)) {
char user_name[24];
sprintf(user_name, "webcam%d", found);
cis[found].display_name = ASTRDUP(user_name);
cis[found].in_use = 0;
found++;
}
camera_device_close(cd);
} else {
break;
}
}
return found;
}