1955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/*
2955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * Copyright (C) 2011 The Android Open Source Project
3955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *
4955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * Licensed under the Apache License, Version 2.0 (the "License");
5955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * you may not use this file except in compliance with the License.
6955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * You may obtain a copy of the License at
7955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *
8955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *      http://www.apache.org/licenses/LICENSE-2.0
9955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *
10955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * Unless required by applicable law or agreed to in writing, software
11955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * distributed under the License is distributed on an "AS IS" BASIS,
12955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * See the License for the specific language governing permissions and
14955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * limitations under the License.
15955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine */
16955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
17955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/*
18955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * Contains code that is used to capture video frames from a camera device
19955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * on Mac. This code uses QTKit API to work with camera devices, and requires
20955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * Mac OS at least 10.5
21955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine */
22955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
23955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine#import <Cocoa/Cocoa.h>
24ba951b488cefd1dcb4512a39e323d5000fdafa4cKoan-Sin Tan#import <QTKit/QTKit.h>
25955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine#import <CoreAudio/CoreAudio.h>
26955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine#include "android/camera/camera-capture.h"
27955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine#include "android/camera/camera-format-converters.h"
28955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
29955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine#define  E(...)    derror(__VA_ARGS__)
30955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine#define  W(...)    dwarning(__VA_ARGS__)
31955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine#define  D(...)    VERBOSE_PRINT(camera,__VA_ARGS__)
32955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
33955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/*******************************************************************************
34955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *                     Helper routines
35955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine ******************************************************************************/
36955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
37955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/* Converts internal QT pixel format to a FOURCC value. */
38955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkinestatic uint32_t
39955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine_QTtoFOURCC(uint32_t qt_pix_format)
40955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
41955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine  switch (qt_pix_format) {
42955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    case kCVPixelFormatType_24RGB:
43955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine      return V4L2_PIX_FMT_RGB24;
44955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
45955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    case kCVPixelFormatType_24BGR:
46955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine      return V4L2_PIX_FMT_BGR32;
47955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
48955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    case kCVPixelFormatType_32ARGB:
49955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    case kCVPixelFormatType_32RGBA:
50955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine      return V4L2_PIX_FMT_RGB32;
51955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
52955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    case kCVPixelFormatType_32BGRA:
53955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    case kCVPixelFormatType_32ABGR:
54955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine      return V4L2_PIX_FMT_BGR32;
55955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
56955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    case kCVPixelFormatType_422YpCbCr8:
57955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine      return V4L2_PIX_FMT_UYVY;
58955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
59955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    case kCVPixelFormatType_420YpCbCr8Planar:
60955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine      return V4L2_PIX_FMT_YVU420;
61955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
62955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    case 'yuvs':  // kCVPixelFormatType_422YpCbCr8_yuvs - undeclared?
63955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine      return V4L2_PIX_FMT_YUYV;
64955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
65955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    default:
66955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine      E("Unrecognized pixel format '%.4s'", (const char*)&qt_pix_format);
67955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine      return 0;
68955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine  }
69955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
70955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
71955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/*******************************************************************************
72955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *                     MacCamera implementation
73955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine ******************************************************************************/
74955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
75955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/* Encapsulates a camera device on MacOS */
76955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine@interface MacCamera : NSObject {
77955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Capture session. */
78955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    QTCaptureSession*             capture_session;
79955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Camera capture device. */
80955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    QTCaptureDevice*              capture_device;
81955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Input device registered with the capture session. */
82955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    QTCaptureDeviceInput*         input_device;
83955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Output device registered with the capture session. */
84955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    QTCaptureVideoPreviewOutput*  output_device;
85955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Current framebuffer. */
86955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    CVImageBufferRef              current_frame;
87955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Desired frame width */
88955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    int                           desired_width;
89955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Desired frame height */
90955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    int                           desired_height;
91955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
92955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
93955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/* Initializes MacCamera instance.
94955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * Return:
95955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *  Pointer to initialized instance on success, or nil on failure.
96955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine */
97955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine- (MacCamera*)init;
98955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
99955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/* Undoes 'init' */
100955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine- (void)free;
101955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
102955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/* Starts capturing video frames.
103955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * Param:
104955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *  width, height - Requested dimensions for the captured video frames.
105955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * Return:
106955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *  0 on success, or !=0 on failure.
107955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine */
108955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine- (int)start_capturing:(int)width:(int)height;
109955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
110955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/* Captures a frame from the camera device.
111955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * Param:
112955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *  framebuffers - Array of framebuffers where to read the frame. Size of this
113955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *      array is defined by the 'fbs_num' parameter. Note that the caller must
114955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *      make sure that buffers are large enough to contain entire frame captured
115955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *      from the device.
116955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *  fbs_num - Number of entries in the 'framebuffers' array.
117955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * Return:
118955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *  0 on success, or non-zero value on failure. There is a special vaule 1
119955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *  returned from this routine which indicates that frames are not yet available
120955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *  in the device. The client should respond to this value by repeating the
121955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *  read, rather than reporting an error.
122955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine */
12337fb84f8b26e3061c1ccb404bf4c962eed5e6057Vladimir Chtchetkine- (int)read_frame:(ClientFrameBuffer*)framebuffers:(int)fbs_num:(float)r_scale:(float)g_scale:(float)b_scale:(float)exp_comp;
124955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
125955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine@end
126955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
127955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine@implementation MacCamera
128955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
129955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine- (MacCamera*)init
130955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
131955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    NSError *error;
132955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    BOOL success;
133955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
134955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Obtain the capture device, make sure it's not used by another
135955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine     * application, and open it. */
136955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    capture_device =
137955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo];
138955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (capture_device == nil) {
139955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("There are no available video devices found.");
140955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [self release];
141955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return nil;
142955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
143955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if ([capture_device isInUseByAnotherApplication]) {
144955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("Default camera device is in use by another application.");
145955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [capture_device release];
146955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        capture_device = nil;
147955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [self release];
148955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return nil;
149955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
150955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    success = [capture_device open:&error];
151955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (!success) {
152955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("Unable to open camera device: '%s'",
153955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine          [[error localizedDescription] UTF8String]);
154955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [self free];
155955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [self release];
156955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return nil;
157955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
158955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
159955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Create capture session. */
160955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    capture_session = [[QTCaptureSession alloc] init];
161955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (capture_session == nil) {
162955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("Unable to create capure session.");
163955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [self free];
164955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [self release];
165955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return nil;
166955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
167955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
168955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Create an input device and register it with the capture session. */
169955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    input_device = [[QTCaptureDeviceInput alloc] initWithDevice:capture_device];
170955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    success = [capture_session addInput:input_device error:&error];
171955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (!success) {
172955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("Unable to initialize input device: '%s'",
173955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine          [[error localizedDescription] UTF8String]);
174955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [input_device release];
175955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        input_device = nil;
176955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [self free];
177955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [self release];
178955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return nil;
179955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
180955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
181955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Create an output device and register it with the capture session. */
182955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    output_device = [[QTCaptureVideoPreviewOutput alloc] init];
183955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    success = [capture_session addOutput:output_device error:&error];
184955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (!success) {
185955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("Unable to initialize output device: '%s'",
186955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine          [[error localizedDescription] UTF8String]);
187955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [output_device release];
188955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        output_device = nil;
189955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [self free];
190955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [self release];
191955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return nil;
192955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
193955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    [output_device setDelegate:self];
194955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
195955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    return self;
196955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
197955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
198955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine- (void)free
199955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
200955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Uninitialize capture session. */
201955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (capture_session != nil) {
202955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        /* Make sure that capturing is stopped. */
203955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        if ([capture_session isRunning]) {
204955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            [capture_session stopRunning];
205955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        }
206955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        /* Detach input and output devices from the session. */
207955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        if (input_device != nil) {
208955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            [capture_session removeInput:input_device];
209955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            [input_device release];
210955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            input_device = nil;
211955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        }
212955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        if (output_device != nil) {
213955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            [capture_session removeOutput:output_device];
214955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            [output_device release];
215955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            output_device = nil;
216955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        }
217955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        /* Destroy capture session. */
218955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [capture_session release];
219955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        capture_session = nil;
220955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
221955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
222955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Uninitialize capture device. */
223955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (capture_device != nil) {
224955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        /* Make sure device is not opened. */
225955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        if ([capture_device isOpen]) {
226955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            [capture_device close];
227955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        }
228955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [capture_device release];
229955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        capture_device = nil;
230955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
231955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
232955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Release current framebuffer. */
233955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (current_frame != nil) {
234955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine       CVBufferRelease(current_frame);
235955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine       current_frame = nil;
236955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
237955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
238955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
239955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine- (int)start_capturing:(int)width:(int)height
240955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
241955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine  if (![capture_session isRunning]) {
242955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        /* Set desired frame dimensions. */
243955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        desired_width = width;
244955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        desired_height = height;
245955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [output_device setPixelBufferAttributes:
246955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine          [NSDictionary dictionaryWithObjectsAndKeys:
247955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine              [NSNumber numberWithInt: width], kCVPixelBufferWidthKey,
248955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine              [NSNumber numberWithInt: height], kCVPixelBufferHeightKey,
249955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine              nil]];
250955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [capture_session startRunning];
251955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return 0;
252955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine  } else if (width == desired_width && height == desired_height) {
253955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine      W("%s: Already capturing %dx%d frames",
254955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        __FUNCTION__, desired_width, desired_height);
255955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine      return -1;
256955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine  } else {
257955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine      E("%s: Already capturing %dx%d frames. Requested frame dimensions are %dx%d",
258955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        __FUNCTION__, desired_width, desired_height, width, height);
259955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine      return -1;
260955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine  }
261955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
262955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
26337fb84f8b26e3061c1ccb404bf4c962eed5e6057Vladimir Chtchetkine- (int)read_frame:(ClientFrameBuffer*)framebuffers:(int)fbs_num:(float)r_scale:(float)g_scale:(float)b_scale:(float)exp_comp
264955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
265955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    int res = -1;
266955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
267955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Frames are pushed by QT in another thread.
268955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine     * So we need a protection here. */
269955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    @synchronized (self)
270955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    {
271955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        if (current_frame != nil) {
272955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            /* Collect frame info. */
273955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            const uint32_t pixel_format =
274955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine                _QTtoFOURCC(CVPixelBufferGetPixelFormatType(current_frame));
275955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            const int frame_width = CVPixelBufferGetWidth(current_frame);
276955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            const int frame_height = CVPixelBufferGetHeight(current_frame);
277955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            const size_t frame_size =
278955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine                CVPixelBufferGetBytesPerRow(current_frame) * frame_height;
279955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
280955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            /* Get framebuffer pointer. */
281955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            CVPixelBufferLockBaseAddress(current_frame, 0);
282955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            const void* pixels = CVPixelBufferGetBaseAddress(current_frame);
283955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            if (pixels != nil) {
284955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine                /* Convert framebuffer. */
285955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine                res = convert_frame(pixels, pixel_format, frame_size,
286955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine                                    frame_width, frame_height,
28737fb84f8b26e3061c1ccb404bf4c962eed5e6057Vladimir Chtchetkine                                    framebuffers, fbs_num,
28837fb84f8b26e3061c1ccb404bf4c962eed5e6057Vladimir Chtchetkine                                    r_scale, g_scale, b_scale, exp_comp);
289955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            } else {
290955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine                E("%s: Unable to obtain framebuffer", __FUNCTION__);
291955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine                res = -1;
292955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            }
293955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            CVPixelBufferUnlockBaseAddress(current_frame, 0);
294955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        } else {
295955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            /* First frame didn't come in just yet. Let the caller repeat. */
296955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            res = 1;
297955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        }
298955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
299955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
300955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    return res;
301955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
302955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
303955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine- (void)captureOutput:(QTCaptureOutput*) captureOutput
304955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine                      didOutputVideoFrame:(CVImageBufferRef)videoFrame
305955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine                      withSampleBuffer:(QTSampleBuffer*) sampleBuffer
306955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine                      fromConnection:(QTCaptureConnection*) connection
307955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
308955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    CVImageBufferRef to_release;
309955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    CVBufferRetain(videoFrame);
310955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
311955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Frames are pulled by the client in another thread.
312955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine     * So we need a protection here. */
313955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    @synchronized (self)
314955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    {
315955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        to_release = current_frame;
316955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        current_frame = videoFrame;
317955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
318955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    CVBufferRelease(to_release);
319955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
320955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
321955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine@end
322955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
323955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/*******************************************************************************
324955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *                     CameraDevice routines
325955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine ******************************************************************************/
326955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
327955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkinetypedef struct MacCameraDevice MacCameraDevice;
328955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/* MacOS-specific camera device descriptor. */
329955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkinestruct MacCameraDevice {
330955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Common camera device descriptor. */
331955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    CameraDevice  header;
332955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Actual camera device object. */
333955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    MacCamera*    device;
334955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine};
335955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
336955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/* Allocates an instance of MacCameraDevice structure.
337955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * Return:
338955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *  Allocated instance of MacCameraDevice structure. Note that this routine
339955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *  also sets 'opaque' field in the 'header' structure to point back to the
340955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *  containing MacCameraDevice instance.
341955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine */
342955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkinestatic MacCameraDevice*
343955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine_camera_device_alloc(void)
344955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
345955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    MacCameraDevice* cd = (MacCameraDevice*)malloc(sizeof(MacCameraDevice));
346955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (cd != NULL) {
347955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        memset(cd, 0, sizeof(MacCameraDevice));
348955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        cd->header.opaque = cd;
349955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    } else {
350955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("%s: Unable to allocate MacCameraDevice instance", __FUNCTION__);
351955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
352955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    return cd;
353955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
354955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
355955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/* Uninitializes and frees MacCameraDevice descriptor.
356955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * Note that upon return from this routine memory allocated for the descriptor
357955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * will be freed.
358955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine */
359955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkinestatic void
360955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine_camera_device_free(MacCameraDevice* cd)
361955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
362955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (cd != NULL) {
363955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        if (cd->device != NULL) {
364955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            [cd->device free];
365955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            [cd->device release];
366955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine            cd->device = nil;
367955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        }
368955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        AFREE(cd);
369955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    } else {
370955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        W("%s: No descriptor", __FUNCTION__);
371955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
372955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
373955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
374955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/* Resets camera device after capturing.
375955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * Since new capture request may require different frame dimensions we must
376955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * reset frame info cached in the capture window. The only way to do that would
377955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * be closing, and reopening it again. */
378955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkinestatic void
379955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine_camera_device_reset(MacCameraDevice* cd)
380955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
381955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (cd != NULL && cd->device) {
382955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [cd->device free];
383955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        cd->device = [cd->device init];
384955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
385955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
386955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
387955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/*******************************************************************************
388955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine *                     CameraDevice API
389955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine ******************************************************************************/
390955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
391955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir ChtchetkineCameraDevice*
392955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkinecamera_device_open(const char* name, int inp_channel)
393955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
394955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    MacCameraDevice* mcd;
395955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
396955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    mcd = _camera_device_alloc();
397955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (mcd == NULL) {
398955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("%s: Unable to allocate MacCameraDevice instance", __FUNCTION__);
399955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return NULL;
400955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
401955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    mcd->device = [[MacCamera alloc] init];
402955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (mcd->device == nil) {
403955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("%s: Unable to initialize camera device.", __FUNCTION__);
404955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return NULL;
405955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
406955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    return &mcd->header;
407955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
408955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
409955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkineint
410955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkinecamera_device_start_capturing(CameraDevice* cd,
411955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine                              uint32_t pixel_format,
412955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine                              int frame_width,
413955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine                              int frame_height)
414955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
415955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    MacCameraDevice* mcd;
416955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
417955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Sanity checks. */
418955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (cd == NULL || cd->opaque == NULL) {
419955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("%s: Invalid camera device descriptor", __FUNCTION__);
420955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return -1;
421955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
422955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    mcd = (MacCameraDevice*)cd->opaque;
423955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (mcd->device == nil) {
424955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("%s: Camera device is not opened", __FUNCTION__);
425955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return -1;
426955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
427955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
428955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    return [mcd->device start_capturing:frame_width:frame_height];
429955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
430955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
431955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkineint
432955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkinecamera_device_stop_capturing(CameraDevice* cd)
433955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
434955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    MacCameraDevice* mcd;
435955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
436955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Sanity checks. */
437955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (cd == NULL || cd->opaque == NULL) {
438955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("%s: Invalid camera device descriptor", __FUNCTION__);
439955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return -1;
440955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
441955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    mcd = (MacCameraDevice*)cd->opaque;
442955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (mcd->device == nil) {
443955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("%s: Camera device is not opened", __FUNCTION__);
444955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return -1;
445955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
446955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
447955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Reset capture settings, so next call to capture can set its own. */
448955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    _camera_device_reset(mcd);
449955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
450955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    return 0;
451955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
452955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
453955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkineint
454955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkinecamera_device_read_frame(CameraDevice* cd,
455955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine                         ClientFrameBuffer* framebuffers,
45637fb84f8b26e3061c1ccb404bf4c962eed5e6057Vladimir Chtchetkine                         int fbs_num,
45737fb84f8b26e3061c1ccb404bf4c962eed5e6057Vladimir Chtchetkine                         float r_scale,
45837fb84f8b26e3061c1ccb404bf4c962eed5e6057Vladimir Chtchetkine                         float g_scale,
45937fb84f8b26e3061c1ccb404bf4c962eed5e6057Vladimir Chtchetkine                         float b_scale,
46037fb84f8b26e3061c1ccb404bf4c962eed5e6057Vladimir Chtchetkine                         float exp_comp)
461955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
462955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    MacCameraDevice* mcd;
463955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
464955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Sanity checks. */
465955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (cd == NULL || cd->opaque == NULL) {
466955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("%s: Invalid camera device descriptor", __FUNCTION__);
467955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return -1;
468955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
469955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    mcd = (MacCameraDevice*)cd->opaque;
470955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (mcd->device == nil) {
471955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("%s: Camera device is not opened", __FUNCTION__);
472955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return -1;
473955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
474955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
47537fb84f8b26e3061c1ccb404bf4c962eed5e6057Vladimir Chtchetkine    return [mcd->device read_frame:framebuffers:fbs_num:r_scale:g_scale:b_scale:exp_comp];
476955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
477955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
478955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkinevoid
479955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkinecamera_device_close(CameraDevice* cd)
480955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
481955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Sanity checks. */
482955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (cd == NULL || cd->opaque == NULL) {
483955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("%s: Invalid camera device descriptor", __FUNCTION__);
484955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    } else {
485955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        _camera_device_free((MacCameraDevice*)cd->opaque);
486955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
487955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
488955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
489955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkineint
490955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkineenumerate_camera_devices(CameraInfo* cis, int max)
491955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
492955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine/* Array containing emulated webcam frame dimensions.
493955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * QT API provides device independent frame dimensions, by scaling frames
494955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * received from the device to whatever dimensions were requested for the
495955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * output device. So, we can just use a small set of frame dimensions to
496955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine * emulate.
497955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine */
498955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkinestatic const CameraFrameDim _emulate_dims[] =
499955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine{
500955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine  /* Emulates 640x480 frame. */
501955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine  {640, 480},
5028d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine  /* Emulates 352x288 frame (required by camera framework). */
5038d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine  {352, 288},
504e080a45d3e29e41bce06669b79d5eb04f0fd05efVladimir Chtchetkine  /* Emulates 320x240 frame (required by camera framework). */
505e080a45d3e29e41bce06669b79d5eb04f0fd05efVladimir Chtchetkine  {320, 240},
5068d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine  /* Emulates 176x144 frame (required by camera framework). */
5078d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine  {176, 144}
508955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine};
509955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
510955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Obtain default video device. QT API doesn't really provide a reliable
511955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine     * way to identify camera devices. There is a QTCaptureDevice::uniqueId
512955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine     * method that supposedly does that, but in some cases it just doesn't
513955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine     * work. Until we figure out a reliable device identification, we will
514955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine     * stick to using only one (default) camera for emulation. */
515955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    QTCaptureDevice* video_dev =
516955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo];
517955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (video_dev == nil) {
518955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        D("No web cameras are connected to the host.");
519955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return 0;
520955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
521955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
522955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Obtain pixel format for the device. */
523955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    NSArray* pix_formats = [video_dev formatDescriptions];
524955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (pix_formats == nil || [pix_formats count] == 0) {
525955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("Unable to obtain pixel format for the default camera device.");
526955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [video_dev release];
527955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return 0;
528955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
529955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    const uint32_t qt_pix_format = [[pix_formats objectAtIndex:0] formatType];
530955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    [pix_formats release];
531955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
532955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Obtain FOURCC pixel format for the device. */
533955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    cis[0].pixel_format = _QTtoFOURCC(qt_pix_format);
534955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (cis[0].pixel_format == 0) {
535955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        /* Unsupported pixel format. */
536955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("Pixel format '%.4s' reported by the camera device is unsupported",
537955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine          (const char*)&qt_pix_format);
538955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [video_dev release];
539955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return 0;
540955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
541955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine
542955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    /* Initialize camera info structure. */
543955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    cis[0].frame_sizes = (CameraFrameDim*)malloc(sizeof(_emulate_dims));
544955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    if (cis[0].frame_sizes != NULL) {
545955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        cis[0].frame_sizes_num = sizeof(_emulate_dims) / sizeof(*_emulate_dims);
546955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        memcpy(cis[0].frame_sizes, _emulate_dims, sizeof(_emulate_dims));
547955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        cis[0].device_name = ASTRDUP("webcam0");
548955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        cis[0].inp_channel = 0;
549955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        cis[0].display_name = ASTRDUP("webcam0");
550955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        cis[0].in_use = 0;
551955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [video_dev release];
552955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return 1;
553955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    } else {
554955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        E("Unable to allocate memory for camera information.");
555955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        [video_dev release];
556955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine        return 0;
557955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine    }
558955a99781dd7af82e7a55525a4e51ce5d0814021Vladimir Chtchetkine}
559