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

Side by Side Diff: media/capture/video/mac/video_capture_device_avfoundation_mac.mm

Issue 2529493002: mac: Remove more media/base/mac glue unneeded now that we target 10.9 (Closed)
Patch Set: . Created 4 years 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
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "media/capture/video/mac/video_capture_device_avfoundation_mac.h" 5 #import "media/capture/video/mac/video_capture_device_avfoundation_mac.h"
6 6
7 #import <AVFoundation/AVFoundation.h>
7 #import <CoreMedia/CoreMedia.h> 8 #import <CoreMedia/CoreMedia.h>
8 #import <CoreVideo/CoreVideo.h> 9 #import <CoreVideo/CoreVideo.h>
9 #include <stddef.h> 10 #include <stddef.h>
10 #include <stdint.h> 11 #include <stdint.h>
11 12
12 #include "base/location.h" 13 #include "base/location.h"
13 #include "base/logging.h" 14 #include "base/logging.h"
14 #include "base/mac/foundation_util.h" 15 #include "base/mac/foundation_util.h"
15 #include "base/mac/mac_util.h" 16 #include "base/mac/mac_util.h"
16 #include "base/metrics/histogram_macros.h" 17 #include "base/metrics/histogram_macros.h"
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
95 } 96 }
96 } 97 }
97 } 98 }
98 99
99 // This function translates Mac Core Video pixel formats to Chromium pixel 100 // This function translates Mac Core Video pixel formats to Chromium pixel
100 // formats. 101 // formats.
101 media::VideoPixelFormat FourCCToChromiumPixelFormat(FourCharCode code) { 102 media::VideoPixelFormat FourCCToChromiumPixelFormat(FourCharCode code) {
102 switch (code) { 103 switch (code) {
103 case kCVPixelFormatType_422YpCbCr8: 104 case kCVPixelFormatType_422YpCbCr8:
104 return media::PIXEL_FORMAT_UYVY; 105 return media::PIXEL_FORMAT_UYVY;
105 case CoreMediaGlue::kCMPixelFormat_422YpCbCr8_yuvs: 106 case kCMPixelFormat_422YpCbCr8_yuvs:
106 return media::PIXEL_FORMAT_YUY2; 107 return media::PIXEL_FORMAT_YUY2;
107 case CoreMediaGlue::kCMVideoCodecType_JPEG_OpenDML: 108 case kCMVideoCodecType_JPEG_OpenDML:
108 return media::PIXEL_FORMAT_MJPEG; 109 return media::PIXEL_FORMAT_MJPEG;
109 default: 110 default:
110 return media::PIXEL_FORMAT_UNKNOWN; 111 return media::PIXEL_FORMAT_UNKNOWN;
111 } 112 }
112 } 113 }
113 114
114 // Extracts |base_address| and |length| out of a SampleBuffer. 115 // Extracts |base_address| and |length| out of a SampleBuffer.
115 void ExtractBaseAddressAndLength( 116 void ExtractBaseAddressAndLength(char** base_address,
116 char** base_address, 117 size_t* length,
117 size_t* length, 118 CMSampleBufferRef sample_buffer) {
118 CoreMediaGlue::CMSampleBufferRef sample_buffer) { 119 CMBlockBufferRef block_buffer = CMSampleBufferGetDataBuffer(sample_buffer);
119 CoreMediaGlue::CMBlockBufferRef block_buffer =
120 CoreMediaGlue::CMSampleBufferGetDataBuffer(sample_buffer);
121 DCHECK(block_buffer); 120 DCHECK(block_buffer);
122 121
123 size_t length_at_offset; 122 size_t length_at_offset;
124 const OSStatus status = CoreMediaGlue::CMBlockBufferGetDataPointer( 123 const OSStatus status = CMBlockBufferGetDataPointer(
125 block_buffer, 0, &length_at_offset, length, base_address); 124 block_buffer, 0, &length_at_offset, length, base_address);
126 DCHECK_EQ(noErr, status); 125 DCHECK_EQ(noErr, status);
127 // Expect the (M)JPEG data to be available as a contiguous reference, i.e. 126 // Expect the (M)JPEG data to be available as a contiguous reference, i.e.
128 // not covered by multiple memory blocks. 127 // not covered by multiple memory blocks.
129 DCHECK_EQ(length_at_offset, *length); 128 DCHECK_EQ(length_at_offset, *length);
130 } 129 }
131 130
132 } // anonymous namespace 131 } // anonymous namespace
133 132
134 @implementation VideoCaptureDeviceAVFoundation 133 @implementation VideoCaptureDeviceAVFoundation
135 134
136 #pragma mark Class methods 135 #pragma mark Class methods
137 136
138 + (void)getDeviceNames:(NSMutableDictionary*)deviceNames { 137 + (void)getDeviceNames:(NSMutableDictionary*)deviceNames {
139 // At this stage we already know that AVFoundation is supported and the whole 138 // At this stage we already know that AVFoundation is supported and the whole
140 // library is loaded and initialised, by the device monitoring. 139 // library is loaded and initialised, by the device monitoring.
141 NSArray* devices = [AVCaptureDeviceGlue devices]; 140 NSArray* devices = [AVCaptureDevice devices];
142 int number_of_suspended_devices = 0; 141 int number_of_suspended_devices = 0;
143 for (CrAVCaptureDevice* device in devices) { 142 for (AVCaptureDevice* device in devices) {
144 if ([device hasMediaType:AVFoundationGlue::AVMediaTypeVideo()] || 143 if ([device hasMediaType:AVMediaTypeVideo] ||
145 [device hasMediaType:AVFoundationGlue::AVMediaTypeMuxed()]) { 144 [device hasMediaType:AVMediaTypeMuxed]) {
146 if ([device isSuspended]) { 145 if ([device isSuspended]) {
147 ++number_of_suspended_devices; 146 ++number_of_suspended_devices;
148 continue; 147 continue;
149 } 148 }
150 DeviceNameAndTransportType* nameAndTransportType = 149 DeviceNameAndTransportType* nameAndTransportType =
151 [[[DeviceNameAndTransportType alloc] 150 [[[DeviceNameAndTransportType alloc]
152 initWithName:[device localizedName] 151 initWithName:[device localizedName]
153 transportType:[device transportType]] autorelease]; 152 transportType:[device transportType]] autorelease];
154 [deviceNames setObject:nameAndTransportType forKey:[device uniqueID]]; 153 [deviceNames setObject:nameAndTransportType forKey:[device uniqueID]];
155 } 154 }
156 } 155 }
157 MaybeWriteUma([deviceNames count], number_of_suspended_devices); 156 MaybeWriteUma([deviceNames count], number_of_suspended_devices);
158 } 157 }
159 158
160 + (NSDictionary*)deviceNames { 159 + (NSDictionary*)deviceNames {
161 NSMutableDictionary* deviceNames = 160 NSMutableDictionary* deviceNames =
162 [[[NSMutableDictionary alloc] init] autorelease]; 161 [[[NSMutableDictionary alloc] init] autorelease];
163 // The device name retrieval is not going to happen in the main thread, and 162 // The device name retrieval is not going to happen in the main thread, and
164 // this might cause instabilities (it did in QTKit), so keep an eye here. 163 // this might cause instabilities (it did in QTKit), so keep an eye here.
165 [self getDeviceNames:deviceNames]; 164 [self getDeviceNames:deviceNames];
166 return deviceNames; 165 return deviceNames;
167 } 166 }
168 167
169 + (void)getDevice:(const media::VideoCaptureDeviceDescriptor&)descriptor 168 + (void)getDevice:(const media::VideoCaptureDeviceDescriptor&)descriptor
170 supportedFormats:(media::VideoCaptureFormats*)formats { 169 supportedFormats:(media::VideoCaptureFormats*)formats {
171 NSArray* devices = [AVCaptureDeviceGlue devices]; 170 NSArray* devices = [AVCaptureDevice devices];
172 CrAVCaptureDevice* device = nil; 171 AVCaptureDevice* device = nil;
173 for (device in devices) { 172 for (device in devices) {
174 if ([[device uniqueID] UTF8String] == descriptor.device_id) 173 if ([[device uniqueID] UTF8String] == descriptor.device_id)
175 break; 174 break;
176 } 175 }
177 if (device == nil) 176 if (device == nil)
178 return; 177 return;
179 for (CrAVCaptureDeviceFormat* format in device.formats) { 178 for (AVCaptureDeviceFormat* format in device.formats) {
180 // MediaSubType is a CMPixelFormatType but can be used as CVPixelFormatType 179 // MediaSubType is a CMPixelFormatType but can be used as CVPixelFormatType
181 // as well according to CMFormatDescription.h 180 // as well according to CMFormatDescription.h
182 const media::VideoPixelFormat pixelFormat = FourCCToChromiumPixelFormat( 181 const media::VideoPixelFormat pixelFormat = FourCCToChromiumPixelFormat(
183 CoreMediaGlue::CMFormatDescriptionGetMediaSubType( 182 CMFormatDescriptionGetMediaSubType([format formatDescription]));
184 [format formatDescription]));
185 183
186 CoreMediaGlue::CMVideoDimensions dimensions = 184 CMVideoDimensions dimensions =
187 CoreMediaGlue::CMVideoFormatDescriptionGetDimensions( 185 CMVideoFormatDescriptionGetDimensions([format formatDescription]);
188 [format formatDescription]);
189 186
190 for (CrAVFrameRateRange* frameRate in 187 for (AVFrameRateRange* frameRate in
191 [format videoSupportedFrameRateRanges]) { 188 [format videoSupportedFrameRateRanges]) {
192 media::VideoCaptureFormat format( 189 media::VideoCaptureFormat format(
193 gfx::Size(dimensions.width, dimensions.height), 190 gfx::Size(dimensions.width, dimensions.height),
194 frameRate.maxFrameRate, pixelFormat); 191 frameRate.maxFrameRate, pixelFormat);
195 formats->push_back(format); 192 formats->push_back(format);
196 DVLOG(2) << descriptor.display_name << " " 193 DVLOG(2) << descriptor.display_name << " "
197 << media::VideoCaptureFormat::ToString(format); 194 << media::VideoCaptureFormat::ToString(format);
198 } 195 }
199 } 196 }
200 } 197 }
201 198
202 #pragma mark Public methods 199 #pragma mark Public methods
203 200
204 - (id)initWithFrameReceiver:(media::VideoCaptureDeviceMac*)frameReceiver { 201 - (id)initWithFrameReceiver:(media::VideoCaptureDeviceMac*)frameReceiver {
205 if ((self = [super init])) { 202 if ((self = [super init])) {
206 DCHECK(main_thread_checker_.CalledOnValidThread()); 203 DCHECK(main_thread_checker_.CalledOnValidThread());
207 DCHECK(frameReceiver); 204 DCHECK(frameReceiver);
208 [self setFrameReceiver:frameReceiver]; 205 [self setFrameReceiver:frameReceiver];
209 captureSession_.reset( 206 captureSession_.reset([[AVCaptureSession alloc] init]);
210 [[AVFoundationGlue::AVCaptureSessionClass() alloc] init]);
211 } 207 }
212 return self; 208 return self;
213 } 209 }
214 210
215 - (void)dealloc { 211 - (void)dealloc {
216 [self stopCapture]; 212 [self stopCapture];
217 [super dealloc]; 213 [super dealloc];
218 } 214 }
219 215
220 - (void)setFrameReceiver:(media::VideoCaptureDeviceMac*)frameReceiver { 216 - (void)setFrameReceiver:(media::VideoCaptureDeviceMac*)frameReceiver {
(...skipping 14 matching lines...) Expand all
235 [captureSession_ removeOutput:stillImageOutput_]; 231 [captureSession_ removeOutput:stillImageOutput_];
236 if (captureDeviceInput_) { 232 if (captureDeviceInput_) {
237 [captureSession_ removeInput:captureDeviceInput_]; 233 [captureSession_ removeInput:captureDeviceInput_];
238 // No need to release |captureDeviceInput_|, is owned by the session. 234 // No need to release |captureDeviceInput_|, is owned by the session.
239 captureDeviceInput_ = nil; 235 captureDeviceInput_ = nil;
240 } 236 }
241 return YES; 237 return YES;
242 } 238 }
243 239
244 // Look for input device with requested name. 240 // Look for input device with requested name.
245 captureDevice_ = [AVCaptureDeviceGlue deviceWithUniqueID:deviceId]; 241 captureDevice_ = [AVCaptureDevice deviceWithUniqueID:deviceId];
246 if (!captureDevice_) { 242 if (!captureDevice_) {
247 [self 243 [self
248 sendErrorString:[NSString stringWithUTF8String: 244 sendErrorString:[NSString stringWithUTF8String:
249 "Could not open video capture device."]]; 245 "Could not open video capture device."]];
250 return NO; 246 return NO;
251 } 247 }
252 248
253 // Create the capture input associated with the device. Easy peasy. 249 // Create the capture input associated with the device. Easy peasy.
254 NSError* error = nil; 250 NSError* error = nil;
255 captureDeviceInput_ = 251 captureDeviceInput_ =
256 [AVCaptureDeviceInputGlue deviceInputWithDevice:captureDevice_ 252 [AVCaptureDeviceInput deviceInputWithDevice:captureDevice_ error:&error];
257 error:&error];
258 if (!captureDeviceInput_) { 253 if (!captureDeviceInput_) {
259 captureDevice_ = nil; 254 captureDevice_ = nil;
260 [self sendErrorString: 255 [self sendErrorString:
261 [NSString stringWithFormat: 256 [NSString stringWithFormat:
262 @"Could not create video capture input (%@): %@", 257 @"Could not create video capture input (%@): %@",
263 [error localizedDescription], 258 [error localizedDescription],
264 [error localizedFailureReason]]]; 259 [error localizedFailureReason]]];
265 return NO; 260 return NO;
266 } 261 }
267 [captureSession_ addInput:captureDeviceInput_]; 262 [captureSession_ addInput:captureDeviceInput_];
268 263
269 // Create a new data output for video. The data output is configured to 264 // Create a new data output for video. The data output is configured to
270 // discard late frames by default. 265 // discard late frames by default.
271 captureVideoDataOutput_.reset( 266 captureVideoDataOutput_.reset([[AVCaptureVideoDataOutput alloc] init]);
272 [[AVFoundationGlue::AVCaptureVideoDataOutputClass() alloc] init]);
273 if (!captureVideoDataOutput_) { 267 if (!captureVideoDataOutput_) {
274 [captureSession_ removeInput:captureDeviceInput_]; 268 [captureSession_ removeInput:captureDeviceInput_];
275 [self sendErrorString:[NSString stringWithUTF8String: 269 [self sendErrorString:[NSString stringWithUTF8String:
276 "Could not create video data output."]]; 270 "Could not create video data output."]];
277 return NO; 271 return NO;
278 } 272 }
279 [captureVideoDataOutput_ setAlwaysDiscardsLateVideoFrames:true]; 273 [captureVideoDataOutput_ setAlwaysDiscardsLateVideoFrames:true];
280 [captureVideoDataOutput_ 274 [captureVideoDataOutput_
281 setSampleBufferDelegate:self 275 setSampleBufferDelegate:self
282 queue:dispatch_get_global_queue( 276 queue:dispatch_get_global_queue(
283 DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; 277 DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
284 [captureSession_ addOutput:captureVideoDataOutput_]; 278 [captureSession_ addOutput:captureVideoDataOutput_];
285 279
286 // Create and plug the still image capture output. This should happen in 280 // Create and plug the still image capture output. This should happen in
287 // advance of the actual picture to allow for the 3A to stabilize. 281 // advance of the actual picture to allow for the 3A to stabilize.
288 stillImageOutput_.reset( 282 stillImageOutput_.reset([[AVCaptureStillImageOutput alloc] init]);
289 [[AVFoundationGlue::AVCaptureStillImageOutputClass() alloc] init]);
290 if (stillImageOutput_ && [captureSession_ canAddOutput:stillImageOutput_]) 283 if (stillImageOutput_ && [captureSession_ canAddOutput:stillImageOutput_])
291 [captureSession_ addOutput:stillImageOutput_]; 284 [captureSession_ addOutput:stillImageOutput_];
292 285
293 return YES; 286 return YES;
294 } 287 }
295 288
296 - (BOOL)setCaptureHeight:(int)height 289 - (BOOL)setCaptureHeight:(int)height
297 width:(int)width 290 width:(int)width
298 frameRate:(float)frameRate { 291 frameRate:(float)frameRate {
299 DCHECK(![captureSession_ isRunning] && 292 DCHECK(![captureSession_ isRunning] &&
300 main_thread_checker_.CalledOnValidThread()); 293 main_thread_checker_.CalledOnValidThread());
301 294
302 frameWidth_ = width; 295 frameWidth_ = width;
303 frameHeight_ = height; 296 frameHeight_ = height;
304 frameRate_ = frameRate; 297 frameRate_ = frameRate;
305 298
306 FourCharCode best_fourcc = kCVPixelFormatType_422YpCbCr8; 299 FourCharCode best_fourcc = kCVPixelFormatType_422YpCbCr8;
307 const bool prefer_mjpeg = 300 const bool prefer_mjpeg =
308 width > kMjpegWidthThreshold || height > kMjpegHeightThreshold; 301 width > kMjpegWidthThreshold || height > kMjpegHeightThreshold;
309 for (CrAVCaptureDeviceFormat* format in captureDevice_.formats) { 302 for (AVCaptureDeviceFormat* format in captureDevice_.formats) {
310 const FourCharCode fourcc = 303 const FourCharCode fourcc =
311 CoreMediaGlue::CMFormatDescriptionGetMediaSubType( 304 CMFormatDescriptionGetMediaSubType([format formatDescription]);
312 [format formatDescription]); 305 if (prefer_mjpeg && fourcc == kCMVideoCodecType_JPEG_OpenDML) {
313 if (prefer_mjpeg &&
314 fourcc == CoreMediaGlue::kCMVideoCodecType_JPEG_OpenDML) {
315 best_fourcc = fourcc; 306 best_fourcc = fourcc;
316 break; 307 break;
317 } 308 }
318 309
319 // Compare according to Chromium preference. 310 // Compare according to Chromium preference.
320 if (media::VideoCaptureFormat::ComparePixelFormatPreference( 311 if (media::VideoCaptureFormat::ComparePixelFormatPreference(
321 FourCCToChromiumPixelFormat(fourcc), 312 FourCCToChromiumPixelFormat(fourcc),
322 FourCCToChromiumPixelFormat(best_fourcc))) { 313 FourCCToChromiumPixelFormat(best_fourcc))) {
323 best_fourcc = fourcc; 314 best_fourcc = fourcc;
324 } 315 }
325 } 316 }
326 317
327 if (best_fourcc == CoreMediaGlue::kCMVideoCodecType_JPEG_OpenDML) { 318 if (best_fourcc == kCMVideoCodecType_JPEG_OpenDML) {
328 [captureSession_ removeOutput:stillImageOutput_]; 319 [captureSession_ removeOutput:stillImageOutput_];
329 stillImageOutput_.reset(); 320 stillImageOutput_.reset();
330 } 321 }
331 322
332 // The capture output has to be configured, despite Mac documentation 323 // The capture output has to be configured, despite Mac documentation
333 // detailing that setting the sessionPreset would be enough. The reason for 324 // detailing that setting the sessionPreset would be enough. The reason for
334 // this mismatch is probably because most of the AVFoundation docs are written 325 // this mismatch is probably because most of the AVFoundation docs are written
335 // for iOS and not for MacOsX. AVVideoScalingModeKey() refers to letterboxing 326 // for iOS and not for MacOsX. AVVideoScalingModeKey() refers to letterboxing
336 // yes/no and preserve aspect ratio yes/no when scaling. Currently we set 327 // yes/no and preserve aspect ratio yes/no when scaling. Currently we set
337 // cropping and preservation. 328 // cropping and preservation.
338 NSDictionary* videoSettingsDictionary = @{ 329 NSDictionary* videoSettingsDictionary = @{
339 (id) kCVPixelBufferWidthKey : @(width), (id) 330 (id)kCVPixelBufferWidthKey : @(width),
340 kCVPixelBufferHeightKey : @(height), (id) 331 (id)kCVPixelBufferHeightKey : @(height),
341 kCVPixelBufferPixelFormatTypeKey : @(best_fourcc), 332 (id)kCVPixelBufferPixelFormatTypeKey : @(best_fourcc),
342 AVFoundationGlue::AVVideoScalingModeKey() : 333 AVVideoScalingModeKey : AVVideoScalingModeResizeAspectFill
343 AVFoundationGlue::AVVideoScalingModeResizeAspectFill()
344 }; 334 };
345 [captureVideoDataOutput_ setVideoSettings:videoSettingsDictionary]; 335 [captureVideoDataOutput_ setVideoSettings:videoSettingsDictionary];
346 336
347 CrAVCaptureConnection* captureConnection = [captureVideoDataOutput_ 337 AVCaptureConnection* captureConnection =
348 connectionWithMediaType:AVFoundationGlue::AVMediaTypeVideo()]; 338 [captureVideoDataOutput_ connectionWithMediaType:AVMediaTypeVideo];
349 // Check selector existence, related to bugs http://crbug.com/327532 and 339 // Check selector existence, related to bugs http://crbug.com/327532 and
350 // http://crbug.com/328096. 340 // http://crbug.com/328096.
351 // CMTimeMake accepts integer argumenst but |frameRate| is float, round it. 341 // CMTimeMake accepts integer argumenst but |frameRate| is float, round it.
352 if ([captureConnection 342 if ([captureConnection
353 respondsToSelector:@selector(isVideoMinFrameDurationSupported)] && 343 respondsToSelector:@selector(isVideoMinFrameDurationSupported)] &&
354 [captureConnection isVideoMinFrameDurationSupported]) { 344 [captureConnection isVideoMinFrameDurationSupported]) {
355 [captureConnection 345 [captureConnection
356 setVideoMinFrameDuration:CoreMediaGlue::CMTimeMake( 346 setVideoMinFrameDuration:CMTimeMake(media::kFrameRatePrecision,
357 media::kFrameRatePrecision, 347 (int)(frameRate *
358 (int)(frameRate * 348 media::kFrameRatePrecision))];
359 media::kFrameRatePrecision))];
360 } 349 }
361 if ([captureConnection 350 if ([captureConnection
362 respondsToSelector:@selector(isVideoMaxFrameDurationSupported)] && 351 respondsToSelector:@selector(isVideoMaxFrameDurationSupported)] &&
363 [captureConnection isVideoMaxFrameDurationSupported]) { 352 [captureConnection isVideoMaxFrameDurationSupported]) {
364 [captureConnection 353 [captureConnection
365 setVideoMaxFrameDuration:CoreMediaGlue::CMTimeMake( 354 setVideoMaxFrameDuration:CMTimeMake(media::kFrameRatePrecision,
366 media::kFrameRatePrecision, 355 (int)(frameRate *
367 (int)(frameRate * 356 media::kFrameRatePrecision))];
368 media::kFrameRatePrecision))];
369 } 357 }
370 return YES; 358 return YES;
371 } 359 }
372 360
373 - (BOOL)startCapture { 361 - (BOOL)startCapture {
374 DCHECK(main_thread_checker_.CalledOnValidThread()); 362 DCHECK(main_thread_checker_.CalledOnValidThread());
375 if (!captureSession_) { 363 if (!captureSession_) {
376 DLOG(ERROR) << "Video capture session not initialized."; 364 DLOG(ERROR) << "Video capture session not initialized.";
377 return NO; 365 return NO;
378 } 366 }
379 // Connect the notifications. 367 // Connect the notifications.
380 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; 368 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
381 [nc addObserver:self 369 [nc addObserver:self
382 selector:@selector(onVideoError:) 370 selector:@selector(onVideoError:)
383 name:AVFoundationGlue::AVCaptureSessionRuntimeErrorNotification() 371 name:AVCaptureSessionRuntimeErrorNotification
384 object:captureSession_]; 372 object:captureSession_];
385 [captureSession_ startRunning]; 373 [captureSession_ startRunning];
386 return YES; 374 return YES;
387 } 375 }
388 376
389 - (void)stopCapture { 377 - (void)stopCapture {
390 DCHECK(main_thread_checker_.CalledOnValidThread()); 378 DCHECK(main_thread_checker_.CalledOnValidThread());
391 if ([captureSession_ isRunning]) 379 if ([captureSession_ isRunning])
392 [captureSession_ stopRunning]; // Synchronous. 380 [captureSession_ stopRunning]; // Synchronous.
393 [[NSNotificationCenter defaultCenter] removeObserver:self]; 381 [[NSNotificationCenter defaultCenter] removeObserver:self];
394 } 382 }
395 383
396 - (void)takePhoto { 384 - (void)takePhoto {
397 DCHECK(main_thread_checker_.CalledOnValidThread()); 385 DCHECK(main_thread_checker_.CalledOnValidThread());
398 DCHECK([captureSession_ isRunning]); 386 DCHECK([captureSession_ isRunning]);
399 if (!stillImageOutput_) 387 if (!stillImageOutput_)
400 return; 388 return;
401 389
402 DCHECK_EQ(1u, [[stillImageOutput_ connections] count]); 390 DCHECK_EQ(1u, [[stillImageOutput_ connections] count]);
403 CrAVCaptureConnection* const connection = 391 AVCaptureConnection* const connection =
404 [[stillImageOutput_ connections] firstObject]; 392 [[stillImageOutput_ connections] firstObject];
405 if (!connection) { 393 if (!connection) {
406 base::AutoLock lock(lock_); 394 base::AutoLock lock(lock_);
407 frameReceiver_->OnPhotoError(); 395 frameReceiver_->OnPhotoError();
408 return; 396 return;
409 } 397 }
410 398
411 const auto handler = ^(CoreMediaGlue::CMSampleBufferRef sampleBuffer, 399 const auto handler = ^(CMSampleBufferRef sampleBuffer, NSError* error) {
412 NSError* error) {
413 base::AutoLock lock(lock_); 400 base::AutoLock lock(lock_);
414 if (!frameReceiver_) 401 if (!frameReceiver_)
415 return; 402 return;
416 if (error != nil) { 403 if (error != nil) {
417 frameReceiver_->OnPhotoError(); 404 frameReceiver_->OnPhotoError();
418 return; 405 return;
419 } 406 }
420 407
421 // Recommended compressed pixel format is JPEG, we don't expect surprises. 408 // Recommended compressed pixel format is JPEG, we don't expect surprises.
422 // TODO(mcasas): Consider using [1] for merging EXIF output information: 409 // TODO(mcasas): Consider using [1] for merging EXIF output information:
423 // [1] +(NSData*)jpegStillImageNSDataRepresentation:jpegSampleBuffer; 410 // [1] +(NSData*)jpegStillImageNSDataRepresentation:jpegSampleBuffer;
424 DCHECK_EQ( 411 DCHECK_EQ(kCMVideoCodecType_JPEG,
425 CoreMediaGlue::kCMVideoCodecType_JPEG, 412 CMFormatDescriptionGetMediaSubType(
426 CoreMediaGlue::CMFormatDescriptionGetMediaSubType( 413 CMSampleBufferGetFormatDescription(sampleBuffer)));
427 CoreMediaGlue::CMSampleBufferGetFormatDescription(sampleBuffer)));
428 414
429 char* baseAddress = 0; 415 char* baseAddress = 0;
430 size_t length = 0; 416 size_t length = 0;
431 ExtractBaseAddressAndLength(&baseAddress, &length, sampleBuffer); 417 ExtractBaseAddressAndLength(&baseAddress, &length, sampleBuffer);
432 frameReceiver_->OnPhotoTaken(reinterpret_cast<uint8_t*>(baseAddress), 418 frameReceiver_->OnPhotoTaken(reinterpret_cast<uint8_t*>(baseAddress),
433 length, "image/jpeg"); 419 length, "image/jpeg");
434 }; 420 };
435 421
436 [stillImageOutput_ captureStillImageAsynchronouslyFromConnection:connection 422 [stillImageOutput_ captureStillImageAsynchronouslyFromConnection:connection
437 completionHandler:handler]; 423 completionHandler:handler];
438 } 424 }
439 425
440 #pragma mark Private methods 426 #pragma mark Private methods
441 427
442 // |captureOutput| is called by the capture device to deliver a new frame. 428 // |captureOutput| is called by the capture device to deliver a new frame.
443 // AVFoundation calls from a number of threads, depending on, at least, if 429 // AVFoundation calls from a number of threads, depending on, at least, if
444 // Chrome is on foreground or background. 430 // Chrome is on foreground or background.
445 - (void)captureOutput:(CrAVCaptureOutput*)captureOutput 431 - (void)captureOutput:(AVCaptureOutput*)captureOutput
446 didOutputSampleBuffer:(CoreMediaGlue::CMSampleBufferRef)sampleBuffer 432 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
447 fromConnection:(CrAVCaptureConnection*)connection { 433 fromConnection:(AVCaptureConnection*)connection {
448 const CoreMediaGlue::CMFormatDescriptionRef formatDescription = 434 const CMFormatDescriptionRef formatDescription =
449 CoreMediaGlue::CMSampleBufferGetFormatDescription(sampleBuffer); 435 CMSampleBufferGetFormatDescription(sampleBuffer);
450 const FourCharCode fourcc = 436 const FourCharCode fourcc =
451 CoreMediaGlue::CMFormatDescriptionGetMediaSubType(formatDescription); 437 CMFormatDescriptionGetMediaSubType(formatDescription);
452 const CoreMediaGlue::CMVideoDimensions dimensions = 438 const CMVideoDimensions dimensions =
453 CoreMediaGlue::CMVideoFormatDescriptionGetDimensions(formatDescription); 439 CMVideoFormatDescriptionGetDimensions(formatDescription);
454 const media::VideoCaptureFormat captureFormat( 440 const media::VideoCaptureFormat captureFormat(
455 gfx::Size(dimensions.width, dimensions.height), frameRate_, 441 gfx::Size(dimensions.width, dimensions.height), frameRate_,
456 FourCCToChromiumPixelFormat(fourcc)); 442 FourCCToChromiumPixelFormat(fourcc));
457 443
458 char* baseAddress = 0; 444 char* baseAddress = 0;
459 size_t frameSize = 0; 445 size_t frameSize = 0;
460 CVImageBufferRef videoFrame = nil; 446 CVImageBufferRef videoFrame = nil;
461 if (fourcc == CoreMediaGlue::kCMVideoCodecType_JPEG_OpenDML) { 447 if (fourcc == kCMVideoCodecType_JPEG_OpenDML) {
462 ExtractBaseAddressAndLength(&baseAddress, &frameSize, sampleBuffer); 448 ExtractBaseAddressAndLength(&baseAddress, &frameSize, sampleBuffer);
463 } else { 449 } else {
464 videoFrame = CoreMediaGlue::CMSampleBufferGetImageBuffer(sampleBuffer); 450 videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer);
465 // Lock the frame and calculate frame size. 451 // Lock the frame and calculate frame size.
466 if (CVPixelBufferLockBaseAddress(videoFrame, kCVPixelBufferLock_ReadOnly) == 452 if (CVPixelBufferLockBaseAddress(videoFrame, kCVPixelBufferLock_ReadOnly) ==
467 kCVReturnSuccess) { 453 kCVReturnSuccess) {
468 baseAddress = static_cast<char*>(CVPixelBufferGetBaseAddress(videoFrame)); 454 baseAddress = static_cast<char*>(CVPixelBufferGetBaseAddress(videoFrame));
469 frameSize = CVPixelBufferGetHeight(videoFrame) * 455 frameSize = CVPixelBufferGetHeight(videoFrame) *
470 CVPixelBufferGetBytesPerRow(videoFrame); 456 CVPixelBufferGetBytesPerRow(videoFrame);
471 } else { 457 } else {
472 videoFrame = nil; 458 videoFrame = nil;
473 } 459 }
474 } 460 }
475 461
476 { 462 {
477 base::AutoLock lock(lock_); 463 base::AutoLock lock(lock_);
478 const CoreMediaGlue::CMTime cm_timestamp = 464 const CMTime cm_timestamp =
479 CoreMediaGlue::CMSampleBufferGetPresentationTimeStamp(sampleBuffer); 465 CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
480 const base::TimeDelta timestamp = 466 const base::TimeDelta timestamp =
481 CMTIME_IS_VALID(cm_timestamp) 467 CMTIME_IS_VALID(cm_timestamp)
482 ? base::TimeDelta::FromMicroseconds( 468 ? base::TimeDelta::FromMicroseconds(
483 cm_timestamp.value * base::TimeTicks::kMicrosecondsPerSecond / 469 cm_timestamp.value * base::TimeTicks::kMicrosecondsPerSecond /
484 cm_timestamp.timescale) 470 cm_timestamp.timescale)
485 : media::kNoTimestamp; 471 : media::kNoTimestamp;
486 472
487 if (frameReceiver_ && baseAddress) { 473 if (frameReceiver_ && baseAddress) {
488 frameReceiver_->ReceiveFrame(reinterpret_cast<uint8_t*>(baseAddress), 474 frameReceiver_->ReceiveFrame(reinterpret_cast<uint8_t*>(baseAddress),
489 frameSize, captureFormat, 0, 0, timestamp); 475 frameSize, captureFormat, 0, 0, timestamp);
490 } 476 }
491 } 477 }
492 478
493 if (videoFrame) 479 if (videoFrame)
494 CVPixelBufferUnlockBaseAddress(videoFrame, kCVPixelBufferLock_ReadOnly); 480 CVPixelBufferUnlockBaseAddress(videoFrame, kCVPixelBufferLock_ReadOnly);
495 } 481 }
496 482
497 - (void)onVideoError:(NSNotification*)errorNotification { 483 - (void)onVideoError:(NSNotification*)errorNotification {
498 NSError* error = base::mac::ObjCCast<NSError>([[errorNotification userInfo] 484 NSError* error = base::mac::ObjCCast<NSError>(
499 objectForKey:AVFoundationGlue::AVCaptureSessionErrorKey()]); 485 [[errorNotification userInfo] objectForKey:AVCaptureSessionErrorKey]);
500 [self sendErrorString:[NSString 486 [self sendErrorString:[NSString
501 stringWithFormat:@"%@: %@", 487 stringWithFormat:@"%@: %@",
502 [error localizedDescription], 488 [error localizedDescription],
503 [error localizedFailureReason]]]; 489 [error localizedFailureReason]]];
504 } 490 }
505 491
506 - (void)sendErrorString:(NSString*)error { 492 - (void)sendErrorString:(NSString*)error {
507 DLOG(ERROR) << [error UTF8String]; 493 DLOG(ERROR) << [error UTF8String];
508 base::AutoLock lock(lock_); 494 base::AutoLock lock(lock_);
509 if (frameReceiver_) 495 if (frameReceiver_)
510 frameReceiver_->ReceiveError(FROM_HERE, [error UTF8String]); 496 frameReceiver_->ReceiveError(FROM_HERE, [error UTF8String]);
511 } 497 }
512 498
513 @end 499 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698