/* * 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 #include #include #include "android/camera/camera-capture.h" #include "android/camera/camera-format-converters.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) { int r; do { r = ioctl(fd, request, arg); } while (-1 == r && EINTR == errno); return r; } /* 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 != EINTR && 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; }