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