14ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/* 24ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * Copyright (C) 2011 The Android Open Source Project 34ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * 44ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * Licensed under the Apache License, Version 2.0 (the "License"); 54ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * you may not use this file except in compliance with the License. 64ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * You may obtain a copy of the License at 74ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * 84ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * http://www.apache.org/licenses/LICENSE-2.0 94ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * 104ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * Unless required by applicable law or agreed to in writing, software 114ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * distributed under the License is distributed on an "AS IS" BASIS, 124ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * See the License for the specific language governing permissions and 144ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * limitations under the License. 154ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine */ 164ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 174ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/* 184ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * Contains code capturing video frames from a camera device on Windows. 194ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * This code uses capXxx API, available via capCreateCaptureWindow. 204ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine */ 21cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 227f661af7cfca4b7857d30d598923dd2095f78ff0Andrew Hsieh#include <windows.h> 234ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#include <vfw.h> 244ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#include "android/camera/camera-capture.h" 254ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#include "android/camera/camera-format-converters.h" 264ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 27c68dbbef0118eab4256acfc0d9430f0e557a82a1Vladimir Chtchetkine#define E(...) derror(__VA_ARGS__) 28c68dbbef0118eab4256acfc0d9430f0e557a82a1Vladimir Chtchetkine#define W(...) dwarning(__VA_ARGS__) 294ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#define D(...) VERBOSE_PRINT(camera,__VA_ARGS__) 304ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#define D_ACTIVE VERBOSE_CHECK(camera) 314ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 324ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/* the T(...) macro is used to dump traffic */ 334ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#define T_ACTIVE 0 344ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 354ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#if T_ACTIVE 364ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#define T(...) VERBOSE_PRINT(camera,__VA_ARGS__) 374ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#else 384ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#define T(...) ((void)0) 394ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#endif 404ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 414ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/* Default name for the capture window. */ 424ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinestatic const char* _default_window_name = "AndroidEmulatorVC"; 434ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 444ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinetypedef struct WndCameraDevice WndCameraDevice; 454ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/* Windows-specific camera device descriptor. */ 464ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinestruct WndCameraDevice { 474ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* Common camera device descriptor. */ 484ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine CameraDevice header; 494ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* Capture window name. (default is AndroidEmulatorVC) */ 504ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine char* window_name; 514ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* Input channel (video driver index). (default is 0) */ 524ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine int input_channel; 534ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 544ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* 554ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * Set when framework gets initialized. 564ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine */ 574ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 584ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* Video capturing window. Null indicates that device is not connected. */ 594ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine HWND cap_window; 604ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* DC for frame bitmap manipulation. Null indicates that frames are not 614ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * being capturing. */ 624ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine HDC dc; 63cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Bitmap info for the frames obtained from the video capture driver. */ 644ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine BITMAPINFO* frame_bitmap; 65cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Bitmap info to use for GetDIBits calls. We can't really use bitmap info 66cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * obtained from the video capture driver, because of the two issues. First, 67cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * the driver may return an incompatible 'biCompresstion' value. For instance, 68cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * sometimes it returns a "fourcc' pixel format value instead of BI_XXX, 69cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * which causes GetDIBits to fail. Second, the bitmap that represents a frame 70cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * that has been actually obtained from the device is not necessarily matches 71cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * bitmap info that capture driver has returned. Sometimes the captured bitmap 72cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * is a 32-bit RGB, while bit count reported by the driver is 16. So, to 73cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * address these issues we need to have another bitmap info, that can be used 74cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * in GetDIBits calls. */ 75cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine BITMAPINFO* gdi_bitmap; 764ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* Framebuffer large enough to fit the frame. */ 774ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine uint8_t* framebuffer; 78cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Framebuffer size. */ 79cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine size_t framebuffer_size; 80cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Framebuffer's pixel format. */ 81cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine uint32_t pixel_format; 82cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* If != 0, frame bitmap is "top-down". If 0, frame bitmap is "bottom-up". */ 83cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine int is_top_down; 849d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Flags whether frame should be captured using clipboard (1), or via frame 859d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine * callback (0) */ 869d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine int use_clipboard; 879d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Contains last frame captured via frame callback. */ 889d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine void* last_frame; 899d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Byte size of the 'last_frame' buffer. */ 909d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine uint32_t last_frame_size; 914ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine}; 924ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 934ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/******************************************************************************* 944ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * CameraDevice routines 954ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine ******************************************************************************/ 964ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 974ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/* Allocates an instance of WndCameraDevice structure. 984ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * Return: 994ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * Allocated instance of WndCameraDevice structure. Note that this routine 1004ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * also sets 'opaque' field in the 'header' structure to point back to the 1014ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * containing WndCameraDevice instance. 1024ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine */ 1034ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinestatic WndCameraDevice* 1044ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine_camera_device_alloc(void) 1054ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine{ 1064ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine WndCameraDevice* cd = (WndCameraDevice*)malloc(sizeof(WndCameraDevice)); 1074ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (cd != NULL) { 1084ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine memset(cd, 0, sizeof(WndCameraDevice)); 1094ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine cd->header.opaque = cd; 1104ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } else { 1114ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine E("%s: Unable to allocate WndCameraDevice instance", __FUNCTION__); 1124ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 1134ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine return cd; 1144ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine} 1154ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 1164ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/* Uninitializes and frees WndCameraDevice descriptor. 1174ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * Note that upon return from this routine memory allocated for the descriptor 1184ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * will be freed. 1194ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine */ 1204ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinestatic void 1214ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine_camera_device_free(WndCameraDevice* cd) 1224ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine{ 1234ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (cd != NULL) { 1244ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (cd->cap_window != NULL) { 125cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Disconnect from the driver. */ 1264ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine capDriverDisconnect(cd->cap_window); 1274ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 1284ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (cd->dc != NULL) { 1294ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine W("%s: Frames should not be capturing at this point", 1304ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine __FUNCTION__); 1314ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine ReleaseDC(cd->cap_window, cd->dc); 1324ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine cd->dc = NULL; 1334ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 1344ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* Destroy the capturing window. */ 1354ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine DestroyWindow(cd->cap_window); 1364ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine cd->cap_window = NULL; 1374ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 138cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (cd->gdi_bitmap != NULL) { 139cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine free(cd->gdi_bitmap); 140cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 1414ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (cd->frame_bitmap != NULL) { 1424ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine free(cd->frame_bitmap); 1434ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 1444ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (cd->window_name != NULL) { 1454ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine free(cd->window_name); 1464ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 1474ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (cd->framebuffer != NULL) { 1484ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine free(cd->framebuffer); 1494ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 1509d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine if (cd->last_frame != NULL) { 1519d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine free(cd->last_frame); 1529d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine } 1534ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine AFREE(cd); 1544ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } else { 1554ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine W("%s: No descriptor", __FUNCTION__); 1564ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 1574ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine} 1584ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 159a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine/* Resets camera device after capturing. 160a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine * Since new capture request may require different frame dimensions we must 161a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine * reset frame info cached in the capture window. The only way to do that would 162a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine * be closing, and reopening it again. */ 163a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkinestatic void 164a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine_camera_device_reset(WndCameraDevice* cd) 165a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine{ 166a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine if (cd != NULL && cd->cap_window != NULL) { 167a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine capDriverDisconnect(cd->cap_window); 168a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine if (cd->dc != NULL) { 169a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine ReleaseDC(cd->cap_window, cd->dc); 170a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine cd->dc = NULL; 171a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine } 172a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine if (cd->gdi_bitmap != NULL) { 173a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine free(cd->gdi_bitmap); 174a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine cd->gdi_bitmap = NULL; 175a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine } 176a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine if (cd->frame_bitmap != NULL) { 177a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine free(cd->frame_bitmap); 178a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine cd->frame_bitmap = NULL; 179a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine } 180a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine if (cd->framebuffer != NULL) { 181a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine free(cd->framebuffer); 182a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine cd->framebuffer = NULL; 183a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine } 1849d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine if (cd->last_frame != NULL) { 1859d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine free(cd->last_frame); 1869d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine cd->last_frame = NULL; 1879d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine } 1889d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine cd->last_frame_size = 0; 189a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine 190a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine /* Recreate the capturing window. */ 191a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine DestroyWindow(cd->cap_window); 192a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine cd->cap_window = capCreateCaptureWindow(cd->window_name, WS_CHILD, 0, 0, 193a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine 0, 0, HWND_MESSAGE, 1); 1949d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine if (cd->cap_window != NULL) { 1959d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Save capture window descriptor as window's user data. */ 1969d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine capSetUserData(cd->cap_window, cd); 1979d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine } 198a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine } 199a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine} 200a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine 201a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine/* Gets an absolute value out of a signed integer. */ 202a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkinestatic __inline__ int 203a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine_abs(int val) 204a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine{ 205a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine return (val < 0) ? -val : val; 206a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine} 207a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine 2089d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine/* Callback that is invoked when a frame gets captured in capGrabFrameNoStop */ 2099d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkinestatic LRESULT CALLBACK 2109d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine_on_captured_frame(HWND hwnd, LPVIDEOHDR hdr) 2119d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine{ 2129d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Capture window descriptor is saved in window's user data. */ 2139d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine WndCameraDevice* wcd = (WndCameraDevice*)capGetUserData(hwnd); 2149d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine 2159d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Reallocate frame buffer (if needed) */ 2169d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine if (wcd->last_frame_size < hdr->dwBytesUsed) { 2179d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine wcd->last_frame_size = hdr->dwBytesUsed; 2189d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine if (wcd->last_frame != NULL) { 2199d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine free(wcd->last_frame); 2209d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine } 2219d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine wcd->last_frame = malloc(wcd->last_frame_size); 2229d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine } 2239d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine 2249d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Copy captured frame. */ 2259d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine memcpy(wcd->last_frame, hdr->lpData, hdr->dwBytesUsed); 2269d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine 227a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine /* If biCompression is set to default (RGB), set correct pixel format 228a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine * for converters. */ 229a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine if (wcd->frame_bitmap->bmiHeader.biCompression == BI_RGB) { 230a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine if (wcd->frame_bitmap->bmiHeader.biBitCount == 32) { 231a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine wcd->pixel_format = V4L2_PIX_FMT_BGR32; 232a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine } else if (wcd->frame_bitmap->bmiHeader.biBitCount == 16) { 233a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine wcd->pixel_format = V4L2_PIX_FMT_RGB565; 234a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine } else { 235a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine wcd->pixel_format = V4L2_PIX_FMT_BGR24; 236a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine } 237a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine } else { 238a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine wcd->pixel_format = wcd->frame_bitmap->bmiHeader.biCompression; 239a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine } 240a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine 2419d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine return (LRESULT)0; 2429d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine} 2439d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine 2444ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/******************************************************************************* 2454ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * CameraDevice API 2464ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine ******************************************************************************/ 2474ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 2484ed09fd35085c96ae8edbda87757187f75eeac8dVladimir ChtchetkineCameraDevice* 249cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkinecamera_device_open(const char* name, int inp_channel) 2504ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine{ 2514ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine WndCameraDevice* wcd; 2524ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 2534ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* Allocate descriptor and initialize windows-specific fields. */ 2544ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine wcd = _camera_device_alloc(); 2554ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (wcd == NULL) { 2564ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine E("%s: Unable to allocate WndCameraDevice instance", __FUNCTION__); 2574ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine return NULL; 2584ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 2594ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine wcd->window_name = (name != NULL) ? ASTRDUP(name) : 2604ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine ASTRDUP(_default_window_name); 2614ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (wcd->window_name == NULL) { 2624ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine E("%s: Unable to save window name", __FUNCTION__); 2634ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine _camera_device_free(wcd); 2644ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine return NULL; 2654ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 2664ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine wcd->input_channel = inp_channel; 2674ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 2684ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* Create capture window that is a child of HWND_MESSAGE window. 2694ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * We make it invisible, so it doesn't mess with the UI. Also 2704ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * note that we supply standard HWND_MESSAGE window handle as 2714ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * the parent window, since we don't want video capturing 2724ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * machinery to be dependent on the details of our UI. */ 2734ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine wcd->cap_window = capCreateCaptureWindow(wcd->window_name, WS_CHILD, 0, 0, 2744ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 0, 0, HWND_MESSAGE, 1); 2754ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (wcd->cap_window == NULL) { 276cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine E("%s: Unable to create video capturing window '%s': %d", 277cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine __FUNCTION__, wcd->window_name, GetLastError()); 2784ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine _camera_device_free(wcd); 2794ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine return NULL; 2804ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 2819d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Save capture window descriptor as window's user data. */ 2829d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine capSetUserData(wcd->cap_window, wcd); 2834ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 284cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return &wcd->header; 285cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine} 286cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 287cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkineint 288cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkinecamera_device_start_capturing(CameraDevice* cd, 289cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine uint32_t pixel_format, 290cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine int frame_width, 291cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine int frame_height) 292cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine{ 293cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine WndCameraDevice* wcd; 294cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine HBITMAP bm_handle; 295cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine BITMAP bitmap; 296cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine size_t format_info_size; 2979d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine CAPTUREPARMS cap_param; 298cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 299cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (cd == NULL || cd->opaque == NULL) { 300cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine E("%s: Invalid camera device descriptor", __FUNCTION__); 301cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return -1; 302cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 303cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd = (WndCameraDevice*)cd->opaque; 304cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 305cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* wcd->dc is an indicator of capturing: !NULL - capturing, NULL - not */ 306cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (wcd->dc != NULL) { 307cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine W("%s: Capturing is already on on device '%s'", 308cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine __FUNCTION__, wcd->window_name); 309cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return 0; 310cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 311cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 3124ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* Connect capture window to the video capture driver. */ 3134ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (!capDriverConnect(wcd->cap_window, wcd->input_channel)) { 314cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return -1; 3154ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 3164ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 317a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine /* Get current frame information from the driver. */ 3184ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine format_info_size = capGetVideoFormatSize(wcd->cap_window); 3194ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (format_info_size == 0) { 3204ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine E("%s: Unable to get video format size: %d", 3214ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine __FUNCTION__, GetLastError()); 322a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine _camera_device_reset(wcd); 323cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return -1; 3244ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 3254ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine wcd->frame_bitmap = (BITMAPINFO*)malloc(format_info_size); 3264ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (wcd->frame_bitmap == NULL) { 3274ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine E("%s: Unable to allocate frame bitmap info buffer", __FUNCTION__); 328a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine _camera_device_reset(wcd); 329cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return -1; 3304ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 3314ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (!capGetVideoFormat(wcd->cap_window, wcd->frame_bitmap, 332cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine format_info_size)) { 3334ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine E("%s: Unable to obtain video format: %d", __FUNCTION__, GetLastError()); 334a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine _camera_device_reset(wcd); 335cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return -1; 3364ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 3374ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 338a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine /* Lets see if we need to set different frame dimensions */ 339a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine if (wcd->frame_bitmap->bmiHeader.biWidth != frame_width || 340a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine abs(wcd->frame_bitmap->bmiHeader.biHeight) != frame_height) { 341a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine /* Dimensions don't match. Set new frame info. */ 342a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biWidth = frame_width; 343a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biHeight = frame_height; 344a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine /* We need to recalculate image size, since the capture window / driver 345a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine * will use image size provided by us. */ 346a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine if (wcd->frame_bitmap->bmiHeader.biBitCount == 24) { 347a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine /* Special case that may require WORD boundary alignment. */ 348a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine uint32_t bpl = (frame_width * 3 + 1) & ~1; 349a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biSizeImage = bpl * frame_height; 350a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine } else { 351a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biSizeImage = 352a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine (frame_width * frame_height * wcd->frame_bitmap->bmiHeader.biBitCount) / 8; 353a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine } 354a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine if (!capSetVideoFormat(wcd->cap_window, wcd->frame_bitmap, 355a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine format_info_size)) { 356a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine E("%s: Unable to set video format: %d", __FUNCTION__, GetLastError()); 357a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine _camera_device_reset(wcd); 358a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine return -1; 359a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine } 360a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine } 361a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine 362cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (wcd->frame_bitmap->bmiHeader.biCompression > BI_PNG) { 363cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine D("%s: Video capturing driver has reported pixel format %.4s", 364cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine __FUNCTION__, (const char*)&wcd->frame_bitmap->bmiHeader.biCompression); 3654ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 3664ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 367cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Most of the time frame bitmaps come in "bottom-up" form, where its origin 368cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * is the lower-left corner. However, it could be in the normal "top-down" 369cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * form with the origin in the upper-left corner. So, we must adjust the 370cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * biHeight field, since the way "top-down" form is reported here is by 371cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * setting biHeight to a negative value. */ 372cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (wcd->frame_bitmap->bmiHeader.biHeight < 0) { 373cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biHeight = 374cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine -wcd->frame_bitmap->bmiHeader.biHeight; 375cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->is_top_down = 1; 376cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } else { 377cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->is_top_down = 0; 3784ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 3794ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 3804ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* Get DC for the capturing window that will be used when we deal with 3814ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * bitmaps obtained from the camera device during frame capturing. */ 3824ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine wcd->dc = GetDC(wcd->cap_window); 3834ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (wcd->dc == NULL) { 3844ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine E("%s: Unable to obtain DC for %s: %d", 3854ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine __FUNCTION__, wcd->window_name, GetLastError()); 386a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine _camera_device_reset(wcd); 3874ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine return -1; 3884ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 3894ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 3909d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Setup some capture parameters. */ 3919d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine if (capCaptureGetSetup(wcd->cap_window, &cap_param, sizeof(cap_param))) { 3929d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Use separate thread to capture video stream. */ 3939d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine cap_param.fYield = TRUE; 3949d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Don't show any dialogs. */ 3959d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine cap_param.fMakeUserHitOKToCapture = FALSE; 3969d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine capCaptureSetSetup(wcd->cap_window, &cap_param, sizeof(cap_param)); 3979d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine } 3989d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine 399cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* 400cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * At this point we need to grab a frame to properly setup framebuffer, and 401cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * calculate pixel format. The problem is that bitmap information obtained 402cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * from the driver doesn't necessarily match the actual bitmap we're going to 403cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * obtain via capGrabFrame / capEditCopy / GetClipboardData 404cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine */ 405cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 406cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Grab a frame, and post it to the clipboard. Not very effective, but this 407cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * is how capXxx API is operating. */ 408cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (!capGrabFrameNoStop(wcd->cap_window) || 409cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine !capEditCopy(wcd->cap_window) || 410cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine !OpenClipboard(wcd->cap_window)) { 411cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine E("%s: Device '%s' is unable to save frame to the clipboard: %d", 412cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine __FUNCTION__, wcd->window_name, GetLastError()); 413a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine _camera_device_reset(wcd); 414cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return -1; 415cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 416cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 417cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Get bitmap handle saved into clipboard. Note that bitmap is still 418cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * owned by the clipboard here! */ 419cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP); 420cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (bm_handle == NULL) { 421cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine E("%s: Device '%s' is unable to obtain frame from the clipboard: %d", 422cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine __FUNCTION__, wcd->window_name, GetLastError()); 423cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine CloseClipboard(); 424a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine _camera_device_reset(wcd); 425cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return -1; 426cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 427cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 428cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Get bitmap object that is initialized with the actual bitmap info. */ 429cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (!GetObject(bm_handle, sizeof(BITMAP), &bitmap)) { 430cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine E("%s: Device '%s' is unable to obtain frame's bitmap: %d", 431cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine __FUNCTION__, wcd->window_name, GetLastError()); 4329d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine EmptyClipboard(); 433cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine CloseClipboard(); 434a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine _camera_device_reset(wcd); 435cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return -1; 436cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 437cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 438cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Now that we have all we need in 'bitmap' */ 4399d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine EmptyClipboard(); 440cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine CloseClipboard(); 441cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 442cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Make sure that dimensions match. Othewise - fail. */ 443cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (wcd->frame_bitmap->bmiHeader.biWidth != bitmap.bmWidth || 444cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biHeight != bitmap.bmHeight ) { 445cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine E("%s: Requested dimensions %dx%d do not match the actual %dx%d", 446cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine __FUNCTION__, frame_width, frame_height, 447cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biWidth, 448cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biHeight); 449a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine _camera_device_reset(wcd); 450cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return -1; 451cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 452cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 453cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Create bitmap info that will be used with GetDIBits. */ 454cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->gdi_bitmap = (BITMAPINFO*)malloc(wcd->frame_bitmap->bmiHeader.biSize); 455cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (wcd->gdi_bitmap == NULL) { 456cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine E("%s: Unable to allocate gdi bitmap info", __FUNCTION__); 457a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine _camera_device_reset(wcd); 458cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return -1; 459cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 460cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine memcpy(wcd->gdi_bitmap, wcd->frame_bitmap, 461cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biSize); 462cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->gdi_bitmap->bmiHeader.biCompression = BI_RGB; 463cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->gdi_bitmap->bmiHeader.biBitCount = bitmap.bmBitsPixel; 464cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->gdi_bitmap->bmiHeader.biSizeImage = bitmap.bmWidthBytes * bitmap.bmWidth; 465cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Adjust GDI's bitmap biHeight for proper frame direction ("top-down", or 466cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * "bottom-up") We do this trick in order to simplify pixel format conversion 467cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * routines, where we always assume "top-down" frames. The trick he is to 468cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * have negative biHeight in 'gdi_bitmap' if driver provides "bottom-up" 469cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * frames, and positive biHeight in 'gdi_bitmap' if driver provides "top-down" 470cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine * frames. This way GetGDIBits will always return "top-down" frames. */ 471cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (wcd->is_top_down) { 472cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->gdi_bitmap->bmiHeader.biHeight = 473cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biHeight; 474cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } else { 475cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->gdi_bitmap->bmiHeader.biHeight = 476cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine -wcd->frame_bitmap->bmiHeader.biHeight; 477cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 478cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 479cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Allocate framebuffer. */ 480cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->framebuffer = (uint8_t*)malloc(wcd->gdi_bitmap->bmiHeader.biSizeImage); 481cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (wcd->framebuffer == NULL) { 482cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine E("%s: Unable to allocate %d bytes for framebuffer", 483cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine __FUNCTION__, wcd->gdi_bitmap->bmiHeader.biSizeImage); 484a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine _camera_device_reset(wcd); 485cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return -1; 486cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 487cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 488cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Lets see what pixel format we will use. */ 489cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (wcd->gdi_bitmap->bmiHeader.biBitCount == 16) { 490cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->pixel_format = V4L2_PIX_FMT_RGB565; 491cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } else if (wcd->gdi_bitmap->bmiHeader.biBitCount == 24) { 4926ef999d93532fdb0b9f22e14bf2095e4d3ee0355Vladimir Chtchetkine wcd->pixel_format = V4L2_PIX_FMT_BGR24; 493cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } else if (wcd->gdi_bitmap->bmiHeader.biBitCount == 32) { 4946ef999d93532fdb0b9f22e14bf2095e4d3ee0355Vladimir Chtchetkine wcd->pixel_format = V4L2_PIX_FMT_BGR32; 495cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } else { 496cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine E("%s: Unsupported number of bits per pixel %d", 497cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine __FUNCTION__, wcd->gdi_bitmap->bmiHeader.biBitCount); 498a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine _camera_device_reset(wcd); 499cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return -1; 500cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 501cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 502a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine D("%s: Capturing device '%s': %d bits per pixel in %.4s [%dx%d] frame", 503cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine __FUNCTION__, wcd->window_name, wcd->gdi_bitmap->bmiHeader.biBitCount, 504cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine (const char*)&wcd->pixel_format, wcd->frame_bitmap->bmiHeader.biWidth, 505cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biHeight); 506cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 5079d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Try to setup capture frame callback. */ 5089d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine wcd->use_clipboard = 1; 5099d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine if (capSetCallbackOnFrame(wcd->cap_window, _on_captured_frame)) { 5109d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Callback is set. Don't use clipboard when capturing frames. */ 5119d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine wcd->use_clipboard = 0; 5129d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine } 5139d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine 5144ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine return 0; 5154ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine} 5164ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 5174ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkineint 5184ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinecamera_device_stop_capturing(CameraDevice* cd) 5194ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine{ 5204ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine WndCameraDevice* wcd; 5214ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (cd == NULL || cd->opaque == NULL) { 5224ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine E("%s: Invalid camera device descriptor", __FUNCTION__); 5234ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine return -1; 5244ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 5254ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine wcd = (WndCameraDevice*)cd->opaque; 526cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 5279d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Disable frame callback. */ 5289d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine capSetCallbackOnFrame(wcd->cap_window, NULL); 5299d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine 530cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* wcd->dc is the indicator of capture. */ 5314ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (wcd->dc == NULL) { 532cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine W("%s: Device '%s' is not capturing video", 533cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine __FUNCTION__, wcd->window_name); 5344ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine return 0; 5354ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 5364ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine ReleaseDC(wcd->cap_window, wcd->dc); 5374ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine wcd->dc = NULL; 5384ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 539a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine /* Reset the device in preparation for the next capture. */ 540a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine _camera_device_reset(wcd); 541cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 5424ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine return 0; 5434ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine} 5444ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 5459d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine/* Capture frame using frame callback. 5469d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine * Parameters and return value for this routine matches _camera_device_read_frame 5479d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine */ 5489d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkinestatic int 5499d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine_camera_device_read_frame_callback(WndCameraDevice* wcd, 5509d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine ClientFrameBuffer* framebuffers, 5519d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine int fbs_num, 5529d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine float r_scale, 5539d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine float g_scale, 5549d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine float b_scale, 5559d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine float exp_comp) 5564ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine{ 5579d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Grab the frame. Note that this call will cause frame callback to be 5589d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine * invoked before capGrabFrameNoStop returns. */ 5599d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine if (!capGrabFrameNoStop(wcd->cap_window) || wcd->last_frame == NULL) { 5609d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine E("%s: Device '%s' is unable to grab a frame: %d", 5619d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine __FUNCTION__, wcd->window_name, GetLastError()); 5624ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine return -1; 5634ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 5644ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 5659d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Convert framebuffer. */ 5669d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine return convert_frame(wcd->last_frame, 567a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine wcd->pixel_format, 5689d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biSizeImage, 5699d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biWidth, 5709d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biHeight, 5719d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine framebuffers, fbs_num, 5729d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine r_scale, g_scale, b_scale, exp_comp); 5739d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine} 5749d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine 5759d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine/* Capture frame using clipboard. 5769d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine * Parameters and return value for this routine matches _camera_device_read_frame 5779d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine */ 5789d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkinestatic int 5799d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine_camera_device_read_frame_clipboard(WndCameraDevice* wcd, 5809d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine ClientFrameBuffer* framebuffers, 5819d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine int fbs_num, 5829d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine float r_scale, 5839d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine float g_scale, 5849d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine float b_scale, 5859d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine float exp_comp) 5869d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine{ 5879d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine HBITMAP bm_handle; 5889d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine 5894ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* Grab a frame, and post it to the clipboard. Not very effective, but this 5904ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * is how capXxx API is operating. */ 5914ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (!capGrabFrameNoStop(wcd->cap_window) || 5924ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine !capEditCopy(wcd->cap_window) || 5934ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine !OpenClipboard(wcd->cap_window)) { 594cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine E("%s: Device '%s' is unable to save frame to the clipboard: %d", 5954ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine __FUNCTION__, wcd->window_name, GetLastError()); 5964ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine return -1; 5974ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 5984ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 5994ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* Get bitmap handle saved into clipboard. Note that bitmap is still 6004ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * owned by the clipboard here! */ 6014ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP); 6024ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (bm_handle == NULL) { 603cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine E("%s: Device '%s' is unable to obtain frame from the clipboard: %d", 6044ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine __FUNCTION__, wcd->window_name, GetLastError()); 6059d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine EmptyClipboard(); 606cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine CloseClipboard(); 6074ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine return -1; 6084ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 6094ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 610cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Get bitmap buffer. */ 611cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (wcd->gdi_bitmap->bmiHeader.biHeight > 0) { 612cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight; 6134ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 6144ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 6154ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (!GetDIBits(wcd->dc, bm_handle, 0, wcd->frame_bitmap->bmiHeader.biHeight, 616cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->framebuffer, wcd->gdi_bitmap, DIB_RGB_COLORS)) { 617cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine E("%s: Device '%s' is unable to transfer frame to the framebuffer: %d", 6184ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine __FUNCTION__, wcd->window_name, GetLastError()); 6199d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine EmptyClipboard(); 620cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine CloseClipboard(); 6214ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine return -1; 6224ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 6234ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 624cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (wcd->gdi_bitmap->bmiHeader.biHeight < 0) { 625cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight; 6264ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 6274ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 6289d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine EmptyClipboard(); 629cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine CloseClipboard(); 630cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 631cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Convert framebuffer. */ 632cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return convert_frame(wcd->framebuffer, 633cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->pixel_format, 634cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->gdi_bitmap->bmiHeader.biSizeImage, 635cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biWidth, 636cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine wcd->frame_bitmap->bmiHeader.biHeight, 63737fb84f8b26e3061c1ccb404bf4c962eed5e6057Vladimir Chtchetkine framebuffers, fbs_num, 63837fb84f8b26e3061c1ccb404bf4c962eed5e6057Vladimir Chtchetkine r_scale, g_scale, b_scale, exp_comp); 6394ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine} 6404ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine 6419d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkineint 6429d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkinecamera_device_read_frame(CameraDevice* cd, 6439d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine ClientFrameBuffer* framebuffers, 6449d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine int fbs_num, 6459d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine float r_scale, 6469d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine float g_scale, 6479d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine float b_scale, 6489d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine float exp_comp) 6499d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine{ 6509d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine WndCameraDevice* wcd; 6519d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine 6529d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Sanity checks. */ 6539d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine if (cd == NULL || cd->opaque == NULL) { 6549d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine E("%s: Invalid camera device descriptor", __FUNCTION__); 6559d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine return -1; 6569d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine } 6579d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine wcd = (WndCameraDevice*)cd->opaque; 6589d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine if (wcd->dc == NULL) { 6599d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine W("%s: Device '%s' is not captuing video", 6609d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine __FUNCTION__, wcd->window_name); 6619d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine return -1; 6629d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine } 6639d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine 6649d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine /* Dispatch the call to an appropriate routine: grabbing a frame using 6659d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine * clipboard, or using a frame callback. */ 6669d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine return wcd->use_clipboard ? 6679d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine _camera_device_read_frame_clipboard(wcd, framebuffers, fbs_num, r_scale, 6689d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine g_scale, b_scale, exp_comp) : 6699d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine _camera_device_read_frame_callback(wcd, framebuffers, fbs_num, r_scale, 6709d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine g_scale, b_scale, exp_comp); 6719d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine} 6729d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine 6734ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinevoid 6744ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinecamera_device_close(CameraDevice* cd) 6754ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine{ 6764ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine /* Sanity checks. */ 6774ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine if (cd == NULL || cd->opaque == NULL) { 6784ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine E("%s: Invalid camera device descriptor", __FUNCTION__); 6794ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } else { 6804ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque; 6814ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine _camera_device_free(wcd); 6824ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine } 6834ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine} 684cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 685cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkineint 686cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkineenumerate_camera_devices(CameraInfo* cis, int max) 687cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine{ 6888d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine/* Array containing emulated webcam frame dimensions. 6898d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine * capXxx API provides device independent frame dimensions, by scaling frames 6908d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine * received from the device to whatever dimensions were requested by the user. 6918d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine * So, we can just use a small set of frame dimensions to emulate. 6928d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine */ 6938d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkinestatic const CameraFrameDim _emulate_dims[] = 6948d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine{ 6958d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine /* Emulates 640x480 frame. */ 6968d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine {640, 480}, 6978d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine /* Emulates 352x288 frame (required by camera framework). */ 6988d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine {352, 288}, 699e080a45d3e29e41bce06669b79d5eb04f0fd05efVladimir Chtchetkine /* Emulates 320x240 frame (required by camera framework). */ 700e080a45d3e29e41bce06669b79d5eb04f0fd05efVladimir Chtchetkine {320, 240}, 7018d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine /* Emulates 176x144 frame (required by camera framework). */ 7028d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine {176, 144} 7038d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine}; 704cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine int inp_channel, found = 0; 705cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 706cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine for (inp_channel = 0; inp_channel < 10 && found < max; inp_channel++) { 707cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine char name[256]; 708cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine CameraDevice* cd; 709cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 710cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine snprintf(name, sizeof(name), "%s%d", _default_window_name, found); 711cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine cd = camera_device_open(name, inp_channel); 712cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (cd != NULL) { 713cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque; 714cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 715cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* Unfortunately, on Windows we have to start capturing in order to get the 7168d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine * actual frame properties. */ 717cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (!camera_device_start_capturing(cd, V4L2_PIX_FMT_RGB32, 640, 480)) { 7188d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine cis[found].frame_sizes = (CameraFrameDim*)malloc(sizeof(_emulate_dims)); 719cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine if (cis[found].frame_sizes != NULL) { 720b8dcaffaf7dcb0c795d2776abf3bb75196f8527cVladimir Chtchetkine char disp_name[24]; 721b8dcaffaf7dcb0c795d2776abf3bb75196f8527cVladimir Chtchetkine sprintf(disp_name, "webcam%d", found); 722b8dcaffaf7dcb0c795d2776abf3bb75196f8527cVladimir Chtchetkine cis[found].display_name = ASTRDUP(disp_name); 723cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine cis[found].device_name = ASTRDUP(name); 724b8dcaffaf7dcb0c795d2776abf3bb75196f8527cVladimir Chtchetkine cis[found].direction = ASTRDUP("front"); 725cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine cis[found].inp_channel = inp_channel; 7268d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine cis[found].frame_sizes_num = sizeof(_emulate_dims) / sizeof(*_emulate_dims); 7278d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine memcpy(cis[found].frame_sizes, _emulate_dims, sizeof(_emulate_dims)); 728cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine cis[found].pixel_format = wcd->pixel_format; 729cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine cis[found].in_use = 0; 730cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine found++; 731cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } else { 732cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine E("%s: Unable to allocate dimensions", __FUNCTION__); 733cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 734a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine camera_device_stop_capturing(cd); 735cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } else { 736cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* No more cameras. */ 737cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine camera_device_close(cd); 738cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine break; 739cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 740cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine camera_device_close(cd); 741cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } else { 742cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine /* No more cameras. */ 743cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine break; 744cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 745cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine } 746cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine 747cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine return found; 748cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine} 749