From 9d5d34571cb8f596e6ed215e5778c94b8207a33b Mon Sep 17 00:00:00 2001 From: Vladimir Chtchetkine Date: Thu, 2 Feb 2012 09:19:08 -0800 Subject: Fix problem with using clipboard in Windows camera emulation This is a fix for a user reported issue #24794. In the nutshell, when emulator grabs a frame from webcam it leaves its bitmap in the clipboard (which is global). So, when user does a 'paste' somewhere else, that bitmap will be pasted there. Being bad by itself, it gets worst, because 'copy / paste' will be globally broken while camera application is running in the emulator. I.e, if you have camera app running in the emulator, and at the same time you're editing a Word file (for example), copy / paste will not work in Word. This CL contains a fix such as when possible, camera emulator on windows will not use clipboard to grab video frames, but will grab video frames using frame callback. Change-Id: I115a9c653b252620cf5407173c1e1eec69b4a2ee --- android/camera/camera-capture-windows.c | 160 ++++++++++++++++++++++++++++---- 1 file changed, 140 insertions(+), 20 deletions(-) (limited to 'android') diff --git a/android/camera/camera-capture-windows.c b/android/camera/camera-capture-windows.c index c83b502..5479e23 100755 --- a/android/camera/camera-capture-windows.c +++ b/android/camera/camera-capture-windows.c @@ -80,6 +80,13 @@ struct WndCameraDevice { uint32_t pixel_format; /* If != 0, frame bitmap is "top-down". If 0, frame bitmap is "bottom-up". */ int is_top_down; + /* Flags whether frame should be captured using clipboard (1), or via frame + * callback (0) */ + int use_clipboard; + /* Contains last frame captured via frame callback. */ + void* last_frame; + /* Byte size of the 'last_frame' buffer. */ + uint32_t last_frame_size; }; /******************************************************************************* @@ -139,6 +146,9 @@ _camera_device_free(WndCameraDevice* cd) if (cd->framebuffer != NULL) { free(cd->framebuffer); } + if (cd->last_frame != NULL) { + free(cd->last_frame); + } AFREE(cd); } else { W("%s: No descriptor", __FUNCTION__); @@ -170,11 +180,20 @@ _camera_device_reset(WndCameraDevice* cd) free(cd->framebuffer); cd->framebuffer = NULL; } + if (cd->last_frame != NULL) { + free(cd->last_frame); + cd->last_frame = NULL; + } + cd->last_frame_size = 0; /* Recreate the capturing window. */ DestroyWindow(cd->cap_window); cd->cap_window = capCreateCaptureWindow(cd->window_name, WS_CHILD, 0, 0, 0, 0, HWND_MESSAGE, 1); + if (cd->cap_window != NULL) { + /* Save capture window descriptor as window's user data. */ + capSetUserData(cd->cap_window, cd); + } } } @@ -185,6 +204,28 @@ _abs(int val) return (val < 0) ? -val : val; } +/* Callback that is invoked when a frame gets captured in capGrabFrameNoStop */ +static LRESULT CALLBACK +_on_captured_frame(HWND hwnd, LPVIDEOHDR hdr) +{ + /* Capture window descriptor is saved in window's user data. */ + WndCameraDevice* wcd = (WndCameraDevice*)capGetUserData(hwnd); + + /* Reallocate frame buffer (if needed) */ + if (wcd->last_frame_size < hdr->dwBytesUsed) { + wcd->last_frame_size = hdr->dwBytesUsed; + if (wcd->last_frame != NULL) { + free(wcd->last_frame); + } + wcd->last_frame = malloc(wcd->last_frame_size); + } + + /* Copy captured frame. */ + memcpy(wcd->last_frame, hdr->lpData, hdr->dwBytesUsed); + + return (LRESULT)0; +} + /******************************************************************************* * CameraDevice API ******************************************************************************/ @@ -222,6 +263,8 @@ camera_device_open(const char* name, int inp_channel) _camera_device_free(wcd); return NULL; } + /* Save capture window descriptor as window's user data. */ + capSetUserData(wcd->cap_window, wcd); return &wcd->header; } @@ -236,6 +279,7 @@ camera_device_start_capturing(CameraDevice* cd, HBITMAP bm_handle; BITMAP bitmap; size_t format_info_size; + CAPTUREPARMS cap_param; if (cd == NULL || cd->opaque == NULL) { E("%s: Invalid camera device descriptor", __FUNCTION__); @@ -328,6 +372,15 @@ camera_device_start_capturing(CameraDevice* cd, return -1; } + /* Setup some capture parameters. */ + if (capCaptureGetSetup(wcd->cap_window, &cap_param, sizeof(cap_param))) { + /* Use separate thread to capture video stream. */ + cap_param.fYield = TRUE; + /* Don't show any dialogs. */ + cap_param.fMakeUserHitOKToCapture = FALSE; + capCaptureSetSetup(wcd->cap_window, &cap_param, sizeof(cap_param)); + } + /* * At this point we need to grab a frame to properly setup framebuffer, and * calculate pixel format. The problem is that bitmap information obtained @@ -361,12 +414,14 @@ camera_device_start_capturing(CameraDevice* cd, if (!GetObject(bm_handle, sizeof(BITMAP), &bitmap)) { E("%s: Device '%s' is unable to obtain frame's bitmap: %d", __FUNCTION__, wcd->window_name, GetLastError()); + EmptyClipboard(); CloseClipboard(); _camera_device_reset(wcd); return -1; } /* Now that we have all we need in 'bitmap' */ + EmptyClipboard(); CloseClipboard(); /* Make sure that dimensions match. Othewise - fail. */ @@ -434,6 +489,13 @@ camera_device_start_capturing(CameraDevice* cd, (const char*)&wcd->pixel_format, wcd->frame_bitmap->bmiHeader.biWidth, wcd->frame_bitmap->bmiHeader.biHeight); + /* Try to setup capture frame callback. */ + wcd->use_clipboard = 1; + if (capSetCallbackOnFrame(wcd->cap_window, _on_captured_frame)) { + /* Callback is set. Don't use clipboard when capturing frames. */ + wcd->use_clipboard = 0; + } + return 0; } @@ -447,6 +509,9 @@ camera_device_stop_capturing(CameraDevice* cd) } wcd = (WndCameraDevice*)cd->opaque; + /* Disable frame callback. */ + capSetCallbackOnFrame(wcd->cap_window, NULL); + /* wcd->dc is the indicator of capture. */ if (wcd->dc == NULL) { W("%s: Device '%s' is not capturing video", @@ -462,30 +527,50 @@ camera_device_stop_capturing(CameraDevice* cd) return 0; } -int -camera_device_read_frame(CameraDevice* cd, - ClientFrameBuffer* framebuffers, - int fbs_num, - float r_scale, - float g_scale, - float b_scale, - float exp_comp) +/* Capture frame using frame callback. + * Parameters and return value for this routine matches _camera_device_read_frame + */ +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) { - WndCameraDevice* wcd; - HBITMAP bm_handle; - - /* Sanity checks. */ - if (cd == NULL || cd->opaque == NULL) { - E("%s: Invalid camera device descriptor", __FUNCTION__); - return -1; - } - wcd = (WndCameraDevice*)cd->opaque; - if (wcd->dc == NULL) { - W("%s: Device '%s' is not captuing video", - __FUNCTION__, wcd->window_name); + /* Grab the frame. Note that this call will cause frame callback to be + * invoked before capGrabFrameNoStop returns. */ + if (!capGrabFrameNoStop(wcd->cap_window) || wcd->last_frame == NULL) { + E("%s: Device '%s' is unable to grab a frame: %d", + __FUNCTION__, wcd->window_name, GetLastError()); return -1; } + /* Convert framebuffer. */ + return convert_frame(wcd->last_frame, + wcd->frame_bitmap->bmiHeader.biCompression, + wcd->frame_bitmap->bmiHeader.biSizeImage, + wcd->frame_bitmap->bmiHeader.biWidth, + wcd->frame_bitmap->bmiHeader.biHeight, + framebuffers, fbs_num, + r_scale, g_scale, b_scale, exp_comp); +} + +/* Capture frame using clipboard. + * Parameters and return value for this routine matches _camera_device_read_frame + */ +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) +{ + HBITMAP bm_handle; + /* Grab a frame, and post it to the clipboard. Not very effective, but this * is how capXxx API is operating. */ if (!capGrabFrameNoStop(wcd->cap_window) || @@ -502,6 +587,7 @@ camera_device_read_frame(CameraDevice* cd, if (bm_handle == NULL) { E("%s: Device '%s' is unable to obtain frame from the clipboard: %d", __FUNCTION__, wcd->window_name, GetLastError()); + EmptyClipboard(); CloseClipboard(); return -1; } @@ -515,6 +601,7 @@ camera_device_read_frame(CameraDevice* cd, wcd->framebuffer, wcd->gdi_bitmap, DIB_RGB_COLORS)) { E("%s: Device '%s' is unable to transfer frame to the framebuffer: %d", __FUNCTION__, wcd->window_name, GetLastError()); + EmptyClipboard(); CloseClipboard(); return -1; } @@ -523,6 +610,7 @@ camera_device_read_frame(CameraDevice* cd, wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight; } + EmptyClipboard(); CloseClipboard(); /* Convert framebuffer. */ @@ -535,6 +623,38 @@ camera_device_read_frame(CameraDevice* cd, r_scale, g_scale, b_scale, exp_comp); } +int +camera_device_read_frame(CameraDevice* cd, + ClientFrameBuffer* framebuffers, + int fbs_num, + float r_scale, + float g_scale, + float b_scale, + float exp_comp) +{ + WndCameraDevice* wcd; + + /* Sanity checks. */ + if (cd == NULL || cd->opaque == NULL) { + E("%s: Invalid camera device descriptor", __FUNCTION__); + return -1; + } + wcd = (WndCameraDevice*)cd->opaque; + if (wcd->dc == NULL) { + W("%s: Device '%s' is not captuing video", + __FUNCTION__, wcd->window_name); + return -1; + } + + /* Dispatch the call to an appropriate routine: grabbing a frame using + * clipboard, or using a frame callback. */ + return wcd->use_clipboard ? + _camera_device_read_frame_clipboard(wcd, framebuffers, fbs_num, r_scale, + g_scale, b_scale, exp_comp) : + _camera_device_read_frame_callback(wcd, framebuffers, fbs_num, r_scale, + g_scale, b_scale, exp_comp); +} + void camera_device_close(CameraDevice* cd) { -- cgit v1.1