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