blob: c83b5020cde8152a62a628249dfdcb84a0dcb752 [file] [log] [blame]
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -07001/*
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 */
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -070021
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -070022#include <vfw.h>
23#include "android/camera/camera-capture.h"
24#include "android/camera/camera-format-converters.h"
25
Vladimir Chtchetkinec68dbbe2011-09-23 07:58:34 -070026#define E(...) derror(__VA_ARGS__)
27#define W(...) dwarning(__VA_ARGS__)
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -070028#define D(...) VERBOSE_PRINT(camera,__VA_ARGS__)
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -070029#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. */
41static const char* _default_window_name = "AndroidEmulatorVC";
42
43typedef struct WndCameraDevice WndCameraDevice;
44/* Windows-specific camera device descriptor. */
45struct 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;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -070052
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;
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -070062 /* Bitmap info for the frames obtained from the video capture driver. */
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -070063 BITMAPINFO* frame_bitmap;
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -070064 /* 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;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -070075 /* Framebuffer large enough to fit the frame. */
76 uint8_t* framebuffer;
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -070077 /* 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;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -070083};
84
85/*******************************************************************************
86 * CameraDevice routines
87 ******************************************************************************/
88
89/* Allocates an instance of WndCameraDevice structure.
90 * Return:
91 * Allocated instance of WndCameraDevice structure. Note that this routine
92 * also sets 'opaque' field in the 'header' structure to point back to the
93 * containing WndCameraDevice instance.
94 */
95static WndCameraDevice*
96_camera_device_alloc(void)
97{
98 WndCameraDevice* cd = (WndCameraDevice*)malloc(sizeof(WndCameraDevice));
99 if (cd != NULL) {
100 memset(cd, 0, sizeof(WndCameraDevice));
101 cd->header.opaque = cd;
102 } else {
103 E("%s: Unable to allocate WndCameraDevice instance", __FUNCTION__);
104 }
105 return cd;
106}
107
108/* Uninitializes and frees WndCameraDevice descriptor.
109 * Note that upon return from this routine memory allocated for the descriptor
110 * will be freed.
111 */
112static void
113_camera_device_free(WndCameraDevice* cd)
114{
115 if (cd != NULL) {
116 if (cd->cap_window != NULL) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700117 /* Disconnect from the driver. */
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700118 capDriverDisconnect(cd->cap_window);
119
120 if (cd->dc != NULL) {
121 W("%s: Frames should not be capturing at this point",
122 __FUNCTION__);
123 ReleaseDC(cd->cap_window, cd->dc);
124 cd->dc = NULL;
125 }
126 /* Destroy the capturing window. */
127 DestroyWindow(cd->cap_window);
128 cd->cap_window = NULL;
129 }
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700130 if (cd->gdi_bitmap != NULL) {
131 free(cd->gdi_bitmap);
132 }
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700133 if (cd->frame_bitmap != NULL) {
134 free(cd->frame_bitmap);
135 }
136 if (cd->window_name != NULL) {
137 free(cd->window_name);
138 }
139 if (cd->framebuffer != NULL) {
140 free(cd->framebuffer);
141 }
142 AFREE(cd);
143 } else {
144 W("%s: No descriptor", __FUNCTION__);
145 }
146}
147
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700148/* Resets camera device after capturing.
149 * Since new capture request may require different frame dimensions we must
150 * reset frame info cached in the capture window. The only way to do that would
151 * be closing, and reopening it again. */
152static void
153_camera_device_reset(WndCameraDevice* cd)
154{
155 if (cd != NULL && cd->cap_window != NULL) {
156 capDriverDisconnect(cd->cap_window);
157 if (cd->dc != NULL) {
158 ReleaseDC(cd->cap_window, cd->dc);
159 cd->dc = NULL;
160 }
161 if (cd->gdi_bitmap != NULL) {
162 free(cd->gdi_bitmap);
163 cd->gdi_bitmap = NULL;
164 }
165 if (cd->frame_bitmap != NULL) {
166 free(cd->frame_bitmap);
167 cd->frame_bitmap = NULL;
168 }
169 if (cd->framebuffer != NULL) {
170 free(cd->framebuffer);
171 cd->framebuffer = NULL;
172 }
173
174 /* Recreate the capturing window. */
175 DestroyWindow(cd->cap_window);
176 cd->cap_window = capCreateCaptureWindow(cd->window_name, WS_CHILD, 0, 0,
177 0, 0, HWND_MESSAGE, 1);
178 }
179}
180
181/* Gets an absolute value out of a signed integer. */
182static __inline__ int
183_abs(int val)
184{
185 return (val < 0) ? -val : val;
186}
187
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700188/*******************************************************************************
189 * CameraDevice API
190 ******************************************************************************/
191
192CameraDevice*
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700193camera_device_open(const char* name, int inp_channel)
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700194{
195 WndCameraDevice* wcd;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700196
197 /* Allocate descriptor and initialize windows-specific fields. */
198 wcd = _camera_device_alloc();
199 if (wcd == NULL) {
200 E("%s: Unable to allocate WndCameraDevice instance", __FUNCTION__);
201 return NULL;
202 }
203 wcd->window_name = (name != NULL) ? ASTRDUP(name) :
204 ASTRDUP(_default_window_name);
205 if (wcd->window_name == NULL) {
206 E("%s: Unable to save window name", __FUNCTION__);
207 _camera_device_free(wcd);
208 return NULL;
209 }
210 wcd->input_channel = inp_channel;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700211
212 /* Create capture window that is a child of HWND_MESSAGE window.
213 * We make it invisible, so it doesn't mess with the UI. Also
214 * note that we supply standard HWND_MESSAGE window handle as
215 * the parent window, since we don't want video capturing
216 * machinery to be dependent on the details of our UI. */
217 wcd->cap_window = capCreateCaptureWindow(wcd->window_name, WS_CHILD, 0, 0,
218 0, 0, HWND_MESSAGE, 1);
219 if (wcd->cap_window == NULL) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700220 E("%s: Unable to create video capturing window '%s': %d",
221 __FUNCTION__, wcd->window_name, GetLastError());
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700222 _camera_device_free(wcd);
223 return NULL;
224 }
225
226 return &wcd->header;
227}
228
229int
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700230camera_device_start_capturing(CameraDevice* cd,
231 uint32_t pixel_format,
232 int frame_width,
233 int frame_height)
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700234{
235 WndCameraDevice* wcd;
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700236 HBITMAP bm_handle;
237 BITMAP bitmap;
238 size_t format_info_size;
239
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700240 if (cd == NULL || cd->opaque == NULL) {
241 E("%s: Invalid camera device descriptor", __FUNCTION__);
242 return -1;
243 }
244 wcd = (WndCameraDevice*)cd->opaque;
245
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700246 /* wcd->dc is an indicator of capturing: !NULL - capturing, NULL - not */
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700247 if (wcd->dc != NULL) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700248 W("%s: Capturing is already on on device '%s'",
249 __FUNCTION__, wcd->window_name);
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700250 return 0;
251 }
252
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700253 /* Connect capture window to the video capture driver. */
254 if (!capDriverConnect(wcd->cap_window, wcd->input_channel)) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700255 return -1;
256 }
257
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700258 /* Get current frame information from the driver. */
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700259 format_info_size = capGetVideoFormatSize(wcd->cap_window);
260 if (format_info_size == 0) {
261 E("%s: Unable to get video format size: %d",
262 __FUNCTION__, GetLastError());
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700263 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700264 return -1;
265 }
266 wcd->frame_bitmap = (BITMAPINFO*)malloc(format_info_size);
267 if (wcd->frame_bitmap == NULL) {
268 E("%s: Unable to allocate frame bitmap info buffer", __FUNCTION__);
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700269 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700270 return -1;
271 }
272 if (!capGetVideoFormat(wcd->cap_window, wcd->frame_bitmap,
273 format_info_size)) {
274 E("%s: Unable to obtain video format: %d", __FUNCTION__, GetLastError());
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700275 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700276 return -1;
277 }
278
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700279 /* Lets see if we need to set different frame dimensions */
280 if (wcd->frame_bitmap->bmiHeader.biWidth != frame_width ||
281 abs(wcd->frame_bitmap->bmiHeader.biHeight) != frame_height) {
282 /* Dimensions don't match. Set new frame info. */
283 wcd->frame_bitmap->bmiHeader.biWidth = frame_width;
284 wcd->frame_bitmap->bmiHeader.biHeight = frame_height;
285 /* We need to recalculate image size, since the capture window / driver
286 * will use image size provided by us. */
287 if (wcd->frame_bitmap->bmiHeader.biBitCount == 24) {
288 /* Special case that may require WORD boundary alignment. */
289 uint32_t bpl = (frame_width * 3 + 1) & ~1;
290 wcd->frame_bitmap->bmiHeader.biSizeImage = bpl * frame_height;
291 } else {
292 wcd->frame_bitmap->bmiHeader.biSizeImage =
293 (frame_width * frame_height * wcd->frame_bitmap->bmiHeader.biBitCount) / 8;
294 }
295 if (!capSetVideoFormat(wcd->cap_window, wcd->frame_bitmap,
296 format_info_size)) {
297 E("%s: Unable to set video format: %d", __FUNCTION__, GetLastError());
298 _camera_device_reset(wcd);
299 return -1;
300 }
301 }
302
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700303 if (wcd->frame_bitmap->bmiHeader.biCompression > BI_PNG) {
304 D("%s: Video capturing driver has reported pixel format %.4s",
305 __FUNCTION__, (const char*)&wcd->frame_bitmap->bmiHeader.biCompression);
306 }
307
308 /* Most of the time frame bitmaps come in "bottom-up" form, where its origin
309 * is the lower-left corner. However, it could be in the normal "top-down"
310 * form with the origin in the upper-left corner. So, we must adjust the
311 * biHeight field, since the way "top-down" form is reported here is by
312 * setting biHeight to a negative value. */
313 if (wcd->frame_bitmap->bmiHeader.biHeight < 0) {
314 wcd->frame_bitmap->bmiHeader.biHeight =
315 -wcd->frame_bitmap->bmiHeader.biHeight;
316 wcd->is_top_down = 1;
317 } else {
318 wcd->is_top_down = 0;
319 }
320
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700321 /* Get DC for the capturing window that will be used when we deal with
322 * bitmaps obtained from the camera device during frame capturing. */
323 wcd->dc = GetDC(wcd->cap_window);
324 if (wcd->dc == NULL) {
325 E("%s: Unable to obtain DC for %s: %d",
326 __FUNCTION__, wcd->window_name, GetLastError());
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700327 _camera_device_reset(wcd);
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700328 return -1;
329 }
330
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700331 /*
332 * At this point we need to grab a frame to properly setup framebuffer, and
333 * calculate pixel format. The problem is that bitmap information obtained
334 * from the driver doesn't necessarily match the actual bitmap we're going to
335 * obtain via capGrabFrame / capEditCopy / GetClipboardData
336 */
337
338 /* Grab a frame, and post it to the clipboard. Not very effective, but this
339 * is how capXxx API is operating. */
340 if (!capGrabFrameNoStop(wcd->cap_window) ||
341 !capEditCopy(wcd->cap_window) ||
342 !OpenClipboard(wcd->cap_window)) {
343 E("%s: Device '%s' is unable to save frame to the clipboard: %d",
344 __FUNCTION__, wcd->window_name, GetLastError());
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700345 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700346 return -1;
347 }
348
349 /* Get bitmap handle saved into clipboard. Note that bitmap is still
350 * owned by the clipboard here! */
351 bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP);
352 if (bm_handle == NULL) {
353 E("%s: Device '%s' is unable to obtain frame from the clipboard: %d",
354 __FUNCTION__, wcd->window_name, GetLastError());
355 CloseClipboard();
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700356 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700357 return -1;
358 }
359
360 /* Get bitmap object that is initialized with the actual bitmap info. */
361 if (!GetObject(bm_handle, sizeof(BITMAP), &bitmap)) {
362 E("%s: Device '%s' is unable to obtain frame's bitmap: %d",
363 __FUNCTION__, wcd->window_name, GetLastError());
364 CloseClipboard();
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700365 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700366 return -1;
367 }
368
369 /* Now that we have all we need in 'bitmap' */
370 CloseClipboard();
371
372 /* Make sure that dimensions match. Othewise - fail. */
373 if (wcd->frame_bitmap->bmiHeader.biWidth != bitmap.bmWidth ||
374 wcd->frame_bitmap->bmiHeader.biHeight != bitmap.bmHeight ) {
375 E("%s: Requested dimensions %dx%d do not match the actual %dx%d",
376 __FUNCTION__, frame_width, frame_height,
377 wcd->frame_bitmap->bmiHeader.biWidth,
378 wcd->frame_bitmap->bmiHeader.biHeight);
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700379 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700380 return -1;
381 }
382
383 /* Create bitmap info that will be used with GetDIBits. */
384 wcd->gdi_bitmap = (BITMAPINFO*)malloc(wcd->frame_bitmap->bmiHeader.biSize);
385 if (wcd->gdi_bitmap == NULL) {
386 E("%s: Unable to allocate gdi bitmap info", __FUNCTION__);
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700387 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700388 return -1;
389 }
390 memcpy(wcd->gdi_bitmap, wcd->frame_bitmap,
391 wcd->frame_bitmap->bmiHeader.biSize);
392 wcd->gdi_bitmap->bmiHeader.biCompression = BI_RGB;
393 wcd->gdi_bitmap->bmiHeader.biBitCount = bitmap.bmBitsPixel;
394 wcd->gdi_bitmap->bmiHeader.biSizeImage = bitmap.bmWidthBytes * bitmap.bmWidth;
395 /* Adjust GDI's bitmap biHeight for proper frame direction ("top-down", or
396 * "bottom-up") We do this trick in order to simplify pixel format conversion
397 * routines, where we always assume "top-down" frames. The trick he is to
398 * have negative biHeight in 'gdi_bitmap' if driver provides "bottom-up"
399 * frames, and positive biHeight in 'gdi_bitmap' if driver provides "top-down"
400 * frames. This way GetGDIBits will always return "top-down" frames. */
401 if (wcd->is_top_down) {
402 wcd->gdi_bitmap->bmiHeader.biHeight =
403 wcd->frame_bitmap->bmiHeader.biHeight;
404 } else {
405 wcd->gdi_bitmap->bmiHeader.biHeight =
406 -wcd->frame_bitmap->bmiHeader.biHeight;
407 }
408
409 /* Allocate framebuffer. */
410 wcd->framebuffer = (uint8_t*)malloc(wcd->gdi_bitmap->bmiHeader.biSizeImage);
411 if (wcd->framebuffer == NULL) {
412 E("%s: Unable to allocate %d bytes for framebuffer",
413 __FUNCTION__, wcd->gdi_bitmap->bmiHeader.biSizeImage);
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700414 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700415 return -1;
416 }
417
418 /* Lets see what pixel format we will use. */
419 if (wcd->gdi_bitmap->bmiHeader.biBitCount == 16) {
420 wcd->pixel_format = V4L2_PIX_FMT_RGB565;
421 } else if (wcd->gdi_bitmap->bmiHeader.biBitCount == 24) {
Vladimir Chtchetkine6ef999d2011-09-21 16:22:40 -0700422 wcd->pixel_format = V4L2_PIX_FMT_BGR24;
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700423 } else if (wcd->gdi_bitmap->bmiHeader.biBitCount == 32) {
Vladimir Chtchetkine6ef999d2011-09-21 16:22:40 -0700424 wcd->pixel_format = V4L2_PIX_FMT_BGR32;
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700425 } else {
426 E("%s: Unsupported number of bits per pixel %d",
427 __FUNCTION__, wcd->gdi_bitmap->bmiHeader.biBitCount);
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700428 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700429 return -1;
430 }
431
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700432 D("%s: Capturing device '%s': %d bits per pixel in %.4s [%dx%d] frame",
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700433 __FUNCTION__, wcd->window_name, wcd->gdi_bitmap->bmiHeader.biBitCount,
434 (const char*)&wcd->pixel_format, wcd->frame_bitmap->bmiHeader.biWidth,
435 wcd->frame_bitmap->bmiHeader.biHeight);
436
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700437 return 0;
438}
439
440int
441camera_device_stop_capturing(CameraDevice* cd)
442{
443 WndCameraDevice* wcd;
444 if (cd == NULL || cd->opaque == NULL) {
445 E("%s: Invalid camera device descriptor", __FUNCTION__);
446 return -1;
447 }
448 wcd = (WndCameraDevice*)cd->opaque;
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700449
450 /* wcd->dc is the indicator of capture. */
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700451 if (wcd->dc == NULL) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700452 W("%s: Device '%s' is not capturing video",
453 __FUNCTION__, wcd->window_name);
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700454 return 0;
455 }
456 ReleaseDC(wcd->cap_window, wcd->dc);
457 wcd->dc = NULL;
458
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700459 /* Reset the device in preparation for the next capture. */
460 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700461
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700462 return 0;
463}
464
465int
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700466camera_device_read_frame(CameraDevice* cd,
467 ClientFrameBuffer* framebuffers,
Vladimir Chtchetkine37fb84f2011-11-23 13:03:37 -0800468 int fbs_num,
469 float r_scale,
470 float g_scale,
471 float b_scale,
472 float exp_comp)
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700473{
474 WndCameraDevice* wcd;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700475 HBITMAP bm_handle;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700476
477 /* Sanity checks. */
478 if (cd == NULL || cd->opaque == NULL) {
479 E("%s: Invalid camera device descriptor", __FUNCTION__);
480 return -1;
481 }
482 wcd = (WndCameraDevice*)cd->opaque;
483 if (wcd->dc == NULL) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700484 W("%s: Device '%s' is not captuing video",
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700485 __FUNCTION__, wcd->window_name);
486 return -1;
487 }
488
489 /* Grab a frame, and post it to the clipboard. Not very effective, but this
490 * is how capXxx API is operating. */
491 if (!capGrabFrameNoStop(wcd->cap_window) ||
492 !capEditCopy(wcd->cap_window) ||
493 !OpenClipboard(wcd->cap_window)) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700494 E("%s: Device '%s' is unable to save frame to the clipboard: %d",
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700495 __FUNCTION__, wcd->window_name, GetLastError());
496 return -1;
497 }
498
499 /* Get bitmap handle saved into clipboard. Note that bitmap is still
500 * owned by the clipboard here! */
501 bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP);
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700502 if (bm_handle == NULL) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700503 E("%s: Device '%s' is unable to obtain frame from the clipboard: %d",
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700504 __FUNCTION__, wcd->window_name, GetLastError());
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700505 CloseClipboard();
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700506 return -1;
507 }
508
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700509 /* Get bitmap buffer. */
510 if (wcd->gdi_bitmap->bmiHeader.biHeight > 0) {
511 wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700512 }
513
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700514 if (!GetDIBits(wcd->dc, bm_handle, 0, wcd->frame_bitmap->bmiHeader.biHeight,
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700515 wcd->framebuffer, wcd->gdi_bitmap, DIB_RGB_COLORS)) {
516 E("%s: Device '%s' is unable to transfer frame to the framebuffer: %d",
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700517 __FUNCTION__, wcd->window_name, GetLastError());
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700518 CloseClipboard();
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700519 return -1;
520 }
521
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700522 if (wcd->gdi_bitmap->bmiHeader.biHeight < 0) {
523 wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700524 }
525
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700526 CloseClipboard();
527
528 /* Convert framebuffer. */
529 return convert_frame(wcd->framebuffer,
530 wcd->pixel_format,
531 wcd->gdi_bitmap->bmiHeader.biSizeImage,
532 wcd->frame_bitmap->bmiHeader.biWidth,
533 wcd->frame_bitmap->bmiHeader.biHeight,
Vladimir Chtchetkine37fb84f2011-11-23 13:03:37 -0800534 framebuffers, fbs_num,
535 r_scale, g_scale, b_scale, exp_comp);
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700536}
537
538void
539camera_device_close(CameraDevice* cd)
540{
541 /* Sanity checks. */
542 if (cd == NULL || cd->opaque == NULL) {
543 E("%s: Invalid camera device descriptor", __FUNCTION__);
544 } else {
545 WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque;
546 _camera_device_free(wcd);
547 }
548}
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700549
550int
551enumerate_camera_devices(CameraInfo* cis, int max)
552{
Vladimir Chtchetkine8d9a7a52011-11-15 09:17:29 -0800553/* Array containing emulated webcam frame dimensions.
554 * capXxx API provides device independent frame dimensions, by scaling frames
555 * received from the device to whatever dimensions were requested by the user.
556 * So, we can just use a small set of frame dimensions to emulate.
557 */
558static const CameraFrameDim _emulate_dims[] =
559{
560 /* Emulates 640x480 frame. */
561 {640, 480},
562 /* Emulates 352x288 frame (required by camera framework). */
563 {352, 288},
Vladimir Chtchetkinee080a452011-11-15 13:47:39 -0800564 /* Emulates 320x240 frame (required by camera framework). */
565 {320, 240},
Vladimir Chtchetkine8d9a7a52011-11-15 09:17:29 -0800566 /* Emulates 176x144 frame (required by camera framework). */
567 {176, 144}
568};
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700569 int inp_channel, found = 0;
570
571 for (inp_channel = 0; inp_channel < 10 && found < max; inp_channel++) {
572 char name[256];
573 CameraDevice* cd;
574
575 snprintf(name, sizeof(name), "%s%d", _default_window_name, found);
576 cd = camera_device_open(name, inp_channel);
577 if (cd != NULL) {
578 WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque;
579
580 /* Unfortunately, on Windows we have to start capturing in order to get the
Vladimir Chtchetkine8d9a7a52011-11-15 09:17:29 -0800581 * actual frame properties. */
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700582 if (!camera_device_start_capturing(cd, V4L2_PIX_FMT_RGB32, 640, 480)) {
Vladimir Chtchetkine8d9a7a52011-11-15 09:17:29 -0800583 cis[found].frame_sizes = (CameraFrameDim*)malloc(sizeof(_emulate_dims));
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700584 if (cis[found].frame_sizes != NULL) {
Vladimir Chtchetkineb8dcaff2011-09-17 11:15:47 -0700585 char disp_name[24];
586 sprintf(disp_name, "webcam%d", found);
587 cis[found].display_name = ASTRDUP(disp_name);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700588 cis[found].device_name = ASTRDUP(name);
Vladimir Chtchetkineb8dcaff2011-09-17 11:15:47 -0700589 cis[found].direction = ASTRDUP("front");
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700590 cis[found].inp_channel = inp_channel;
Vladimir Chtchetkine8d9a7a52011-11-15 09:17:29 -0800591 cis[found].frame_sizes_num = sizeof(_emulate_dims) / sizeof(*_emulate_dims);
592 memcpy(cis[found].frame_sizes, _emulate_dims, sizeof(_emulate_dims));
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700593 cis[found].pixel_format = wcd->pixel_format;
594 cis[found].in_use = 0;
595 found++;
596 } else {
597 E("%s: Unable to allocate dimensions", __FUNCTION__);
598 }
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700599 camera_device_stop_capturing(cd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700600 } else {
601 /* No more cameras. */
602 camera_device_close(cd);
603 break;
604 }
605 camera_device_close(cd);
606 } else {
607 /* No more cameras. */
608 break;
609 }
610 }
611
612 return found;
613}