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

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

Issue 558623002: Video capture: Refactor GetBestMatchedFormat from Win to OS independent (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix mac syntax error Created 6 years, 3 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/video/capture/mac/video_capture_device_mac.h" 5 #include "media/video/capture/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 10
11 #include "base/bind.h" 11 #include "base/bind.h"
12 #include "base/location.h" 12 #include "base/location.h"
13 #include "base/logging.h" 13 #include "base/logging.h"
14 #include "base/message_loop/message_loop_proxy.h" 14 #include "base/message_loop/message_loop_proxy.h"
15 #include "base/mac/scoped_ioobject.h" 15 #include "base/mac/scoped_ioobject.h"
16 #include "base/mac/scoped_ioplugininterface.h" 16 #include "base/mac/scoped_ioplugininterface.h"
17 #include "base/strings/string_number_conversions.h" 17 #include "base/strings/string_number_conversions.h"
18 #include "base/time/time.h" 18 #include "base/time/time.h"
19 #import "media/base/mac/avfoundation_glue.h" 19 #import "media/base/mac/avfoundation_glue.h"
20 #import "media/video/capture/mac/platform_video_capturing_mac.h" 20 #import "media/video/capture/mac/platform_video_capturing_mac.h"
21 #import "media/video/capture/mac/video_capture_device_avfoundation_mac.h" 21 #import "media/video/capture/mac/video_capture_device_avfoundation_mac.h"
22 #import "media/video/capture/mac/video_capture_device_qtkit_mac.h" 22 #import "media/video/capture/mac/video_capture_device_qtkit_mac.h"
23 #include "ui/gfx/size.h"
23 24
24 @implementation DeviceNameAndTransportType 25 @implementation DeviceNameAndTransportType
25 26
26 - (id)initWithName:(NSString*)deviceName transportType:(int32_t)transportType { 27 - (id)initWithName:(NSString*)deviceName transportType:(int32_t)transportType {
27 if (self = [super init]) { 28 if (self = [super init]) {
28 deviceName_.reset([deviceName copy]); 29 deviceName_.reset([deviceName copy]);
29 transportType_ = transportType; 30 transportType_ = transportType;
30 } 31 }
31 return self; 32 return self;
32 } 33 }
(...skipping 10 matching lines...) Expand all
43 44
44 namespace media { 45 namespace media {
45 46
46 // Mac specific limits for minimum and maximum frame rate. 47 // Mac specific limits for minimum and maximum frame rate.
47 const float kMinFrameRate = 1.0f; 48 const float kMinFrameRate = 1.0f;
48 const float kMaxFrameRate = 30.0f; 49 const float kMaxFrameRate = 30.0f;
49 50
50 // In device identifiers, the USB VID and PID are stored in 4 bytes each. 51 // In device identifiers, the USB VID and PID are stored in 4 bytes each.
51 const size_t kVidPidSize = 4; 52 const size_t kVidPidSize = 4;
52 53
53 const struct Resolution { 54 static const gfx::Size kQVGA(320, 240),
tommi (sloooow) - chröme 2014/09/09 20:11:10 Do these require static initialization? (i.e. code
magjed_chromium 2014/09/10 18:34:21 Ok, cool. I don't think it's possible to remove th
54 const int width; 55 kVGA(640, 480),
55 const int height; 56 kHD(1280, 720);
56 } kQVGA = { 320, 240 },
57 kVGA = { 640, 480 },
58 kHD = { 1280, 720 };
59
60 const struct Resolution* const kWellSupportedResolutions[] = {
61 &kQVGA,
62 &kVGA,
63 &kHD,
64 };
65 57
66 // Rescaling the image to fix the pixel aspect ratio runs the risk of making 58 // Rescaling the image to fix the pixel aspect ratio runs the risk of making
67 // the aspect ratio worse, if QTKit selects a new source mode with a different 59 // the aspect ratio worse, if QTKit selects a new source mode with a different
68 // shape. This constant ensures that we don't take this risk if the current 60 // shape. This constant ensures that we don't take this risk if the current
69 // aspect ratio is tolerable. 61 // aspect ratio is tolerable.
70 const float kMaxPixelAspectRatio = 1.15; 62 const float kMaxPixelAspectRatio = 1.15;
71 63
72 // The following constants are extracted from the specification "Universal 64 // The following constants are extracted from the specification "Universal
73 // Serial Bus Device Class Definition for Video Devices", Rev. 1.1 June 1, 2005. 65 // Serial Bus Device Class Definition for Video Devices", Rev. 1.1 June 1, 2005.
74 // http://www.usb.org/developers/devclass_docs/USB_Video_Class_1_1.zip 66 // http://www.usb.org/developers/devclass_docs/USB_Video_Class_1_1.zip
(...skipping 12 matching lines...) Expand all
87 const int k60Hz = 2; 79 const int k60Hz = 2;
88 const int kPuPowerLineFrequencyControlCommandSize = 1; 80 const int kPuPowerLineFrequencyControlCommandSize = 1;
89 81
90 // Addition to the IOUSB family of structures, with subtype and unit ID. 82 // Addition to the IOUSB family of structures, with subtype and unit ID.
91 typedef struct IOUSBInterfaceDescriptor { 83 typedef struct IOUSBInterfaceDescriptor {
92 IOUSBDescriptorHeader header; 84 IOUSBDescriptorHeader header;
93 UInt8 bDescriptorSubType; 85 UInt8 bDescriptorSubType;
94 UInt8 bUnitID; 86 UInt8 bUnitID;
95 } IOUSBInterfaceDescriptor; 87 } IOUSBInterfaceDescriptor;
96 88
97 // TODO(ronghuawu): Replace this with CapabilityList::GetBestMatchedCapability.
98 void GetBestMatchSupportedResolution(int* width, int* height) {
99 int min_diff = kint32max;
100 int matched_width = *width;
101 int matched_height = *height;
102 int desired_res_area = *width * *height;
103 for (size_t i = 0; i < arraysize(kWellSupportedResolutions); ++i) {
104 int area = kWellSupportedResolutions[i]->width *
105 kWellSupportedResolutions[i]->height;
106 int diff = std::abs(desired_res_area - area);
107 if (diff < min_diff) {
108 min_diff = diff;
109 matched_width = kWellSupportedResolutions[i]->width;
110 matched_height = kWellSupportedResolutions[i]->height;
111 }
112 }
113 *width = matched_width;
114 *height = matched_height;
115 }
116
117 // Tries to create a user-side device interface for a given USB device. Returns 89 // Tries to create a user-side device interface for a given USB device. Returns
118 // true if interface was found and passes it back in |device_interface|. The 90 // true if interface was found and passes it back in |device_interface|. The
119 // caller should release |device_interface|. 91 // caller should release |device_interface|.
120 static bool FindDeviceInterfaceInUsbDevice( 92 static bool FindDeviceInterfaceInUsbDevice(
121 const int vendor_id, 93 const int vendor_id,
122 const int product_id, 94 const int product_id,
123 const io_service_t usb_device, 95 const io_service_t usb_device,
124 IOUSBDeviceInterface*** device_interface) { 96 IOUSBDeviceInterface*** device_interface) {
125 // Create a plug-in, i.e. a user-side controller to manipulate USB device. 97 // Create a plug-in, i.e. a user-side controller to manipulate USB device.
126 IOCFPlugInInterface** plugin; 98 IOCFPlugInInterface** plugin;
(...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 [capture_device_ release]; 333 [capture_device_ release];
362 } 334 }
363 335
364 void VideoCaptureDeviceMac::AllocateAndStart( 336 void VideoCaptureDeviceMac::AllocateAndStart(
365 const VideoCaptureParams& params, 337 const VideoCaptureParams& params,
366 scoped_ptr<VideoCaptureDevice::Client> client) { 338 scoped_ptr<VideoCaptureDevice::Client> client) {
367 DCHECK(task_runner_->BelongsToCurrentThread()); 339 DCHECK(task_runner_->BelongsToCurrentThread());
368 if (state_ != kIdle) { 340 if (state_ != kIdle) {
369 return; 341 return;
370 } 342 }
371 int width = params.requested_format.frame_size.width();
372 int height = params.requested_format.frame_size.height();
373 float frame_rate = params.requested_format.frame_rate;
374 343
375 // QTKit API can scale captured frame to any size requested, which would lead 344 // QTKit API can scale captured frame to any size requested, which would lead
376 // to undesired aspect ratio changes. Try to open the camera with a known 345 // to undesired aspect ratio changes. Try to open the camera with a known
377 // supported format and let the client crop/pad the captured frames. 346 // supported format and let the client crop/pad the captured frames.
378 if (!AVFoundationGlue::IsAVFoundationSupported()) 347 gfx::Size resolution = params.requested_format.frame_size;
379 GetBestMatchSupportedResolution(&width, &height); 348 if (!AVFoundationGlue::IsAVFoundationSupported()) {
349 // We only care about resolution in this case. Therefore, set fps to 0 and
350 // pixel format to unknown
351 static const VideoCaptureFormat kWellSupportedResolutions[] = {
352 VideoCaptureFormat(kQVGA, 0, PIXEL_FORMAT_UNKNOWN),
353 VideoCaptureFormat(kVGA, 0, PIXEL_FORMAT_UNKNOWN),
354 VideoCaptureFormat(kHD, 0, PIXEL_FORMAT_UNKNOWN)};
355 // Get the most closely matched format using CompareVideoFormat
356 const VideoCaptureFormat& best_match =
357 *std::min_element(
358 kWellSupportedResolutions,
359 kWellSupportedResolutions + arraysize(kWellSupportedResolutions),
360 CompareVideoFormat(
361 VideoCaptureFormat(resolution, 0, PIXEL_FORMAT_UNKNOWN)));
362 resolution = best_match.frame_size;
363 }
380 364
381 client_ = client.Pass(); 365 client_ = client.Pass();
382 if (device_name_.capture_api_type() == Name::AVFOUNDATION) 366 if (device_name_.capture_api_type() == Name::AVFOUNDATION)
383 LogMessage("Using AVFoundation for device: " + device_name_.name()); 367 LogMessage("Using AVFoundation for device: " + device_name_.name());
384 else 368 else
385 LogMessage("Using QTKit for device: " + device_name_.name()); 369 LogMessage("Using QTKit for device: " + device_name_.name());
386 NSString* deviceId = 370 NSString* deviceId =
387 [NSString stringWithUTF8String:device_name_.id().c_str()]; 371 [NSString stringWithUTF8String:device_name_.id().c_str()];
388 372
389 [capture_device_ setFrameReceiver:this]; 373 [capture_device_ setFrameReceiver:this];
390 374
391 if (![capture_device_ setCaptureDevice:deviceId]) { 375 if (![capture_device_ setCaptureDevice:deviceId]) {
392 SetErrorState("Could not open capture device."); 376 SetErrorState("Could not open capture device.");
393 return; 377 return;
394 } 378 }
395 if (frame_rate < kMinFrameRate)
396 frame_rate = kMinFrameRate;
397 else if (frame_rate > kMaxFrameRate)
398 frame_rate = kMaxFrameRate;
399 379
400 capture_format_.frame_size.SetSize(width, height); 380 capture_format_.frame_size = resolution;
401 capture_format_.frame_rate = frame_rate; 381 capture_format_.frame_rate =
382 std::max(kMinFrameRate,
383 std::min(params.requested_format.frame_rate, kMaxFrameRate));
402 capture_format_.pixel_format = PIXEL_FORMAT_UYVY; 384 capture_format_.pixel_format = PIXEL_FORMAT_UYVY;
403 385
404 // QTKit: Set the capture resolution only if this is VGA or smaller, otherwise 386 // 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 387 // leave it unconfigured and start capturing: QTKit will produce frames at the
406 // native resolution, allowing us to identify cameras whose native resolution 388 // 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 389 // 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 390 // latency, because the webcam will need to be reopened if its default
409 // resolution is not HD or VGA. 391 // resolution is not HD or VGA.
410 // AVfoundation is configured for all resolutions. 392 // AVfoundation is configured for all resolutions.
411 if (AVFoundationGlue::IsAVFoundationSupported() || width <= kVGA.width || 393 if (AVFoundationGlue::IsAVFoundationSupported() ||
412 height <= kVGA.height) { 394 resolution.width() <= kVGA.width() ||
395 resolution.height() <= kVGA.height()) {
413 if (!UpdateCaptureResolution()) 396 if (!UpdateCaptureResolution())
414 return; 397 return;
415 } 398 }
416 399
417 // Try setting the power line frequency removal (anti-flicker). The built-in 400 // Try setting the power line frequency removal (anti-flicker). The built-in
418 // cameras are normally suspended so the configuration must happen right 401 // cameras are normally suspended so the configuration must happen right
419 // before starting capture and during configuration. 402 // before starting capture and during configuration.
420 const std::string& device_model = device_name_.GetModel(); 403 const std::string& device_model = device_name_.GetModel();
421 if (device_model.length() > 2 * kVidPidSize) { 404 if (device_model.length() > 2 * kVidPidSize) {
422 std::string vendor_id = device_model.substr(0, kVidPidSize); 405 std::string vendor_id = device_model.substr(0, kVidPidSize);
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
471 void VideoCaptureDeviceMac::ReceiveFrame( 454 void VideoCaptureDeviceMac::ReceiveFrame(
472 const uint8* video_frame, 455 const uint8* video_frame,
473 int video_frame_length, 456 int video_frame_length,
474 const VideoCaptureFormat& frame_format, 457 const VideoCaptureFormat& frame_format,
475 int aspect_numerator, 458 int aspect_numerator,
476 int aspect_denominator) { 459 int aspect_denominator) {
477 // This method is safe to call from a device capture thread, i.e. any thread 460 // This method is safe to call from a device capture thread, i.e. any thread
478 // controlled by QTKit/AVFoundation. 461 // controlled by QTKit/AVFoundation.
479 if (!final_resolution_selected_) { 462 if (!final_resolution_selected_) {
480 DCHECK(!AVFoundationGlue::IsAVFoundationSupported()); 463 DCHECK(!AVFoundationGlue::IsAVFoundationSupported());
481 if (capture_format_.frame_size.width() > kVGA.width || 464 if (capture_format_.frame_size.width() > kVGA.width() ||
482 capture_format_.frame_size.height() > kVGA.height) { 465 capture_format_.frame_size.height() > kVGA.height()) {
483 // We are requesting HD. Make sure that the picture is good, otherwise 466 // We are requesting HD. Make sure that the picture is good, otherwise
484 // drop down to VGA. 467 // drop down to VGA.
485 bool change_to_vga = false; 468 bool change_to_vga = false;
486 if (frame_format.frame_size.width() < 469 if (frame_format.frame_size.width() <
487 capture_format_.frame_size.width() || 470 capture_format_.frame_size.width() ||
488 frame_format.frame_size.height() < 471 frame_format.frame_size.height() <
489 capture_format_.frame_size.height()) { 472 capture_format_.frame_size.height()) {
490 // These are the default capture settings, not yet configured to match 473 // These are the default capture settings, not yet configured to match
491 // |capture_format_|. 474 // |capture_format_|.
492 DCHECK(frame_format.frame_rate == 0); 475 DCHECK(frame_format.frame_rate == 0);
493 DVLOG(1) << "Switching to VGA because the default resolution is " << 476 DVLOG(1) << "Switching to VGA because the default resolution is " <<
494 frame_format.frame_size.ToString(); 477 frame_format.frame_size.ToString();
495 change_to_vga = true; 478 change_to_vga = true;
496 } 479 }
497 480
498 if (capture_format_.frame_size == frame_format.frame_size && 481 if (capture_format_.frame_size == frame_format.frame_size &&
499 aspect_numerator != aspect_denominator) { 482 aspect_numerator != aspect_denominator) {
500 DVLOG(1) << "Switching to VGA because HD has nonsquare pixel " << 483 DVLOG(1) << "Switching to VGA because HD has nonsquare pixel " <<
501 "aspect ratio " << aspect_numerator << ":" << aspect_denominator; 484 "aspect ratio " << aspect_numerator << ":" << aspect_denominator;
502 change_to_vga = true; 485 change_to_vga = true;
503 } 486 }
504 487
505 if (change_to_vga) 488 if (change_to_vga)
506 capture_format_.frame_size.SetSize(kVGA.width, kVGA.height); 489 capture_format_.frame_size = kVGA;
507 } 490 }
508 491
509 if (capture_format_.frame_size == frame_format.frame_size && 492 if (capture_format_.frame_size == frame_format.frame_size &&
510 !tried_to_square_pixels_ && 493 !tried_to_square_pixels_ &&
511 (aspect_numerator > kMaxPixelAspectRatio * aspect_denominator || 494 (aspect_numerator > kMaxPixelAspectRatio * aspect_denominator ||
512 aspect_denominator > kMaxPixelAspectRatio * aspect_numerator)) { 495 aspect_denominator > kMaxPixelAspectRatio * aspect_numerator)) {
513 // The requested size results in non-square PAR. Shrink the frame to 1:1 496 // The requested size results in non-square PAR. Shrink the frame to 1:1
514 // PAR (assuming QTKit selects the same input mode, which is not 497 // PAR (assuming QTKit selects the same input mode, which is not
515 // guaranteed). 498 // guaranteed).
516 int new_width = capture_format_.frame_size.width(); 499 int new_width = capture_format_.frame_size.width();
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
573 if (![capture_device_ setCaptureHeight:capture_format_.frame_size.height() 556 if (![capture_device_ setCaptureHeight:capture_format_.frame_size.height()
574 width:capture_format_.frame_size.width() 557 width:capture_format_.frame_size.width()
575 frameRate:capture_format_.frame_rate]) { 558 frameRate:capture_format_.frame_rate]) {
576 ReceiveError("Could not configure capture device."); 559 ReceiveError("Could not configure capture device.");
577 return false; 560 return false;
578 } 561 }
579 return true; 562 return true;
580 } 563 }
581 564
582 } // namespace media 565 } // namespace media
OLDNEW
« no previous file with comments | « no previous file | media/video/capture/video_capture_types.h » ('j') | media/video/capture/video_capture_types.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698