| OLD | NEW |
| 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 "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/location.h" | 8 #include "base/location.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/time/time.h" | 10 #include "base/time/time.h" |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 127 weak_this_(weak_factory_.GetWeakPtr()), | 127 weak_this_(weak_factory_.GetWeakPtr()), |
| 128 capture_device_(nil) { | 128 capture_device_(nil) { |
| 129 } | 129 } |
| 130 | 130 |
| 131 VideoCaptureDeviceMac::~VideoCaptureDeviceMac() { | 131 VideoCaptureDeviceMac::~VideoCaptureDeviceMac() { |
| 132 DCHECK_EQ(loop_proxy_, base::MessageLoopProxy::current()); | 132 DCHECK_EQ(loop_proxy_, base::MessageLoopProxy::current()); |
| 133 [capture_device_ release]; | 133 [capture_device_ release]; |
| 134 } | 134 } |
| 135 | 135 |
| 136 void VideoCaptureDeviceMac::AllocateAndStart( | 136 void VideoCaptureDeviceMac::AllocateAndStart( |
| 137 const VideoCaptureParams& params, | 137 const VideoCaptureCapability& capture_format, |
| 138 scoped_ptr<VideoCaptureDevice::Client> client) { | 138 scoped_ptr<VideoCaptureDevice::Client> client) { |
| 139 DCHECK_EQ(loop_proxy_, base::MessageLoopProxy::current()); | 139 DCHECK_EQ(loop_proxy_, base::MessageLoopProxy::current()); |
| 140 if (state_ != kIdle) { | 140 if (state_ != kIdle) { |
| 141 return; | 141 return; |
| 142 } | 142 } |
| 143 int width = params.requested_format.frame_size.width(); | 143 int width = capture_format.width; |
| 144 int height = params.requested_format.frame_size.height(); | 144 int height = capture_format.height; |
| 145 int frame_rate = params.requested_format.frame_rate; | 145 int frame_rate = capture_format.frame_rate; |
| 146 | 146 |
| 147 // The OS API can scale captured frame to any size requested, which would lead | 147 // The OS API can scale captured frame to any size requested, which would lead |
| 148 // to undesired aspect ratio change. Try to open the camera with a natively | 148 // to undesired aspect ratio change. Try to open the camera with a natively |
| 149 // supported format and let the client crop/pad the captured frames. | 149 // supported format and let the client crop/pad the captured frames. |
| 150 GetBestMatchSupportedResolution(&width, &height); | 150 GetBestMatchSupportedResolution(&width, &height); |
| 151 | 151 |
| 152 client_ = client.Pass(); | 152 client_ = client.Pass(); |
| 153 NSString* deviceId = | 153 NSString* deviceId = |
| 154 [NSString stringWithUTF8String:device_name_.id().c_str()]; | 154 [NSString stringWithUTF8String:device_name_.id().c_str()]; |
| 155 | 155 |
| 156 [capture_device_ setFrameReceiver:this]; | 156 [capture_device_ setFrameReceiver:this]; |
| 157 | 157 |
| 158 if (![capture_device_ setCaptureDevice:deviceId]) { | 158 if (![capture_device_ setCaptureDevice:deviceId]) { |
| 159 SetErrorState("Could not open capture device."); | 159 SetErrorState("Could not open capture device."); |
| 160 return; | 160 return; |
| 161 } | 161 } |
| 162 if (frame_rate < kMinFrameRate) | 162 if (frame_rate < kMinFrameRate) |
| 163 frame_rate = kMinFrameRate; | 163 frame_rate = kMinFrameRate; |
| 164 else if (frame_rate > kMaxFrameRate) | 164 else if (frame_rate > kMaxFrameRate) |
| 165 frame_rate = kMaxFrameRate; | 165 frame_rate = kMaxFrameRate; |
| 166 | 166 |
| 167 capture_format_.frame_size.SetSize(width, height); | 167 current_settings_.color = PIXEL_FORMAT_UYVY; |
| 168 capture_format_.frame_rate = frame_rate; | 168 current_settings_.width = width; |
| 169 capture_format_.pixel_format = PIXEL_FORMAT_UYVY; | 169 current_settings_.height = height; |
| 170 current_settings_.frame_rate = frame_rate; |
| 170 | 171 |
| 171 if (width <= kVGA.width || height <= kVGA.height) { | 172 if (width <= kVGA.width || height <= kVGA.height) { |
| 172 // If the resolution is VGA or QVGA, set the capture resolution to the | 173 // If the resolution is VGA or QVGA, set the capture resolution to the |
| 173 // target size. Essentially all supported cameras offer at least VGA. | 174 // target size. Essentially all supported cameras offer at least VGA. |
| 174 if (!UpdateCaptureResolution()) | 175 if (!UpdateCaptureResolution()) |
| 175 return; | 176 return; |
| 176 } | 177 } |
| 177 // For higher resolutions, we first open at the default resolution to find | 178 // For higher resolutions, we first open at the default resolution to find |
| 178 // out if the request is larger than the camera's native resolution. | 179 // out if the request is larger than the camera's native resolution. |
| 179 | 180 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 218 if (!capture_device_) | 219 if (!capture_device_) |
| 219 return false; | 220 return false; |
| 220 | 221 |
| 221 state_ = kIdle; | 222 state_ = kIdle; |
| 222 return true; | 223 return true; |
| 223 } | 224 } |
| 224 | 225 |
| 225 void VideoCaptureDeviceMac::ReceiveFrame( | 226 void VideoCaptureDeviceMac::ReceiveFrame( |
| 226 const uint8* video_frame, | 227 const uint8* video_frame, |
| 227 int video_frame_length, | 228 int video_frame_length, |
| 228 const VideoCaptureFormat& frame_format, | 229 const VideoCaptureCapability& frame_info, |
| 229 int aspect_numerator, | 230 int aspect_numerator, |
| 230 int aspect_denominator) { | 231 int aspect_denominator) { |
| 231 // This method is safe to call from a device capture thread, | 232 // This method is safe to call from a device capture thread, |
| 232 // i.e. any thread controlled by QTKit. | 233 // i.e. any thread controlled by QTKit. |
| 233 | 234 |
| 234 if (!sent_frame_info_) { | 235 if (!sent_frame_info_) { |
| 235 // Final resolution has not yet been selected. | 236 // Final resolution has not yet been selected. |
| 236 if (capture_format_.frame_size.width() > kVGA.width || | 237 if (current_settings_.width > kVGA.width || |
| 237 capture_format_.frame_size.height() > kVGA.height) { | 238 current_settings_.height > kVGA.height) { |
| 238 // We are requesting HD. Make sure that the picture is good, otherwise | 239 // We are requesting HD. Make sure that the picture is good, otherwise |
| 239 // drop down to VGA. | 240 // drop down to VGA. |
| 240 bool change_to_vga = false; | 241 bool change_to_vga = false; |
| 241 if (frame_format.frame_size.width() < | 242 if (frame_info.width < current_settings_.width || |
| 242 capture_format_.frame_size.width() || | 243 frame_info.height < current_settings_.height) { |
| 243 frame_format.frame_size.height() < | |
| 244 capture_format_.frame_size.height()) { | |
| 245 // These are the default capture settings, not yet configured to match | 244 // These are the default capture settings, not yet configured to match |
| 246 // |capture_format_|. | 245 // |current_settings_|. |
| 247 DCHECK(frame_format.frame_rate == 0); | 246 DCHECK(frame_info.frame_rate == 0); |
| 248 DVLOG(1) << "Switching to VGA because the default resolution is " << | 247 DVLOG(1) << "Switching to VGA because the default resolution is " << |
| 249 frame_format.frame_size.ToString(); | 248 frame_info.width << "x" << frame_info.height; |
| 250 change_to_vga = true; | 249 change_to_vga = true; |
| 251 } | 250 } |
| 252 | 251 |
| 253 if (capture_format_.frame_size == frame_format.frame_size && | 252 if (frame_info.width == current_settings_.width && |
| 253 frame_info.height == current_settings_.height && |
| 254 aspect_numerator != aspect_denominator) { | 254 aspect_numerator != aspect_denominator) { |
| 255 DVLOG(1) << "Switching to VGA because HD has nonsquare pixel " << | 255 DVLOG(1) << "Switching to VGA because HD has nonsquare pixel " << |
| 256 "aspect ratio " << aspect_numerator << ":" << aspect_denominator; | 256 "aspect ratio " << aspect_numerator << ":" << aspect_denominator; |
| 257 change_to_vga = true; | 257 change_to_vga = true; |
| 258 } | 258 } |
| 259 | 259 |
| 260 if (change_to_vga) { | 260 if (change_to_vga) { |
| 261 capture_format_.frame_size.SetSize(kVGA.width, kVGA.height); | 261 current_settings_.width = kVGA.width; |
| 262 current_settings_.height = kVGA.height; |
| 262 } | 263 } |
| 263 } | 264 } |
| 264 | 265 |
| 265 if (capture_format_.frame_size == frame_format.frame_size && | 266 if (current_settings_.width == frame_info.width && |
| 267 current_settings_.height == frame_info.height && |
| 266 !tried_to_square_pixels_ && | 268 !tried_to_square_pixels_ && |
| 267 (aspect_numerator > kMaxPixelAspectRatio * aspect_denominator || | 269 (aspect_numerator > kMaxPixelAspectRatio * aspect_denominator || |
| 268 aspect_denominator > kMaxPixelAspectRatio * aspect_numerator)) { | 270 aspect_denominator > kMaxPixelAspectRatio * aspect_numerator)) { |
| 269 // The requested size results in non-square PAR. | 271 // The requested size results in non-square PAR. |
| 270 // Shrink the frame to 1:1 PAR (assuming QTKit selects the same input | 272 // Shrink the frame to 1:1 PAR (assuming QTKit selects the same input |
| 271 // mode, which is not guaranteed). | 273 // mode, which is not guaranteed). |
| 272 int new_width = capture_format_.frame_size.width(); | 274 int new_width = current_settings_.width; |
| 273 int new_height = capture_format_.frame_size.height(); | 275 int new_height = current_settings_.height; |
| 274 if (aspect_numerator < aspect_denominator) { | 276 if (aspect_numerator < aspect_denominator) { |
| 275 new_width = (new_width * aspect_numerator) / aspect_denominator; | 277 new_width = (new_width * aspect_numerator) / aspect_denominator; |
| 276 } else { | 278 } else { |
| 277 new_height = (new_height * aspect_denominator) / aspect_numerator; | 279 new_height = (new_height * aspect_denominator) / aspect_numerator; |
| 278 } | 280 } |
| 279 capture_format_.frame_size.SetSize(new_width, new_height); | 281 current_settings_.width = new_width; |
| 282 current_settings_.height = new_height; |
| 280 tried_to_square_pixels_ = true; | 283 tried_to_square_pixels_ = true; |
| 281 } | 284 } |
| 282 | 285 |
| 283 if (capture_format_.frame_size == frame_format.frame_size) { | 286 if (current_settings_.width == frame_info.width && |
| 287 current_settings_.height == frame_info.height) { |
| 284 sent_frame_info_ = true; | 288 sent_frame_info_ = true; |
| 285 } else { | 289 } else { |
| 286 UpdateCaptureResolution(); | 290 UpdateCaptureResolution(); |
| 287 // OnFrameInfo has not yet been called. OnIncomingCapturedFrame must | 291 // OnFrameInfo has not yet been called. OnIncomingCapturedFrame must |
| 288 // not be called until after OnFrameInfo, so we return early. | 292 // not be called until after OnFrameInfo, so we return early. |
| 289 return; | 293 return; |
| 290 } | 294 } |
| 291 } | 295 } |
| 292 | 296 |
| 293 DCHECK_EQ(capture_format_.frame_size.width(), | 297 DCHECK(current_settings_.width == frame_info.width && |
| 294 frame_format.frame_size.width()); | 298 current_settings_.height == frame_info.height); |
| 295 DCHECK_EQ(capture_format_.frame_size.height(), | |
| 296 frame_format.frame_size.height()); | |
| 297 | 299 |
| 298 client_->OnIncomingCapturedFrame(video_frame, | 300 client_->OnIncomingCapturedFrame(video_frame, |
| 299 video_frame_length, | 301 video_frame_length, |
| 300 base::Time::Now(), | 302 base::Time::Now(), |
| 301 0, | 303 0, |
| 302 false, | 304 false, |
| 303 false, | 305 false, |
| 304 capture_format_); | 306 current_settings_); |
| 305 } | 307 } |
| 306 | 308 |
| 307 void VideoCaptureDeviceMac::ReceiveError(const std::string& reason) { | 309 void VideoCaptureDeviceMac::ReceiveError(const std::string& reason) { |
| 308 loop_proxy_->PostTask(FROM_HERE, | 310 loop_proxy_->PostTask(FROM_HERE, |
| 309 base::Bind(&VideoCaptureDeviceMac::SetErrorState, weak_this_, | 311 base::Bind(&VideoCaptureDeviceMac::SetErrorState, weak_this_, |
| 310 reason)); | 312 reason)); |
| 311 } | 313 } |
| 312 | 314 |
| 313 void VideoCaptureDeviceMac::SetErrorState(const std::string& reason) { | 315 void VideoCaptureDeviceMac::SetErrorState(const std::string& reason) { |
| 314 DCHECK_EQ(loop_proxy_, base::MessageLoopProxy::current()); | 316 DCHECK_EQ(loop_proxy_, base::MessageLoopProxy::current()); |
| 315 DLOG(ERROR) << reason; | 317 DLOG(ERROR) << reason; |
| 316 state_ = kError; | 318 state_ = kError; |
| 317 client_->OnError(); | 319 client_->OnError(); |
| 318 } | 320 } |
| 319 | 321 |
| 320 bool VideoCaptureDeviceMac::UpdateCaptureResolution() { | 322 bool VideoCaptureDeviceMac::UpdateCaptureResolution() { |
| 321 if (![capture_device_ setCaptureHeight:capture_format_.frame_size.height() | 323 if (![capture_device_ setCaptureHeight:current_settings_.height |
| 322 width:capture_format_.frame_size.width() | 324 width:current_settings_.width |
| 323 frameRate:capture_format_.frame_rate]) { | 325 frameRate:current_settings_.frame_rate]) { |
| 324 ReceiveError("Could not configure capture device."); | 326 ReceiveError("Could not configure capture device."); |
| 325 return false; | 327 return false; |
| 326 } | 328 } |
| 327 return true; | 329 return true; |
| 328 } | 330 } |
| 329 | 331 |
| 330 } // namespace media | 332 } // namespace media |
| OLD | NEW |