• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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*
_camera_device_alloc(void)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
_camera_device_free(WndCameraDevice * cd)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
_camera_device_reset(WndCameraDevice * cd)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
_abs(int val)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
_on_captured_frame(HWND hwnd,LPVIDEOHDR hdr)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*
camera_device_open(const char * name,int inp_channel)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
camera_device_start_capturing(CameraDevice * cd,uint32_t pixel_format,int frame_width,int frame_height)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
camera_device_stop_capturing(CameraDevice * cd)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
_camera_device_read_frame_callback(WndCameraDevice * wcd,ClientFrameBuffer * framebuffers,int fbs_num,float r_scale,float g_scale,float b_scale,float exp_comp)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
_camera_device_read_frame_clipboard(WndCameraDevice * wcd,ClientFrameBuffer * framebuffers,int fbs_num,float r_scale,float g_scale,float b_scale,float exp_comp)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
camera_device_read_frame(CameraDevice * cd,ClientFrameBuffer * framebuffers,int fbs_num,float r_scale,float g_scale,float b_scale,float exp_comp)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
camera_device_close(CameraDevice * cd)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
enumerate_camera_devices(CameraInfo * cis,int max)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