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