Home | History | Annotate | Download | only in qtkit
      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 
     22 using namespace webrtc;
     23 using 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