1 /* 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 that is used to capture video frames from a camera device 19 * on Mac. This code uses QTKit API to work with camera devices, and requires 20 * Mac OS at least 10.5 21 */ 22 23 #import <Cocoa/Cocoa.h> 24 #import <QTKit/QTKit.h> 25 #import <CoreAudio/CoreAudio.h> 26 #include "android/camera/camera-capture.h" 27 #include "android/camera/camera-format-converters.h" 28 29 #define E(...) derror(__VA_ARGS__) 30 #define W(...) dwarning(__VA_ARGS__) 31 #define D(...) VERBOSE_PRINT(camera,__VA_ARGS__) 32 33 /******************************************************************************* 34 * Helper routines 35 ******************************************************************************/ 36 37 /* Converts internal QT pixel format to a FOURCC value. */ 38 static uint32_t 39 _QTtoFOURCC(uint32_t qt_pix_format) 40 { 41 switch (qt_pix_format) { 42 case kCVPixelFormatType_24RGB: 43 return V4L2_PIX_FMT_RGB24; 44 45 case kCVPixelFormatType_24BGR: 46 return V4L2_PIX_FMT_BGR32; 47 48 case kCVPixelFormatType_32ARGB: 49 case kCVPixelFormatType_32RGBA: 50 return V4L2_PIX_FMT_RGB32; 51 52 case kCVPixelFormatType_32BGRA: 53 case kCVPixelFormatType_32ABGR: 54 return V4L2_PIX_FMT_BGR32; 55 56 case kCVPixelFormatType_422YpCbCr8: 57 return V4L2_PIX_FMT_UYVY; 58 59 case kCVPixelFormatType_420YpCbCr8Planar: 60 return V4L2_PIX_FMT_YVU420; 61 62 case 'yuvs': // kCVPixelFormatType_422YpCbCr8_yuvs - undeclared? 63 return V4L2_PIX_FMT_YUYV; 64 65 default: 66 E("Unrecognized pixel format '%.4s'", (const char*)&qt_pix_format); 67 return 0; 68 } 69 } 70 71 /******************************************************************************* 72 * MacCamera implementation 73 ******************************************************************************/ 74 75 /* Encapsulates a camera device on MacOS */ 76 @interface MacCamera : NSObject { 77 /* Capture session. */ 78 QTCaptureSession* capture_session; 79 /* Camera capture device. */ 80 QTCaptureDevice* capture_device; 81 /* Input device registered with the capture session. */ 82 QTCaptureDeviceInput* input_device; 83 /* Output device registered with the capture session. */ 84 QTCaptureVideoPreviewOutput* output_device; 85 /* Current framebuffer. */ 86 CVImageBufferRef current_frame; 87 /* Desired frame width */ 88 int desired_width; 89 /* Desired frame height */ 90 int desired_height; 91 } 92 93 /* Initializes MacCamera instance. 94 * Return: 95 * Pointer to initialized instance on success, or nil on failure. 96 */ 97 - (MacCamera*)init; 98 99 /* Undoes 'init' */ 100 - (void)free; 101 102 /* Starts capturing video frames. 103 * Param: 104 * width, height - Requested dimensions for the captured video frames. 105 * Return: 106 * 0 on success, or !=0 on failure. 107 */ 108 - (int)start_capturing:(int)width:(int)height; 109 110 /* Captures a frame from the camera device. 111 * Param: 112 * framebuffers - Array of framebuffers where to read the frame. Size of this 113 * array is defined by the 'fbs_num' parameter. Note that the caller must 114 * make sure that buffers are large enough to contain entire frame captured 115 * from the device. 116 * fbs_num - Number of entries in the 'framebuffers' array. 117 * Return: 118 * 0 on success, or non-zero value on failure. There is a special vaule 1 119 * returned from this routine which indicates that frames are not yet available 120 * in the device. The client should respond to this value by repeating the 121 * read, rather than reporting an error. 122 */ 123 - (int)read_frame:(ClientFrameBuffer*)framebuffers:(int)fbs_num:(float)r_scale:(float)g_scale:(float)b_scale:(float)exp_comp; 124 125 @end 126 127 @implementation MacCamera 128 129 - (MacCamera*)init 130 { 131 NSError *error; 132 BOOL success; 133 134 /* Obtain the capture device, make sure it's not used by another 135 * application, and open it. */ 136 capture_device = 137 [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo]; 138 if (capture_device == nil) { 139 E("There are no available video devices found."); 140 [self release]; 141 return nil; 142 } 143 if ([capture_device isInUseByAnotherApplication]) { 144 E("Default camera device is in use by another application."); 145 [capture_device release]; 146 capture_device = nil; 147 [self release]; 148 return nil; 149 } 150 success = [capture_device open:&error]; 151 if (!success) { 152 E("Unable to open camera device: '%s'", 153 [[error localizedDescription] UTF8String]); 154 [self free]; 155 [self release]; 156 return nil; 157 } 158 159 /* Create capture session. */ 160 capture_session = [[QTCaptureSession alloc] init]; 161 if (capture_session == nil) { 162 E("Unable to create capure session."); 163 [self free]; 164 [self release]; 165 return nil; 166 } 167 168 /* Create an input device and register it with the capture session. */ 169 input_device = [[QTCaptureDeviceInput alloc] initWithDevice:capture_device]; 170 success = [capture_session addInput:input_device error:&error]; 171 if (!success) { 172 E("Unable to initialize input device: '%s'", 173 [[error localizedDescription] UTF8String]); 174 [input_device release]; 175 input_device = nil; 176 [self free]; 177 [self release]; 178 return nil; 179 } 180 181 /* Create an output device and register it with the capture session. */ 182 output_device = [[QTCaptureVideoPreviewOutput alloc] init]; 183 success = [capture_session addOutput:output_device error:&error]; 184 if (!success) { 185 E("Unable to initialize output device: '%s'", 186 [[error localizedDescription] UTF8String]); 187 [output_device release]; 188 output_device = nil; 189 [self free]; 190 [self release]; 191 return nil; 192 } 193 [output_device setDelegate:self]; 194 195 return self; 196 } 197 198 - (void)free 199 { 200 /* Uninitialize capture session. */ 201 if (capture_session != nil) { 202 /* Make sure that capturing is stopped. */ 203 if ([capture_session isRunning]) { 204 [capture_session stopRunning]; 205 } 206 /* Detach input and output devices from the session. */ 207 if (input_device != nil) { 208 [capture_session removeInput:input_device]; 209 [input_device release]; 210 input_device = nil; 211 } 212 if (output_device != nil) { 213 [capture_session removeOutput:output_device]; 214 [output_device release]; 215 output_device = nil; 216 } 217 /* Destroy capture session. */ 218 [capture_session release]; 219 capture_session = nil; 220 } 221 222 /* Uninitialize capture device. */ 223 if (capture_device != nil) { 224 /* Make sure device is not opened. */ 225 if ([capture_device isOpen]) { 226 [capture_device close]; 227 } 228 [capture_device release]; 229 capture_device = nil; 230 } 231 232 /* Release current framebuffer. */ 233 if (current_frame != nil) { 234 CVBufferRelease(current_frame); 235 current_frame = nil; 236 } 237 } 238 239 - (int)start_capturing:(int)width:(int)height 240 { 241 if (![capture_session isRunning]) { 242 /* Set desired frame dimensions. */ 243 desired_width = width; 244 desired_height = height; 245 [output_device setPixelBufferAttributes: 246 [NSDictionary dictionaryWithObjectsAndKeys: 247 [NSNumber numberWithInt: width], kCVPixelBufferWidthKey, 248 [NSNumber numberWithInt: height], kCVPixelBufferHeightKey, 249 nil]]; 250 [capture_session startRunning]; 251 return 0; 252 } else if (width == desired_width && height == desired_height) { 253 W("%s: Already capturing %dx%d frames", 254 __FUNCTION__, desired_width, desired_height); 255 return -1; 256 } else { 257 E("%s: Already capturing %dx%d frames. Requested frame dimensions are %dx%d", 258 __FUNCTION__, desired_width, desired_height, width, height); 259 return -1; 260 } 261 } 262 263 - (int)read_frame:(ClientFrameBuffer*)framebuffers:(int)fbs_num:(float)r_scale:(float)g_scale:(float)b_scale:(float)exp_comp 264 { 265 int res = -1; 266 267 /* Frames are pushed by QT in another thread. 268 * So we need a protection here. */ 269 @synchronized (self) 270 { 271 if (current_frame != nil) { 272 /* Collect frame info. */ 273 const uint32_t pixel_format = 274 _QTtoFOURCC(CVPixelBufferGetPixelFormatType(current_frame)); 275 const int frame_width = CVPixelBufferGetWidth(current_frame); 276 const int frame_height = CVPixelBufferGetHeight(current_frame); 277 const size_t frame_size = 278 CVPixelBufferGetBytesPerRow(current_frame) * frame_height; 279 280 /* Get framebuffer pointer. */ 281 CVPixelBufferLockBaseAddress(current_frame, 0); 282 const void* pixels = CVPixelBufferGetBaseAddress(current_frame); 283 if (pixels != nil) { 284 /* Convert framebuffer. */ 285 res = convert_frame(pixels, pixel_format, frame_size, 286 frame_width, frame_height, 287 framebuffers, fbs_num, 288 r_scale, g_scale, b_scale, exp_comp); 289 } else { 290 E("%s: Unable to obtain framebuffer", __FUNCTION__); 291 res = -1; 292 } 293 CVPixelBufferUnlockBaseAddress(current_frame, 0); 294 } else { 295 /* First frame didn't come in just yet. Let the caller repeat. */ 296 res = 1; 297 } 298 } 299 300 return res; 301 } 302 303 - (void)captureOutput:(QTCaptureOutput*) captureOutput 304 didOutputVideoFrame:(CVImageBufferRef)videoFrame 305 withSampleBuffer:(QTSampleBuffer*) sampleBuffer 306 fromConnection:(QTCaptureConnection*) connection 307 { 308 CVImageBufferRef to_release; 309 CVBufferRetain(videoFrame); 310 311 /* Frames are pulled by the client in another thread. 312 * So we need a protection here. */ 313 @synchronized (self) 314 { 315 to_release = current_frame; 316 current_frame = videoFrame; 317 } 318 CVBufferRelease(to_release); 319 } 320 321 @end 322 323 /******************************************************************************* 324 * CameraDevice routines 325 ******************************************************************************/ 326 327 typedef struct MacCameraDevice MacCameraDevice; 328 /* MacOS-specific camera device descriptor. */ 329 struct MacCameraDevice { 330 /* Common camera device descriptor. */ 331 CameraDevice header; 332 /* Actual camera device object. */ 333 MacCamera* device; 334 }; 335 336 /* Allocates an instance of MacCameraDevice structure. 337 * Return: 338 * Allocated instance of MacCameraDevice structure. Note that this routine 339 * also sets 'opaque' field in the 'header' structure to point back to the 340 * containing MacCameraDevice instance. 341 */ 342 static MacCameraDevice* 343 _camera_device_alloc(void) 344 { 345 MacCameraDevice* cd = (MacCameraDevice*)malloc(sizeof(MacCameraDevice)); 346 if (cd != NULL) { 347 memset(cd, 0, sizeof(MacCameraDevice)); 348 cd->header.opaque = cd; 349 } else { 350 E("%s: Unable to allocate MacCameraDevice instance", __FUNCTION__); 351 } 352 return cd; 353 } 354 355 /* Uninitializes and frees MacCameraDevice descriptor. 356 * Note that upon return from this routine memory allocated for the descriptor 357 * will be freed. 358 */ 359 static void 360 _camera_device_free(MacCameraDevice* cd) 361 { 362 if (cd != NULL) { 363 if (cd->device != NULL) { 364 [cd->device free]; 365 [cd->device release]; 366 cd->device = nil; 367 } 368 AFREE(cd); 369 } else { 370 W("%s: No descriptor", __FUNCTION__); 371 } 372 } 373 374 /* Resets camera device after capturing. 375 * Since new capture request may require different frame dimensions we must 376 * reset frame info cached in the capture window. The only way to do that would 377 * be closing, and reopening it again. */ 378 static void 379 _camera_device_reset(MacCameraDevice* cd) 380 { 381 if (cd != NULL && cd->device) { 382 [cd->device free]; 383 cd->device = [cd->device init]; 384 } 385 } 386 387 /******************************************************************************* 388 * CameraDevice API 389 ******************************************************************************/ 390 391 CameraDevice* 392 camera_device_open(const char* name, int inp_channel) 393 { 394 MacCameraDevice* mcd; 395 396 mcd = _camera_device_alloc(); 397 if (mcd == NULL) { 398 E("%s: Unable to allocate MacCameraDevice instance", __FUNCTION__); 399 return NULL; 400 } 401 mcd->device = [[MacCamera alloc] init]; 402 if (mcd->device == nil) { 403 E("%s: Unable to initialize camera device.", __FUNCTION__); 404 return NULL; 405 } 406 return &mcd->header; 407 } 408 409 int 410 camera_device_start_capturing(CameraDevice* cd, 411 uint32_t pixel_format, 412 int frame_width, 413 int frame_height) 414 { 415 MacCameraDevice* mcd; 416 417 /* Sanity checks. */ 418 if (cd == NULL || cd->opaque == NULL) { 419 E("%s: Invalid camera device descriptor", __FUNCTION__); 420 return -1; 421 } 422 mcd = (MacCameraDevice*)cd->opaque; 423 if (mcd->device == nil) { 424 E("%s: Camera device is not opened", __FUNCTION__); 425 return -1; 426 } 427 428 return [mcd->device start_capturing:frame_width:frame_height]; 429 } 430 431 int 432 camera_device_stop_capturing(CameraDevice* cd) 433 { 434 MacCameraDevice* mcd; 435 436 /* Sanity checks. */ 437 if (cd == NULL || cd->opaque == NULL) { 438 E("%s: Invalid camera device descriptor", __FUNCTION__); 439 return -1; 440 } 441 mcd = (MacCameraDevice*)cd->opaque; 442 if (mcd->device == nil) { 443 E("%s: Camera device is not opened", __FUNCTION__); 444 return -1; 445 } 446 447 /* Reset capture settings, so next call to capture can set its own. */ 448 _camera_device_reset(mcd); 449 450 return 0; 451 } 452 453 int 454 camera_device_read_frame(CameraDevice* cd, 455 ClientFrameBuffer* framebuffers, 456 int fbs_num, 457 float r_scale, 458 float g_scale, 459 float b_scale, 460 float exp_comp) 461 { 462 MacCameraDevice* mcd; 463 464 /* Sanity checks. */ 465 if (cd == NULL || cd->opaque == NULL) { 466 E("%s: Invalid camera device descriptor", __FUNCTION__); 467 return -1; 468 } 469 mcd = (MacCameraDevice*)cd->opaque; 470 if (mcd->device == nil) { 471 E("%s: Camera device is not opened", __FUNCTION__); 472 return -1; 473 } 474 475 return [mcd->device read_frame:framebuffers:fbs_num:r_scale:g_scale:b_scale:exp_comp]; 476 } 477 478 void 479 camera_device_close(CameraDevice* cd) 480 { 481 /* Sanity checks. */ 482 if (cd == NULL || cd->opaque == NULL) { 483 E("%s: Invalid camera device descriptor", __FUNCTION__); 484 } else { 485 _camera_device_free((MacCameraDevice*)cd->opaque); 486 } 487 } 488 489 int 490 enumerate_camera_devices(CameraInfo* cis, int max) 491 { 492 /* Array containing emulated webcam frame dimensions. 493 * QT API provides device independent frame dimensions, by scaling frames 494 * received from the device to whatever dimensions were requested for the 495 * output device. So, we can just use a small set of frame dimensions to 496 * emulate. 497 */ 498 static const CameraFrameDim _emulate_dims[] = 499 { 500 /* Emulates 640x480 frame. */ 501 {640, 480}, 502 /* Emulates 352x288 frame (required by camera framework). */ 503 {352, 288}, 504 /* Emulates 320x240 frame (required by camera framework). */ 505 {320, 240}, 506 /* Emulates 176x144 frame (required by camera framework). */ 507 {176, 144} 508 }; 509 510 /* Obtain default video device. QT API doesn't really provide a reliable 511 * way to identify camera devices. There is a QTCaptureDevice::uniqueId 512 * method that supposedly does that, but in some cases it just doesn't 513 * work. Until we figure out a reliable device identification, we will 514 * stick to using only one (default) camera for emulation. */ 515 QTCaptureDevice* video_dev = 516 [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo]; 517 if (video_dev == nil) { 518 D("No web cameras are connected to the host."); 519 return 0; 520 } 521 522 /* Obtain pixel format for the device. */ 523 NSArray* pix_formats = [video_dev formatDescriptions]; 524 if (pix_formats == nil || [pix_formats count] == 0) { 525 E("Unable to obtain pixel format for the default camera device."); 526 [video_dev release]; 527 return 0; 528 } 529 const uint32_t qt_pix_format = [[pix_formats objectAtIndex:0] formatType]; 530 [pix_formats release]; 531 532 /* Obtain FOURCC pixel format for the device. */ 533 cis[0].pixel_format = _QTtoFOURCC(qt_pix_format); 534 if (cis[0].pixel_format == 0) { 535 /* Unsupported pixel format. */ 536 E("Pixel format '%.4s' reported by the camera device is unsupported", 537 (const char*)&qt_pix_format); 538 [video_dev release]; 539 return 0; 540 } 541 542 /* Initialize camera info structure. */ 543 cis[0].frame_sizes = (CameraFrameDim*)malloc(sizeof(_emulate_dims)); 544 if (cis[0].frame_sizes != NULL) { 545 cis[0].frame_sizes_num = sizeof(_emulate_dims) / sizeof(*_emulate_dims); 546 memcpy(cis[0].frame_sizes, _emulate_dims, sizeof(_emulate_dims)); 547 cis[0].device_name = ASTRDUP("webcam0"); 548 cis[0].inp_channel = 0; 549 cis[0].display_name = ASTRDUP("webcam0"); 550 cis[0].in_use = 0; 551 [video_dev release]; 552 return 1; 553 } else { 554 E("Unable to allocate memory for camera information."); 555 [video_dev release]; 556 return 0; 557 } 558 } 559