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* 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 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 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 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 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* 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 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 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 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 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 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 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 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