Implements camera service in emulator

This is fully functional camera service implementation, that works (tested) on both,
Linux and Windows.

Fixed little/big endian bugs in the coverter code. Moved preview frames to use RGB32
instead of RGB565: RGB32 conversions are simpler and faster in the guest.

Made "payload size send" a separate routine

Change-Id: I96954f4c2cb4e4ef4dd6a20e41897d79c5037bae
diff --git a/android/camera/camera-capture-linux.c b/android/camera/camera-capture-linux.c
index 8944270..5c7e242 100644
--- a/android/camera/camera-capture-linux.c
+++ b/android/camera/camera-capture-linux.c
@@ -23,11 +23,6 @@
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
-#include <linux/videodev2.h>
-#include "qemu-common.h"
-#include "android/utils/debug.h"
-#include "android/utils/misc.h"
-#include "android/utils/system.h"
 #include "android/camera/camera-capture.h"
 #include "android/camera/camera-format-converters.h"
 
@@ -47,6 +42,21 @@
 
 #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. */
@@ -77,8 +87,6 @@
     char*                       device_name;
     /* Input channel. (default is 0) */
     int                         input_channel;
-    /* Requested pixel format. */
-    uint32_t                    req_pixel_format;
 
     /*
      * Set by the framework after initializing camera connection.
@@ -88,7 +96,7 @@
     int                         handle;
     /* Device capabilities. */
     struct v4l2_capability      caps;
-    /* Actual pixel format reported by the device. */
+    /* 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;
@@ -98,6 +106,37 @@
     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_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
  ******************************************************************************/
@@ -112,6 +151,41 @@
   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
  ******************************************************************************/
@@ -154,7 +228,7 @@
                 break;
 
             default:
-                E("Invalid I/O type %d", io_type);
+                E("%s: Invalid I/O type %d", __FUNCTION__, io_type);
                 break;
         }
     }
@@ -203,7 +277,7 @@
         }
         AFREE(lcd);
     } else {
-        W("%s: No descriptor", __FUNCTION__);
+        E("%s: No descriptor", __FUNCTION__);
     }
 }
 
@@ -226,7 +300,7 @@
      * than requested. */
     if(_xioctl(cd->handle, VIDIOC_REQBUFS, &req)) {
         if (EINVAL == errno) {
-            D("%s: %s does not support memory mapping",
+            D("%s: Device '%s' does not support memory mapping",
               __FUNCTION__, cd->device_name);
             return 1;
         } else {
@@ -303,7 +377,7 @@
      * than requested. */
     if(_xioctl(cd->handle, VIDIOC_REQBUFS, &req)) {
         if (EINVAL == errno) {
-            D("%s: %s does not support user pointers",
+            D("%s: Device '%s' does not support user pointers",
               __FUNCTION__, cd->device_name);
             return 1;
         } else {
@@ -337,7 +411,7 @@
         CLEAR(buf);
         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
         buf.memory = V4L2_MEMORY_USERPTR;
-        buf.m.userptr = cd->framebuffers[cd->framebuffer_num].data;
+        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));
@@ -379,6 +453,12 @@
     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)
 {
@@ -391,14 +471,14 @@
     }
 
     if (!S_ISCHR(st.st_mode)) {
-        E("%s: %s is not a device", __FUNCTION__, cd->device_name);
+        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\n",
+        E("%s: Cannot open camera device '%s': %s",
           __FUNCTION__, cd->device_name, strerror(errno));
         return -1;
     }
@@ -410,7 +490,7 @@
             cd->handle = -1;
             return -1;
         } else {
-            E("%s: Unable to query camera '%s' capabilities",
+            E("%s: Unable to query capabilities for camera device '%s'",
               __FUNCTION__, cd->device_name);
             close(cd->handle);
             cd->handle = -1;
@@ -430,25 +510,270 @@
     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",
+              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;
+    int fmt_num = 0;
+    int out_num = 0;
+    struct v4l2_fmtdesc fmt_enum;
+    QemuPixelFormat* arr;
+
+    /* Calculate number of supported formats. */
+    for (n = 0; ; n++) {
+        fmt_enum.index = n;
+        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; out_num < fmt_num; n++) {
+        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",
+              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 num_pix_fmts;
+    }
+
+    /* 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,
-                   uint32_t pixel_format)
+camera_device_open(const char* name, int inp_channel)
 {
     struct v4l2_cropcap cropcap;
     struct v4l2_crop crop;
-    struct v4l2_format fmt;
     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;
-    cd->req_pixel_format = pixel_format;
 
     /* Open the device. */
     if (_camera_device_open(cd)) {
@@ -463,45 +788,19 @@
     crop.c = cropcap.defrect; /* reset to default */
     _xioctl (cd->handle, VIDIOC_S_CROP, &crop);
 
-    /* Image settings. */
-    CLEAR(fmt);
-    fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-    fmt.fmt.pix.width       = 0;
-    fmt.fmt.pix.height      = 0;
-    fmt.fmt.pix.pixelformat = 0;
-    if (_xioctl(cd->handle, VIDIOC_G_FMT, &fmt) < 0) {
-        E("%s: Unable to obtain pixel format", __FUNCTION__);
-        _camera_device_free(cd);
-        return NULL;
-    }
-    if (_xioctl(cd->handle, VIDIOC_S_FMT, &fmt) < 0) {
-        char fmt_str[5];
-        memcpy(fmt_str, &cd->req_pixel_format, 4);
-        fmt_str[4] = '\0';
-        E("%s: Camera '%s' does not support requested pixel format '%s'",
-          __FUNCTION__, cd->device_name, fmt_str);
-        _camera_device_free(cd);
-        return NULL;
-    }
-    /* VIDIOC_S_FMT has changed some properties of the structure, adjusting them
-     * to the actual values, supported by the device. */
-    memcpy(&cd->actual_pixel_format, &fmt.fmt.pix,
-           sizeof(cd->actual_pixel_format));
-    {
-        char fmt_str[5];
-        memcpy(fmt_str, &cd->req_pixel_format, 4);
-        fmt_str[4] = '\0';
-        D("%s: Camera '%s' uses pixel format '%s'",
-          __FUNCTION__, cd->device_name, fmt_str);
-    }
-
     return &cd->header;
 }
 
 int
-camera_device_start_capturing(CameraDevice* ccd)
+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) {
@@ -510,13 +809,37 @@
     }
     cd = (LinuxCameraDevice*)ccd->opaque;
 
+    /* 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);
+        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);
+        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). */
-    int r = _camera_device_mmap_framebuffer(cd);
+    r = _camera_device_mmap_framebuffer(cd);
     if (r < 0) {
         /* Some critical error has ocurred. Bail out. */
         return -1;
@@ -530,7 +853,7 @@
         } 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: Device '%s' doesn't support direct read",
+                E("%s: Don't know how to access frames on device '%s'",
                   __FUNCTION__, cd->device_name);
                 return -1;
             }
@@ -547,12 +870,11 @@
         enum v4l2_buf_type type;
         type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
         if (_xioctl (cd->handle, VIDIOC_STREAMON, &type) < 0) {
-            E("%s: VIDIOC_STREAMON has failed: %s",
-              __FUNCTION__, strerror(errno));
+            E("%s: VIDIOC_STREAMON on camera '%s' has failed: %s",
+              __FUNCTION__, cd->device_name, strerror(errno));
             return -1;
         }
     }
-
     return 0;
 }
 
