Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(23)

Side by Side Diff: webrtc/modules/video_capture/ios/rtc_video_capture_ios_objc.mm

Issue 2381853002: Revert of Unify the macOS and iOS capturer implementations (Closed)
Patch Set: Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (c) 2013 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 #if !defined(__has_feature) || !__has_feature(objc_arc)
12 #error "This file requires ARC support."
13 #endif
14
15 #import <UIKit/UIKit.h>
16
17 #import "webrtc/modules/video_capture/ios/device_info_ios_objc.h"
18 #import "webrtc/modules/video_capture/ios/rtc_video_capture_ios_objc.h"
19
20 #include "webrtc/system_wrappers/include/trace.h"
21
22 using namespace webrtc;
23 using namespace webrtc::videocapturemodule;
24
25 @interface RTCVideoCaptureIosObjC (hidden)
26 - (int)changeCaptureInputWithName:(NSString*)captureDeviceName;
27 @end
28
29 @implementation RTCVideoCaptureIosObjC {
30 webrtc::videocapturemodule::VideoCaptureIos* _owner;
31 webrtc::VideoCaptureCapability _capability;
32 AVCaptureSession* _captureSession;
33 int _captureId;
34 BOOL _orientationHasChanged;
35 AVCaptureConnection* _connection;
36 BOOL _captureChanging; // Guarded by _captureChangingCondition.
37 NSCondition* _captureChangingCondition;
38 }
39
40 @synthesize frameRotation = _framRotation;
41
42 - (id)initWithOwner:(VideoCaptureIos*)owner captureId:(int)captureId {
43 if (self == [super init]) {
44 _owner = owner;
45 _captureId = captureId;
46 _captureSession = [[AVCaptureSession alloc] init];
47 #if defined(__IPHONE_7_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_0
48 NSString* version = [[UIDevice currentDevice] systemVersion];
49 if ([version integerValue] >= 7) {
50 _captureSession.usesApplicationAudioSession = NO;
51 }
52 #endif
53 _captureChanging = NO;
54 _captureChangingCondition = [[NSCondition alloc] init];
55
56 if (!_captureSession || !_captureChangingCondition) {
57 return nil;
58 }
59
60 // create and configure a new output (using callbacks)
61 AVCaptureVideoDataOutput* captureOutput =
62 [[AVCaptureVideoDataOutput alloc] init];
63 NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey;
64
65 NSNumber* val = [NSNumber
66 numberWithUnsignedInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange];
67 NSDictionary* videoSettings =
68 [NSDictionary dictionaryWithObject:val forKey:key];
69 captureOutput.videoSettings = videoSettings;
70
71 // add new output
72 if ([_captureSession canAddOutput:captureOutput]) {
73 [_captureSession addOutput:captureOutput];
74 } else {
75 WEBRTC_TRACE(kTraceError,
76 kTraceVideoCapture,
77 _captureId,
78 "%s:%s:%d Could not add output to AVCaptureSession ",
79 __FILE__,
80 __FUNCTION__,
81 __LINE__);
82 }
83
84 [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
85
86 NSNotificationCenter* notify = [NSNotificationCenter defaultCenter];
87 [notify addObserver:self
88 selector:@selector(onVideoError:)
89 name:AVCaptureSessionRuntimeErrorNotification
90 object:_captureSession];
91 [notify addObserver:self
92 selector:@selector(deviceOrientationDidChange:)
93 name:UIDeviceOrientationDidChangeNotification
94 object:nil];
95 }
96
97 return self;
98 }
99
100 - (void)directOutputToSelf {
101 [[self currentOutput]
102 setSampleBufferDelegate:self
103 queue:dispatch_get_global_queue(
104 DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
105 }
106
107 - (void)directOutputToNil {
108 [[self currentOutput] setSampleBufferDelegate:nil queue:NULL];
109 }
110
111 - (void)deviceOrientationDidChange:(NSNotification*)notification {
112 _orientationHasChanged = YES;
113 [self setRelativeVideoOrientation];
114 }
115
116 - (void)dealloc {
117 [[NSNotificationCenter defaultCenter] removeObserver:self];
118 }
119
120 - (BOOL)setCaptureDeviceByUniqueId:(NSString*)uniqueId {
121 [self waitForCaptureChangeToFinish];
122 // check to see if the camera is already set
123 if (_captureSession) {
124 NSArray* currentInputs = [NSArray arrayWithArray:[_captureSession inputs]];
125 if ([currentInputs count] > 0) {
126 AVCaptureDeviceInput* currentInput = [currentInputs objectAtIndex:0];
127 if ([uniqueId isEqualToString:[currentInput.device localizedName]]) {
128 return YES;
129 }
130 }
131 }
132
133 return [self changeCaptureInputByUniqueId:uniqueId];
134 }
135
136 - (BOOL)startCaptureWithCapability:(const VideoCaptureCapability&)capability {
137 [self waitForCaptureChangeToFinish];
138 if (!_captureSession) {
139 return NO;
140 }
141
142 // check limits of the resolution
143 if (capability.maxFPS < 0 || capability.maxFPS > 60) {
144 return NO;
145 }
146
147 if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1920x1080]) {
148 if (capability.width > 1920 || capability.height > 1080) {
149 return NO;
150 }
151 } else if ([_captureSession
152 canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
153 if (capability.width > 1280 || capability.height > 720) {
154 return NO;
155 }
156 } else if ([_captureSession
157 canSetSessionPreset:AVCaptureSessionPreset640x480]) {
158 if (capability.width > 640 || capability.height > 480) {
159 return NO;
160 }
161 } else if ([_captureSession
162 canSetSessionPreset:AVCaptureSessionPreset352x288]) {
163 if (capability.width > 352 || capability.height > 288) {
164 return NO;
165 }
166 } else if (capability.width < 0 || capability.height < 0) {
167 return NO;
168 }
169
170 _capability = capability;
171
172 AVCaptureVideoDataOutput* currentOutput = [self currentOutput];
173 if (!currentOutput)
174 return NO;
175
176 [self directOutputToSelf];
177
178 _orientationHasChanged = NO;
179 _captureChanging = YES;
180 dispatch_async(
181 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
182 ^(void) { [self startCaptureInBackgroundWithOutput:currentOutput]; });
183 return YES;
184 }
185
186 - (AVCaptureVideoDataOutput*)currentOutput {
187 return [[_captureSession outputs] firstObject];
188 }
189
190 - (void)startCaptureInBackgroundWithOutput:
191 (AVCaptureVideoDataOutput*)currentOutput {
192 NSString* captureQuality =
193 [NSString stringWithString:AVCaptureSessionPresetLow];
194 if (_capability.width >= 1920 || _capability.height >= 1080) {
195 captureQuality =
196 [NSString stringWithString:AVCaptureSessionPreset1920x1080];
197 } else if (_capability.width >= 1280 || _capability.height >= 720) {
198 captureQuality = [NSString stringWithString:AVCaptureSessionPreset1280x720];
199 } else if (_capability.width >= 640 || _capability.height >= 480) {
200 captureQuality = [NSString stringWithString:AVCaptureSessionPreset640x480];
201 } else if (_capability.width >= 352 || _capability.height >= 288) {
202 captureQuality = [NSString stringWithString:AVCaptureSessionPreset352x288];
203 }
204
205 // begin configuration for the AVCaptureSession
206 [_captureSession beginConfiguration];
207
208 // picture resolution
209 [_captureSession setSessionPreset:captureQuality];
210
211 // take care of capture framerate now
212 NSArray* sessionInputs = _captureSession.inputs;
213 AVCaptureDeviceInput* deviceInput = [sessionInputs count] > 0 ?
214 sessionInputs[0] : nil;
215 AVCaptureDevice* inputDevice = deviceInput.device;
216 if (inputDevice) {
217 AVCaptureDeviceFormat* activeFormat = inputDevice.activeFormat;
218 NSArray* supportedRanges = activeFormat.videoSupportedFrameRateRanges;
219 AVFrameRateRange* targetRange = [supportedRanges count] > 0 ?
220 supportedRanges[0] : nil;
221 // Find the largest supported framerate less than capability maxFPS.
222 for (AVFrameRateRange* range in supportedRanges) {
223 if (range.maxFrameRate <= _capability.maxFPS &&
224 targetRange.maxFrameRate <= range.maxFrameRate) {
225 targetRange = range;
226 }
227 }
228 if (targetRange && [inputDevice lockForConfiguration:NULL]) {
229 inputDevice.activeVideoMinFrameDuration = targetRange.minFrameDuration;
230 inputDevice.activeVideoMaxFrameDuration = targetRange.minFrameDuration;
231 [inputDevice unlockForConfiguration];
232 }
233 }
234
235 _connection = [currentOutput connectionWithMediaType:AVMediaTypeVideo];
236 [self setRelativeVideoOrientation];
237
238 // finished configuring, commit settings to AVCaptureSession.
239 [_captureSession commitConfiguration];
240
241 [_captureSession startRunning];
242 [self signalCaptureChangeEnd];
243 }
244
245 - (void)setRelativeVideoOrientation {
246 if (!_connection.supportsVideoOrientation) {
247 return;
248 }
249
250 switch ([UIDevice currentDevice].orientation) {
251 case UIDeviceOrientationPortrait:
252 _connection.videoOrientation =
253 AVCaptureVideoOrientationPortrait;
254 break;
255 case UIDeviceOrientationPortraitUpsideDown:
256 _connection.videoOrientation =
257 AVCaptureVideoOrientationPortraitUpsideDown;
258 break;
259 case UIDeviceOrientationLandscapeLeft:
260 _connection.videoOrientation =
261 AVCaptureVideoOrientationLandscapeRight;
262 break;
263 case UIDeviceOrientationLandscapeRight:
264 _connection.videoOrientation =
265 AVCaptureVideoOrientationLandscapeLeft;
266 break;
267 case UIDeviceOrientationFaceUp:
268 case UIDeviceOrientationFaceDown:
269 case UIDeviceOrientationUnknown:
270 if (!_orientationHasChanged) {
271 _connection.videoOrientation =
272 AVCaptureVideoOrientationPortrait;
273 }
274 break;
275 }
276 }
277
278 - (void)onVideoError:(NSNotification*)notification {
279 NSLog(@"onVideoError: %@", notification);
280 // TODO(sjlee): make the specific error handling with this notification.
281 WEBRTC_TRACE(kTraceError,
282 kTraceVideoCapture,
283 _captureId,
284 "%s:%s:%d [AVCaptureSession startRunning] error.",
285 __FILE__,
286 __FUNCTION__,
287 __LINE__);
288 }
289
290 - (BOOL)stopCapture {
291 [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
292 _orientationHasChanged = NO;
293 [self waitForCaptureChangeToFinish];
294 [self directOutputToNil];
295
296 if (!_captureSession) {
297 return NO;
298 }
299
300 _captureChanging = YES;
301 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
302 ^(void) { [self stopCaptureInBackground]; });
303 return YES;
304 }
305
306 - (void)stopCaptureInBackground {
307 [_captureSession stopRunning];
308 [self signalCaptureChangeEnd];
309 }
310
311 - (BOOL)changeCaptureInputByUniqueId:(NSString*)uniqueId {
312 [self waitForCaptureChangeToFinish];
313 NSArray* currentInputs = [_captureSession inputs];
314 // remove current input
315 if ([currentInputs count] > 0) {
316 AVCaptureInput* currentInput =
317 (AVCaptureInput*)[currentInputs objectAtIndex:0];
318
319 [_captureSession removeInput:currentInput];
320 }
321
322 // Look for input device with the name requested (as our input param)
323 // get list of available capture devices
324 int captureDeviceCount = [DeviceInfoIosObjC captureDeviceCount];
325 if (captureDeviceCount <= 0) {
326 return NO;
327 }
328
329 AVCaptureDevice* captureDevice =
330 [DeviceInfoIosObjC captureDeviceForUniqueId:uniqueId];
331
332 if (!captureDevice) {
333 return NO;
334 }
335
336 // now create capture session input out of AVCaptureDevice
337 NSError* deviceError = nil;
338 AVCaptureDeviceInput* newCaptureInput =
339 [AVCaptureDeviceInput deviceInputWithDevice:captureDevice
340 error:&deviceError];
341
342 if (!newCaptureInput) {
343 const char* errorMessage = [[deviceError localizedDescription] UTF8String];
344
345 WEBRTC_TRACE(kTraceError,
346 kTraceVideoCapture,
347 _captureId,
348 "%s:%s:%d deviceInputWithDevice error:%s",
349 __FILE__,
350 __FUNCTION__,
351 __LINE__,
352 errorMessage);
353
354 return NO;
355 }
356
357 // try to add our new capture device to the capture session
358 [_captureSession beginConfiguration];
359
360 BOOL addedCaptureInput = NO;
361 if ([_captureSession canAddInput:newCaptureInput]) {
362 [_captureSession addInput:newCaptureInput];
363 addedCaptureInput = YES;
364 } else {
365 addedCaptureInput = NO;
366 }
367
368 [_captureSession commitConfiguration];
369
370 return addedCaptureInput;
371 }
372
373 - (void)captureOutput:(AVCaptureOutput*)captureOutput
374 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
375 fromConnection:(AVCaptureConnection*)connection {
376 const int kFlags = 0;
377 CVImageBufferRef videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer);
378
379 if (CVPixelBufferLockBaseAddress(videoFrame, kFlags) != kCVReturnSuccess) {
380 return;
381 }
382
383 const int kYPlaneIndex = 0;
384 const int kUVPlaneIndex = 1;
385
386 uint8_t* baseAddress =
387 (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(videoFrame, kYPlaneIndex);
388 size_t yPlaneBytesPerRow =
389 CVPixelBufferGetBytesPerRowOfPlane(videoFrame, kYPlaneIndex);
390 size_t yPlaneHeight = CVPixelBufferGetHeightOfPlane(videoFrame, kYPlaneIndex);
391 size_t uvPlaneBytesPerRow =
392 CVPixelBufferGetBytesPerRowOfPlane(videoFrame, kUVPlaneIndex);
393 size_t uvPlaneHeight =
394 CVPixelBufferGetHeightOfPlane(videoFrame, kUVPlaneIndex);
395 size_t frameSize =
396 yPlaneBytesPerRow * yPlaneHeight + uvPlaneBytesPerRow * uvPlaneHeight;
397
398 VideoCaptureCapability tempCaptureCapability;
399 tempCaptureCapability.width = CVPixelBufferGetWidth(videoFrame);
400 tempCaptureCapability.height = CVPixelBufferGetHeight(videoFrame);
401 tempCaptureCapability.maxFPS = _capability.maxFPS;
402 tempCaptureCapability.rawType = kVideoNV12;
403
404 _owner->IncomingFrame(baseAddress, frameSize, tempCaptureCapability, 0);
405
406 CVPixelBufferUnlockBaseAddress(videoFrame, kFlags);
407 }
408
409 - (void)signalCaptureChangeEnd {
410 [_captureChangingCondition lock];
411 _captureChanging = NO;
412 [_captureChangingCondition signal];
413 [_captureChangingCondition unlock];
414 }
415
416 - (void)waitForCaptureChangeToFinish {
417 [_captureChangingCondition lock];
418 while (_captureChanging) {
419 [_captureChangingCondition wait];
420 }
421 [_captureChangingCondition unlock];
422 }
423 @end
OLDNEW
« no previous file with comments | « webrtc/modules/video_capture/ios/rtc_video_capture_ios_objc.h ('k') | webrtc/modules/video_capture/ios/video_capture_ios.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698