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