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