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