blob: 3d85aef47622426828f839bdf80086b4d1e61085 [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 Chtchetkine4ed09fd2011-08-18 09:42:40 -0700148/*******************************************************************************
149 * CameraDevice API
150 ******************************************************************************/
151
152CameraDevice*
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700153camera_device_open(const char* name, int inp_channel)
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700154{
155 WndCameraDevice* wcd;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700156
157 /* Allocate descriptor and initialize windows-specific fields. */
158 wcd = _camera_device_alloc();
159 if (wcd == NULL) {
160 E("%s: Unable to allocate WndCameraDevice instance", __FUNCTION__);
161 return NULL;
162 }
163 wcd->window_name = (name != NULL) ? ASTRDUP(name) :
164 ASTRDUP(_default_window_name);
165 if (wcd->window_name == NULL) {
166 E("%s: Unable to save window name", __FUNCTION__);
167 _camera_device_free(wcd);
168 return NULL;
169 }
170 wcd->input_channel = inp_channel;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700171
172 /* Create capture window that is a child of HWND_MESSAGE window.
173 * We make it invisible, so it doesn't mess with the UI. Also
174 * note that we supply standard HWND_MESSAGE window handle as
175 * the parent window, since we don't want video capturing
176 * machinery to be dependent on the details of our UI. */
177 wcd->cap_window = capCreateCaptureWindow(wcd->window_name, WS_CHILD, 0, 0,
178 0, 0, HWND_MESSAGE, 1);
179 if (wcd->cap_window == NULL) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700180 E("%s: Unable to create video capturing window '%s': %d",
181 __FUNCTION__, wcd->window_name, GetLastError());
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700182 _camera_device_free(wcd);
183 return NULL;
184 }
185
186 return &wcd->header;
187}
188
189int
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700190camera_device_start_capturing(CameraDevice* cd,
191 uint32_t pixel_format,
192 int frame_width,
193 int frame_height)
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700194{
195 WndCameraDevice* wcd;
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700196 HBITMAP bm_handle;
197 BITMAP bitmap;
198 size_t format_info_size;
199
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700200 if (cd == NULL || cd->opaque == NULL) {
201 E("%s: Invalid camera device descriptor", __FUNCTION__);
202 return -1;
203 }
204 wcd = (WndCameraDevice*)cd->opaque;
205
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700206 /* wcd->dc is an indicator of capturing: !NULL - capturing, NULL - not */
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700207 if (wcd->dc != NULL) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700208 W("%s: Capturing is already on on device '%s'",
209 __FUNCTION__, wcd->window_name);
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700210 return 0;
211 }
212
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700213 /* Connect capture window to the video capture driver. */
214 if (!capDriverConnect(wcd->cap_window, wcd->input_channel)) {
215 E("%s: Unable to connect to the video capturing driver #%d: %d",
216 __FUNCTION__, wcd->input_channel, GetLastError());
217 return -1;
218 }
219
220 /* Get frame information from the driver. */
221 format_info_size = capGetVideoFormatSize(wcd->cap_window);
222 if (format_info_size == 0) {
223 E("%s: Unable to get video format size: %d",
224 __FUNCTION__, GetLastError());
225 return -1;
226 }
227 wcd->frame_bitmap = (BITMAPINFO*)malloc(format_info_size);
228 if (wcd->frame_bitmap == NULL) {
229 E("%s: Unable to allocate frame bitmap info buffer", __FUNCTION__);
230 return -1;
231 }
232 if (!capGetVideoFormat(wcd->cap_window, wcd->frame_bitmap,
233 format_info_size)) {
234 E("%s: Unable to obtain video format: %d", __FUNCTION__, GetLastError());
235 return -1;
236 }
237
238 if (wcd->frame_bitmap->bmiHeader.biCompression > BI_PNG) {
239 D("%s: Video capturing driver has reported pixel format %.4s",
240 __FUNCTION__, (const char*)&wcd->frame_bitmap->bmiHeader.biCompression);
241 }
242
243 /* Most of the time frame bitmaps come in "bottom-up" form, where its origin
244 * is the lower-left corner. However, it could be in the normal "top-down"
245 * form with the origin in the upper-left corner. So, we must adjust the
246 * biHeight field, since the way "top-down" form is reported here is by
247 * setting biHeight to a negative value. */
248 if (wcd->frame_bitmap->bmiHeader.biHeight < 0) {
249 wcd->frame_bitmap->bmiHeader.biHeight =
250 -wcd->frame_bitmap->bmiHeader.biHeight;
251 wcd->is_top_down = 1;
252 } else {
253 wcd->is_top_down = 0;
254 }
255
256 /* Make sure that frame dimensions match. */
257 if (frame_width != wcd->frame_bitmap->bmiHeader.biWidth ||
258 frame_height != wcd->frame_bitmap->bmiHeader.biHeight) {
259 E("%s: Requested dimensions %dx%d do not match the actual %dx%d",
260 __FUNCTION__, frame_width, frame_height,
261 wcd->frame_bitmap->bmiHeader.biWidth,
262 wcd->frame_bitmap->bmiHeader.biHeight);
263 return -1;
264 }
265
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700266 /* Get DC for the capturing window that will be used when we deal with
267 * bitmaps obtained from the camera device during frame capturing. */
268 wcd->dc = GetDC(wcd->cap_window);
269 if (wcd->dc == NULL) {
270 E("%s: Unable to obtain DC for %s: %d",
271 __FUNCTION__, wcd->window_name, GetLastError());
272 return -1;
273 }
274
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700275 /*
276 * At this point we need to grab a frame to properly setup framebuffer, and
277 * calculate pixel format. The problem is that bitmap information obtained
278 * from the driver doesn't necessarily match the actual bitmap we're going to
279 * obtain via capGrabFrame / capEditCopy / GetClipboardData
280 */
281
282 /* Grab a frame, and post it to the clipboard. Not very effective, but this
283 * is how capXxx API is operating. */
284 if (!capGrabFrameNoStop(wcd->cap_window) ||
285 !capEditCopy(wcd->cap_window) ||
286 !OpenClipboard(wcd->cap_window)) {
287 E("%s: Device '%s' is unable to save frame to the clipboard: %d",
288 __FUNCTION__, wcd->window_name, GetLastError());
289 return -1;
290 }
291
292 /* Get bitmap handle saved into clipboard. Note that bitmap is still
293 * owned by the clipboard here! */
294 bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP);
295 if (bm_handle == NULL) {
296 E("%s: Device '%s' is unable to obtain frame from the clipboard: %d",
297 __FUNCTION__, wcd->window_name, GetLastError());
298 CloseClipboard();
299 return -1;
300 }
301
302 /* Get bitmap object that is initialized with the actual bitmap info. */
303 if (!GetObject(bm_handle, sizeof(BITMAP), &bitmap)) {
304 E("%s: Device '%s' is unable to obtain frame's bitmap: %d",
305 __FUNCTION__, wcd->window_name, GetLastError());
306 CloseClipboard();
307 return -1;
308 }
309
310 /* Now that we have all we need in 'bitmap' */
311 CloseClipboard();
312
313 /* Make sure that dimensions match. Othewise - fail. */
314 if (wcd->frame_bitmap->bmiHeader.biWidth != bitmap.bmWidth ||
315 wcd->frame_bitmap->bmiHeader.biHeight != bitmap.bmHeight ) {
316 E("%s: Requested dimensions %dx%d do not match the actual %dx%d",
317 __FUNCTION__, frame_width, frame_height,
318 wcd->frame_bitmap->bmiHeader.biWidth,
319 wcd->frame_bitmap->bmiHeader.biHeight);
320 return -1;
321 }
322
323 /* Create bitmap info that will be used with GetDIBits. */
324 wcd->gdi_bitmap = (BITMAPINFO*)malloc(wcd->frame_bitmap->bmiHeader.biSize);
325 if (wcd->gdi_bitmap == NULL) {
326 E("%s: Unable to allocate gdi bitmap info", __FUNCTION__);
327 return -1;
328 }
329 memcpy(wcd->gdi_bitmap, wcd->frame_bitmap,
330 wcd->frame_bitmap->bmiHeader.biSize);
331 wcd->gdi_bitmap->bmiHeader.biCompression = BI_RGB;
332 wcd->gdi_bitmap->bmiHeader.biBitCount = bitmap.bmBitsPixel;
333 wcd->gdi_bitmap->bmiHeader.biSizeImage = bitmap.bmWidthBytes * bitmap.bmWidth;
334 /* Adjust GDI's bitmap biHeight for proper frame direction ("top-down", or
335 * "bottom-up") We do this trick in order to simplify pixel format conversion
336 * routines, where we always assume "top-down" frames. The trick he is to
337 * have negative biHeight in 'gdi_bitmap' if driver provides "bottom-up"
338 * frames, and positive biHeight in 'gdi_bitmap' if driver provides "top-down"
339 * frames. This way GetGDIBits will always return "top-down" frames. */
340 if (wcd->is_top_down) {
341 wcd->gdi_bitmap->bmiHeader.biHeight =
342 wcd->frame_bitmap->bmiHeader.biHeight;
343 } else {
344 wcd->gdi_bitmap->bmiHeader.biHeight =
345 -wcd->frame_bitmap->bmiHeader.biHeight;
346 }
347
348 /* Allocate framebuffer. */
349 wcd->framebuffer = (uint8_t*)malloc(wcd->gdi_bitmap->bmiHeader.biSizeImage);
350 if (wcd->framebuffer == NULL) {
351 E("%s: Unable to allocate %d bytes for framebuffer",
352 __FUNCTION__, wcd->gdi_bitmap->bmiHeader.biSizeImage);
353 return -1;
354 }
355
356 /* Lets see what pixel format we will use. */
357 if (wcd->gdi_bitmap->bmiHeader.biBitCount == 16) {
358 wcd->pixel_format = V4L2_PIX_FMT_RGB565;
359 } else if (wcd->gdi_bitmap->bmiHeader.biBitCount == 24) {
Vladimir Chtchetkine6ef999d2011-09-21 16:22:40 -0700360 wcd->pixel_format = V4L2_PIX_FMT_BGR24;
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700361 } else if (wcd->gdi_bitmap->bmiHeader.biBitCount == 32) {
Vladimir Chtchetkine6ef999d2011-09-21 16:22:40 -0700362 wcd->pixel_format = V4L2_PIX_FMT_BGR32;
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700363 } else {
364 E("%s: Unsupported number of bits per pixel %d",
365 __FUNCTION__, wcd->gdi_bitmap->bmiHeader.biBitCount);
366 return -1;
367 }
368
369 D("%s: Capturing device '%s': %d bytes in %.4s [%dx%d] frame",
370 __FUNCTION__, wcd->window_name, wcd->gdi_bitmap->bmiHeader.biBitCount,
371 (const char*)&wcd->pixel_format, wcd->frame_bitmap->bmiHeader.biWidth,
372 wcd->frame_bitmap->bmiHeader.biHeight);
373
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700374 return 0;
375}
376
377int
378camera_device_stop_capturing(CameraDevice* cd)
379{
380 WndCameraDevice* wcd;
381 if (cd == NULL || cd->opaque == NULL) {
382 E("%s: Invalid camera device descriptor", __FUNCTION__);
383 return -1;
384 }
385 wcd = (WndCameraDevice*)cd->opaque;
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700386
387 /* wcd->dc is the indicator of capture. */
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700388 if (wcd->dc == NULL) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700389 W("%s: Device '%s' is not capturing video",
390 __FUNCTION__, wcd->window_name);
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700391 return 0;
392 }
393 ReleaseDC(wcd->cap_window, wcd->dc);
394 wcd->dc = NULL;
395
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700396 /* Disconnect from the driver. */
397 capDriverDisconnect(wcd->cap_window);
398
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700399 return 0;
400}
401
402int
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700403camera_device_read_frame(CameraDevice* cd,
404 ClientFrameBuffer* framebuffers,
405 int fbs_num)
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700406{
407 WndCameraDevice* wcd;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700408 HBITMAP bm_handle;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700409
410 /* Sanity checks. */
411 if (cd == NULL || cd->opaque == NULL) {
412 E("%s: Invalid camera device descriptor", __FUNCTION__);
413 return -1;
414 }
415 wcd = (WndCameraDevice*)cd->opaque;
416 if (wcd->dc == NULL) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700417 W("%s: Device '%s' is not captuing video",
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700418 __FUNCTION__, wcd->window_name);
419 return -1;
420 }
421
422 /* Grab a frame, and post it to the clipboard. Not very effective, but this
423 * is how capXxx API is operating. */
424 if (!capGrabFrameNoStop(wcd->cap_window) ||
425 !capEditCopy(wcd->cap_window) ||
426 !OpenClipboard(wcd->cap_window)) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700427 E("%s: Device '%s' is unable to save frame to the clipboard: %d",
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700428 __FUNCTION__, wcd->window_name, GetLastError());
429 return -1;
430 }
431
432 /* Get bitmap handle saved into clipboard. Note that bitmap is still
433 * owned by the clipboard here! */
434 bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP);
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700435 if (bm_handle == NULL) {
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700436 E("%s: Device '%s' is unable to obtain frame from the clipboard: %d",
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700437 __FUNCTION__, wcd->window_name, GetLastError());
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700438 CloseClipboard();
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700439 return -1;
440 }
441
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700442 /* Get bitmap buffer. */
443 if (wcd->gdi_bitmap->bmiHeader.biHeight > 0) {
444 wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700445 }
446
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700447 if (!GetDIBits(wcd->dc, bm_handle, 0, wcd->frame_bitmap->bmiHeader.biHeight,
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700448 wcd->framebuffer, wcd->gdi_bitmap, DIB_RGB_COLORS)) {
449 E("%s: Device '%s' is unable to transfer frame to the framebuffer: %d",
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700450 __FUNCTION__, wcd->window_name, GetLastError());
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700451 CloseClipboard();
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700452 return -1;
453 }
454
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700455 if (wcd->gdi_bitmap->bmiHeader.biHeight < 0) {
456 wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight;
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700457 }
458
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700459 CloseClipboard();
460
461 /* Convert framebuffer. */
462 return convert_frame(wcd->framebuffer,
463 wcd->pixel_format,
464 wcd->gdi_bitmap->bmiHeader.biSizeImage,
465 wcd->frame_bitmap->bmiHeader.biWidth,
466 wcd->frame_bitmap->bmiHeader.biHeight,
467 framebuffers, fbs_num);
Vladimir Chtchetkine4ed09fd2011-08-18 09:42:40 -0700468}
469
470void
471camera_device_close(CameraDevice* cd)
472{
473 /* Sanity checks. */
474 if (cd == NULL || cd->opaque == NULL) {
475 E("%s: Invalid camera device descriptor", __FUNCTION__);
476 } else {
477 WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque;
478 _camera_device_free(wcd);
479 }
480}
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700481
482int
483enumerate_camera_devices(CameraInfo* cis, int max)
484{
485 int inp_channel, found = 0;
486
487 for (inp_channel = 0; inp_channel < 10 && found < max; inp_channel++) {
488 char name[256];
489 CameraDevice* cd;
490
491 snprintf(name, sizeof(name), "%s%d", _default_window_name, found);
492 cd = camera_device_open(name, inp_channel);
493 if (cd != NULL) {
494 WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque;
495
496 /* Unfortunately, on Windows we have to start capturing in order to get the
497 * actual frame properties. Note that on Windows camera_device_start_capturing
498 * will ignore the pixel format parameter, since it will be determined during
499 * the course of the routine. Also note that on Windows all frames will be
500 * 640x480. */
501 if (!camera_device_start_capturing(cd, V4L2_PIX_FMT_RGB32, 640, 480)) {
502 camera_device_stop_capturing(cd);
503 /* capXxx API supports only single frame size (always observed 640x480,
504 * but the actual numbers may vary). */
505 cis[found].frame_sizes = (CameraFrameDim*)malloc(sizeof(CameraFrameDim));
506 if (cis[found].frame_sizes != NULL) {
Vladimir Chtchetkineb8dcaff2011-09-17 11:15:47 -0700507 char disp_name[24];
508 sprintf(disp_name, "webcam%d", found);
509 cis[found].display_name = ASTRDUP(disp_name);
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700510 cis[found].device_name = ASTRDUP(name);
Vladimir Chtchetkineb8dcaff2011-09-17 11:15:47 -0700511 cis[found].direction = ASTRDUP("front");
Vladimir Chtchetkinecf1c2c72011-09-04 09:51:40 -0700512 cis[found].inp_channel = inp_channel;
513 cis[found].frame_sizes->width = wcd->frame_bitmap->bmiHeader.biWidth;
514 cis[found].frame_sizes->height = wcd->frame_bitmap->bmiHeader.biHeight;
515 cis[found].frame_sizes_num = 1;
516 cis[found].pixel_format = wcd->pixel_format;
517 cis[found].in_use = 0;
518 found++;
519 } else {
520 E("%s: Unable to allocate dimensions", __FUNCTION__);
521 }
522 } else {
523 /* No more cameras. */
524 camera_device_close(cd);
525 break;
526 }
527 camera_device_close(cd);
528 } else {
529 /* No more cameras. */
530 break;
531 }
532 }
533
534 return found;
535}