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

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

Issue 1815983003: Remove deprecated QTKit Video Capture Support (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebased Created 4 years, 8 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 #include "media/capture/video/mac/video_capture_device_mac.h" 5 #include "media/capture/video/mac/video_capture_device_mac.h"
6 6
7 #include <IOKit/IOCFPlugIn.h> 7 #include <IOKit/IOCFPlugIn.h>
8 #include <IOKit/usb/IOUSBLib.h> 8 #include <IOKit/usb/IOUSBLib.h>
9 #include <IOKit/usb/USBSpec.h> 9 #include <IOKit/usb/USBSpec.h>
10 #include <stddef.h> 10 #include <stddef.h>
11 #include <stdint.h> 11 #include <stdint.h>
12 12
13 #include <limits> 13 #include <limits>
14 #include <utility> 14 #include <utility>
15 15
16 #include "base/bind.h" 16 #include "base/bind.h"
17 #include "base/location.h" 17 #include "base/location.h"
18 #include "base/logging.h" 18 #include "base/logging.h"
19 #include "base/mac/scoped_ioobject.h" 19 #include "base/mac/scoped_ioobject.h"
20 #include "base/mac/scoped_ioplugininterface.h" 20 #include "base/mac/scoped_ioplugininterface.h"
21 #include "base/macros.h" 21 #include "base/macros.h"
22 #include "base/single_thread_task_runner.h" 22 #include "base/single_thread_task_runner.h"
23 #include "base/strings/string_number_conversions.h" 23 #include "base/strings/string_number_conversions.h"
24 #include "base/thread_task_runner_handle.h" 24 #include "base/thread_task_runner_handle.h"
25 #include "base/time/time.h" 25 #include "base/time/time.h"
26 #import "media/base/mac/avfoundation_glue.h" 26 #import "media/base/mac/avfoundation_glue.h"
27 #include "media/base/timestamp_constants.h" 27 #include "media/base/timestamp_constants.h"
28 #import "media/capture/video/mac/platform_video_capturing_mac.h" 28 #import "media/capture/video/mac/platform_video_capturing_mac.h"
29 #import "media/capture/video/mac/video_capture_device_avfoundation_mac.h" 29 #import "media/capture/video/mac/video_capture_device_avfoundation_mac.h"
30 #import "media/capture/video/mac/video_capture_device_qtkit_mac.h"
31 #include "ui/gfx/geometry/size.h" 30 #include "ui/gfx/geometry/size.h"
32 31
33 @implementation DeviceNameAndTransportType 32 @implementation DeviceNameAndTransportType
34 33
35 - (id)initWithName:(NSString*)deviceName transportType:(int32_t)transportType { 34 - (id)initWithName:(NSString*)deviceName transportType:(int32_t)transportType {
36 if (self = [super init]) { 35 if (self = [super init]) {
37 deviceName_.reset([deviceName copy]); 36 deviceName_.reset([deviceName copy]);
38 transportType_ = transportType; 37 transportType_ = transportType;
39 } 38 }
40 return self; 39 return self;
(...skipping 11 matching lines...) Expand all
52 51
53 namespace media { 52 namespace media {
54 53
55 // Mac specific limits for minimum and maximum frame rate. 54 // Mac specific limits for minimum and maximum frame rate.
56 const float kMinFrameRate = 1.0f; 55 const float kMinFrameRate = 1.0f;
57 const float kMaxFrameRate = 30.0f; 56 const float kMaxFrameRate = 30.0f;
58 57
59 // In device identifiers, the USB VID and PID are stored in 4 bytes each. 58 // In device identifiers, the USB VID and PID are stored in 4 bytes each.
60 const size_t kVidPidSize = 4; 59 const size_t kVidPidSize = 4;
61 60
62 const struct Resolution {
63 const int width;
64 const int height;
65 } kQVGA = {320, 240}, kVGA = {640, 480}, kHD = {1280, 720};
66
67 const struct Resolution* const kWellSupportedResolutions[] = {
68 &kQVGA,
69 &kVGA,
70 &kHD,
71 };
72
73 // Rescaling the image to fix the pixel aspect ratio runs the risk of making
74 // the aspect ratio worse, if QTKit selects a new source mode with a different
75 // shape. This constant ensures that we don't take this risk if the current
76 // aspect ratio is tolerable.
77 const float kMaxPixelAspectRatio = 1.15;
78
79 // The following constants are extracted from the specification "Universal 61 // The following constants are extracted from the specification "Universal
80 // Serial Bus Device Class Definition for Video Devices", Rev. 1.1 June 1, 2005. 62 // Serial Bus Device Class Definition for Video Devices", Rev. 1.1 June 1, 2005.
81 // http://www.usb.org/developers/devclass_docs/USB_Video_Class_1_1.zip 63 // http://www.usb.org/developers/devclass_docs/USB_Video_Class_1_1.zip
82 // CS_INTERFACE: Sec. A.4 "Video Class-Specific Descriptor Types". 64 // CS_INTERFACE: Sec. A.4 "Video Class-Specific Descriptor Types".
83 const int kVcCsInterface = 0x24; 65 const int kVcCsInterface = 0x24;
84 // VC_PROCESSING_UNIT: Sec. A.5 "Video Class-Specific VC Interface Descriptor 66 // VC_PROCESSING_UNIT: Sec. A.5 "Video Class-Specific VC Interface Descriptor
85 // Subtypes". 67 // Subtypes".
86 const int kVcProcessingUnit = 0x5; 68 const int kVcProcessingUnit = 0x5;
87 // SET_CUR: Sec. A.8 "Video Class-Specific Request Codes". 69 // SET_CUR: Sec. A.8 "Video Class-Specific Request Codes".
88 const int kVcRequestCodeSetCur = 0x1; 70 const int kVcRequestCodeSetCur = 0x1;
89 // PU_POWER_LINE_FREQUENCY_CONTROL: Sec. A.9.5 "Processing Unit Control 71 // PU_POWER_LINE_FREQUENCY_CONTROL: Sec. A.9.5 "Processing Unit Control
90 // Selectors". 72 // Selectors".
91 const int kPuPowerLineFrequencyControl = 0x5; 73 const int kPuPowerLineFrequencyControl = 0x5;
92 // Sec. 4.2.2.3.5 Power Line Frequency Control. 74 // Sec. 4.2.2.3.5 Power Line Frequency Control.
93 const int k50Hz = 1; 75 const int k50Hz = 1;
94 const int k60Hz = 2; 76 const int k60Hz = 2;
95 const int kPuPowerLineFrequencyControlCommandSize = 1; 77 const int kPuPowerLineFrequencyControlCommandSize = 1;
96 78
97 // Addition to the IOUSB family of structures, with subtype and unit ID. 79 // Addition to the IOUSB family of structures, with subtype and unit ID.
98 typedef struct IOUSBInterfaceDescriptor { 80 typedef struct IOUSBInterfaceDescriptor {
99 IOUSBDescriptorHeader header; 81 IOUSBDescriptorHeader header;
100 UInt8 bDescriptorSubType; 82 UInt8 bDescriptorSubType;
101 UInt8 bUnitID; 83 UInt8 bUnitID;
102 } IOUSBInterfaceDescriptor; 84 } IOUSBInterfaceDescriptor;
103 85
104 static void GetBestMatchSupportedResolution(gfx::Size* resolution) {
105 int min_diff = std::numeric_limits<int32_t>::max();
106 const int desired_area = resolution->GetArea();
107 for (size_t i = 0; i < arraysize(kWellSupportedResolutions); ++i) {
108 const int area = kWellSupportedResolutions[i]->width *
109 kWellSupportedResolutions[i]->height;
110 const int diff = std::abs(desired_area - area);
111 if (diff < min_diff) {
112 min_diff = diff;
113 resolution->SetSize(kWellSupportedResolutions[i]->width,
114 kWellSupportedResolutions[i]->height);
115 }
116 }
117 }
118
119 // Tries to create a user-side device interface for a given USB device. Returns 86 // Tries to create a user-side device interface for a given USB device. Returns
120 // true if interface was found and passes it back in |device_interface|. The 87 // true if interface was found and passes it back in |device_interface|. The
121 // caller should release |device_interface|. 88 // caller should release |device_interface|.
122 static bool FindDeviceInterfaceInUsbDevice( 89 static bool FindDeviceInterfaceInUsbDevice(
123 const int vendor_id, 90 const int vendor_id,
124 const int product_id, 91 const int product_id,
125 const io_service_t usb_device, 92 const io_service_t usb_device,
126 IOUSBDeviceInterface*** device_interface) { 93 IOUSBDeviceInterface*** device_interface) {
127 // Create a plugin, i.e. a user-side controller to manipulate USB device. 94 // Create a plugin, i.e. a user-side controller to manipulate USB device.
128 IOCFPlugInInterface** plugin; 95 IOCFPlugInInterface** plugin;
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
340 const size_t vid_location = unique_id_.size() - 2 * kVidPidSize; 307 const size_t vid_location = unique_id_.size() - 2 * kVidPidSize;
341 std::string id_vendor = unique_id_.substr(vid_location, kVidPidSize); 308 std::string id_vendor = unique_id_.substr(vid_location, kVidPidSize);
342 const size_t pid_location = unique_id_.size() - kVidPidSize; 309 const size_t pid_location = unique_id_.size() - kVidPidSize;
343 std::string id_product = unique_id_.substr(pid_location, kVidPidSize); 310 std::string id_product = unique_id_.substr(pid_location, kVidPidSize);
344 311
345 return id_vendor + ":" + id_product; 312 return id_vendor + ":" + id_product;
346 } 313 }
347 314
348 VideoCaptureDeviceMac::VideoCaptureDeviceMac(const Name& device_name) 315 VideoCaptureDeviceMac::VideoCaptureDeviceMac(const Name& device_name)
349 : device_name_(device_name), 316 : device_name_(device_name),
350 tried_to_square_pixels_(false),
351 task_runner_(base::ThreadTaskRunnerHandle::Get()), 317 task_runner_(base::ThreadTaskRunnerHandle::Get()),
352 state_(kNotInitialized), 318 state_(kNotInitialized),
353 capture_device_(nil), 319 capture_device_(nil),
354 first_timestamp_(media::kNoTimestamp()), 320 first_timestamp_(media::kNoTimestamp()),
355 weak_factory_(this) { 321 weak_factory_(this) {
356 // Avoid reconfiguring AVFoundation or blacklisted devices.
357 final_resolution_selected_ = AVFoundationGlue::IsAVFoundationSupported() ||
358 device_name.is_blacklisted();
359 } 322 }
360 323
361 VideoCaptureDeviceMac::~VideoCaptureDeviceMac() { 324 VideoCaptureDeviceMac::~VideoCaptureDeviceMac() {
362 DCHECK(task_runner_->BelongsToCurrentThread()); 325 DCHECK(task_runner_->BelongsToCurrentThread());
363 [capture_device_ release]; 326 [capture_device_ release];
364 } 327 }
365 328
366 void VideoCaptureDeviceMac::AllocateAndStart( 329 void VideoCaptureDeviceMac::AllocateAndStart(
367 const VideoCaptureParams& params, 330 const VideoCaptureParams& params,
368 scoped_ptr<VideoCaptureDevice::Client> client) { 331 scoped_ptr<VideoCaptureDevice::Client> client) {
369 DCHECK(task_runner_->BelongsToCurrentThread()); 332 DCHECK(task_runner_->BelongsToCurrentThread());
370 if (state_ != kIdle) { 333 if (state_ != kIdle) {
371 return; 334 return;
372 } 335 }
373 336
374 // QTKit API can scale captured frame to any size requested, which would lead
375 // to undesired aspect ratio changes. Try to open the camera with a known
376 // supported format and let the client crop/pad the captured frames.
377 gfx::Size resolution = params.requested_format.frame_size;
378 if (!AVFoundationGlue::IsAVFoundationSupported())
379 GetBestMatchSupportedResolution(&resolution);
380
381 client_ = std::move(client); 337 client_ = std::move(client);
382 if (device_name_.capture_api_type() == Name::AVFOUNDATION) 338 if (device_name_.capture_api_type() == Name::AVFOUNDATION)
383 LogMessage("Using AVFoundation for device: " + device_name_.name()); 339 LogMessage("Using AVFoundation for device: " + device_name_.name());
384 else 340
385 LogMessage("Using QTKit for device: " + device_name_.name());
386 NSString* deviceId = 341 NSString* deviceId =
387 [NSString stringWithUTF8String:device_name_.id().c_str()]; 342 [NSString stringWithUTF8String:device_name_.id().c_str()];
388 343
389 [capture_device_ setFrameReceiver:this]; 344 [capture_device_ setFrameReceiver:this];
390 345
391 if (![capture_device_ setCaptureDevice:deviceId]) { 346 if (![capture_device_ setCaptureDevice:deviceId]) {
392 SetErrorState(FROM_HERE, "Could not open capture device."); 347 SetErrorState(FROM_HERE, "Could not open capture device.");
393 return; 348 return;
394 } 349 }
395 350
396 capture_format_.frame_size = resolution; 351 capture_format_.frame_size = params.requested_format.frame_size;
397 capture_format_.frame_rate = 352 capture_format_.frame_rate =
398 std::max(kMinFrameRate, 353 std::max(kMinFrameRate,
399 std::min(params.requested_format.frame_rate, kMaxFrameRate)); 354 std::min(params.requested_format.frame_rate, kMaxFrameRate));
400 // Leave the pixel format selection to AVFoundation/QTKit. The pixel format 355 // Leave the pixel format selection to AVFoundation. The pixel format
401 // will be passed to |ReceiveFrame|. 356 // will be passed to |ReceiveFrame|.
402 capture_format_.pixel_format = PIXEL_FORMAT_UNKNOWN; 357 capture_format_.pixel_format = PIXEL_FORMAT_UNKNOWN;
403 358
404 // QTKit: Set the capture resolution only if this is VGA or smaller, otherwise
405 // leave it unconfigured and start capturing: QTKit will produce frames at the
406 // native resolution, allowing us to identify cameras whose native resolution
407 // is too low for HD. This additional information comes at a cost in startup
408 // latency, because the webcam will need to be reopened if its default
409 // resolution is not HD or VGA.
410 // AVfoundation is configured for all resolutions.
411 if (AVFoundationGlue::IsAVFoundationSupported() ||
412 resolution.width() <= kVGA.width || resolution.height() <= kVGA.height) {
413 if (!UpdateCaptureResolution()) 359 if (!UpdateCaptureResolution())
414 return; 360 return;
415 }
416 361
417 // Try setting the power line frequency removal (anti-flicker). The built-in 362 // Try setting the power line frequency removal (anti-flicker). The built-in
418 // cameras are normally suspended so the configuration must happen right 363 // cameras are normally suspended so the configuration must happen right
419 // before starting capture and during configuration. 364 // before starting capture and during configuration.
420 const std::string& device_model = device_name_.GetModel(); 365 const std::string& device_model = device_name_.GetModel();
421 if (device_model.length() > 2 * kVidPidSize) { 366 if (device_model.length() > 2 * kVidPidSize) {
422 std::string vendor_id = device_model.substr(0, kVidPidSize); 367 std::string vendor_id = device_model.substr(0, kVidPidSize);
423 std::string model_id = device_model.substr(kVidPidSize + 1); 368 std::string model_id = device_model.substr(kVidPidSize + 1);
424 int vendor_id_as_int, model_id_as_int; 369 int vendor_id_as_int, model_id_as_int;
425 if (base::HexStringToInt(base::StringPiece(vendor_id), &vendor_id_as_int) && 370 if (base::HexStringToInt(base::StringPiece(vendor_id), &vendor_id_as_int) &&
(...skipping 12 matching lines...) Expand all
438 } 383 }
439 384
440 void VideoCaptureDeviceMac::StopAndDeAllocate() { 385 void VideoCaptureDeviceMac::StopAndDeAllocate() {
441 DCHECK(task_runner_->BelongsToCurrentThread()); 386 DCHECK(task_runner_->BelongsToCurrentThread());
442 DCHECK(state_ == kCapturing || state_ == kError) << state_; 387 DCHECK(state_ == kCapturing || state_ == kError) << state_;
443 388
444 [capture_device_ setCaptureDevice:nil]; 389 [capture_device_ setCaptureDevice:nil];
445 [capture_device_ setFrameReceiver:nil]; 390 [capture_device_ setFrameReceiver:nil];
446 client_.reset(); 391 client_.reset();
447 state_ = kIdle; 392 state_ = kIdle;
448 tried_to_square_pixels_ = false;
449 } 393 }
450 394
451 bool VideoCaptureDeviceMac::Init( 395 bool VideoCaptureDeviceMac::Init(
452 VideoCaptureDevice::Name::CaptureApiType capture_api_type) { 396 VideoCaptureDevice::Name::CaptureApiType capture_api_type) {
453 DCHECK(task_runner_->BelongsToCurrentThread()); 397 DCHECK(task_runner_->BelongsToCurrentThread());
454 DCHECK_EQ(state_, kNotInitialized); 398 DCHECK_EQ(state_, kNotInitialized);
455 399
456 if (capture_api_type == Name::AVFOUNDATION) { 400 if (capture_api_type == Name::AVFOUNDATION) {
457 capture_device_ = 401 capture_device_ =
458 [[VideoCaptureDeviceAVFoundation alloc] initWithFrameReceiver:this]; 402 [[VideoCaptureDeviceAVFoundation alloc] initWithFrameReceiver:this];
459 } else {
460 capture_device_ =
461 [[VideoCaptureDeviceQTKit alloc] initWithFrameReceiver:this];
462 } 403 }
463 404
464 if (!capture_device_) 405 if (!capture_device_)
465 return false; 406 return false;
466 407
467 state_ = kIdle; 408 state_ = kIdle;
468 return true; 409 return true;
469 } 410 }
470 411
471 void VideoCaptureDeviceMac::ReceiveFrame(const uint8_t* video_frame, 412 void VideoCaptureDeviceMac::ReceiveFrame(const uint8_t* video_frame,
472 int video_frame_length, 413 int video_frame_length,
473 const VideoCaptureFormat& frame_format, 414 const VideoCaptureFormat& frame_format,
474 int aspect_numerator, 415 int aspect_numerator,
475 int aspect_denominator, 416 int aspect_denominator,
476 base::TimeDelta timestamp) { 417 base::TimeDelta timestamp) {
477 // This method is safe to call from a device capture thread, i.e. any thread 418 // This method is safe to call from a device capture thread, i.e. any thread
478 // controlled by QTKit/AVFoundation. 419 // controlled by AVFoundation.
479 if (!final_resolution_selected_) { 420 if (capture_format_.frame_size != frame_format.frame_size) {
480 DCHECK(!AVFoundationGlue::IsAVFoundationSupported());
481 if (capture_format_.frame_size.width() > kVGA.width ||
482 capture_format_.frame_size.height() > kVGA.height) {
483 // We are requesting HD. Make sure that the picture is good, otherwise
484 // drop down to VGA.
485 bool change_to_vga = false;
486 if (frame_format.frame_size.width() <
487 capture_format_.frame_size.width() ||
488 frame_format.frame_size.height() <
489 capture_format_.frame_size.height()) {
490 // These are the default capture settings, not yet configured to match
491 // |capture_format_|.
492 DCHECK(frame_format.frame_rate == 0);
493 DVLOG(1) << "Switching to VGA because the default resolution is "
494 << frame_format.frame_size.ToString();
495 change_to_vga = true;
496 }
497
498 if (capture_format_.frame_size == frame_format.frame_size &&
499 aspect_numerator != aspect_denominator) {
500 DVLOG(1) << "Switching to VGA because HD has nonsquare pixel "
501 << "aspect ratio " << aspect_numerator << ":"
502 << aspect_denominator;
503 change_to_vga = true;
504 }
505
506 if (change_to_vga)
507 capture_format_.frame_size.SetSize(kVGA.width, kVGA.height);
508 }
509
510 if (capture_format_.frame_size == frame_format.frame_size &&
511 !tried_to_square_pixels_ &&
512 (aspect_numerator > kMaxPixelAspectRatio * aspect_denominator ||
513 aspect_denominator > kMaxPixelAspectRatio * aspect_numerator)) {
514 // The requested size results in non-square PAR. Shrink the frame to 1:1
515 // PAR (assuming QTKit selects the same input mode, which is not
516 // guaranteed).
517 int new_width = capture_format_.frame_size.width();
518 int new_height = capture_format_.frame_size.height();
519 if (aspect_numerator < aspect_denominator)
520 new_width = (new_width * aspect_numerator) / aspect_denominator;
521 else
522 new_height = (new_height * aspect_denominator) / aspect_numerator;
523 capture_format_.frame_size.SetSize(new_width, new_height);
524 tried_to_square_pixels_ = true;
525 }
526
527 if (capture_format_.frame_size == frame_format.frame_size) {
528 final_resolution_selected_ = true;
529 } else {
530 UpdateCaptureResolution();
531 // Let the resolution update sink through QTKit and wait for next frame.
532 return;
533 }
534 }
535
536 // QTKit capture source can change resolution if someone else reconfigures the
537 // camera, and that is fine: http://crbug.com/353620. In AVFoundation, this
538 // should not happen, it should resize internally.
539 if (!AVFoundationGlue::IsAVFoundationSupported()) {
540 capture_format_.frame_size = frame_format.frame_size;
541 } else if (capture_format_.frame_size != frame_format.frame_size) {
542 ReceiveError(FROM_HERE, 421 ReceiveError(FROM_HERE,
543 "Captured resolution " + frame_format.frame_size.ToString() + 422 "Captured resolution " + frame_format.frame_size.ToString() +
544 ", and expected " + capture_format_.frame_size.ToString()); 423 ", and expected " + capture_format_.frame_size.ToString());
545 return; 424 return;
546 } 425 }
547 426
548 base::TimeTicks aligned_timestamp; 427 base::TimeTicks aligned_timestamp;
549 if (timestamp == media::kNoTimestamp()) { 428 if (timestamp == media::kNoTimestamp()) {
550 aligned_timestamp = base::TimeTicks::Now(); 429 aligned_timestamp = base::TimeTicks::Now();
551 } else { 430 } else {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
585 if (![capture_device_ setCaptureHeight:capture_format_.frame_size.height() 464 if (![capture_device_ setCaptureHeight:capture_format_.frame_size.height()
586 width:capture_format_.frame_size.width() 465 width:capture_format_.frame_size.width()
587 frameRate:capture_format_.frame_rate]) { 466 frameRate:capture_format_.frame_rate]) {
588 ReceiveError(FROM_HERE, "Could not configure capture device."); 467 ReceiveError(FROM_HERE, "Could not configure capture device.");
589 return false; 468 return false;
590 } 469 }
591 return true; 470 return true;
592 } 471 }
593 472
594 } // namespace media 473 } // namespace media
OLDNEW
« no previous file with comments | « media/capture/video/mac/video_capture_device_mac.h ('k') | media/capture/video/mac/video_capture_device_qtkit_mac.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698