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 |
(...skipping 17 matching lines...) Expand all Loading... | |
28 } | 28 } |
29 | 29 |
30 @interface RTCCameraVideoCapturer ()<AVCaptureVideoDataOutputSampleBufferDelegat e> | 30 @interface RTCCameraVideoCapturer ()<AVCaptureVideoDataOutputSampleBufferDelegat e> |
31 @property(nonatomic, readonly) dispatch_queue_t frameQueue; | 31 @property(nonatomic, readonly) dispatch_queue_t frameQueue; |
32 @end | 32 @end |
33 | 33 |
34 @implementation RTCCameraVideoCapturer { | 34 @implementation RTCCameraVideoCapturer { |
35 AVCaptureVideoDataOutput *_videoDataOutput; | 35 AVCaptureVideoDataOutput *_videoDataOutput; |
36 AVCaptureSession *_captureSession; | 36 AVCaptureSession *_captureSession; |
37 AVCaptureDevice *_currentDevice; | 37 AVCaptureDevice *_currentDevice; |
38 RTCVideoRotation _rotation; | |
39 BOOL _hasRetriedOnFatalError; | 38 BOOL _hasRetriedOnFatalError; |
40 BOOL _isRunning; | 39 BOOL _isRunning; |
41 // Will the session be running once all asynchronous operations have been comp leted? | 40 // Will the session be running once all asynchronous operations have been comp leted? |
42 BOOL _willBeRunning; | 41 BOOL _willBeRunning; |
42 #if TARGET_OS_IPHONE | |
43 UIDeviceOrientation _orientation; | |
44 #endif | |
43 } | 45 } |
44 | 46 |
45 @synthesize frameQueue = _frameQueue; | 47 @synthesize frameQueue = _frameQueue; |
46 @synthesize captureSession = _captureSession; | 48 @synthesize captureSession = _captureSession; |
47 | 49 |
48 - (instancetype)initWithDelegate:(__weak id<RTCVideoCapturerDelegate>)delegate { | 50 - (instancetype)initWithDelegate:(__weak id<RTCVideoCapturerDelegate>)delegate { |
49 if (self = [super initWithDelegate:delegate]) { | 51 if (self = [super initWithDelegate:delegate]) { |
50 // Create the capture session and all relevant inputs and outputs. We need | 52 // 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 | 53 // 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 | 54 // before we start the capturer for e.g. AVCapturePreviewLayer. All objects |
53 // created here are retained until dealloc and never recreated. | 55 // created here are retained until dealloc and never recreated. |
54 if (![self setupCaptureSession]) { | 56 if (![self setupCaptureSession]) { |
55 return nil; | 57 return nil; |
56 } | 58 } |
57 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; | 59 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; |
58 #if TARGET_OS_IPHONE | 60 #if TARGET_OS_IPHONE |
61 _orientation = UIDeviceOrientationPortrait; | |
59 [center addObserver:self | 62 [center addObserver:self |
60 selector:@selector(deviceOrientationDidChange:) | 63 selector:@selector(deviceOrientationDidChange:) |
61 name:UIDeviceOrientationDidChangeNotification | 64 name:UIDeviceOrientationDidChangeNotification |
62 object:nil]; | 65 object:nil]; |
63 [center addObserver:self | 66 [center addObserver:self |
64 selector:@selector(handleCaptureSessionInterruption:) | 67 selector:@selector(handleCaptureSessionInterruption:) |
65 name:AVCaptureSessionWasInterruptedNotification | 68 name:AVCaptureSessionWasInterruptedNotification |
66 object:_captureSession]; | 69 object:_captureSession]; |
67 [center addObserver:self | 70 [center addObserver:self |
68 selector:@selector(handleCaptureSessionInterruptionEnded:) | 71 selector:@selector(handleCaptureSessionInterruptionEnded:) |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
110 [eligibleDeviceFormats addObject:format]; | 113 [eligibleDeviceFormats addObject:format]; |
111 } | 114 } |
112 } | 115 } |
113 | 116 |
114 return eligibleDeviceFormats; | 117 return eligibleDeviceFormats; |
115 } | 118 } |
116 | 119 |
117 - (void)startCaptureWithDevice:(AVCaptureDevice *)device | 120 - (void)startCaptureWithDevice:(AVCaptureDevice *)device |
118 format:(AVCaptureDeviceFormat *)format | 121 format:(AVCaptureDeviceFormat *)format |
119 fps:(NSInteger)fps { | 122 fps:(NSInteger)fps { |
123 _isRunning = false; | |
magjed_webrtc
2017/06/30 17:50:50
Why is this needed?
jtt_webrtc
2017/06/30 17:54:49
We need to stop frames from being processed until
jtt_webrtc
2017/06/30 18:07:47
I removed this check and the flicker came back.
T
magjed_webrtc
2017/07/03 08:23:46
Why does it flicker if we remove this? I thought i
| |
120 _willBeRunning = true; | 124 _willBeRunning = true; |
121 [RTCDispatcher | 125 [RTCDispatcher |
122 dispatchAsyncOnType:RTCDispatcherTypeCaptureSession | 126 dispatchAsyncOnType:RTCDispatcherTypeCaptureSession |
123 block:^{ | 127 block:^{ |
124 RTCLogInfo("startCaptureWithDevice %@ @ %zd fps", format, fps); | 128 RTCLogInfo("startCaptureWithDevice %@ @ %zd fps", format, fps); |
125 | 129 |
126 #if TARGET_OS_IPHONE | 130 #if TARGET_OS_IPHONE |
127 [[UIDevice currentDevice] beginGeneratingDeviceOrientation Notifications]; | 131 [[UIDevice currentDevice] beginGeneratingDeviceOrientation Notifications]; |
128 #endif | 132 #endif |
129 | 133 |
130 _currentDevice = device; | 134 _currentDevice = device; |
131 | 135 |
132 NSError *error = nil; | 136 NSError *error = nil; |
133 if (![_currentDevice lockForConfiguration:&error]) { | 137 if (![_currentDevice lockForConfiguration:&error]) { |
134 RTCLogError( | 138 RTCLogError( |
135 @"Failed to lock device %@. Error: %@", _currentDevi ce, error.userInfo); | 139 @"Failed to lock device %@. Error: %@", _currentDevi ce, error.userInfo); |
140 _isRunning = true; | |
magjed_webrtc
2017/06/30 17:50:50
Same here, why is this change needed?
jtt_webrtc
2017/06/30 17:54:49
See above. We don't want to process frames until t
| |
136 return; | 141 return; |
137 } | 142 } |
138 | 143 |
139 [self reconfigureCaptureSessionInput]; | 144 [self reconfigureCaptureSessionInput]; |
140 [self updateOrientation]; | 145 [self updateOrientation]; |
141 [_captureSession startRunning]; | 146 [_captureSession startRunning]; |
142 [self updateDeviceCaptureFormat:format fps:fps]; | 147 [self updateDeviceCaptureFormat:format fps:fps]; |
143 [_currentDevice unlockForConfiguration]; | 148 [_currentDevice unlockForConfiguration]; |
144 _isRunning = true; | 149 _isRunning = true; |
145 }]; | 150 }]; |
(...skipping 28 matching lines...) Expand all Loading... | |
174 }]; | 179 }]; |
175 } | 180 } |
176 #endif | 181 #endif |
177 | 182 |
178 #pragma mark AVCaptureVideoDataOutputSampleBufferDelegate | 183 #pragma mark AVCaptureVideoDataOutputSampleBufferDelegate |
179 | 184 |
180 - (void)captureOutput:(AVCaptureOutput *)captureOutput | 185 - (void)captureOutput:(AVCaptureOutput *)captureOutput |
181 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer | 186 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer |
182 fromConnection:(AVCaptureConnection *)connection { | 187 fromConnection:(AVCaptureConnection *)connection { |
183 NSParameterAssert(captureOutput == _videoDataOutput); | 188 NSParameterAssert(captureOutput == _videoDataOutput); |
189 NSParameterAssert(1 == connection.inputPorts.count); | |
184 | 190 |
185 if (CMSampleBufferGetNumSamples(sampleBuffer) != 1 || !CMSampleBufferIsValid(s ampleBuffer) || | 191 if (!_isRunning || !_willBeRunning || |
magjed_webrtc
2017/06/30 17:50:50
Why do we need these extra checks? What happens ot
jtt_webrtc
2017/06/30 17:54:49
See above, we still get the flicker but more rare.
| |
186 !CMSampleBufferDataIsReady(sampleBuffer)) { | 192 CMSampleBufferGetNumSamples(sampleBuffer) != 1 || |
193 !CMSampleBufferIsValid(sampleBuffer) || | |
194 !CMSampleBufferDataIsReady(sampleBuffer) || !connection.enabled || | |
195 !connection.active) { | |
187 return; | 196 return; |
188 } | 197 } |
189 | 198 |
190 CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); | 199 CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); |
191 if (pixelBuffer == nil) { | 200 if (pixelBuffer == nil) { |
192 return; | 201 return; |
193 } | 202 } |
194 | 203 |
204 #if TARGET_OS_IPHONE | |
205 // Default to portrait orientation on iPhone. | |
206 RTCVideoRotation rotation = RTCVideoRotation_90; | |
207 AVCaptureDeviceInput *deviceInput = | |
208 (AVCaptureDeviceInput *)((AVCaptureInputPort *) | |
209 connection.inputPorts.firstObject) | |
210 .input; | |
211 BOOL usingFrontCamera = | |
212 deviceInput.device.position == AVCaptureDevicePositionFront; | |
213 switch (_orientation) { | |
214 case UIDeviceOrientationPortrait: | |
215 rotation = RTCVideoRotation_90; | |
216 break; | |
217 case UIDeviceOrientationPortraitUpsideDown: | |
218 rotation = RTCVideoRotation_270; | |
219 break; | |
220 case UIDeviceOrientationLandscapeLeft: | |
221 rotation = usingFrontCamera ? RTCVideoRotation_180 : RTCVideoRotation_0; | |
222 break; | |
223 case UIDeviceOrientationLandscapeRight: | |
224 rotation = usingFrontCamera ? RTCVideoRotation_0 : RTCVideoRotation_180; | |
225 break; | |
226 case UIDeviceOrientationFaceUp: | |
227 case UIDeviceOrientationFaceDown: | |
228 case UIDeviceOrientationUnknown: | |
229 // Ignore. | |
230 break; | |
231 } | |
232 #else | |
233 // No rotation on Mac. | |
234 RTCVideoRotation rotation = RTCVideoRotation_0; | |
235 #endif | |
236 | |
195 RTCCVPixelBuffer *rtcPixelBuffer = [[RTCCVPixelBuffer alloc] initWithPixelBuff er:pixelBuffer]; | 237 RTCCVPixelBuffer *rtcPixelBuffer = [[RTCCVPixelBuffer alloc] initWithPixelBuff er:pixelBuffer]; |
196 int64_t timeStampNs = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp( sampleBuffer)) * | 238 int64_t timeStampNs = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp( sampleBuffer)) * |
197 kNanosecondsPerSecond; | 239 kNanosecondsPerSecond; |
198 RTCVideoFrame *videoFrame = [[RTCVideoFrame alloc] initWithBuffer:rtcPixelBuff er | 240 RTCVideoFrame *videoFrame = |
199 rotation:_rotation | 241 [[RTCVideoFrame alloc] initWithBuffer:rtcPixelBuffer |
200 timeStampNs:timeStampNs] ; | 242 rotation:rotation |
243 timeStampNs:timeStampNs]; | |
201 [self.delegate capturer:self didCaptureVideoFrame:videoFrame]; | 244 [self.delegate capturer:self didCaptureVideoFrame:videoFrame]; |
202 } | 245 } |
203 | 246 |
204 - (void)captureOutput:(AVCaptureOutput *)captureOutput | 247 - (void)captureOutput:(AVCaptureOutput *)captureOutput |
205 didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer | 248 didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer |
206 fromConnection:(AVCaptureConnection *)connection { | 249 fromConnection:(AVCaptureConnection *)connection { |
207 RTCLogError(@"Dropped sample buffer."); | 250 RTCLogError(@"Dropped sample buffer."); |
208 } | 251 } |
209 | 252 |
210 #pragma mark - AVCaptureSession notifications | 253 #pragma mark - AVCaptureSession notifications |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
392 } else { | 435 } else { |
393 RTCLogError(@"Cannot add camera as an input to the session."); | 436 RTCLogError(@"Cannot add camera as an input to the session."); |
394 } | 437 } |
395 [_captureSession commitConfiguration]; | 438 [_captureSession commitConfiguration]; |
396 } | 439 } |
397 | 440 |
398 - (void)updateOrientation { | 441 - (void)updateOrientation { |
399 NSAssert([RTCDispatcher isOnQueueForType:RTCDispatcherTypeCaptureSession], | 442 NSAssert([RTCDispatcher isOnQueueForType:RTCDispatcherTypeCaptureSession], |
400 @"updateOrientation must be called on the capture queue."); | 443 @"updateOrientation must be called on the capture queue."); |
401 #if TARGET_OS_IPHONE | 444 #if TARGET_OS_IPHONE |
402 BOOL usingFrontCamera = _currentDevice.position == AVCaptureDevicePositionFron t; | 445 _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 | 446 #endif |
423 } | 447 } |
424 | 448 |
425 @end | 449 @end |
OLD | NEW |