Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright 2017 The WebRTC project authors. All Rights Reserved. | 2 * Copyright 2017 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 #import <Foundation/Foundation.h> | 11 #import <Foundation/Foundation.h> |
| 12 | 12 |
| 13 #include "webrtc/rtc_base/atomicops.h" | |
| 14 | |
| 13 #import "WebRTC/RTCCameraVideoCapturer.h" | 15 #import "WebRTC/RTCCameraVideoCapturer.h" |
| 14 #import "WebRTC/RTCLogging.h" | 16 #import "WebRTC/RTCLogging.h" |
| 15 #import "WebRTC/RTCVideoFrameBuffer.h" | 17 #import "WebRTC/RTCVideoFrameBuffer.h" |
| 16 | 18 |
| 17 #if TARGET_OS_IPHONE | 19 #if TARGET_OS_IPHONE |
| 18 #import "WebRTC/UIDevice+RTCDevice.h" | 20 #import "WebRTC/UIDevice+RTCDevice.h" |
| 19 #endif | 21 #endif |
| 20 | 22 |
| 21 #import "RTCDispatcher+Private.h" | 23 #import "RTCDispatcher+Private.h" |
| 24 #import "RTCImageHelper.h" | |
| 22 | 25 |
| 23 const int64_t kNanosecondsPerSecond = 1000000000; | 26 const int64_t kNanosecondsPerSecond = 1000000000; |
| 24 | 27 |
| 25 static inline BOOL IsMediaSubTypeSupported(FourCharCode mediaSubType) { | 28 static inline BOOL IsMediaSubTypeSupported(FourCharCode mediaSubType) { |
| 26 return (mediaSubType == kCVPixelFormatType_420YpCbCr8PlanarFullRange || | 29 return (mediaSubType == kCVPixelFormatType_420YpCbCr8PlanarFullRange || |
| 27 mediaSubType == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange); | 30 mediaSubType == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange); |
| 28 } | 31 } |
| 29 | 32 |
| 30 @interface RTCCameraVideoCapturer ()<AVCaptureVideoDataOutputSampleBufferDelegat e> | 33 @interface RTCCameraVideoCapturer ()<AVCaptureVideoDataOutputSampleBufferDelegat e> |
| 31 @property(nonatomic, readonly) dispatch_queue_t frameQueue; | 34 @property(nonatomic, readonly) dispatch_queue_t frameQueue; |
| 32 @end | 35 @end |
| 33 | 36 |
| 34 @implementation RTCCameraVideoCapturer { | 37 @implementation RTCCameraVideoCapturer { |
| 35 AVCaptureVideoDataOutput *_videoDataOutput; | 38 AVCaptureVideoDataOutput *_videoDataOutput; |
| 36 AVCaptureSession *_captureSession; | 39 AVCaptureSession *_captureSession; |
| 37 AVCaptureDevice *_currentDevice; | 40 AVCaptureDevice *_currentDevice; |
| 38 RTCVideoRotation _rotation; | |
| 39 BOOL _hasRetriedOnFatalError; | 41 BOOL _hasRetriedOnFatalError; |
| 40 BOOL _isRunning; | 42 BOOL _isRunning; |
| 41 // Will the session be running once all asynchronous operations have been comp leted? | 43 // Will the session be running once all asynchronous operations have been comp leted? |
| 42 BOOL _willBeRunning; | 44 BOOL _willBeRunning; |
| 45 #if TARGET_OS_IPHONE | |
| 46 volatile int _switchingCameras; | |
| 47 UIDeviceOrientation _orientation; | |
| 48 #endif | |
| 43 } | 49 } |
| 44 | 50 |
| 45 @synthesize frameQueue = _frameQueue; | 51 @synthesize frameQueue = _frameQueue; |
| 46 @synthesize captureSession = _captureSession; | 52 @synthesize captureSession = _captureSession; |
| 47 | 53 |
| 48 - (instancetype)initWithDelegate:(__weak id<RTCVideoCapturerDelegate>)delegate { | 54 - (instancetype)initWithDelegate:(__weak id<RTCVideoCapturerDelegate>)delegate { |
| 49 if (self = [super initWithDelegate:delegate]) { | 55 if (self = [super initWithDelegate:delegate]) { |
| 50 // Create the capture session and all relevant inputs and outputs. We need | 56 // Create the capture session and all relevant inputs and outputs. We need |
| 51 // to do this in init because the application may want the capture session | 57 // to do this in init because the application may want the capture session |
| 52 // before we start the capturer for e.g. AVCapturePreviewLayer. All objects | 58 // before we start the capturer for e.g. AVCapturePreviewLayer. All objects |
| 53 // created here are retained until dealloc and never recreated. | 59 // created here are retained until dealloc and never recreated. |
| 54 if (![self setupCaptureSession]) { | 60 if (![self setupCaptureSession]) { |
| 55 return nil; | 61 return nil; |
| 56 } | 62 } |
| 57 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; | 63 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; |
| 58 #if TARGET_OS_IPHONE | 64 #if TARGET_OS_IPHONE |
| 65 _switchingCameras = 0; | |
| 66 _orientation = UIDeviceOrientationPortrait; | |
| 59 [center addObserver:self | 67 [center addObserver:self |
| 60 selector:@selector(deviceOrientationDidChange:) | 68 selector:@selector(deviceOrientationDidChange:) |
| 61 name:UIDeviceOrientationDidChangeNotification | 69 name:UIDeviceOrientationDidChangeNotification |
| 62 object:nil]; | 70 object:nil]; |
| 63 [center addObserver:self | 71 [center addObserver:self |
| 64 selector:@selector(handleCaptureSessionInterruption:) | 72 selector:@selector(handleCaptureSessionInterruption:) |
| 65 name:AVCaptureSessionWasInterruptedNotification | 73 name:AVCaptureSessionWasInterruptedNotification |
| 66 object:_captureSession]; | 74 object:_captureSession]; |
| 67 [center addObserver:self | 75 [center addObserver:self |
| 68 selector:@selector(handleCaptureSessionInterruptionEnded:) | 76 selector:@selector(handleCaptureSessionInterruptionEnded:) |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 110 [eligibleDeviceFormats addObject:format]; | 118 [eligibleDeviceFormats addObject:format]; |
| 111 } | 119 } |
| 112 } | 120 } |
| 113 | 121 |
| 114 return eligibleDeviceFormats; | 122 return eligibleDeviceFormats; |
| 115 } | 123 } |
| 116 | 124 |
| 117 - (void)startCaptureWithDevice:(AVCaptureDevice *)device | 125 - (void)startCaptureWithDevice:(AVCaptureDevice *)device |
| 118 format:(AVCaptureDeviceFormat *)format | 126 format:(AVCaptureDeviceFormat *)format |
| 119 fps:(NSInteger)fps { | 127 fps:(NSInteger)fps { |
| 120 _willBeRunning = true; | 128 _willBeRunning = YES; |
| 121 [RTCDispatcher | 129 [RTCDispatcher |
| 122 dispatchAsyncOnType:RTCDispatcherTypeCaptureSession | 130 dispatchAsyncOnType:RTCDispatcherTypeCaptureSession |
| 123 block:^{ | 131 block:^{ |
| 124 RTCLogInfo("startCaptureWithDevice %@ @ %zd fps", format, fps); | 132 RTCLogInfo("startCaptureWithDevice %@ @ %zd fps", format, fps); |
| 125 | 133 |
| 126 #if TARGET_OS_IPHONE | 134 #if TARGET_OS_IPHONE |
| 127 [[UIDevice currentDevice] beginGeneratingDeviceOrientation Notifications]; | 135 [[UIDevice currentDevice] beginGeneratingDeviceOrientation Notifications]; |
| 128 #endif | 136 #endif |
| 129 | 137 |
| 130 _currentDevice = device; | 138 _currentDevice = device; |
| 131 | 139 |
| 132 NSError *error = nil; | 140 NSError *error = nil; |
| 133 if (![_currentDevice lockForConfiguration:&error]) { | 141 if (![_currentDevice lockForConfiguration:&error]) { |
| 134 RTCLogError( | 142 RTCLogError( |
| 135 @"Failed to lock device %@. Error: %@", _currentDevi ce, error.userInfo); | 143 @"Failed to lock device %@. Error: %@", _currentDevi ce, error.userInfo); |
| 136 return; | 144 return; |
| 137 } | 145 } |
| 138 | 146 #if TARGET_OS_IPHONE |
| 147 rtc::AtomicOps::Increment(&_switchingCameras); | |
| 148 #endif | |
| 139 [self reconfigureCaptureSessionInput]; | 149 [self reconfigureCaptureSessionInput]; |
| 140 [self updateOrientation]; | 150 [self updateOrientation]; |
| 141 [_captureSession startRunning]; | 151 [_captureSession startRunning]; |
| 142 [self updateDeviceCaptureFormat:format fps:fps]; | 152 [self updateDeviceCaptureFormat:format fps:fps]; |
| 143 [_currentDevice unlockForConfiguration]; | 153 [_currentDevice unlockForConfiguration]; |
| 144 _isRunning = true; | 154 _isRunning = YES; |
| 155 #if TARGET_OS_IPHONE | |
| 156 rtc::AtomicOps::Decrement(&_switchingCameras); | |
| 157 #endif | |
| 145 }]; | 158 }]; |
| 146 } | 159 } |
| 147 | 160 |
| 148 - (void)stopCapture { | 161 - (void)stopCapture { |
| 149 _willBeRunning = false; | 162 _willBeRunning = NO; |
| 150 [RTCDispatcher | 163 [RTCDispatcher |
| 151 dispatchAsyncOnType:RTCDispatcherTypeCaptureSession | 164 dispatchAsyncOnType:RTCDispatcherTypeCaptureSession |
| 152 block:^{ | 165 block:^{ |
| 153 RTCLogInfo("Stop"); | 166 RTCLogInfo("Stop"); |
| 154 _currentDevice = nil; | 167 _currentDevice = nil; |
| 155 for (AVCaptureDeviceInput *oldInput in [_captureSession.in puts copy]) { | 168 for (AVCaptureDeviceInput *oldInput in [_captureSession.in puts copy]) { |
| 156 [_captureSession removeInput:oldInput]; | 169 [_captureSession removeInput:oldInput]; |
| 157 } | 170 } |
| 158 [_captureSession stopRunning]; | 171 [_captureSession stopRunning]; |
| 159 | 172 |
| 160 #if TARGET_OS_IPHONE | 173 #if TARGET_OS_IPHONE |
| 161 [[UIDevice currentDevice] endGeneratingDeviceOrientationNo tifications]; | 174 [[UIDevice currentDevice] endGeneratingDeviceOrientationNo tifications]; |
| 162 #endif | 175 #endif |
| 163 _isRunning = false; | 176 _isRunning = NO; |
| 164 }]; | 177 }]; |
| 165 } | 178 } |
| 166 | 179 |
| 167 #pragma mark iOS notifications | 180 #pragma mark iOS notifications |
| 168 | 181 |
| 169 #if TARGET_OS_IPHONE | 182 #if TARGET_OS_IPHONE |
| 170 - (void)deviceOrientationDidChange:(NSNotification *)notification { | 183 - (void)deviceOrientationDidChange:(NSNotification *)notification { |
| 171 [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeCaptureSession | 184 [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeCaptureSession |
| 172 block:^{ | 185 block:^{ |
| 173 [self updateOrientation]; | 186 [self updateOrientation]; |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 185 if (CMSampleBufferGetNumSamples(sampleBuffer) != 1 || !CMSampleBufferIsValid(s ampleBuffer) || | 198 if (CMSampleBufferGetNumSamples(sampleBuffer) != 1 || !CMSampleBufferIsValid(s ampleBuffer) || |
| 186 !CMSampleBufferDataIsReady(sampleBuffer)) { | 199 !CMSampleBufferDataIsReady(sampleBuffer)) { |
| 187 return; | 200 return; |
| 188 } | 201 } |
| 189 | 202 |
| 190 CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); | 203 CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); |
| 191 if (pixelBuffer == nil) { | 204 if (pixelBuffer == nil) { |
| 192 return; | 205 return; |
| 193 } | 206 } |
| 194 | 207 |
| 208 #if TARGET_OS_IPHONE | |
| 209 // Default to portrait orientation on iPhone. | |
| 210 RTCVideoRotation rotation = RTCVideoRotation_90; | |
| 211 // Check here, which camera this frame is from, to avoid any race conditions. | |
| 212 AVCaptureDeviceInput *deviceInput = | |
| 213 (AVCaptureDeviceInput *)((AVCaptureInputPort *)connection.inputPorts.first Object).input; | |
| 214 BOOL usingFrontCamera = deviceInput.device.position == AVCaptureDevicePosition Front; | |
| 215 if (_switchingCameras) { | |
|
tkchin_webrtc
2017/07/24 23:25:21
since you are using atomicops, use AcquireLoad
(th
| |
| 216 // Check the image's EXIF for the actual camera the image came as the image could have been | |
| 217 // delayed as we set alwaysDiscardsLateVideoFrames to NO. | |
| 218 AVCaptureDevicePosition cameraPosition = | |
| 219 [RTCImageHelper devicePositionForSampleBuffer:sampleBuffer]; | |
| 220 if (cameraPosition != AVCaptureDevicePositionUnspecified) { | |
| 221 usingFrontCamera = cameraPosition == AVCaptureDevicePositionFront; | |
| 222 } | |
| 223 } | |
| 224 switch (_orientation) { | |
| 225 case UIDeviceOrientationPortrait: | |
| 226 rotation = RTCVideoRotation_90; | |
| 227 break; | |
| 228 case UIDeviceOrientationPortraitUpsideDown: | |
| 229 rotation = RTCVideoRotation_270; | |
| 230 break; | |
| 231 case UIDeviceOrientationLandscapeLeft: | |
| 232 rotation = usingFrontCamera ? RTCVideoRotation_180 : RTCVideoRotation_0; | |
| 233 break; | |
| 234 case UIDeviceOrientationLandscapeRight: | |
| 235 rotation = usingFrontCamera ? RTCVideoRotation_0 : RTCVideoRotation_180; | |
| 236 break; | |
| 237 case UIDeviceOrientationFaceUp: | |
| 238 case UIDeviceOrientationFaceDown: | |
| 239 case UIDeviceOrientationUnknown: | |
| 240 // Ignore. | |
| 241 break; | |
| 242 } | |
| 243 #else | |
| 244 // No rotation on Mac. | |
| 245 RTCVideoRotation rotation = RTCVideoRotation_0; | |
| 246 #endif | |
| 247 | |
| 195 RTCCVPixelBuffer *rtcPixelBuffer = [[RTCCVPixelBuffer alloc] initWithPixelBuff er:pixelBuffer]; | 248 RTCCVPixelBuffer *rtcPixelBuffer = [[RTCCVPixelBuffer alloc] initWithPixelBuff er:pixelBuffer]; |
| 196 int64_t timeStampNs = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp( sampleBuffer)) * | 249 int64_t timeStampNs = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp( sampleBuffer)) * |
| 197 kNanosecondsPerSecond; | 250 kNanosecondsPerSecond; |
| 198 RTCVideoFrame *videoFrame = [[RTCVideoFrame alloc] initWithBuffer:rtcPixelBuff er | 251 RTCVideoFrame *videoFrame = [[RTCVideoFrame alloc] initWithBuffer:rtcPixelBuff er |
| 199 rotation:_rotation | 252 rotation:rotation |
| 200 timeStampNs:timeStampNs] ; | 253 timeStampNs:timeStampNs] ; |
| 201 [self.delegate capturer:self didCaptureVideoFrame:videoFrame]; | 254 [self.delegate capturer:self didCaptureVideoFrame:videoFrame]; |
| 202 } | 255 } |
| 203 | 256 |
| 204 - (void)captureOutput:(AVCaptureOutput *)captureOutput | 257 - (void)captureOutput:(AVCaptureOutput *)captureOutput |
| 205 didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer | 258 didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer |
| 206 fromConnection:(AVCaptureConnection *)connection { | 259 fromConnection:(AVCaptureConnection *)connection { |
| 207 RTCLogError(@"Dropped sample buffer."); | 260 RTCLogError(@"Dropped sample buffer."); |
| 208 } | 261 } |
| 209 | 262 |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 392 } else { | 445 } else { |
| 393 RTCLogError(@"Cannot add camera as an input to the session."); | 446 RTCLogError(@"Cannot add camera as an input to the session."); |
| 394 } | 447 } |
| 395 [_captureSession commitConfiguration]; | 448 [_captureSession commitConfiguration]; |
| 396 } | 449 } |
| 397 | 450 |
| 398 - (void)updateOrientation { | 451 - (void)updateOrientation { |
| 399 NSAssert([RTCDispatcher isOnQueueForType:RTCDispatcherTypeCaptureSession], | 452 NSAssert([RTCDispatcher isOnQueueForType:RTCDispatcherTypeCaptureSession], |
| 400 @"updateOrientation must be called on the capture queue."); | 453 @"updateOrientation must be called on the capture queue."); |
| 401 #if TARGET_OS_IPHONE | 454 #if TARGET_OS_IPHONE |
| 402 BOOL usingFrontCamera = _currentDevice.position == AVCaptureDevicePositionFron t; | 455 _orientation = [UIDevice currentDevice].orientation; |
| 403 switch ([UIDevice currentDevice].orientation) { | |
| 404 case UIDeviceOrientationPortrait: | |
| 405 _rotation = RTCVideoRotation_90; | |
| 406 break; | |
| 407 case UIDeviceOrientationPortraitUpsideDown: | |
| 408 _rotation = RTCVideoRotation_270; | |
| 409 break; | |
| 410 case UIDeviceOrientationLandscapeLeft: | |
| 411 _rotation = usingFrontCamera ? RTCVideoRotation_180 : RTCVideoRotation_0; | |
| 412 break; | |
| 413 case UIDeviceOrientationLandscapeRight: | |
| 414 _rotation = usingFrontCamera ? RTCVideoRotation_0 : RTCVideoRotation_180; | |
| 415 break; | |
| 416 case UIDeviceOrientationFaceUp: | |
| 417 case UIDeviceOrientationFaceDown: | |
| 418 case UIDeviceOrientationUnknown: | |
| 419 // Ignore. | |
| 420 break; | |
| 421 } | |
| 422 #endif | 456 #endif |
| 423 } | 457 } |
| 424 | 458 |
| 425 @end | 459 @end |
| OLD | NEW |