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. */
42static const char* _default_window_name = "AndroidEmulatorVC";
43
44typedef struct WndCameraDevice WndCameraDevice;
45/* Windows-specific camera device descriptor. */
46struct 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 */
103static 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 */
120static 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. */
163static 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. */
202static __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 */
209static 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
248CameraDevice*
249camera_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
287int
288camera_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
517int
518camera_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 */
548static 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 */
578static 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
641int
642camera_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
673void
674camera_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
685int
686enumerate_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 */
693static 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