blob: fff078e3b3223fd6121609ecdce8238d8093fd7c [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
26#define D(...) VERBOSE_PRINT(camera,__VA_ARGS__)
27#define W(...) VERBOSE_PRINT(camera,__VA_ARGS__)
28#define E(...) VERBOSE_PRINT(camera,__VA_ARGS__)
29#define D_ACTIVE VERBOSE_CHECK(camera)
30
31/* the T(...) macro is used to dump traffic */
32#define T_ACTIVE 0
33
34#if T_ACTIVE
35#define T(...) VERBOSE_PRINT(camera,__VA_ARGS__)
36#else
37#define T(...) ((void)0)
38#endif
39
40/* Default name for the capture window. */
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)) {
255 E("%s: Unable to connect to the video capturing driver #%d: %d",
256 __FUNCTION__, wcd->input_channel, GetLastError());
257 return -1;
258 }
259
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700260 /* Get current frame information from the driver. */
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700261 format_info_size = capGetVideoFormatSize(wcd->cap_window);
262 if (format_info_size == 0) {
263 E("%s: Unable to get video format size: %d",
264 __FUNCTION__, GetLastError());
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700265 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700266 return -1;
267 }
268 wcd->frame_bitmap = (BITMAPINFO*)malloc(format_info_size);
269 if (wcd->frame_bitmap == NULL) {
270 E("%s: Unable to allocate frame bitmap info buffer", __FUNCTION__);
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700271 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700272 return -1;
273 }
274 if (!capGetVideoFormat(wcd->cap_window, wcd->frame_bitmap,
275 format_info_size)) {
276 E("%s: Unable to obtain video format: %d", __FUNCTION__, GetLastError());
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700277 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700278 return -1;
279 }
280
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700281 /* Lets see if we need to set different frame dimensions */
282 if (wcd->frame_bitmap->bmiHeader.biWidth != frame_width ||
283 abs(wcd->frame_bitmap->bmiHeader.biHeight) != frame_height) {
284 /* Dimensions don't match. Set new frame info. */
285 wcd->frame_bitmap->bmiHeader.biWidth = frame_width;
286 wcd->frame_bitmap->bmiHeader.biHeight = frame_height;
287 /* We need to recalculate image size, since the capture window / driver
288 * will use image size provided by us. */
289 if (wcd->frame_bitmap->bmiHeader.biBitCount == 24) {
290 /* Special case that may require WORD boundary alignment. */
291 uint32_t bpl = (frame_width * 3 + 1) & ~1;
292 wcd->frame_bitmap->bmiHeader.biSizeImage = bpl * frame_height;
293 } else {
294 wcd->frame_bitmap->bmiHeader.biSizeImage =
295 (frame_width * frame_height * wcd->frame_bitmap->bmiHeader.biBitCount) / 8;
296 }
297 if (!capSetVideoFormat(wcd->cap_window, wcd->frame_bitmap,
298 format_info_size)) {
299 E("%s: Unable to set video format: %d", __FUNCTION__, GetLastError());
300 _camera_device_reset(wcd);
301 return -1;
302 }
303 }
304
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700305 if (wcd->frame_bitmap->bmiHeader.biCompression > BI_PNG) {
306 D("%s: Video capturing driver has reported pixel format %.4s",
307 __FUNCTION__, (const char*)&wcd->frame_bitmap->bmiHeader.biCompression);
308 }
309
310 /* Most of the time frame bitmaps come in "bottom-up" form, where its origin
311 * is the lower-left corner. However, it could be in the normal "top-down"
312 * form with the origin in the upper-left corner. So, we must adjust the
313 * biHeight field, since the way "top-down" form is reported here is by
314 * setting biHeight to a negative value. */
315 if (wcd->frame_bitmap->bmiHeader.biHeight < 0) {
316 wcd->frame_bitmap->bmiHeader.biHeight =
317 -wcd->frame_bitmap->bmiHeader.biHeight;
318 wcd->is_top_down = 1;
319 } else {
320 wcd->is_top_down = 0;
321 }
322
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700323 /* Get DC for the capturing window that will be used when we deal with
324 * bitmaps obtained from the camera device during frame capturing. */
325 wcd->dc = GetDC(wcd->cap_window);
326 if (wcd->dc == NULL) {
327 E("%s: Unable to obtain DC for %s: %d",
328 __FUNCTION__, wcd->window_name, GetLastError());
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700329 _camera_device_reset(wcd);
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700330 return -1;
331 }
332
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700333 /*
334 * At this point we need to grab a frame to properly setup framebuffer, and
335 * calculate pixel format. The problem is that bitmap information obtained
336 * from the driver doesn't necessarily match the actual bitmap we're going to
337 * obtain via capGrabFrame / capEditCopy / GetClipboardData
338 */
339
340 /* Grab a frame, and post it to the clipboard. Not very effective, but this
341 * is how capXxx API is operating. */
342 if (!capGrabFrameNoStop(wcd->cap_window) ||
343 !capEditCopy(wcd->cap_window) ||
344 !OpenClipboard(wcd->cap_window)) {
345 E("%s: Device '%s' is unable to save frame to the clipboard: %d",
346 __FUNCTION__, wcd->window_name, GetLastError());
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700347 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700348 return -1;
349 }
350
351 /* Get bitmap handle saved into clipboard. Note that bitmap is still
352 * owned by the clipboard here! */
353 bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP);
354 if (bm_handle == NULL) {
355 E("%s: Device '%s' is unable to obtain frame from the clipboard: %d",
356 __FUNCTION__, wcd->window_name, GetLastError());
357 CloseClipboard();
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700358 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700359 return -1;
360 }
361
362 /* Get bitmap object that is initialized with the actual bitmap info. */
363 if (!GetObject(bm_handle, sizeof(BITMAP), &bitmap)) {
364 E("%s: Device '%s' is unable to obtain frame's bitmap: %d",
365 __FUNCTION__, wcd->window_name, GetLastError());
366 CloseClipboard();
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700367 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700368 return -1;
369 }
370
371 /* Now that we have all we need in 'bitmap' */
372 CloseClipboard();
373
374 /* Make sure that dimensions match. Othewise - fail. */
375 if (wcd->frame_bitmap->bmiHeader.biWidth != bitmap.bmWidth ||
376 wcd->frame_bitmap->bmiHeader.biHeight != bitmap.bmHeight ) {
377 E("%s: Requested dimensions %dx%d do not match the actual %dx%d",
378 __FUNCTION__, frame_width, frame_height,
379 wcd->frame_bitmap->bmiHeader.biWidth,
380 wcd->frame_bitmap->bmiHeader.biHeight);
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700381 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700382 return -1;
383 }
384
385 /* Create bitmap info that will be used with GetDIBits. */
386 wcd->gdi_bitmap = (BITMAPINFO*)malloc(wcd->frame_bitmap->bmiHeader.biSize);
387 if (wcd->gdi_bitmap == NULL) {
388 E("%s: Unable to allocate gdi bitmap info", __FUNCTION__);
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700389 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700390 return -1;
391 }
392 memcpy(wcd->gdi_bitmap, wcd->frame_bitmap,
393 wcd->frame_bitmap->bmiHeader.biSize);
394 wcd->gdi_bitmap->bmiHeader.biCompression = BI_RGB;
395 wcd->gdi_bitmap->bmiHeader.biBitCount = bitmap.bmBitsPixel;
396 wcd->gdi_bitmap->bmiHeader.biSizeImage = bitmap.bmWidthBytes * bitmap.bmWidth;
397 /* Adjust GDI's bitmap biHeight for proper frame direction ("top-down", or
398 * "bottom-up") We do this trick in order to simplify pixel format conversion
399 * routines, where we always assume "top-down" frames. The trick he is to
400 * have negative biHeight in 'gdi_bitmap' if driver provides "bottom-up"
401 * frames, and positive biHeight in 'gdi_bitmap' if driver provides "top-down"
402 * frames. This way GetGDIBits will always return "top-down" frames. */
403 if (wcd->is_top_down) {
404 wcd->gdi_bitmap->bmiHeader.biHeight =
405 wcd->frame_bitmap->bmiHeader.biHeight;
406 } else {
407 wcd->gdi_bitmap->bmiHeader.biHeight =
408 -wcd->frame_bitmap->bmiHeader.biHeight;
409 }
410
411 /* Allocate framebuffer. */
412 wcd->framebuffer = (uint8_t*)malloc(wcd->gdi_bitmap->bmiHeader.biSizeImage);
413 if (wcd->framebuffer == NULL) {
414 E("%s: Unable to allocate %d bytes for framebuffer",
415 __FUNCTION__, wcd->gdi_bitmap->bmiHeader.biSizeImage);
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700416 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700417 return -1;
418 }
419
420 /* Lets see what pixel format we will use. */
421 if (wcd->gdi_bitmap->bmiHeader.biBitCount == 16) {
422 wcd->pixel_format = V4L2_PIX_FMT_RGB565;
423 } else if (wcd->gdi_bitmap->bmiHeader.biBitCount == 24) {
Vladimir Chtchetkine6ef999d2011-09-21 16:22:40 -0700424 wcd->pixel_format = V4L2_PIX_FMT_BGR24;
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700425 } else if (wcd->gdi_bitmap->bmiHeader.biBitCount == 32) {
Vladimir Chtchetkine6ef999d2011-09-21 16:22:40 -0700426 wcd->pixel_format = V4L2_PIX_FMT_BGR32;
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700427 } else {
428 E("%s: Unsupported number of bits per pixel %d",
429 __FUNCTION__, wcd->gdi_bitmap->bmiHeader.biBitCount);
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700430 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700431 return -1;
432 }
433
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700434 D("%s: Capturing device '%s': %d bits per pixel in %.4s [%dx%d] frame",
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700435 __FUNCTION__, wcd->window_name, wcd->gdi_bitmap->bmiHeader.biBitCount,
436 (const char*)&wcd->pixel_format, wcd->frame_bitmap->bmiHeader.biWidth,
437 wcd->frame_bitmap->bmiHeader.biHeight);
438
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700439 return 0;
440}
441
442int
443camera_device_stop_capturing(CameraDevice* cd)
444{
445 WndCameraDevice* wcd;
446 if (cd == NULL || cd->opaque == NULL) {
447 E("%s: Invalid camera device descriptor", __FUNCTION__);
448 return -1;
449 }
450 wcd = (WndCameraDevice*)cd->opaque;
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700451
452 /* wcd->dc is the indicator of capture. */
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700453 if (wcd->dc == NULL) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700454 W("%s: Device '%s' is not capturing video",
455 __FUNCTION__, wcd->window_name);
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700456 return 0;
457 }
458 ReleaseDC(wcd->cap_window, wcd->dc);
459 wcd->dc = NULL;
460
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700461 /* Reset the device in preparation for the next capture. */
462 _camera_device_reset(wcd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700463
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700464 return 0;
465}
466
467int
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700468camera_device_read_frame(CameraDevice* cd,
469 ClientFrameBuffer* framebuffers,
470 int fbs_num)
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700471{
472 WndCameraDevice* wcd;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700473 HBITMAP bm_handle;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700474
475 /* Sanity checks. */
476 if (cd == NULL || cd->opaque == NULL) {
477 E("%s: Invalid camera device descriptor", __FUNCTION__);
478 return -1;
479 }
480 wcd = (WndCameraDevice*)cd->opaque;
481 if (wcd->dc == NULL) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700482 W("%s: Device '%s' is not captuing video",
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700483 __FUNCTION__, wcd->window_name);
484 return -1;
485 }
486
487 /* Grab a frame, and post it to the clipboard. Not very effective, but this
488 * is how capXxx API is operating. */
489 if (!capGrabFrameNoStop(wcd->cap_window) ||
490 !capEditCopy(wcd->cap_window) ||
491 !OpenClipboard(wcd->cap_window)) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700492 E("%s: Device '%s' is unable to save frame to the clipboard: %d",
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700493 __FUNCTION__, wcd->window_name, GetLastError());
494 return -1;
495 }
496
497 /* Get bitmap handle saved into clipboard. Note that bitmap is still
498 * owned by the clipboard here! */
499 bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP);
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700500 if (bm_handle == NULL) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700501 E("%s: Device '%s' is unable to obtain frame from the clipboard: %d",
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700502 __FUNCTION__, wcd->window_name, GetLastError());
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700503 CloseClipboard();
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700504 return -1;
505 }
506
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700507 /* Get bitmap buffer. */
508 if (wcd->gdi_bitmap->bmiHeader.biHeight > 0) {
509 wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700510 }
511
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700512 if (!GetDIBits(wcd->dc, bm_handle, 0, wcd->frame_bitmap->bmiHeader.biHeight,
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700513 wcd->framebuffer, wcd->gdi_bitmap, DIB_RGB_COLORS)) {
514 E("%s: Device '%s' is unable to transfer frame to the framebuffer: %d",
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700515 __FUNCTION__, wcd->window_name, GetLastError());
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700516 CloseClipboard();
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700517 return -1;
518 }
519
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700520 if (wcd->gdi_bitmap->bmiHeader.biHeight < 0) {
521 wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700522 }
523
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700524 CloseClipboard();
525
526 /* Convert framebuffer. */
527 return convert_frame(wcd->framebuffer,
528 wcd->pixel_format,
529 wcd->gdi_bitmap->bmiHeader.biSizeImage,
530 wcd->frame_bitmap->bmiHeader.biWidth,
531 wcd->frame_bitmap->bmiHeader.biHeight,
532 framebuffers, fbs_num);
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700533}
534
535void
536camera_device_close(CameraDevice* cd)
537{
538 /* Sanity checks. */
539 if (cd == NULL || cd->opaque == NULL) {
540 E("%s: Invalid camera device descriptor", __FUNCTION__);
541 } else {
542 WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque;
543 _camera_device_free(wcd);
544 }
545}
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700546
547int
548enumerate_camera_devices(CameraInfo* cis, int max)
549{
550 int inp_channel, found = 0;
551
552 for (inp_channel = 0; inp_channel < 10 && found < max; inp_channel++) {
553 char name[256];
554 CameraDevice* cd;
555
556 snprintf(name, sizeof(name), "%s%d", _default_window_name, found);
557 cd = camera_device_open(name, inp_channel);
558 if (cd != NULL) {
559 WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque;
560
561 /* Unfortunately, on Windows we have to start capturing in order to get the
562 * actual frame properties. Note that on Windows camera_device_start_capturing
563 * will ignore the pixel format parameter, since it will be determined during
564 * the course of the routine. Also note that on Windows all frames will be
565 * 640x480. */
566 if (!camera_device_start_capturing(cd, V4L2_PIX_FMT_RGB32, 640, 480)) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700567 /* capXxx API supports only single frame size (always observed 640x480,
568 * but the actual numbers may vary). */
569 cis[found].frame_sizes = (CameraFrameDim*)malloc(sizeof(CameraFrameDim));
570 if (cis[found].frame_sizes != NULL) {
Vladimir Chtchetkineb8dcaff2011-09-17 11:15:47 -0700571 char disp_name[24];
572 sprintf(disp_name, "webcam%d", found);
573 cis[found].display_name = ASTRDUP(disp_name);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700574 cis[found].device_name = ASTRDUP(name);
Vladimir Chtchetkineb8dcaff2011-09-17 11:15:47 -0700575 cis[found].direction = ASTRDUP("front");
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700576 cis[found].inp_channel = inp_channel;
577 cis[found].frame_sizes->width = wcd->frame_bitmap->bmiHeader.biWidth;
578 cis[found].frame_sizes->height = wcd->frame_bitmap->bmiHeader.biHeight;
579 cis[found].frame_sizes_num = 1;
580 cis[found].pixel_format = wcd->pixel_format;
581 cis[found].in_use = 0;
582 found++;
583 } else {
584 E("%s: Unable to allocate dimensions", __FUNCTION__);
585 }
Vladimir Chtchetkinea83cf902011-09-22 08:51:59 -0700586 camera_device_stop_capturing(cd);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700587 } else {
588 /* No more cameras. */
589 camera_device_close(cd);
590 break;
591 }
592 camera_device_close(cd);
593 } else {
594 /* No more cameras. */
595 break;
596 }
597 }
598
599 return found;
600}