Merge "Implemented Mac camera emulation"
diff --git a/Makefile.android b/Makefile.android
index 6888173..e58f984 100644
--- a/Makefile.android
+++ b/Makefile.android
@@ -190,7 +190,7 @@
 endif
 
 ifeq ($(HOST_OS),darwin)
-  QEMU_SYSTEM_LDLIBS += -Wl,-framework,Cocoa
+  QEMU_SYSTEM_LDLIBS += -Wl,-framework,Cocoa,-framework,QTKit,-framework,CoreVideo
 endif
 
 include $(LOCAL_PATH)/Makefile.common
diff --git a/Makefile.common b/Makefile.common
index a816a8a..71100f3 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -457,7 +457,7 @@
 endif
 
 ifeq ($(HOST_OS),darwin)
-  CORE_MISC_SOURCES   += android/camera/camera-capture-mac.c
+  CORE_MISC_SOURCES   += android/camera/camera-capture-mac.m
 endif
 
 LOCAL_SRC_FILES += $(CORE_MISC_SOURCES)
diff --git a/android/camera/camera-capture-mac.c b/android/camera/camera-capture-mac.c
deleted file mode 100644
index 764a055..0000000
--- a/android/camera/camera-capture-mac.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Contains code capturing video frames from a camera device on Mac.
- * This code uses capXxx API, available via capCreateCaptureWindow.
- */
-
-#include "android/camera/camera-capture.h"
-
-#define  E(...)    derror(__VA_ARGS__)
-#define  W(...)    dwarning(__VA_ARGS__)
-#define  D(...)    VERBOSE_PRINT(camera,__VA_ARGS__)
-#define  D_ACTIVE  VERBOSE_CHECK(camera)
-
-/* the T(...) macro is used to dump traffic */
-#define  T_ACTIVE   0
-
-#if T_ACTIVE
-#define  T(...)    VERBOSE_PRINT(camera,__VA_ARGS__)
-#else
-#define  T(...)    ((void)0)
-#endif
-
-/*******************************************************************************
- *                     CameraDevice API
- ******************************************************************************/
-
-CameraDevice*
-camera_device_open(const char* name, int inp_channel)
-{
-    /* TODO: Implement */
-    return NULL;
-}
-
-int
-camera_device_start_capturing(CameraDevice* cd,
-                              uint32_t pixel_format,
-                              int frame_width,
-                              int frame_height)
-{
-    /* TODO: Implement */
-    return -1;
-}
-
-int
-camera_device_stop_capturing(CameraDevice* cd)
-{
-    /* TODO: Implement */
-    return -1;
-}
-
-int
-camera_device_read_frame(CameraDevice* cd,
-                         ClientFrameBuffer* framebuffers,
-                         int fbs_num)
-{
-    /* TODO: Implement */
-    return -1;
-}
-
-void
-camera_device_close(CameraDevice* cd)
-{
-    /* TODO: Implement */
-}
-
-int
-enumerate_camera_devices(CameraInfo* cis, int max)
-{
-    /* TODO: Implement */
-    return 0;
-}
diff --git a/android/camera/camera-capture-mac.m b/android/camera/camera-capture-mac.m
new file mode 100644
index 0000000..1c1c3d5
--- /dev/null
+++ b/android/camera/camera-capture-mac.m
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Contains code that is used to capture video frames from a camera device
+ * on Mac. This code uses QTKit API to work with camera devices, and requires
+ * Mac OS at least 10.5
+ */
+
+#import <Cocoa/Cocoa.h>
+#import <QTKit/QTkit.h>
+#import <CoreAudio/CoreAudio.h>
+#include "android/camera/camera-capture.h"
+#include "android/camera/camera-format-converters.h"
+
+#define  E(...)    derror(__VA_ARGS__)
+#define  W(...)    dwarning(__VA_ARGS__)
+#define  D(...)    VERBOSE_PRINT(camera,__VA_ARGS__)
+
+/*******************************************************************************
+ *                     Helper routines
+ ******************************************************************************/
+
+/* Converts internal QT pixel format to a FOURCC value. */
+static uint32_t
+_QTtoFOURCC(uint32_t qt_pix_format)
+{
+  switch (qt_pix_format) {
+    case kCVPixelFormatType_24RGB:
+      return V4L2_PIX_FMT_RGB24;
+
+    case kCVPixelFormatType_24BGR:
+      return V4L2_PIX_FMT_BGR32;
+
+    case kCVPixelFormatType_32ARGB:
+    case kCVPixelFormatType_32RGBA:
+      return V4L2_PIX_FMT_RGB32;
+
+    case kCVPixelFormatType_32BGRA:
+    case kCVPixelFormatType_32ABGR:
+      return V4L2_PIX_FMT_BGR32;
+
+    case kCVPixelFormatType_422YpCbCr8:
+      return V4L2_PIX_FMT_UYVY;
+
+    case kCVPixelFormatType_420YpCbCr8Planar:
+      return V4L2_PIX_FMT_YVU420;
+
+    case 'yuvs':  // kCVPixelFormatType_422YpCbCr8_yuvs - undeclared?
+      return V4L2_PIX_FMT_YUYV;
+
+    default:
+      E("Unrecognized pixel format '%.4s'", (const char*)&qt_pix_format);
+      return 0;
+  }
+}
+
+/*******************************************************************************
+ *                     MacCamera implementation
+ ******************************************************************************/
+
+/* Encapsulates a camera device on MacOS */
+@interface MacCamera : NSObject {
+    /* Capture session. */
+    QTCaptureSession*             capture_session;
+    /* Camera capture device. */
+    QTCaptureDevice*              capture_device;
+    /* Input device registered with the capture session. */
+    QTCaptureDeviceInput*         input_device;
+    /* Output device registered with the capture session. */
+    QTCaptureVideoPreviewOutput*  output_device;
+    /* Current framebuffer. */
+    CVImageBufferRef              current_frame;
+    /* Desired frame width */
+    int                           desired_width;
+    /* Desired frame height */
+    int                           desired_height;
+}
+
+/* Initializes MacCamera instance.
+ * Return:
+ *  Pointer to initialized instance on success, or nil on failure.
+ */
+- (MacCamera*)init;
+
+/* Undoes 'init' */
+- (void)free;
+
+/* Starts capturing video frames.
+ * Param:
+ *  width, height - Requested dimensions for the captured video frames.
+ * Return:
+ *  0 on success, or !=0 on failure.
+ */
+- (int)start_capturing:(int)width:(int)height;
+
+/* Captures a frame from the camera device.
+ * Param:
+ *  framebuffers - Array of framebuffers where to read the frame. Size of this
+ *      array is defined by the 'fbs_num' parameter. Note that the caller must
+ *      make sure that buffers are large enough to contain entire frame captured
+ *      from the device.
+ *  fbs_num - Number of entries in the 'framebuffers' array.
+ * Return:
+ *  0 on success, or non-zero value on failure. There is a special vaule 1
+ *  returned from this routine which indicates that frames are not yet available
+ *  in the device. The client should respond to this value by repeating the
+ *  read, rather than reporting an error.
+ */
+- (int)read_frame:(ClientFrameBuffer*)framebuffers:(int)fbs_num;
+
+@end
+
+@implementation MacCamera
+
+- (MacCamera*)init
+{
+    NSError *error;
+    BOOL success;
+
+    /* Obtain the capture device, make sure it's not used by another
+     * application, and open it. */
+    capture_device =
+        [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo];
+    if (capture_device == nil) {
+        E("There are no available video devices found.");
+        [self release];
+        return nil;
+    }
+    if ([capture_device isInUseByAnotherApplication]) {
+        E("Default camera device is in use by another application.");
+        [capture_device release];
+        capture_device = nil;
+        [self release];
+        return nil;
+    }
+    success = [capture_device open:&error];
+    if (!success) {
+        E("Unable to open camera device: '%s'",
+          [[error localizedDescription] UTF8String]);
+        [self free];
+        [self release];
+        return nil;
+    }
+
+    /* Create capture session. */
+    capture_session = [[QTCaptureSession alloc] init];
+    if (capture_session == nil) {
+        E("Unable to create capure session.");
+        [self free];
+        [self release];
+        return nil;
+    }
+
+    /* Create an input device and register it with the capture session. */
+    input_device = [[QTCaptureDeviceInput alloc] initWithDevice:capture_device];
+    success = [capture_session addInput:input_device error:&error];
+    if (!success) {
+        E("Unable to initialize input device: '%s'",
+          [[error localizedDescription] UTF8String]);
+        [input_device release];
+        input_device = nil;
+        [self free];
+        [self release];
+        return nil;
+    }
+
+    /* Create an output device and register it with the capture session. */
+    output_device = [[QTCaptureVideoPreviewOutput alloc] init];
+    success = [capture_session addOutput:output_device error:&error];
+    if (!success) {
+        E("Unable to initialize output device: '%s'",
+          [[error localizedDescription] UTF8String]);
+        [output_device release];
+        output_device = nil;
+        [self free];
+        [self release];
+        return nil;
+    }
+    [output_device setDelegate:self];
+
+    return self;
+}
+
+- (void)free
+{
+    /* Uninitialize capture session. */
+    if (capture_session != nil) {
+        /* Make sure that capturing is stopped. */
+        if ([capture_session isRunning]) {
+            [capture_session stopRunning];
+        }
+        /* Detach input and output devices from the session. */
+        if (input_device != nil) {
+            [capture_session removeInput:input_device];
+            [input_device release];
+            input_device = nil;
+        }
+        if (output_device != nil) {
+            [capture_session removeOutput:output_device];
+            [output_device release];
+            output_device = nil;
+        }
+        /* Destroy capture session. */
+        [capture_session release];
+        capture_session = nil;
+    }
+
+    /* Uninitialize capture device. */
+    if (capture_device != nil) {
+        /* Make sure device is not opened. */
+        if ([capture_device isOpen]) {
+            [capture_device close];
+        }
+        [capture_device release];
+        capture_device = nil;
+    }
+
+    /* Release current framebuffer. */
+    if (current_frame != nil) {
+       CVBufferRelease(current_frame);
+       current_frame = nil;
+    }
+}
+
+- (int)start_capturing:(int)width:(int)height
+{
+  if (![capture_session isRunning]) {
+        /* Set desired frame dimensions. */
+        desired_width = width;
+        desired_height = height;
+        [output_device setPixelBufferAttributes:
+          [NSDictionary dictionaryWithObjectsAndKeys:
+              [NSNumber numberWithInt: width], kCVPixelBufferWidthKey,
+              [NSNumber numberWithInt: height], kCVPixelBufferHeightKey,
+              nil]];
+        [capture_session startRunning];
+        return 0;
+  } else if (width == desired_width && height == desired_height) {
+      W("%s: Already capturing %dx%d frames",
+        __FUNCTION__, desired_width, desired_height);
+      return -1;
+  } else {
+      E("%s: Already capturing %dx%d frames. Requested frame dimensions are %dx%d",
+        __FUNCTION__, desired_width, desired_height, width, height);
+      return -1;
+  }
+}
+
+- (int)read_frame:(ClientFrameBuffer*)framebuffers:(int)fbs_num
+{
+    int res = -1;
+
+    /* Frames are pushed by QT in another thread.
+     * So we need a protection here. */
+    @synchronized (self)
+    {
+        if (current_frame != nil) {
+            /* Collect frame info. */
+            const uint32_t pixel_format =
+                _QTtoFOURCC(CVPixelBufferGetPixelFormatType(current_frame));
+            const int frame_width = CVPixelBufferGetWidth(current_frame);
+            const int frame_height = CVPixelBufferGetHeight(current_frame);
+            const size_t frame_size =
+                CVPixelBufferGetBytesPerRow(current_frame) * frame_height;
+
+            /* Get framebuffer pointer. */
+            CVPixelBufferLockBaseAddress(current_frame, 0);
+            const void* pixels = CVPixelBufferGetBaseAddress(current_frame);
+            if (pixels != nil) {
+                /* Convert framebuffer. */
+                res = convert_frame(pixels, pixel_format, frame_size,
+                                    frame_width, frame_height,
+                                    framebuffers, fbs_num);
+            } else {
+                E("%s: Unable to obtain framebuffer", __FUNCTION__);
+                res = -1;
+            }
+            CVPixelBufferUnlockBaseAddress(current_frame, 0);
+        } else {
+            /* First frame didn't come in just yet. Let the caller repeat. */
+            res = 1;
+        }
+    }
+
+    return res;
+}
+
+- (void)captureOutput:(QTCaptureOutput*) captureOutput
+                      didOutputVideoFrame:(CVImageBufferRef)videoFrame
+                      withSampleBuffer:(QTSampleBuffer*) sampleBuffer
+                      fromConnection:(QTCaptureConnection*) connection
+{
+    CVImageBufferRef to_release;
+    CVBufferRetain(videoFrame);
+
+    /* Frames are pulled by the client in another thread.
+     * So we need a protection here. */
+    @synchronized (self)
+    {
+        to_release = current_frame;
+        current_frame = videoFrame;
+    }
+    CVBufferRelease(to_release);
+}
+
+@end
+
+/*******************************************************************************
+ *                     CameraDevice routines
+ ******************************************************************************/
+
+typedef struct MacCameraDevice MacCameraDevice;
+/* MacOS-specific camera device descriptor. */
+struct MacCameraDevice {
+    /* Common camera device descriptor. */
+    CameraDevice  header;
+    /* Actual camera device object. */
+    MacCamera*    device;
+};
+
+/* Allocates an instance of MacCameraDevice structure.
+ * Return:
+ *  Allocated instance of MacCameraDevice structure. Note that this routine
+ *  also sets 'opaque' field in the 'header' structure to point back to the
+ *  containing MacCameraDevice instance.
+ */
+static MacCameraDevice*
+_camera_device_alloc(void)
+{
+    MacCameraDevice* cd = (MacCameraDevice*)malloc(sizeof(MacCameraDevice));
+    if (cd != NULL) {
+        memset(cd, 0, sizeof(MacCameraDevice));
+        cd->header.opaque = cd;
+    } else {
+        E("%s: Unable to allocate MacCameraDevice instance", __FUNCTION__);
+    }
+    return cd;
+}
+
+/* Uninitializes and frees MacCameraDevice descriptor.
+ * Note that upon return from this routine memory allocated for the descriptor
+ * will be freed.
+ */
+static void
+_camera_device_free(MacCameraDevice* cd)
+{
+    if (cd != NULL) {
+        if (cd->device != NULL) {
+            [cd->device free];
+            [cd->device release];
+            cd->device = nil;
+        }
+        AFREE(cd);
+    } else {
+        W("%s: No descriptor", __FUNCTION__);
+    }
+}
+
+/* Resets camera device after capturing.
+ * Since new capture request may require different frame dimensions we must
+ * reset frame info cached in the capture window. The only way to do that would
+ * be closing, and reopening it again. */
+static void
+_camera_device_reset(MacCameraDevice* cd)
+{
+    if (cd != NULL && cd->device) {
+        [cd->device free];
+        cd->device = [cd->device init];
+    }
+}
+
+/*******************************************************************************
+ *                     CameraDevice API
+ ******************************************************************************/
+
+CameraDevice*
+camera_device_open(const char* name, int inp_channel)
+{
+    MacCameraDevice* mcd;
+
+    mcd = _camera_device_alloc();
+    if (mcd == NULL) {
+        E("%s: Unable to allocate MacCameraDevice instance", __FUNCTION__);
+        return NULL;
+    }
+    mcd->device = [[MacCamera alloc] init];
+    if (mcd->device == nil) {
+        E("%s: Unable to initialize camera device.", __FUNCTION__);
+        return NULL;
+    }
+    return &mcd->header;
+}
+
+int
+camera_device_start_capturing(CameraDevice* cd,
+                              uint32_t pixel_format,
+                              int frame_width,
+                              int frame_height)
+{
+    MacCameraDevice* mcd;
+
+    /* Sanity checks. */
+    if (cd == NULL || cd->opaque == NULL) {
+        E("%s: Invalid camera device descriptor", __FUNCTION__);
+        return -1;
+    }
+    mcd = (MacCameraDevice*)cd->opaque;
+    if (mcd->device == nil) {
+        E("%s: Camera device is not opened", __FUNCTION__);
+        return -1;
+    }
+
+    return [mcd->device start_capturing:frame_width:frame_height];
+}
+
+int
+camera_device_stop_capturing(CameraDevice* cd)
+{
+    MacCameraDevice* mcd;
+
+    /* Sanity checks. */
+    if (cd == NULL || cd->opaque == NULL) {
+        E("%s: Invalid camera device descriptor", __FUNCTION__);
+        return -1;
+    }
+    mcd = (MacCameraDevice*)cd->opaque;
+    if (mcd->device == nil) {
+        E("%s: Camera device is not opened", __FUNCTION__);
+        return -1;
+    }
+
+    /* Reset capture settings, so next call to capture can set its own. */
+    _camera_device_reset(mcd);
+
+    return 0;
+}
+
+int
+camera_device_read_frame(CameraDevice* cd,
+                         ClientFrameBuffer* framebuffers,
+                         int fbs_num)
+{
+    MacCameraDevice* mcd;
+
+    /* Sanity checks. */
+    if (cd == NULL || cd->opaque == NULL) {
+        E("%s: Invalid camera device descriptor", __FUNCTION__);
+        return -1;
+    }
+    mcd = (MacCameraDevice*)cd->opaque;
+    if (mcd->device == nil) {
+        E("%s: Camera device is not opened", __FUNCTION__);
+        return -1;
+    }
+
+    return [mcd->device read_frame:framebuffers:fbs_num];
+}
+
+void
+camera_device_close(CameraDevice* cd)
+{
+    /* Sanity checks. */
+    if (cd == NULL || cd->opaque == NULL) {
+        E("%s: Invalid camera device descriptor", __FUNCTION__);
+    } else {
+        _camera_device_free((MacCameraDevice*)cd->opaque);
+    }
+}
+
+int
+enumerate_camera_devices(CameraInfo* cis, int max)
+{
+/* Array containing emulated webcam frame dimensions.
+ * QT API provides device independent frame dimensions, by scaling frames
+ * received from the device to whatever dimensions were requested for the
+ * output device. So, we can just use a small set of frame dimensions to
+ * emulate.
+ */
+static const CameraFrameDim _emulate_dims[] =
+{
+  /* Emulates 640x480 frame. */
+  {640, 480},
+  /* Emulates 320x240 frame. */
+  {320, 240},
+};
+
+    /* Obtain default video device. QT API doesn't really provide a reliable
+     * way to identify camera devices. There is a QTCaptureDevice::uniqueId
+     * method that supposedly does that, but in some cases it just doesn't
+     * work. Until we figure out a reliable device identification, we will
+     * stick to using only one (default) camera for emulation. */
+    QTCaptureDevice* video_dev =
+        [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo];
+    if (video_dev == nil) {
+        D("No web cameras are connected to the host.");
+        return 0;
+    }
+
+    /* Obtain pixel format for the device. */
+    NSArray* pix_formats = [video_dev formatDescriptions];
+    if (pix_formats == nil || [pix_formats count] == 0) {
+        E("Unable to obtain pixel format for the default camera device.");
+        [video_dev release];
+        return 0;
+    }
+    const uint32_t qt_pix_format = [[pix_formats objectAtIndex:0] formatType];
+    [pix_formats release];
+
+    /* Obtain FOURCC pixel format for the device. */
+    cis[0].pixel_format = _QTtoFOURCC(qt_pix_format);
+    if (cis[0].pixel_format == 0) {
+        /* Unsupported pixel format. */
+        E("Pixel format '%.4s' reported by the camera device is unsupported",
+          (const char*)&qt_pix_format);
+        [video_dev release];
+        return 0;
+    }
+
+    /* Initialize camera info structure. */
+    cis[0].frame_sizes = (CameraFrameDim*)malloc(sizeof(_emulate_dims));
+    if (cis[0].frame_sizes != NULL) {
+        cis[0].frame_sizes_num = sizeof(_emulate_dims) / sizeof(*_emulate_dims);
+        memcpy(cis[0].frame_sizes, _emulate_dims, sizeof(_emulate_dims));
+        cis[0].device_name = ASTRDUP("webcam0");
+        cis[0].inp_channel = 0;
+        cis[0].display_name = ASTRDUP("webcam0");
+        cis[0].in_use = 0;
+        [video_dev release];
+        return 1;
+    } else {
+        E("Unable to allocate memory for camera information.");
+        [video_dev release];
+        return 0;
+    }
+}