Home | History | Annotate | Download | only in camera
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /*
     18  * Contains code capturing video frames from a camera device on Windows.
     19  * This code uses capXxx API, available via capCreateCaptureWindow.
     20  */
     21 
     22 #include <vfw.h>
     23 #include "android/camera/camera-capture.h"
     24 #include "android/camera/camera-format-converters.h"
     25 
     26 #define  E(...)    derror(__VA_ARGS__)
     27 #define  W(...)    dwarning(__VA_ARGS__)
     28 #define  D(...)    VERBOSE_PRINT(camera,__VA_ARGS__)
     29 #define  D_ACTIVE  VERBOSE_CHECK(camera)
     30 
     31 /* the T(...) macro is used to dump traffic */
     32 #define  T_ACTIVE   0
     33 
     34 #if T_ACTIVE
     35 #define  T(...)    VERBOSE_PRINT(camera,__VA_ARGS__)
     36 #else
     37 #define  T(...)    ((void)0)
     38 #endif
     39 
     40 /* Default name for the capture window. */
     41 static const char* _default_window_name = "AndroidEmulatorVC";
     42 
     43 typedef struct WndCameraDevice WndCameraDevice;
     44 /* Windows-specific camera device descriptor. */
     45 struct WndCameraDevice {
     46     /* Common camera device descriptor. */
     47     CameraDevice        header;
     48     /* Capture window name. (default is AndroidEmulatorVC) */
     49     char*               window_name;
     50     /* Input channel (video driver index). (default is 0) */
     51     int                 input_channel;
     52 
     53     /*
     54      * Set when framework gets initialized.
     55      */
     56 
     57     /* Video capturing window. Null indicates that device is not connected. */
     58     HWND                cap_window;
     59     /* DC for frame bitmap manipulation. Null indicates that frames are not
     60      * being capturing. */
     61     HDC                 dc;
     62     /* Bitmap info for the frames obtained from the video capture driver. */
     63     BITMAPINFO*         frame_bitmap;
     64      /* Bitmap info to use for GetDIBits calls. We can't really use bitmap info
     65       * obtained from the video capture driver, because of the two issues. First,
     66       * the driver may return an incompatible 'biCompresstion' value. For instance,
     67       * sometimes it returns a "fourcc' pixel format value instead of BI_XXX,
     68       * which causes GetDIBits to fail. Second, the bitmap that represents a frame
     69       * that has been actually obtained from the device is not necessarily matches
     70       * bitmap info that capture driver has returned. Sometimes the captured bitmap
     71       * is a 32-bit RGB, while bit count reported by the driver is 16. So, to
     72       * address these issues we need to have another bitmap info, that can be used
     73       * in GetDIBits calls. */
     74     BITMAPINFO*         gdi_bitmap;
     75     /* Framebuffer large enough to fit the frame. */
     76     uint8_t*            framebuffer;
     77     /* Framebuffer size. */
     78     size_t              framebuffer_size;
     79     /* Framebuffer's pixel format. */
     80     uint32_t            pixel_format;
     81     /* If != 0, frame bitmap is "top-down". If 0, frame bitmap is "bottom-up". */
     82     int                 is_top_down;
     83 };
     84 
     85 /*******************************************************************************
     86  *                     CameraDevice routines
     87  ******************************************************************************/
     88 
     89 /* Allocates an instance of WndCameraDevice structure.
     90  * Return:
     91  *  Allocated instance of WndCameraDevice structure. Note that this routine
     92  *  also sets 'opaque' field in the 'header' structure to point back to the
     93  *  containing WndCameraDevice instance.
     94  */
     95 static WndCameraDevice*
     96 _camera_device_alloc(void)
     97 {
     98     WndCameraDevice* cd = (WndCameraDevice*)malloc(sizeof(WndCameraDevice));
     99     if (cd != NULL) {
    100         memset(cd, 0, sizeof(WndCameraDevice));
    101         cd->header.opaque = cd;
    102     } else {
    103         E("%s: Unable to allocate WndCameraDevice instance", __FUNCTION__);
    104     }
    105     return cd;
    106 }
    107 
    108 /* Uninitializes and frees WndCameraDevice descriptor.
    109  * Note that upon return from this routine memory allocated for the descriptor
    110  * will be freed.
    111  */
    112 static void
    113 _camera_device_free(WndCameraDevice* cd)
    114 {
    115     if (cd != NULL) {
    116         if (cd->cap_window != NULL) {
    117             /* Disconnect from the driver. */
    118             capDriverDisconnect(cd->cap_window);
    119 
    120             if (cd->dc != NULL) {
    121                 W("%s: Frames should not be capturing at this point",
    122                   __FUNCTION__);
    123                 ReleaseDC(cd->cap_window, cd->dc);
    124                 cd->dc = NULL;
    125             }
    126             /* Destroy the capturing window. */
    127             DestroyWindow(cd->cap_window);
    128             cd->cap_window = NULL;
    129         }
    130         if (cd->gdi_bitmap != NULL) {
    131             free(cd->gdi_bitmap);
    132         }
    133         if (cd->frame_bitmap != NULL) {
    134             free(cd->frame_bitmap);
    135         }
    136         if (cd->window_name != NULL) {
    137             free(cd->window_name);
    138         }
    139         if (cd->framebuffer != NULL) {
    140             free(cd->framebuffer);
    141         }
    142         AFREE(cd);
    143     } else {
    144         W("%s: No descriptor", __FUNCTION__);
    145     }
    146 }
    147 
    148 /* Resets camera device after capturing.
    149  * Since new capture request may require different frame dimensions we must
    150  * reset frame info cached in the capture window. The only way to do that would
    151  * be closing, and reopening it again. */
    152 static void
    153 _camera_device_reset(WndCameraDevice* cd)
    154 {
    155     if (cd != NULL && cd->cap_window != NULL) {
    156         capDriverDisconnect(cd->cap_window);
    157         if (cd->dc != NULL) {
    158             ReleaseDC(cd->cap_window, cd->dc);
    159             cd->dc = NULL;
    160         }
    161         if (cd->gdi_bitmap != NULL) {
    162             free(cd->gdi_bitmap);
    163             cd->gdi_bitmap = NULL;
    164         }
    165         if (cd->frame_bitmap != NULL) {
    166             free(cd->frame_bitmap);
    167             cd->frame_bitmap = NULL;
    168         }
    169         if (cd->framebuffer != NULL) {
    170             free(cd->framebuffer);
    171             cd->framebuffer = NULL;
    172         }
    173 
    174         /* Recreate the capturing window. */
    175         DestroyWindow(cd->cap_window);
    176         cd->cap_window = capCreateCaptureWindow(cd->window_name, WS_CHILD, 0, 0,
    177                                                 0, 0, HWND_MESSAGE, 1);
    178     }
    179 }
    180 
    181 /* Gets an absolute value out of a signed integer. */
    182 static __inline__ int
    183 _abs(int val)
    184 {
    185     return (val < 0) ? -val : val;
    186 }
    187 
    188 /*******************************************************************************
    189  *                     CameraDevice API
    190  ******************************************************************************/
    191 
    192 CameraDevice*
    193 camera_device_open(const char* name, int inp_channel)
    194 {
    195     WndCameraDevice* wcd;
    196 
    197     /* Allocate descriptor and initialize windows-specific fields. */
    198     wcd = _camera_device_alloc();
    199     if (wcd == NULL) {
    200         E("%s: Unable to allocate WndCameraDevice instance", __FUNCTION__);
    201         return NULL;
    202     }
    203     wcd->window_name = (name != NULL) ? ASTRDUP(name) :
    204                                         ASTRDUP(_default_window_name);
    205     if (wcd->window_name == NULL) {
    206         E("%s: Unable to save window name", __FUNCTION__);
    207         _camera_device_free(wcd);
    208         return NULL;
    209     }
    210     wcd->input_channel = inp_channel;
    211 
    212     /* Create capture window that is a child of HWND_MESSAGE window.
    213      * We make it invisible, so it doesn't mess with the UI. Also
    214      * note that we supply standard HWND_MESSAGE window handle as
    215      * the parent window, since we don't want video capturing
    216      * machinery to be dependent on the details of our UI. */
    217     wcd->cap_window = capCreateCaptureWindow(wcd->window_name, WS_CHILD, 0, 0,
    218                                              0, 0, HWND_MESSAGE, 1);
    219     if (wcd->cap_window == NULL) {
    220         E("%s: Unable to create video capturing window '%s': %d",
    221           __FUNCTION__, wcd->window_name, GetLastError());
    222         _camera_device_free(wcd);
    223         return NULL;
    224     }
    225 
    226     return &wcd->header;
    227 }
    228 
    229 int
    230 camera_device_start_capturing(CameraDevice* cd,
    231                               uint32_t pixel_format,
    232                               int frame_width,
    233                               int frame_height)
    234 {
    235     WndCameraDevice* wcd;
    236     HBITMAP bm_handle;
    237     BITMAP  bitmap;
    238     size_t format_info_size;
    239 
    240     if (cd == NULL || cd->opaque == NULL) {
    241         E("%s: Invalid camera device descriptor", __FUNCTION__);
    242         return -1;
    243     }
    244     wcd = (WndCameraDevice*)cd->opaque;
    245 
    246     /* wcd->dc is an indicator of capturing: !NULL - capturing, NULL - not */
    247     if (wcd->dc != NULL) {
    248         W("%s: Capturing is already on on device '%s'",
    249           __FUNCTION__, wcd->window_name);
    250         return 0;
    251     }
    252 
    253     /* Connect capture window to the video capture driver. */
    254     if (!capDriverConnect(wcd->cap_window, wcd->input_channel)) {
    255         return -1;
    256     }
    257 
    258     /* Get current frame information from the driver. */
    259     format_info_size = capGetVideoFormatSize(wcd->cap_window);
    260     if (format_info_size == 0) {
    261         E("%s: Unable to get video format size: %d",
    262           __FUNCTION__, GetLastError());
    263         _camera_device_reset(wcd);
    264         return -1;
    265     }
    266     wcd->frame_bitmap = (BITMAPINFO*)malloc(format_info_size);
    267     if (wcd->frame_bitmap == NULL) {
    268         E("%s: Unable to allocate frame bitmap info buffer", __FUNCTION__);
    269         _camera_device_reset(wcd);
    270         return -1;
    271     }
    272     if (!capGetVideoFormat(wcd->cap_window, wcd->frame_bitmap,
    273                            format_info_size)) {
    274         E("%s: Unable to obtain video format: %d", __FUNCTION__, GetLastError());
    275         _camera_device_reset(wcd);
    276         return -1;
    277     }
    278 
    279     /* Lets see if we need to set different frame dimensions */
    280     if (wcd->frame_bitmap->bmiHeader.biWidth != frame_width ||
    281             abs(wcd->frame_bitmap->bmiHeader.biHeight) != frame_height) {
    282         /* Dimensions don't match. Set new frame info. */
    283         wcd->frame_bitmap->bmiHeader.biWidth = frame_width;
    284         wcd->frame_bitmap->bmiHeader.biHeight = frame_height;
    285         /* We need to recalculate image size, since the capture window / driver
    286          * will use image size provided by us. */
    287         if (wcd->frame_bitmap->bmiHeader.biBitCount == 24) {
    288             /* Special case that may require WORD boundary alignment. */
    289             uint32_t bpl = (frame_width * 3 + 1) & ~1;
    290             wcd->frame_bitmap->bmiHeader.biSizeImage = bpl * frame_height;
    291         } else {
    292             wcd->frame_bitmap->bmiHeader.biSizeImage =
    293                 (frame_width * frame_height * wcd->frame_bitmap->bmiHeader.biBitCount) / 8;
    294         }
    295         if (!capSetVideoFormat(wcd->cap_window, wcd->frame_bitmap,
    296                                format_info_size)) {
    297             E("%s: Unable to set video format: %d", __FUNCTION__, GetLastError());
    298             _camera_device_reset(wcd);
    299             return -1;
    300         }
    301     }
    302 
    303     if (wcd->frame_bitmap->bmiHeader.biCompression > BI_PNG) {
    304         D("%s: Video capturing driver has reported pixel format %.4s",
    305           __FUNCTION__, (const char*)&wcd->frame_bitmap->bmiHeader.biCompression);
    306     }
    307 
    308     /* Most of the time frame bitmaps come in "bottom-up" form, where its origin
    309      * is the lower-left corner. However, it could be in the normal "top-down"
    310      * form with the origin in the upper-left corner. So, we must adjust the
    311      * biHeight field, since the way "top-down" form is reported here is by
    312      * setting biHeight to a negative value. */
    313     if (wcd->frame_bitmap->bmiHeader.biHeight < 0) {
    314         wcd->frame_bitmap->bmiHeader.biHeight =
    315             -wcd->frame_bitmap->bmiHeader.biHeight;
    316         wcd->is_top_down = 1;
    317     } else {
    318         wcd->is_top_down = 0;
    319     }
    320 
    321     /* Get DC for the capturing window that will be used when we deal with
    322      * bitmaps obtained from the camera device during frame capturing. */
    323     wcd->dc = GetDC(wcd->cap_window);
    324     if (wcd->dc == NULL) {
    325         E("%s: Unable to obtain DC for %s: %d",
    326           __FUNCTION__, wcd->window_name, GetLastError());
    327         _camera_device_reset(wcd);
    328         return -1;
    329     }
    330 
    331     /*
    332      * At this point we need to grab a frame to properly setup framebuffer, and
    333      * calculate pixel format. The problem is that bitmap information obtained
    334      * from the driver doesn't necessarily match the actual bitmap we're going to
    335      * obtain via capGrabFrame / capEditCopy / GetClipboardData
    336      */
    337 
    338     /* Grab a frame, and post it to the clipboard. Not very effective, but this
    339      * is how capXxx API is operating. */
    340     if (!capGrabFrameNoStop(wcd->cap_window) ||
    341         !capEditCopy(wcd->cap_window) ||
    342         !OpenClipboard(wcd->cap_window)) {
    343         E("%s: Device '%s' is unable to save frame to the clipboard: %d",
    344           __FUNCTION__, wcd->window_name, GetLastError());
    345         _camera_device_reset(wcd);
    346         return -1;
    347     }
    348 
    349     /* Get bitmap handle saved into clipboard. Note that bitmap is still
    350      * owned by the clipboard here! */
    351     bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP);
    352     if (bm_handle == NULL) {
    353         E("%s: Device '%s' is unable to obtain frame from the clipboard: %d",
    354           __FUNCTION__, wcd->window_name, GetLastError());
    355         CloseClipboard();
    356         _camera_device_reset(wcd);
    357         return -1;
    358     }
    359 
    360     /* Get bitmap object that is initialized with the actual bitmap info. */
    361     if (!GetObject(bm_handle, sizeof(BITMAP), &bitmap)) {
    362         E("%s: Device '%s' is unable to obtain frame's bitmap: %d",
    363           __FUNCTION__, wcd->window_name, GetLastError());
    364         CloseClipboard();
    365         _camera_device_reset(wcd);
    366         return -1;
    367     }
    368 
    369     /* Now that we have all we need in 'bitmap' */
    370     CloseClipboard();
    371 
    372     /* Make sure that dimensions match. Othewise - fail. */
    373     if (wcd->frame_bitmap->bmiHeader.biWidth != bitmap.bmWidth ||
    374         wcd->frame_bitmap->bmiHeader.biHeight != bitmap.bmHeight ) {
    375         E("%s: Requested dimensions %dx%d do not match the actual %dx%d",
    376           __FUNCTION__, frame_width, frame_height,
    377           wcd->frame_bitmap->bmiHeader.biWidth,
    378           wcd->frame_bitmap->bmiHeader.biHeight);
    379         _camera_device_reset(wcd);
    380         return -1;
    381     }
    382 
    383     /* Create bitmap info that will be used with GetDIBits. */
    384     wcd->gdi_bitmap = (BITMAPINFO*)malloc(wcd->frame_bitmap->bmiHeader.biSize);
    385     if (wcd->gdi_bitmap == NULL) {
    386         E("%s: Unable to allocate gdi bitmap info", __FUNCTION__);
    387         _camera_device_reset(wcd);
    388         return -1;
    389     }
    390     memcpy(wcd->gdi_bitmap, wcd->frame_bitmap,
    391            wcd->frame_bitmap->bmiHeader.biSize);
    392     wcd->gdi_bitmap->bmiHeader.biCompression = BI_RGB;
    393     wcd->gdi_bitmap->bmiHeader.biBitCount = bitmap.bmBitsPixel;
    394     wcd->gdi_bitmap->bmiHeader.biSizeImage = bitmap.bmWidthBytes * bitmap.bmWidth;
    395     /* Adjust GDI's bitmap biHeight for proper frame direction ("top-down", or
    396      * "bottom-up") We do this trick in order to simplify pixel format conversion
    397      * routines, where we always assume "top-down" frames. The trick he is to
    398      * have negative biHeight in 'gdi_bitmap' if driver provides "bottom-up"
    399      * frames, and positive biHeight in 'gdi_bitmap' if driver provides "top-down"
    400      * frames. This way GetGDIBits will always return "top-down" frames. */
    401     if (wcd->is_top_down) {
    402         wcd->gdi_bitmap->bmiHeader.biHeight =
    403             wcd->frame_bitmap->bmiHeader.biHeight;
    404     } else {
    405         wcd->gdi_bitmap->bmiHeader.biHeight =
    406             -wcd->frame_bitmap->bmiHeader.biHeight;
    407     }
    408 
    409     /* Allocate framebuffer. */
    410     wcd->framebuffer = (uint8_t*)malloc(wcd->gdi_bitmap->bmiHeader.biSizeImage);
    411     if (wcd->framebuffer == NULL) {
    412         E("%s: Unable to allocate %d bytes for framebuffer",
    413           __FUNCTION__, wcd->gdi_bitmap->bmiHeader.biSizeImage);
    414         _camera_device_reset(wcd);
    415         return -1;
    416     }
    417 
    418     /* Lets see what pixel format we will use. */
    419     if (wcd->gdi_bitmap->bmiHeader.biBitCount == 16) {
    420         wcd->pixel_format = V4L2_PIX_FMT_RGB565;
    421     } else if (wcd->gdi_bitmap->bmiHeader.biBitCount == 24) {
    422         wcd->pixel_format = V4L2_PIX_FMT_BGR24;
    423     } else if (wcd->gdi_bitmap->bmiHeader.biBitCount == 32) {
    424         wcd->pixel_format = V4L2_PIX_FMT_BGR32;
    425     } else {
    426         E("%s: Unsupported number of bits per pixel %d",
    427           __FUNCTION__, wcd->gdi_bitmap->bmiHeader.biBitCount);
    428         _camera_device_reset(wcd);
    429         return -1;
    430     }
    431 
    432     D("%s: Capturing device '%s': %d bits per pixel in %.4s [%dx%d] frame",
    433       __FUNCTION__, wcd->window_name, wcd->gdi_bitmap->bmiHeader.biBitCount,
    434       (const char*)&wcd->pixel_format, wcd->frame_bitmap->bmiHeader.biWidth,
    435       wcd->frame_bitmap->bmiHeader.biHeight);
    436 
    437     return 0;
    438 }
    439 
    440 int
    441 camera_device_stop_capturing(CameraDevice* cd)
    442 {
    443     WndCameraDevice* wcd;
    444     if (cd == NULL || cd->opaque == NULL) {
    445         E("%s: Invalid camera device descriptor", __FUNCTION__);
    446         return -1;
    447     }
    448     wcd = (WndCameraDevice*)cd->opaque;
    449 
    450     /* wcd->dc is the indicator of capture. */
    451     if (wcd->dc == NULL) {
    452         W("%s: Device '%s' is not capturing video",
    453           __FUNCTION__, wcd->window_name);
    454         return 0;
    455     }
    456     ReleaseDC(wcd->cap_window, wcd->dc);
    457     wcd->dc = NULL;
    458 
    459     /* Reset the device in preparation for the next capture. */
    460     _camera_device_reset(wcd);
    461 
    462     return 0;
    463 }
    464 
    465 int
    466 camera_device_read_frame(CameraDevice* cd,
    467                          ClientFrameBuffer* framebuffers,
    468                          int fbs_num)
    469 {
    470     WndCameraDevice* wcd;
    471     HBITMAP bm_handle;
    472 
    473     /* Sanity checks. */
    474     if (cd == NULL || cd->opaque == NULL) {
    475         E("%s: Invalid camera device descriptor", __FUNCTION__);
    476         return -1;
    477     }
    478     wcd = (WndCameraDevice*)cd->opaque;
    479     if (wcd->dc == NULL) {
    480         W("%s: Device '%s' is not captuing video",
    481           __FUNCTION__, wcd->window_name);
    482         return -1;
    483     }
    484 
    485     /* Grab a frame, and post it to the clipboard. Not very effective, but this
    486      * is how capXxx API is operating. */
    487     if (!capGrabFrameNoStop(wcd->cap_window) ||
    488         !capEditCopy(wcd->cap_window) ||
    489         !OpenClipboard(wcd->cap_window)) {
    490         E("%s: Device '%s' is unable to save frame to the clipboard: %d",
    491           __FUNCTION__, wcd->window_name, GetLastError());
    492         return -1;
    493     }
    494 
    495     /* Get bitmap handle saved into clipboard. Note that bitmap is still
    496      * owned by the clipboard here! */
    497     bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP);
    498     if (bm_handle == NULL) {
    499         E("%s: Device '%s' is unable to obtain frame from the clipboard: %d",
    500           __FUNCTION__, wcd->window_name, GetLastError());
    501         CloseClipboard();
    502         return -1;
    503     }
    504 
    505     /* Get bitmap buffer. */
    506     if (wcd->gdi_bitmap->bmiHeader.biHeight > 0) {
    507         wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight;
    508     }
    509 
    510     if (!GetDIBits(wcd->dc, bm_handle, 0, wcd->frame_bitmap->bmiHeader.biHeight,
    511                    wcd->framebuffer, wcd->gdi_bitmap, DIB_RGB_COLORS)) {
    512         E("%s: Device '%s' is unable to transfer frame to the framebuffer: %d",
    513           __FUNCTION__, wcd->window_name, GetLastError());
    514         CloseClipboard();
    515         return -1;
    516     }
    517 
    518     if (wcd->gdi_bitmap->bmiHeader.biHeight < 0) {
    519         wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight;
    520     }
    521 
    522     CloseClipboard();
    523 
    524     /* Convert framebuffer. */
    525     return convert_frame(wcd->framebuffer,
    526                          wcd->pixel_format,
    527                          wcd->gdi_bitmap->bmiHeader.biSizeImage,
    528                          wcd->frame_bitmap->bmiHeader.biWidth,
    529                          wcd->frame_bitmap->bmiHeader.biHeight,
    530                          framebuffers, fbs_num);
    531 }
    532 
    533 void
    534 camera_device_close(CameraDevice* cd)
    535 {
    536     /* Sanity checks. */
    537     if (cd == NULL || cd->opaque == NULL) {
    538         E("%s: Invalid camera device descriptor", __FUNCTION__);
    539     } else {
    540         WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque;
    541         _camera_device_free(wcd);
    542     }
    543 }
    544 
    545 int
    546 enumerate_camera_devices(CameraInfo* cis, int max)
    547 {
    548     int inp_channel, found = 0;
    549 
    550     for (inp_channel = 0; inp_channel < 10 && found < max; inp_channel++) {
    551         char name[256];
    552         CameraDevice* cd;
    553 
    554         snprintf(name, sizeof(name), "%s%d", _default_window_name, found);
    555         cd = camera_device_open(name, inp_channel);
    556         if (cd != NULL) {
    557             WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque;
    558 
    559             /* Unfortunately, on Windows we have to start capturing in order to get the
    560              * actual frame properties. Note that on Windows camera_device_start_capturing
    561              * will ignore the pixel format parameter, since it will be determined during
    562              * the course of the routine. Also note that on Windows all frames will be
    563              * 640x480. */
    564             if (!camera_device_start_capturing(cd, V4L2_PIX_FMT_RGB32, 640, 480)) {
    565                 /* capXxx API supports only single frame size (always observed 640x480,
    566                  * but the actual numbers may vary). */
    567                 cis[found].frame_sizes = (CameraFrameDim*)malloc(sizeof(CameraFrameDim));
    568                 if (cis[found].frame_sizes != NULL) {
    569                     char disp_name[24];
    570                     sprintf(disp_name, "webcam%d", found);
    571                     cis[found].display_name = ASTRDUP(disp_name);
    572                     cis[found].device_name = ASTRDUP(name);
    573                     cis[found].direction = ASTRDUP("front");
    574                     cis[found].inp_channel = inp_channel;
    575                     cis[found].frame_sizes->width = wcd->frame_bitmap->bmiHeader.biWidth;
    576                     cis[found].frame_sizes->height = wcd->frame_bitmap->bmiHeader.biHeight;
    577                     cis[found].frame_sizes_num = 1;
    578                     cis[found].pixel_format = wcd->pixel_format;
    579                     cis[found].in_use = 0;
    580                     found++;
    581                 } else {
    582                     E("%s: Unable to allocate dimensions", __FUNCTION__);
    583                 }
    584                 camera_device_stop_capturing(cd);
    585             } else {
    586                 /* No more cameras. */
    587                 camera_device_close(cd);
    588                 break;
    589             }
    590             camera_device_close(cd);
    591         } else {
    592             /* No more cameras. */
    593             break;
    594         }
    595     }
    596 
    597     return found;
    598 }
    599