blob: ac571ceb6ca1fa476299fae2bf8afdc689283b0e [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 */
21/*
22#include <stddef.h>
23#include <windows.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <tchar.h>
27*/
28#include "qemu-common.h"
29#include "android/utils/debug.h"
30#include "android/utils/misc.h"
31#include "android/utils/system.h"
32#include <vfw.h>
33#include "android/camera/camera-capture.h"
34#include "android/camera/camera-format-converters.h"
35
36#define D(...) VERBOSE_PRINT(camera,__VA_ARGS__)
37#define W(...) VERBOSE_PRINT(camera,__VA_ARGS__)
38#define E(...) VERBOSE_PRINT(camera,__VA_ARGS__)
39#define D_ACTIVE VERBOSE_CHECK(camera)
40
41/* the T(...) macro is used to dump traffic */
42#define T_ACTIVE 0
43
44#if T_ACTIVE
45#define T(...) VERBOSE_PRINT(camera,__VA_ARGS__)
46#else
47#define T(...) ((void)0)
48#endif
49
50/* Default name for the capture window. */
51static const char* _default_window_name = "AndroidEmulatorVC";
52
53typedef struct WndCameraDevice WndCameraDevice;
54/* Windows-specific camera device descriptor. */
55struct WndCameraDevice {
56 /* Common camera device descriptor. */
57 CameraDevice header;
58 /* Capture window name. (default is AndroidEmulatorVC) */
59 char* window_name;
60 /* Input channel (video driver index). (default is 0) */
61 int input_channel;
62 /* Requested pixel format. */
63 uint32_t req_pixel_format;
64
65 /*
66 * Set when framework gets initialized.
67 */
68
69 /* Video capturing window. Null indicates that device is not connected. */
70 HWND cap_window;
71 /* DC for frame bitmap manipulation. Null indicates that frames are not
72 * being capturing. */
73 HDC dc;
74 /* Bitmap info for the frames obtained from the video capture driver.
75 * This information will be used when we get bitmap bits via
76 * GetDIBits API. */
77 BITMAPINFO* frame_bitmap;
78 /* Framebuffer large enough to fit the frame. */
79 uint8_t* framebuffer;
80 /* Converter used to convert camera frames to the format
81 * expected by the client. */
82 FormatConverter converter;
83};
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) {
117 /* Since connecting to the driver is part of the successful
118 * camera initialization, we're safe to assume that driver
119 * is connected to the capture window. */
120 capDriverDisconnect(cd->cap_window);
121
122 if (cd->dc != NULL) {
123 W("%s: Frames should not be capturing at this point",
124 __FUNCTION__);
125 ReleaseDC(cd->cap_window, cd->dc);
126 cd->dc = NULL;
127 }
128 /* Destroy the capturing window. */
129 DestroyWindow(cd->cap_window);
130 cd->cap_window = NULL;
131 }
132 if (cd->frame_bitmap != NULL) {
133 free(cd->frame_bitmap);
134 }
135 if (cd->window_name != NULL) {
136 free(cd->window_name);
137 }
138 if (cd->framebuffer != NULL) {
139 free(cd->framebuffer);
140 }
141 AFREE(cd);
142 } else {
143 W("%s: No descriptor", __FUNCTION__);
144 }
145}
146
147static uint32_t
148_camera_device_convertable_format(WndCameraDevice* cd)
149{
150 if (cd != NULL) {
151 switch (cd->header.pixel_format) {
152 case BI_RGB:
153 switch (cd->frame_bitmap->bmiHeader.biBitCount) {
154 case 24:
155 return V4L2_PIX_FMT_RGB24;
156 default:
157 E("%s: Camera API uses unsupported RGB format RGB%d",
158 __FUNCTION__, cd->frame_bitmap->bmiHeader.biBitCount * 3);
159 return 0;
160 }
161 break;
162 default:
163 E("%s: Camera API uses unsupported format %d",
164 __FUNCTION__, cd->header.pixel_format);
165 break;
166 }
167 } else {
168 E("%s: No descriptor", __FUNCTION__);
169 }
170
171 return 0;
172}
173
174/*******************************************************************************
175 * CameraDevice API
176 ******************************************************************************/
177
178CameraDevice*
179camera_device_open(const char* name,
180 int inp_channel,
181 uint32_t pixel_format)
182{
183 WndCameraDevice* wcd;
184 size_t format_info_size;
185
186 /* Allocate descriptor and initialize windows-specific fields. */
187 wcd = _camera_device_alloc();
188 if (wcd == NULL) {
189 E("%s: Unable to allocate WndCameraDevice instance", __FUNCTION__);
190 return NULL;
191 }
192 wcd->window_name = (name != NULL) ? ASTRDUP(name) :
193 ASTRDUP(_default_window_name);
194 if (wcd->window_name == NULL) {
195 E("%s: Unable to save window name", __FUNCTION__);
196 _camera_device_free(wcd);
197 return NULL;
198 }
199 wcd->input_channel = inp_channel;
200 wcd->req_pixel_format = pixel_format;
201
202 /* Create capture window that is a child of HWND_MESSAGE window.
203 * We make it invisible, so it doesn't mess with the UI. Also
204 * note that we supply standard HWND_MESSAGE window handle as
205 * the parent window, since we don't want video capturing
206 * machinery to be dependent on the details of our UI. */
207 wcd->cap_window = capCreateCaptureWindow(wcd->window_name, WS_CHILD, 0, 0,
208 0, 0, HWND_MESSAGE, 1);
209 if (wcd->cap_window == NULL) {
210 E("%s: Unable to create video capturing window: %d",
211 __FUNCTION__, GetLastError());
212 _camera_device_free(wcd);
213 return NULL;
214 }
215
216 /* Connect capture window to the video capture driver. */
217 if (!capDriverConnect(wcd->cap_window, wcd->input_channel)) {
218 /* Unable to connect to a driver. Need to cleanup everything
219 * now since we're not going to receive camera_cleanup() call
220 * after unsuccessful camera initialization. */
221 E("%s: Unable to connect to the video capturing driver #%d: %d",
222 __FUNCTION__, wcd->input_channel, GetLastError());
223 DestroyWindow(wcd->cap_window);
224 wcd->cap_window = NULL;
225 _camera_device_free(wcd);
226 return NULL;
227 }
228
229 /* Get frame information from the driver. */
230 format_info_size = capGetVideoFormatSize(wcd->cap_window);
231 if (format_info_size == 0) {
232 E("%s: Unable to get video format size: %d",
233 __FUNCTION__, GetLastError());
234 return NULL;
235 }
236 wcd->frame_bitmap = (BITMAPINFO*)malloc(format_info_size);
237 if (wcd->frame_bitmap == NULL) {
238 E("%s: Unable to allocate frame bitmap info buffer", __FUNCTION__);
239 _camera_device_free(wcd);
240 return NULL;
241 }
242 if (!capGetVideoFormat(wcd->cap_window, wcd->frame_bitmap,
243 format_info_size)) {
244 E("%s: Unable to obtain video format: %d", __FUNCTION__, GetLastError());
245 _camera_device_free(wcd);
246 return NULL;
247 }
248
249 /* Initialize the common header. */
250 wcd->header.width = wcd->frame_bitmap->bmiHeader.biWidth;
251 wcd->header.height = wcd->frame_bitmap->bmiHeader.biHeight;
252 wcd->header.bpp = wcd->frame_bitmap->bmiHeader.biBitCount;
253 wcd->header.pixel_format = wcd->frame_bitmap->bmiHeader.biCompression;
254 wcd->header.bpl = (wcd->header.width * wcd->header.bpp) / 8;
255 if ((wcd->header.width * wcd->header.bpp) % 8) {
256 // TODO: Is this correct to assume that new line in framebuffer is aligned
257 // to a byte, or is it alogned to a multiple of bytes occupied by a pixel?
258 wcd->header.bpl++;
259 }
260 wcd->header.framebuffer_size = wcd->header.bpl * wcd->header.height;
261
262 /* Lets see if we have a convertor for the format. */
263 wcd->converter = get_format_converted(_camera_device_convertable_format(wcd),
264 wcd->req_pixel_format);
265 if (wcd->converter == NULL) {
266 E("%s: No converter available", __FUNCTION__);
267 _camera_device_free(wcd);
268 return NULL;
269 }
270
271 /* Allocate framebuffer. */
272 wcd->framebuffer = (uint8_t*)malloc(wcd->header.framebuffer_size);
273 if (wcd->framebuffer == NULL) {
274 E("%s: Unable to allocate framebuffer", __FUNCTION__);
275 _camera_device_free(wcd);
276 return NULL;
277 }
278
279 return &wcd->header;
280}
281
282int
283camera_device_start_capturing(CameraDevice* cd)
284{
285 WndCameraDevice* wcd;
286 if (cd == NULL || cd->opaque == NULL) {
287 E("%s: Invalid camera device descriptor", __FUNCTION__);
288 return -1;
289 }
290 wcd = (WndCameraDevice*)cd->opaque;
291
292 /* wcd->dc is an indicator of capturin: !NULL - capturing, NULL - not */
293 if (wcd->dc != NULL) {
294 /* It's already capturing. */
295 W("%s: Capturing is already on %s", __FUNCTION__, wcd->window_name);
296 return 0;
297 }
298
299 /* Get DC for the capturing window that will be used when we deal with
300 * bitmaps obtained from the camera device during frame capturing. */
301 wcd->dc = GetDC(wcd->cap_window);
302 if (wcd->dc == NULL) {
303 E("%s: Unable to obtain DC for %s: %d",
304 __FUNCTION__, wcd->window_name, GetLastError());
305 return -1;
306 }
307
308 return 0;
309}
310
311int
312camera_device_stop_capturing(CameraDevice* cd)
313{
314 WndCameraDevice* wcd;
315 if (cd == NULL || cd->opaque == NULL) {
316 E("%s: Invalid camera device descriptor", __FUNCTION__);
317 return -1;
318 }
319 wcd = (WndCameraDevice*)cd->opaque;
320 if (wcd->dc == NULL) {
321 W("%s: Windows %s is not captuing video", __FUNCTION__, wcd->window_name);
322 return 0;
323 }
324 ReleaseDC(wcd->cap_window, wcd->dc);
325 wcd->dc = NULL;
326
327 return 0;
328}
329
330int
331camera_device_read_frame(CameraDevice* cd, uint8_t* buffer)
332{
333 WndCameraDevice* wcd;
334 /* Bitmap handle taken from the clipboard. */
335 HBITMAP bm_handle;
336 /* Pitmap placed to the clipboard. */
337 BITMAP bitmap;
338
339 /* Sanity checks. */
340 if (cd == NULL || cd->opaque == NULL) {
341 E("%s: Invalid camera device descriptor", __FUNCTION__);
342 return -1;
343 }
344 wcd = (WndCameraDevice*)cd->opaque;
345 if (wcd->dc == NULL) {
346 W("%s: Windows %s is not captuing video",
347 __FUNCTION__, wcd->window_name);
348 return -1;
349 }
350
351 /* Grab a frame, and post it to the clipboard. Not very effective, but this
352 * is how capXxx API is operating. */
353 if (!capGrabFrameNoStop(wcd->cap_window) ||
354 !capEditCopy(wcd->cap_window) ||
355 !OpenClipboard(wcd->cap_window)) {
356 E("%s: %s: Unable to save frame to the clipboard: %d",
357 __FUNCTION__, wcd->window_name, GetLastError());
358 return -1;
359 }
360
361 /* Get bitmap handle saved into clipboard. Note that bitmap is still
362 * owned by the clipboard here! */
363 bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP);
364 CloseClipboard();
365 if (bm_handle == NULL) {
366 E("%s: %s: Unable to obtain frame from the clipboard: %d",
367 __FUNCTION__, wcd->window_name, GetLastError());
368 return -1;
369 }
370
371 /* Get bitmap information */
372 if (!GetObject(bm_handle, sizeof(BITMAP), &bitmap)) {
373 E("%s: %s Unable to obtain frame's bitmap: %d",
374 __FUNCTION__, wcd->window_name, GetLastError());
375 return -1;
376 }
377
378 /* Save bitmap bits to the framebuffer. */
379 if (!GetDIBits(wcd->dc, bm_handle, 0, wcd->frame_bitmap->bmiHeader.biHeight,
380 wcd->framebuffer, wcd->frame_bitmap, DIB_RGB_COLORS)) {
381 E("%s: %s: Unable to transfer frame to the framebuffer: %d",
382 __FUNCTION__, wcd->window_name, GetLastError());
383 return -1;
384 }
385
386 /* Lets see if conversion is required. */
387 if (_camera_device_convertable_format(wcd) == wcd->req_pixel_format) {
388 /* Formats match. Just copy framebuffer over. */
389 memcpy(buffer, wcd->framebuffer, wcd->header.framebuffer_size);
390 } else {
391 /* Formats do not match. Use the converter. */
392 wcd->converter(wcd->framebuffer, wcd->header.width, wcd->header.height,
393 buffer);
394 }
395
396 return 0;
397}
398
399void
400camera_device_close(CameraDevice* cd)
401{
402 /* Sanity checks. */
403 if (cd == NULL || cd->opaque == NULL) {
404 E("%s: Invalid camera device descriptor", __FUNCTION__);
405 } else {
406 WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque;
407 _camera_device_free(wcd);
408 }
409}