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