@@ -578,8 +900,8 @@
         case CAMERA_IO_USERPTR:
             type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
             if (_xioctl(cd->handle, VIDIOC_STREAMOFF, &type) < 0) {
-	            E("%s: VIDIOC_STREAMOFF has failed: %s",
-                  __FUNCTION__, strerror(errno));
+	            E("%s: VIDIOC_STREAMOFF on camera '%s' has failed: %s",
+                  __FUNCTION__, cd->device_name, strerror(errno));
                 return -1;
             }
             break;
@@ -598,7 +920,9 @@
 }
 
 int
-camera_device_read_frame(CameraDevice* ccd, uint8_t* buff)
+camera_device_read_frame(CameraDevice* ccd,
+                         ClientFrameBuffer* framebuffers,
+                         int fbs_num)
 {
     LinuxCameraDevice* cd;
 
@@ -612,6 +936,8 @@
     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,
@@ -622,44 +948,55 @@
                     case EAGAIN:
                         continue;
                     default:
-                        E("%s: Unable to read from the device: %s",
-                          __FUNCTION__, strerror(errno));
+                        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);
-        return 0;
+        /* 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);
     } 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;
-        if (_xioctl(cd->handle, VIDIOC_DQBUF, &buf) < 0) {
-            switch (errno) {
-                case EAGAIN:
-                  return 1;
-
-                case EIO:
-                  /* Could ignore EIO, see spec. */
-                  /* fall through */
-                default:
-                  E("%s: VIDIOC_DQBUF has failed: %s",
-                    __FUNCTION__, strerror(errno));
-                  return 1;
+        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;
             }
         }
-        /* Copy frame to the buffer. */
-        memcpy(buff, cd->framebuffers[buf.index].data,
-               cd->framebuffers[buf.index].size);
-        /* Requeue the buffer with the device. */
+
+        /* 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);
+
+        /* Requeue the buffer back to the device. */
         if (_xioctl(cd->handle, VIDIOC_QBUF, &buf) < 0) {
-            D("%s: VIDIOC_QBUF has failed: %s",
-              __FUNCTION__, strerror(errno));
+            W("%s: VIDIOC_QBUF on camera '%s' has failed: %s",
+              __FUNCTION__, cd->device_name, strerror(errno));
         }
-        return 0;
+
+        return res;
     }
 }
 
@@ -676,3 +1013,30 @@
         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)) {
+                cis[found].in_use = 0;
+                found++;
+            }
+            camera_device_close(cd);
+        } else {
+            break;
+        }
+    }
+
+    return found;
+}