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     /* Flags whether frame should be captured using clipboard (1), or via frame
     84      * callback (0) */
     85     int                 use_clipboard;
     86     /* Contains last frame captured via frame callback. */
     87     void*               last_frame;
     88     /* Byte size of the 'last_frame' buffer. */
     89     uint32_t            last_frame_size;
     90 };
     91 
     92 /*******************************************************************************
     93  *                     CameraDevice routines
     94  ******************************************************************************/
     95 
     96 /* Allocates an instance of WndCameraDevice structure.
     97  * Return:
     98  *  Allocated instance of WndCameraDevice structure. Note that this routine
     99  *  also sets 'opaque' field in the 'header' structure to point back to the
    100  *  containing WndCameraDevice instance.
    101  */
    102 static WndCameraDevice*
    103 _camera_device_alloc(void)
    104 {
    105     WndCameraDevice* cd = (WndCameraDevice*)malloc(sizeof(WndCameraDevice));
    106     if (cd != NULL) {
    107         memset(cd, 0, sizeof(WndCameraDevice));
    108         cd->header.opaque = cd;
    109     } else {
    110         E("%s: Unable to allocate WndCameraDevice instance", __FUNCTION__);
    111     }
    112     return cd;
    113 }
    114 
    115 /* Uninitializes and frees WndCameraDevice descriptor.
    116  * Note that upon return from this routine memory allocated for the descriptor
    117  * will be freed.
    118  */
    119 static void
    120 _camera_device_free(WndCameraDevice* cd)
    121 {
    122     if (cd != NULL) {
    123         if (cd->cap_window != NULL) {
    124             /* Disconnect from the driver. */
    125             capDriverDisconnect(cd->cap_window);
    126 
    127             if (cd->dc != NULL) {
    128                 W("%s: Frames should not be capturing at this point",
    129                   __FUNCTION__);
    130                 ReleaseDC(cd->cap_window, cd->dc);
    131                 cd->dc = NULL;
    132             }
    133             /* Destroy the capturing window. */
    134             DestroyWindow(cd->cap_window);
    135             cd->cap_window = NULL;
    136         }
    137         if (cd->gdi_bitmap != NULL) {
    138             free(cd->gdi_bitmap);
    139         }
    140         if (cd->frame_bitmap != NULL) {
    141             free(cd->frame_bitmap);
    142         }
    143         if (cd->window_name != NULL) {
    144             free(cd->window_name);
    145         }
    146         if (cd->framebuffer != NULL) {
    147             free(cd->framebuffer);
    148         }
    149         if (cd->last_frame != NULL) {
    150             free(cd->last_frame);
    151         }
    152         AFREE(cd);
    153     } else {
    154         W("%s: No descriptor", __FUNCTION__);
    155     }
    156 }
    157 
    158 /* Resets camera device after capturing.
    159  * Since new capture request may require different frame dimensions we must
    160  * reset frame info cached in the capture window. The only way to do that would
    161  * be closing, and reopening it again. */
    162 static void
    163 _camera_device_reset(WndCameraDevice* cd)
    164 {
    165     if (cd != NULL && cd->cap_window != NULL) {
    166         capDriverDisconnect(cd->cap_window);
    167         if (cd->dc != NULL) {
    168             ReleaseDC(cd->cap_window, cd->dc);
    169             cd->dc = NULL;
    170         }
    171         if (cd->gdi_bitmap != NULL) {
    172             free(cd->gdi_bitmap);
    173             cd->gdi_bitmap = NULL;
    174         }
    175         if (cd->frame_bitmap != NULL) {
    176             free(cd->frame_bitmap);
    177             cd->frame_bitmap = NULL;
    178         }
    179         if (cd->framebuffer != NULL) {
    180             free(cd->framebuffer);
    181             cd->framebuffer = NULL;
    182         }
    183         if (cd->last_frame != NULL) {
    184             free(cd->last_frame);
    185             cd->last_frame = NULL;
    186         }
    187         cd->last_frame_size = 0;
    188 
    189         /* Recreate the capturing window. */
    190         DestroyWindow(cd->cap_window);
    191         cd->cap_window = capCreateCaptureWindow(cd->window_name, WS_CHILD, 0, 0,
    192                                                 0, 0, HWND_MESSAGE, 1);
    193         if (cd->cap_window != NULL) {
    194             /* Save capture window descriptor as window's user data. */
    195             capSetUserData(cd->cap_window, cd);
    196         }
    197     }
    198 }
    199 
    200 /* Gets an absolute value out of a signed integer. */
    201 static __inline__ int
    202 _abs(int val)
    203 {
    204     return (val < 0) ? -val : val;
    205 }
    206 
    207 /* Callback that is invoked when a frame gets captured in capGrabFrameNoStop */
    208 static LRESULT CALLBACK
    209 _on_captured_frame(HWND hwnd, LPVIDEOHDR hdr)
    210 {
    211     /* Capture window descriptor is saved in window's user data. */
    212     WndCameraDevice* wcd = (WndCameraDevice*)capGetUserData(hwnd);
    213 
    214     /* Reallocate frame buffer (if needed) */
    215     if (wcd->last_frame_size < hdr->dwBytesUsed) {
    216         wcd->last_frame_size = hdr->dwBytesUsed;
    217         if (wcd->last_frame != NULL) {
    218             free(wcd->last_frame);
    219         }
    220         wcd->last_frame = malloc(wcd->last_frame_size);
    221     }
    222 
    223     /* Copy captured frame. */
    224     memcpy(wcd->last_frame, hdr->lpData, hdr->dwBytesUsed);
    225 
    226     /* If biCompression is set to default (RGB), set correct pixel format
    227      * for converters. */
    228     if (wcd->frame_bitmap->bmiHeader.biCompression == BI_RGB) {
    229         if (wcd->frame_bitmap->bmiHeader.biBitCount == 32) {
    230             wcd->pixel_format = V4L2_PIX_FMT_BGR32;
    231         } else if (wcd->frame_bitmap->bmiHeader.biBitCount == 16) {
    232             wcd->pixel_format = V4L2_PIX_FMT_RGB565;
    233         } else {
    234             wcd->pixel_format = V4L2_PIX_FMT_BGR24;
    235         }
    236     } else {
    237         wcd->pixel_format = wcd->frame_bitmap->bmiHeader.biCompression;
    238     }
    239 
    240     return (LRESULT)0;
    241 }
    242 
    243 /*******************************************************************************
    244  *                     CameraDevice API
    245  ******************************************************************************/
    246 
    247 CameraDevice*
    248 camera_device_open(const char* name, int inp_channel)
    249 {
    250     WndCameraDevice* wcd;
    251 
    252     /* Allocate descriptor and initialize windows-specific fields. */
    253     wcd = _camera_device_alloc();
    254     if (wcd == NULL) {
    255         E("%s: Unable to allocate WndCameraDevice instance", __FUNCTION__);
    256         return NULL;
    257     }
    258     wcd->window_name = (name != NULL) ? ASTRDUP(name) :
    259                                         ASTRDUP(_default_window_name);
    260     if (wcd->window_name == NULL) {
    261         E("%s: Unable to save window name", __FUNCTION__);
    262         _camera_device_free(wcd);
    263         return NULL;
    264     }
    265     wcd->input_channel = inp_channel;
    266 
    267     /* Create capture window that is a child of HWND_MESSAGE window.
    268      * We make it invisible, so it doesn't mess with the UI. Also
    269      * note that we supply standard HWND_MESSAGE window handle as
    270      * the parent window, since we don't want video capturing
    271      * machinery to be dependent on the details of our UI. */
    272     wcd->cap_window = capCreateCaptureWindow(wcd->window_name, WS_CHILD, 0, 0,
    273                                              0, 0, HWND_MESSAGE, 1);
    274     if (wcd->cap_window == NULL) {
    275         E("%s: Unable to create video capturing window '%s': %d",
    276           __FUNCTION__, wcd->window_name, GetLastError());
    277         _camera_device_free(wcd);
    278         return NULL;
    279     }
    280     /* Save capture window descriptor as window's user data. */
    281     capSetUserData(wcd->cap_window, wcd);
    282 
    283     return &wcd->header;
    284 }
    285 
    286 int
    287 camera_device_start_capturing(CameraDevice* cd,
    288                               uint32_t pixel_format,
    289                               int frame_width,
    290                               int frame_height)
    291 {
    292     WndCameraDevice* wcd;
    293     HBITMAP bm_handle;
    294     BITMAP  bitmap;
    295     size_t format_info_size;
    296     CAPTUREPARMS cap_param;
    297 
    298     if (cd == NULL || cd->opaque == NULL) {
    299         E("%s: Invalid camera device descriptor", __FUNCTION__);
    300         return -1;
    301     }
    302     wcd = (WndCameraDevice*)cd->opaque;
    303 
    304     /* wcd->dc is an indicator of capturing: !NULL - capturing, NULL - not */
    305     if (wcd->dc != NULL) {
    306         W("%s: Capturing is already on on device '%s'",
    307           __FUNCTION__, wcd->window_name);
    308         return 0;
    309     }
    310 
    311     /* Connect capture window to the video capture driver. */
    312     if (!capDriverConnect(wcd->cap_window, wcd->input_channel)) {
    313         return -1;
    314     }
    315 
    316     /* Get current frame information from the driver. */
    317     format_info_size = capGetVideoFormatSize(wcd->cap_window);
    318     if (format_info_size == 0) {
    319         E("%s: Unable to get video format size: %d",
    320           __FUNCTION__, GetLastError());
    321         _camera_device_reset(wcd);
    322         return -1;
    323     }
    324     wcd->frame_bitmap = (BITMAPINFO*)malloc(format_info_size);
    325     if (wcd->frame_bitmap == NULL) {
    326         E("%s: Unable to allocate frame bitmap info buffer", __FUNCTION__);
    327         _camera_device_reset(wcd);
    328         return -1;
    329     }
    330     if (!capGetVideoFormat(wcd->cap_window, wcd->frame_bitmap,
    331                            format_info_size)) {
    332         E("%s: Unable to obtain video format: %d", __FUNCTION__, GetLastError());
    333         _camera_device_reset(wcd);
    334         return -1;
    335     }
    336 
    337     /* Lets see if we need to set different frame dimensions */
    338     if (wcd->frame_bitmap->bmiHeader.biWidth != frame_width ||
    339             abs(wcd->frame_bitmap->bmiHeader.biHeight) != frame_height) {
    340         /* Dimensions don't match. Set new frame info. */
    341         wcd->frame_bitmap->bmiHeader.biWidth = frame_width;
    342         wcd->frame_bitmap->bmiHeader.biHeight = frame_height;
    343         /* We need to recalculate image size, since the capture window / driver
    344          * will use image size provided by us. */
    345         if (wcd->frame_bitmap->bmiHeader.biBitCount == 24) {
    346             /* Special case that may require WORD boundary alignment. */
    347             uint32_t bpl = (frame_width * 3 + 1) & ~1;
    348             wcd->frame_bitmap->bmiHeader.biSizeImage = bpl * frame_height;
    349         } else {
    350             wcd->frame_bitmap->bmiHeader.biSizeImage =
    351                 (frame_width * frame_height * wcd->frame_bitmap->bmiHeader.biBitCount) / 8;
    352         }
    353         if (!capSetVideoFormat(wcd->cap_window, wcd->frame_bitmap,
    354                                format_info_size)) {
    355             E("%s: Unable to set video format: %d", __FUNCTION__, GetLastError());
    356             _camera_device_reset(wcd);
    357             return -1;
    358         }
    359     }
    360 
    361     if (wcd->frame_bitmap->bmiHeader.biCompression > BI_PNG) {
    362         D("%s: Video capturing driver has reported pixel format %.4s",
    363           __FUNCTION__, (const char*)&wcd->frame_bitmap->bmiHeader.biCompression);
    364     }
    365 
    366     /* Most of the time frame bitmaps come in "bottom-up" form, where its origin
    367      * is the lower-left corner. However, it could be in the normal "top-down"
    368      * form with the origin in the upper-left corner. So, we must adjust the
    369      * biHeight field, since the way "top-down" form is reported here is by
    370      * setting biHeight to a negative value. */
    371     if (wcd->frame_bitmap->bmiHeader.biHeight < 0) {
    372         wcd->frame_bitmap->bmiHeader.biHeight =
    373             -wcd->frame_bitmap->bmiHeader.biHeight;
    374         wcd->is_top_down = 1;
    375     } else {
    376         wcd->is_top_down = 0;
    377     }
    378 
    379     /* Get DC for the capturing window that will be used when we deal with
    380      * bitmaps obtained from the camera device during frame capturing. */
    381     wcd->dc = GetDC(wcd->cap_window);
    382     if (wcd->dc == NULL) {
    383         E("%s: Unable to obtain DC for %s: %d",
    384           __FUNCTION__, wcd->window_name, GetLastError());
    385         _camera_device_reset(wcd);
    386         return -1;
    387     }
    388 
    389     /* Setup some capture parameters. */
    390     if (capCaptureGetSetup(wcd->cap_window, &cap_param, sizeof(cap_param))) {
    391         /* Use separate thread to capture video stream. */
    392         cap_param.fYield = TRUE;
    393         /* Don't show any dialogs. */
    394         cap_param.fMakeUserHitOKToCapture = FALSE;
    395         capCaptureSetSetup(wcd->cap_window, &cap_param, sizeof(cap_param));
    396     }
    397 
    398     /*
    399      * At this point we need to grab a frame to properly setup framebuffer, and
    400      * calculate pixel format. The problem is that bitmap information obtained
    401      * from the driver doesn't necessarily match the actual bitmap we're going to
    402      * obtain via capGrabFrame / capEditCopy / GetClipboardData
    403      */
    404 
    405     /* Grab a frame, and post it to the clipboard. Not very effective, but this
    406      * is how capXxx API is operating. */
    407     if (!capGrabFrameNoStop(wcd->cap_window) ||
    408         !capEditCopy(wcd->cap_window) ||
    409         !OpenClipboard(wcd->cap_window)) {
    410         E("%s: Device '%s' is unable to save frame to the clipboard: %d",
    411           __FUNCTION__, wcd->window_name, GetLastError());
    412         _camera_device_reset(wcd);
    413         return -1;
    414     }
    415 
    416     /* Get bitmap handle saved into clipboard. Note that bitmap is still
    417      * owned by the clipboard here! */
    418     bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP);
    419     if (bm_handle == NULL) {
    420         E("%s: Device '%s' is unable to obtain frame from the clipboard: %d",
    421           __FUNCTION__, wcd->window_name, GetLastError());
    422         CloseClipboard();
    423         _camera_device_reset(wcd);
    424         return -1;
    425     }
    426 
    427     /* Get bitmap object that is initialized with the actual bitmap info. */
    428     if (!GetObject(bm_handle, sizeof(BITMAP), &bitmap)) {
    429         E("%s: Device '%s' is unable to obtain frame's bitmap: %d",
    430           __FUNCTION__, wcd->window_name, GetLastError());
    431         EmptyClipboard();
    432         CloseClipboard();
    433         _camera_device_reset(wcd);
    434         return -1;
    435     }
    436 
    437     /* Now that we have all we need in 'bitmap' */
    438     EmptyClipboard();
    439     CloseClipboard();
    440 
    441     /* Make sure that dimensions match. Othewise - fail. */
    442     if (wcd->frame_bitmap->bmiHeader.biWidth != bitmap.bmWidth ||
    443         wcd->frame_bitmap->bmiHeader.biHeight != bitmap.bmHeight ) {
    444         E("%s: Requested dimensions %dx%d do not match the actual %dx%d",
    445           __FUNCTION__, frame_width, frame_height,
    446           wcd->frame_bitmap->bmiHeader.biWidth,
    447           wcd->frame_bitmap->bmiHeader.biHeight);
    448         _camera_device_reset(wcd);
    449         return -1;
    450     }
    451 
    452     /* Create bitmap info that will be used with GetDIBits. */
    453     wcd->gdi_bitmap = (BITMAPINFO*)malloc(wcd->frame_bitmap->bmiHeader.biSize);
    454     if (wcd->gdi_bitmap == NULL) {
    455         E("%s: Unable to allocate gdi bitmap info", __FUNCTION__);
    456         _camera_device_reset(wcd);
    457         return -1;
    458     }
    459     memcpy(wcd->gdi_bitmap, wcd->frame_bitmap,
    460            wcd->frame_bitmap->bmiHeader.biSize);
    461     wcd->gdi_bitmap->bmiHeader.biCompression = BI_RGB;
    462     wcd->gdi_bitmap->bmiHeader.biBitCount = bitmap.bmBitsPixel;
    463     wcd->gdi_bitmap->bmiHeader.biSizeImage = bitmap.bmWidthBytes * bitmap.bmWidth;
    464     /* Adjust GDI's bitmap biHeight for proper frame direction ("top-down", or
    465      * "bottom-up") We do this trick in order to simplify pixel format conversion
    466      * routines, where we always assume "top-down" frames. The trick he is to
    467      * have negative biHeight in 'gdi_bitmap' if driver provides "bottom-up"
    468      * frames, and positive biHeight in 'gdi_bitmap' if driver provides "top-down"
    469      * frames. This way GetGDIBits will always return "top-down" frames. */
    470     if (wcd->is_top_down) {
    471         wcd->gdi_bitmap->bmiHeader.biHeight =
    472             wcd->frame_bitmap->bmiHeader.biHeight;
    473     } else {
    474         wcd->gdi_bitmap->bmiHeader.biHeight =
    475             -wcd->frame_bitmap->bmiHeader.biHeight;
    476     }
    477 
    478     /* Allocate framebuffer. */
    479     wcd->framebuffer = (uint8_t*)malloc(wcd->gdi_bitmap->bmiHeader.biSizeImage);
    480     if (wcd->framebuffer == NULL) {
    481         E("%s: Unable to allocate %d bytes for framebuffer",
    482           __FUNCTION__, wcd->gdi_bitmap->bmiHeader.biSizeImage);
    483         _camera_device_reset(wcd);
    484         return -1;
    485     }
    486 
    487     /* Lets see what pixel format we will use. */
    488     if (wcd->gdi_bitmap->bmiHeader.biBitCount == 16) {
    489         wcd->pixel_format = V4L2_PIX_FMT_RGB565;
    490     } else if (wcd->gdi_bitmap->bmiHeader.biBitCount == 24) {
    491         wcd->pixel_format = V4L2_PIX_FMT_BGR24;
    492     } else if (wcd->gdi_bitmap->bmiHeader.biBitCount == 32) {
    493         wcd->pixel_format = V4L2_PIX_FMT_BGR32;
    494     } else {
    495         E("%s: Unsupported number of bits per pixel %d",
    496           __FUNCTION__, wcd->gdi_bitmap->bmiHeader.biBitCount);
    497         _camera_device_reset(wcd);
    498         return -1;
    499     }
    500 
    501     D("%s: Capturing device '%s': %d bits per pixel in %.4s [%dx%d] frame",
    502       __FUNCTION__, wcd->window_name, wcd->gdi_bitmap->bmiHeader.biBitCount,
    503       (const char*)&wcd->pixel_format, wcd->frame_bitmap->bmiHeader.biWidth,
    504       wcd->frame_bitmap->bmiHeader.biHeight);
    505 
    506     /* Try to setup capture frame callback. */
    507     wcd->use_clipboard = 1;
    508     if (capSetCallbackOnFrame(wcd->cap_window, _on_captured_frame)) {
    509         /* Callback is set. Don't use clipboard when capturing frames. */
    510         wcd->use_clipboard = 0;
    511     }
    512 
    513     return 0;
    514 }
    515 
    516 int
    517 camera_device_stop_capturing(CameraDevice* cd)
    518 {
    519     WndCameraDevice* wcd;
    520     if (cd == NULL || cd->opaque == NULL) {
    521         E("%s: Invalid camera device descriptor", __FUNCTION__);
    522         return -1;
    523     }
    524     wcd = (WndCameraDevice*)cd->opaque;
    525 
    526     /* Disable frame callback. */
    527     capSetCallbackOnFrame(wcd->cap_window, NULL);
    528 
    529     /* wcd->dc is the indicator of capture. */
    530     if (wcd->dc == NULL) {
    531         W("%s: Device '%s' is not capturing video",
    532           __FUNCTION__, wcd->window_name);
    533         return 0;
    534     }
    535     ReleaseDC(wcd->cap_window, wcd->dc);
    536     wcd->dc = NULL;
    537 
    538     /* Reset the device in preparation for the next capture. */
    539     _camera_device_reset(wcd);
    540 
    541     return 0;
    542 }
    543 
    544 /* Capture frame using frame callback.
    545  * Parameters and return value for this routine matches _camera_device_read_frame
    546  */
    547 static int
    548 _camera_device_read_frame_callback(WndCameraDevice* wcd,
    549                                    ClientFrameBuffer* framebuffers,
    550                                    int fbs_num,
    551                                    float r_scale,
    552                                    float g_scale,
    553                                    float b_scale,
    554                                    float exp_comp)
    555 {
    556     /* Grab the frame. Note that this call will cause frame callback to be
    557      * invoked before capGrabFrameNoStop returns. */
    558     if (!capGrabFrameNoStop(wcd->cap_window) || wcd->last_frame == NULL) {
    559         E("%s: Device '%s' is unable to grab a frame: %d",
    560           __FUNCTION__, wcd->window_name, GetLastError());
    561         return -1;
    562     }
    563 
    564     /* Convert framebuffer. */
    565     return convert_frame(wcd->last_frame,
    566                          wcd->pixel_format,
    567                          wcd->frame_bitmap->bmiHeader.biSizeImage,
    568                          wcd->frame_bitmap->bmiHeader.biWidth,
    569                          wcd->frame_bitmap->bmiHeader.biHeight,
    570                          framebuffers, fbs_num,
    571                          r_scale, g_scale, b_scale, exp_comp);
    572 }
    573 
    574 /* Capture frame using clipboard.
    575  * Parameters and return value for this routine matches _camera_device_read_frame
    576  */
    577 static int
    578 _camera_device_read_frame_clipboard(WndCameraDevice* wcd,
    579                                     ClientFrameBuffer* framebuffers,
    580                                     int fbs_num,
    581                                     float r_scale,
    582                                     float g_scale,
    583                                     float b_scale,
    584                                     float exp_comp)
    585 {
    586     HBITMAP bm_handle;
    587 
    588     /* Grab a frame, and post it to the clipboard. Not very effective, but this
    589      * is how capXxx API is operating. */
    590     if (!capGrabFrameNoStop(wcd->cap_window) ||
    591         !capEditCopy(wcd->cap_window) ||
    592         !OpenClipboard(wcd->cap_window)) {
    593         E("%s: Device '%s' is unable to save frame to the clipboard: %d",
    594           __FUNCTION__, wcd->window_name, GetLastError());
    595         return -1;
    596     }
    597 
    598     /* Get bitmap handle saved into clipboard. Note that bitmap is still
    599      * owned by the clipboard here! */
    600     bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP);
    601     if (bm_handle == NULL) {
    602         E("%s: Device '%s' is unable to obtain frame from the clipboard: %d",
    603           __FUNCTION__, wcd->window_name, GetLastError());
    604         EmptyClipboard();
    605         CloseClipboard();
    606         return -1;
    607     }
    608 
    609     /* Get bitmap buffer. */
    610     if (wcd->gdi_bitmap->bmiHeader.biHeight > 0) {
    611         wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight;
    612     }
    613 
    614     if (!GetDIBits(wcd->dc, bm_handle, 0, wcd->frame_bitmap->bmiHeader.biHeight,
    615                    wcd->framebuffer, wcd->gdi_bitmap, DIB_RGB_COLORS)) {
    616         E("%s: Device '%s' is unable to transfer frame to the framebuffer: %d",
    617           __FUNCTION__, wcd->window_name, GetLastError());
    618         EmptyClipboard();
    619         CloseClipboard();
    620         return -1;
    621     }
    622 
    623     if (wcd->gdi_bitmap->bmiHeader.biHeight < 0) {
    624         wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight;
    625     }
    626 
    627     EmptyClipboard();
    628     CloseClipboard();
    629 
    630     /* Convert framebuffer. */
    631     return convert_frame(wcd->framebuffer,
    632                          wcd->pixel_format,
    633                          wcd->gdi_bitmap->bmiHeader.biSizeImage,
    634                          wcd->frame_bitmap->bmiHeader.biWidth,
    635                          wcd->frame_bitmap->bmiHeader.biHeight,
    636                          framebuffers, fbs_num,
    637                          r_scale, g_scale, b_scale, exp_comp);
    638 }
    639 
    640 int
    641 camera_device_read_frame(CameraDevice* cd,
    642                          ClientFrameBuffer* framebuffers,
    643                          int fbs_num,
    644                          float r_scale,
    645                          float g_scale,
    646                          float b_scale,
    647                          float exp_comp)
    648 {
    649     WndCameraDevice* wcd;
    650 
    651     /* Sanity checks. */
    652     if (cd == NULL || cd->opaque == NULL) {
    653         E("%s: Invalid camera device descriptor", __FUNCTION__);
    654         return -1;
    655     }
    656     wcd = (WndCameraDevice*)cd->opaque;
    657     if (wcd->dc == NULL) {
    658         W("%s: Device '%s' is not captuing video",
    659           __FUNCTION__, wcd->window_name);
    660         return -1;
    661     }
    662 
    663     /* Dispatch the call to an appropriate routine: grabbing a frame using
    664      * clipboard, or using a frame callback. */
    665     return wcd->use_clipboard ?
    666         _camera_device_read_frame_clipboard(wcd, framebuffers, fbs_num, r_scale,
    667                                             g_scale, b_scale, exp_comp) :
    668         _camera_device_read_frame_callback(wcd, framebuffers, fbs_num, r_scale,
    669                                            g_scale, b_scale, exp_comp);
    670 }
    671 
    672 void
    673 camera_device_close(CameraDevice* cd)
    674 {
    675     /* Sanity checks. */
    676     if (cd == NULL || cd->opaque == NULL) {
    677         E("%s: Invalid camera device descriptor", __FUNCTION__);
    678     } else {
    679         WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque;
    680         _camera_device_free(wcd);
    681     }
    682 }
    683 
    684 int
    685 enumerate_camera_devices(CameraInfo* cis, int max)
    686 {
    687 /* Array containing emulated webcam frame dimensions.
    688  * capXxx API provides device independent frame dimensions, by scaling frames
    689  * received from the device to whatever dimensions were requested by the user.
    690  * So, we can just use a small set of frame dimensions to emulate.
    691  */
    692 static const CameraFrameDim _emulate_dims[] =
    693 {
    694   /* Emulates 640x480 frame. */
    695   {640, 480},
    696   /* Emulates 352x288 frame (required by camera framework). */
    697   {352, 288},
    698   /* Emulates 320x240 frame (required by camera framework). */
    699   {320, 240},
    700   /* Emulates 176x144 frame (required by camera framework). */
    701   {176, 144}
    702 };
    703     int inp_channel, found = 0;
    704 
    705     for (inp_channel = 0; inp_channel < 10 && found < max; inp_channel++) {
    706         char name[256];
    707         CameraDevice* cd;
    708 
    709         snprintf(name, sizeof(name), "%s%d", _default_window_name, found);
    710         cd = camera_device_open(name, inp_channel);
    711         if (cd != NULL) {
    712             WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque;
    713 
    714             /* Unfortunately, on Windows we have to start capturing in order to get the
    715              * actual frame properties. */
    716             if (!camera_device_start_capturing(cd, V4L2_PIX_FMT_RGB32, 640, 480)) {
    717                 cis[found].frame_sizes = (CameraFrameDim*)malloc(sizeof(_emulate_dims));
    718                 if (cis[found].frame_sizes != NULL) {
    719                     char disp_name[24];
    720                     sprintf(disp_name, "webcam%d", found);
    721                     cis[found].display_name = ASTRDUP(disp_name);
    722                     cis[found].device_name = ASTRDUP(name);
    723                     cis[found].direction = ASTRDUP("front");
    724                     cis[found].inp_channel = inp_channel;
    725                     cis[found].frame_sizes_num = sizeof(_emulate_dims) / sizeof(*_emulate_dims);
    726                     memcpy(cis[found].frame_sizes, _emulate_dims, sizeof(_emulate_dims));
    727                     cis[found].pixel_format = wcd->pixel_format;
    728                     cis[found].in_use = 0;
    729                     found++;
    730                 } else {
    731                     E("%s: Unable to allocate dimensions", __FUNCTION__);
    732                 }
    733                 camera_device_stop_capturing(cd);
    734             } else {
    735                 /* No more cameras. */
    736                 camera_device_close(cd);
    737                 break;
    738             }
    739             camera_device_close(cd);
    740         } else {
    741             /* No more cameras. */
    742             break;
    743         }
    744     }
    745 
    746     return found;
    747 }
    748