1/*
2 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#define DEFAULT_CAPTURE_DEVICE_INDEX    1
12#define DEFAULT_FRAME_RATE              30
13#define DEFAULT_FRAME_WIDTH             352
14#define DEFAULT_FRAME_HEIGHT            288
15#define ROTATE_CAPTURED_FRAME           1
16#define LOW_QUALITY                     1
17
18#import "webrtc/modules/video_capture/mac/qtkit/video_capture_qtkit_objc.h"
19
20#include "webrtc/system_wrappers/include/trace.h"
21
22using namespace webrtc;
23using namespace videocapturemodule;
24
25@implementation VideoCaptureMacQTKitObjC
26
27-(id)init {
28  self = [super init];
29  if (self) {
30    [self initializeVariables];
31  }
32  return self;
33}
34
35- (void)dealloc {
36  if (_captureSession)
37    [_captureSession stopRunning];
38
39  if (_captureVideoDeviceInput) {
40    if ([[_captureVideoDeviceInput device] isOpen])
41      [[_captureVideoDeviceInput device] close];
42
43    [_captureVideoDeviceInput release];
44  }
45
46  [_captureDecompressedVideoOutput release];
47  [_captureSession release];
48  [_captureDevices release];
49  [_lock release];
50
51  [super dealloc];
52}
53
54#pragma mark Public methods
55
56- (void)registerOwner:(VideoCaptureMacQTKit*)owner {
57  [_lock lock];
58  _owner = owner;
59  [_lock unlock];
60}
61
62- (BOOL)setCaptureDeviceById:(char*)uniqueId {
63  if (uniqueId == nil || !strcmp("", uniqueId)) {
64    WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0,
65                 "Incorrect capture id argument");
66    return NO;
67  }
68
69  if (!strcmp(uniqueId, _captureDeviceNameUniqueID))
70    return YES;
71
72  QTCaptureDevice* captureDevice;
73  for(int index = 0; index < _captureDeviceCount; index++) {
74    captureDevice = (QTCaptureDevice*)[_captureDevices objectAtIndex:index];
75    char captureDeviceId[1024] = "";
76    [[captureDevice uniqueID] getCString:captureDeviceId
77                               maxLength:1024
78                                encoding:NSUTF8StringEncoding];
79    if (strcmp(uniqueId, captureDeviceId) == 0) {
80      WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0,
81                   "%s:%d Found capture device id %s as index %d",
82                   __FUNCTION__, __LINE__, captureDeviceId, index);
83      [[captureDevice localizedDisplayName] getCString:_captureDeviceNameUTF8
84                                             maxLength:1024
85                                              encoding:NSUTF8StringEncoding];
86      [[captureDevice uniqueID] getCString:_captureDeviceNameUniqueID
87                                 maxLength:1024
88                                  encoding:NSUTF8StringEncoding];
89      break;
90    }
91    captureDevice = nil;
92  }
93
94  if (!captureDevice)
95    return NO;
96
97  NSError* error;
98  if (![captureDevice open:&error]) {
99    WEBRTC_TRACE(kTraceError, kTraceVideoCapture, 0,
100                 "Failed to open capture device: %s", _captureDeviceNameUTF8);
101    return NO;
102  }
103
104  if (_captureVideoDeviceInput) {
105    [_captureVideoDeviceInput release];
106  }
107  _captureVideoDeviceInput =
108      [[QTCaptureDeviceInput alloc] initWithDevice:captureDevice];
109
110  if (![_captureSession addInput:_captureVideoDeviceInput error:&error]) {
111    WEBRTC_TRACE(kTraceError, kTraceVideoCapture, 0,
112                 "Failed to add input from %s to the capture session",
113                 _captureDeviceNameUTF8);
114    return NO;
115  }
116
117  WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0,
118               "%s:%d successfully added capture device: %s", __FUNCTION__,
119               __LINE__, _captureDeviceNameUTF8);
120  return YES;
121}
122
123- (void)setCaptureHeight:(int)height width:(int)width frameRate:(int)frameRate {
124  _frameWidth = width;
125  _frameHeight = height;
126  _frameRate = frameRate;
127
128  NSDictionary* captureDictionary =
129      [NSDictionary dictionaryWithObjectsAndKeys:
130          [NSNumber numberWithDouble:_frameWidth],
131          (id)kCVPixelBufferWidthKey,
132          [NSNumber numberWithDouble:_frameHeight],
133          (id)kCVPixelBufferHeightKey,
134          [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32ARGB],
135          (id)kCVPixelBufferPixelFormatTypeKey,
136          nil];
137  [_captureDecompressedVideoOutput
138      performSelectorOnMainThread:@selector(setPixelBufferAttributes:)
139                       withObject:captureDictionary
140                    waitUntilDone:YES];
141}
142
143- (void)startCapture {
144  if (_capturing)
145    return;
146
147  [_captureSession startRunning];
148  _capturing = YES;
149}
150
151- (void)stopCapture {
152  if (!_capturing)
153    return;
154
155  [_captureSession stopRunning];
156  _capturing = NO;
157}
158
159#pragma mark Private methods
160
161- (BOOL)initializeVariables {
162  if (NSClassFromString(@"QTCaptureSession") == nil)
163    return NO;
164
165  memset(_captureDeviceNameUTF8, 0, 1024);
166  _framesDelivered = 0;
167  _framesRendered = 0;
168  _captureDeviceCount = 0;
169  _capturing = NO;
170  _captureInitialized = NO;
171  _frameRate = DEFAULT_FRAME_RATE;
172  _frameWidth = DEFAULT_FRAME_WIDTH;
173  _frameHeight = DEFAULT_FRAME_HEIGHT;
174  _lock = [[NSLock alloc] init];
175  _captureSession = [[QTCaptureSession alloc] init];
176  _captureDecompressedVideoOutput =
177      [[QTCaptureDecompressedVideoOutput alloc] init];
178  [_captureDecompressedVideoOutput setDelegate:self];
179
180  [self getCaptureDevices];
181  if (![self initializeVideoCapture])
182    return NO;
183
184  return NO;
185}
186
187- (void)getCaptureDevices {
188  if (_captureDevices)
189    [_captureDevices release];
190
191  _captureDevices = [[NSArray alloc] initWithArray:
192      [QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo]];
193
194  _captureDeviceCount = _captureDevices.count;
195}
196
197- (BOOL)initializeVideoCapture{
198  NSDictionary *captureDictionary =
199      [NSDictionary dictionaryWithObjectsAndKeys:
200          [NSNumber numberWithDouble:_frameWidth],
201          (id)kCVPixelBufferWidthKey,
202          [NSNumber numberWithDouble:_frameHeight],
203          (id)kCVPixelBufferHeightKey,
204          [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32ARGB],
205          (id)kCVPixelBufferPixelFormatTypeKey,
206          nil];
207
208  [_captureDecompressedVideoOutput setPixelBufferAttributes:captureDictionary];
209  [_captureDecompressedVideoOutput setAutomaticallyDropsLateVideoFrames:YES];
210  [_captureDecompressedVideoOutput
211      setMinimumVideoFrameInterval:(NSTimeInterval)1/(float)_frameRate];
212
213  NSError *error;
214  if (![_captureSession addOutput:_captureDecompressedVideoOutput error:&error])
215    return NO;
216
217  return YES;
218}
219
220- (void)captureOutput:(QTCaptureOutput *)captureOutput
221    didDropVideoFrameWithSampleBuffer:(QTSampleBuffer *)sampleBuffer
222                       fromConnection:(QTCaptureConnection *)connection {
223  // TODO(mflodman) Experiment more when this happens.
224}
225
226- (void)captureOutput:(QTCaptureOutput *)captureOutput
227  didOutputVideoFrame:(CVImageBufferRef)videoFrame
228     withSampleBuffer:(QTSampleBuffer *)sampleBuffer
229       fromConnection:(QTCaptureConnection *)connection {
230
231  [_lock lock];
232  if (!_owner) {
233    [_lock unlock];
234    return;
235  }
236
237  const int kFlags = 0;
238  if (CVPixelBufferLockBaseAddress(videoFrame, kFlags) == kCVReturnSuccess) {
239    void *baseAddress = CVPixelBufferGetBaseAddress(videoFrame);
240    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(videoFrame);
241    size_t frameHeight = CVPixelBufferGetHeight(videoFrame);
242    size_t frameSize = bytesPerRow * frameHeight;
243
244    VideoCaptureCapability tempCaptureCapability;
245    tempCaptureCapability.width = _frameWidth;
246    tempCaptureCapability.height = _frameHeight;
247    tempCaptureCapability.maxFPS = _frameRate;
248    // TODO(wu) : Update actual type and not hard-coded value.
249    tempCaptureCapability.rawType = kVideoBGRA;
250
251    _owner->IncomingFrame((unsigned char*)baseAddress, frameSize,
252                          tempCaptureCapability, 0);
253    CVPixelBufferUnlockBaseAddress(videoFrame, kFlags);
254  }
255  [_lock unlock];
256  _framesDelivered++;
257  _framesRendered++;
258}
259
260@end
261