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