diff options
Diffstat (limited to 'chromium/media/video/capture/mac/video_capture_device_mac.mm')
-rw-r--r-- | chromium/media/video/capture/mac/video_capture_device_mac.mm | 209 |
1 files changed, 118 insertions, 91 deletions
diff --git a/chromium/media/video/capture/mac/video_capture_device_mac.mm b/chromium/media/video/capture/mac/video_capture_device_mac.mm index eea861481fe..dba4fa1c6fb 100644 --- a/chromium/media/video/capture/mac/video_capture_device_mac.mm +++ b/chromium/media/video/capture/mac/video_capture_device_mac.mm @@ -4,15 +4,16 @@ #include "media/video/capture/mac/video_capture_device_mac.h" -#import <QTKit/QTKit.h> - #include "base/bind.h" #include "base/location.h" #include "base/logging.h" #include "base/time/time.h" -#include "media/video/capture/mac/video_capture_device_qtkit_mac.h" +#import "media/video/capture/mac/avfoundation_glue.h" +#import "media/video/capture/mac/platform_video_capturing_mac.h" +#import "media/video/capture/mac/video_capture_device_avfoundation_mac.h" +#import "media/video/capture/mac/video_capture_device_qtkit_mac.h" -namespace { +namespace media { const int kMinFrameRate = 1; const int kMaxFrameRate = 30; @@ -35,6 +36,12 @@ const Resolution* const kWellSupportedResolutions[] = { &kHD, }; +// Rescaling the image to fix the pixel aspect ratio runs the risk of making +// the aspect ratio worse, if QTKit selects a new source mode with a different +// shape. This constant ensures that we don't take this risk if the current +// aspect ratio is tolerable. +const float kMaxPixelAspectRatio = 1.15; + // TODO(ronghuawu): Replace this with CapabilityList::GetBestMatchedCapability. void GetBestMatchSupportedResolution(int* width, int* height) { int min_diff = kint32max; @@ -55,15 +62,18 @@ void GetBestMatchSupportedResolution(int* width, int* height) { *height = matched_height; } -} - -namespace media { - void VideoCaptureDevice::GetDeviceNames(Names* device_names) { // Loop through all available devices and add to |device_names|. device_names->clear(); - NSDictionary* capture_devices = [VideoCaptureDeviceQTKit deviceNames]; + NSDictionary* capture_devices; + if (AVFoundationGlue::IsAVFoundationSupported()) { + DVLOG(1) << "Enumerating video capture devices using AVFoundation"; + capture_devices = [VideoCaptureDeviceAVFoundation deviceNames]; + } else { + DVLOG(1) << "Enumerating video capture devices using QTKit"; + capture_devices = [VideoCaptureDeviceQTKit deviceNames]; + } for (NSString* key in capture_devices) { Name name([[capture_devices valueForKey:key] UTF8String], [key UTF8String]); @@ -71,6 +81,12 @@ void VideoCaptureDevice::GetDeviceNames(Names* device_names) { } } +// static +void VideoCaptureDevice::GetDeviceSupportedFormats(const Name& device, + VideoCaptureFormats* formats) { + NOTIMPLEMENTED(); +} + const std::string VideoCaptureDevice::Name::GetModel() const { // Both PID and VID are 4 characters. if (unique_id_.size() < 2 * kVidPidSize) { @@ -99,8 +115,8 @@ VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) { VideoCaptureDeviceMac::VideoCaptureDeviceMac(const Name& device_name) : device_name_(device_name), - observer_(NULL), sent_frame_info_(false), + tried_to_square_pixels_(false), loop_proxy_(base::MessageLoopProxy::current()), state_(kNotInitialized), weak_factory_(this), @@ -113,24 +129,23 @@ VideoCaptureDeviceMac::~VideoCaptureDeviceMac() { [capture_device_ release]; } -void VideoCaptureDeviceMac::Allocate( - const VideoCaptureCapability& capture_format, - EventHandler* observer) { +void VideoCaptureDeviceMac::AllocateAndStart( + const VideoCaptureParams& params, + scoped_ptr<VideoCaptureDevice::Client> client) { DCHECK_EQ(loop_proxy_, base::MessageLoopProxy::current()); if (state_ != kIdle) { return; } - int width = capture_format.width; - int height = capture_format.height; - int frame_rate = capture_format.frame_rate; + int width = params.requested_format.frame_size.width(); + int height = params.requested_format.frame_size.height(); + int frame_rate = params.requested_format.frame_rate; - // QTKit can scale captured frame to any size requested, which would lead to - // undesired aspect ratio change. Tries to open the camera with a natively - // supported format and let the client to crop/pad the captured frames. - GetBestMatchSupportedResolution(&width, - &height); + // The OS API can scale captured frame to any size requested, which would lead + // to undesired aspect ratio change. Try to open the camera with a natively + // supported format and let the client crop/pad the captured frames. + GetBestMatchSupportedResolution(&width, &height); - observer_ = observer; + client_ = client.Pass(); NSString* deviceId = [NSString stringWithUTF8String:device_name_.id().c_str()]; @@ -145,23 +160,18 @@ void VideoCaptureDeviceMac::Allocate( else if (frame_rate > kMaxFrameRate) frame_rate = kMaxFrameRate; - current_settings_.color = PIXEL_FORMAT_UYVY; - current_settings_.width = width; - current_settings_.height = height; - current_settings_.frame_rate = frame_rate; - current_settings_.expected_capture_delay = 0; - current_settings_.interlaced = false; + capture_format_.frame_size.SetSize(width, height); + capture_format_.frame_rate = frame_rate; + capture_format_.pixel_format = PIXEL_FORMAT_UYVY; - if (width != kHD.width || height != kHD.height) { + if (width <= kVGA.width || height <= kVGA.height) { // If the resolution is VGA or QVGA, set the capture resolution to the - // target size. For most cameras (though not all), at these resolutions - // QTKit produces frames with square pixels. + // target size. Essentially all supported cameras offer at least VGA. if (!UpdateCaptureResolution()) return; - - sent_frame_info_ = true; - observer_->OnFrameInfo(current_settings_); } + // For higher resolutions, we first open at the default resolution to find + // out if the request is larger than the camera's native resolution. // If the resolution is HD, start capturing without setting a resolution. // QTKit will produce frames at the native resolution, allowing us to @@ -174,56 +184,46 @@ void VideoCaptureDeviceMac::Allocate( return; } - state_ = kAllocated; -} - -void VideoCaptureDeviceMac::Start() { - DCHECK_EQ(loop_proxy_, base::MessageLoopProxy::current()); - DCHECK_EQ(state_, kAllocated); state_ = kCapturing; - - // This method no longer has any effect. Capturing is triggered by - // the call to Allocate. - // TODO(bemasc, ncarter): Remove this method. } -void VideoCaptureDeviceMac::Stop() { +void VideoCaptureDeviceMac::StopAndDeAllocate() { DCHECK_EQ(loop_proxy_, base::MessageLoopProxy::current()); DCHECK(state_ == kCapturing || state_ == kError) << state_; [capture_device_ stopCapture]; - state_ = kAllocated; -} -void VideoCaptureDeviceMac::DeAllocate() { - DCHECK_EQ(loop_proxy_, base::MessageLoopProxy::current()); - if (state_ != kAllocated && state_ != kCapturing) { - return; - } - if (state_ == kCapturing) { - [capture_device_ stopCapture]; - } [capture_device_ setCaptureDevice:nil]; [capture_device_ setFrameReceiver:nil]; - + client_.reset(); state_ = kIdle; -} - -const VideoCaptureDevice::Name& VideoCaptureDeviceMac::device_name() { - return device_name_; + tried_to_square_pixels_ = false; } bool VideoCaptureDeviceMac::Init() { DCHECK_EQ(loop_proxy_, base::MessageLoopProxy::current()); DCHECK_EQ(state_, kNotInitialized); + // TODO(mcasas): The following check might not be necessary; if the device has + // disappeared after enumeration and before coming here, opening would just + // fail but not necessarily produce a crash. Names device_names; GetDeviceNames(&device_names); - Name* found = device_names.FindById(device_name_.id()); - if (!found) + Names::iterator it = device_names.begin(); + for (; it != device_names.end(); ++it) { + if (it->id() == device_name_.id()) + break; + } + if (it == device_names.end()) return false; - capture_device_ = - [[VideoCaptureDeviceQTKit alloc] initWithFrameReceiver:this]; + if (AVFoundationGlue::IsAVFoundationSupported()) { + capture_device_ = + [[VideoCaptureDeviceAVFoundation alloc] initWithFrameReceiver:this]; + } else { + capture_device_ = + [[VideoCaptureDeviceQTKit alloc] initWithFrameReceiver:this]; + } + if (!capture_device_) return false; @@ -234,54 +234,81 @@ bool VideoCaptureDeviceMac::Init() { void VideoCaptureDeviceMac::ReceiveFrame( const uint8* video_frame, int video_frame_length, - const VideoCaptureCapability& frame_info, + const VideoCaptureFormat& frame_format, int aspect_numerator, int aspect_denominator) { // This method is safe to call from a device capture thread, // i.e. any thread controlled by QTKit. if (!sent_frame_info_) { - if (current_settings_.width == kHD.width && - current_settings_.height == kHD.height) { - bool changeToVga = false; - if (frame_info.width < kHD.width || frame_info.height < kHD.height) { + // Final resolution has not yet been selected. + if (capture_format_.frame_size.width() > kVGA.width || + capture_format_.frame_size.height() > kVGA.height) { + // We are requesting HD. Make sure that the picture is good, otherwise + // drop down to VGA. + bool change_to_vga = false; + if (frame_format.frame_size.width() < + capture_format_.frame_size.width() || + frame_format.frame_size.height() < + capture_format_.frame_size.height()) { // These are the default capture settings, not yet configured to match - // |current_settings_|. - DCHECK(frame_info.frame_rate == 0); + // |capture_format_|. + DCHECK(frame_format.frame_rate == 0); DVLOG(1) << "Switching to VGA because the default resolution is " << - frame_info.width << "x" << frame_info.height; - changeToVga = true; + frame_format.frame_size.ToString(); + change_to_vga = true; } - if (frame_info.width == kHD.width && frame_info.height == kHD.height && + + if (capture_format_.frame_size == frame_format.frame_size && aspect_numerator != aspect_denominator) { DVLOG(1) << "Switching to VGA because HD has nonsquare pixel " << "aspect ratio " << aspect_numerator << ":" << aspect_denominator; - changeToVga = true; + change_to_vga = true; } - if (changeToVga) { - current_settings_.width = kVGA.width; - current_settings_.height = kVGA.height; + if (change_to_vga) { + capture_format_.frame_size.SetSize(kVGA.width, kVGA.height); + } + } + + if (capture_format_.frame_size == frame_format.frame_size && + !tried_to_square_pixels_ && + (aspect_numerator > kMaxPixelAspectRatio * aspect_denominator || + aspect_denominator > kMaxPixelAspectRatio * aspect_numerator)) { + // The requested size results in non-square PAR. + // Shrink the frame to 1:1 PAR (assuming QTKit selects the same input + // mode, which is not guaranteed). + int new_width = capture_format_.frame_size.width(); + int new_height = capture_format_.frame_size.height(); + if (aspect_numerator < aspect_denominator) { + new_width = (new_width * aspect_numerator) / aspect_denominator; + } else { + new_height = (new_height * aspect_denominator) / aspect_numerator; } + capture_format_.frame_size.SetSize(new_width, new_height); + tried_to_square_pixels_ = true; } - if (current_settings_.width == frame_info.width && - current_settings_.height == frame_info.height) { + if (capture_format_.frame_size == frame_format.frame_size) { sent_frame_info_ = true; - observer_->OnFrameInfo(current_settings_); } else { UpdateCaptureResolution(); - // The current frame does not have the right width and height, so it - // must not be passed to |observer_|. + // OnFrameInfo has not yet been called. OnIncomingCapturedFrame must + // not be called until after OnFrameInfo, so we return early. return; } } - DCHECK(current_settings_.width == frame_info.width && - current_settings_.height == frame_info.height); + DCHECK_EQ(capture_format_.frame_size.width(), + frame_format.frame_size.width()); + DCHECK_EQ(capture_format_.frame_size.height(), + frame_format.frame_size.height()); - observer_->OnIncomingCapturedFrame( - video_frame, video_frame_length, base::Time::Now(), 0, false, false); + client_->OnIncomingCapturedFrame(video_frame, + video_frame_length, + base::Time::Now(), + 0, + capture_format_); } void VideoCaptureDeviceMac::ReceiveError(const std::string& reason) { @@ -294,13 +321,13 @@ void VideoCaptureDeviceMac::SetErrorState(const std::string& reason) { DCHECK_EQ(loop_proxy_, base::MessageLoopProxy::current()); DLOG(ERROR) << reason; state_ = kError; - observer_->OnError(); + client_->OnError(); } bool VideoCaptureDeviceMac::UpdateCaptureResolution() { - if (![capture_device_ setCaptureHeight:current_settings_.height - width:current_settings_.width - frameRate:current_settings_.frame_rate]) { + if (![capture_device_ setCaptureHeight:capture_format_.frame_size.height() + width:capture_format_.frame_size.width() + frameRate:capture_format_.frame_rate]) { ReceiveError("Could not configure capture device."); return false; } |