| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "content/renderer/media/media_stream_video_source.h" | 5 #include "content/renderer/media/media_stream_video_source.h" |
| 6 | 6 |
| 7 #include <algorithm> |
| 7 #include <limits> | 8 #include <limits> |
| 8 #include <string> | 9 #include <string> |
| 9 | 10 |
| 10 #include "base/logging.h" | 11 #include "base/logging.h" |
| 11 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
| 12 #include "content/renderer/media/media_stream_dependency_factory.h" | 13 #include "content/renderer/media/media_stream_dependency_factory.h" |
| 13 #include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h" | 14 #include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h" |
| 14 | 15 |
| 15 namespace content { | 16 namespace content { |
| 16 | 17 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 29 const int MediaStreamVideoSource::kDefaultFrameRate = 30; | 30 const int MediaStreamVideoSource::kDefaultFrameRate = 30; |
| 30 | 31 |
| 31 namespace { | 32 namespace { |
| 32 // Constraints keys for http://dev.w3.org/2011/webrtc/editor/getusermedia.html | 33 // Constraints keys for http://dev.w3.org/2011/webrtc/editor/getusermedia.html |
| 33 const char kSourceId[] = "sourceId"; | 34 const char kSourceId[] = "sourceId"; |
| 34 | 35 |
| 35 // Google-specific key prefix. Constraints with this prefix are ignored if they | 36 // Google-specific key prefix. Constraints with this prefix are ignored if they |
| 36 // are unknown. | 37 // are unknown. |
| 37 const char kGooglePrefix[] = "goog"; | 38 const char kGooglePrefix[] = "goog"; |
| 38 | 39 |
| 40 // MediaStreamVideoSource supports cropping of video frames but only up to |
| 41 // kMaxCropFactor. Ie - if a constraint is set to maxHeight 360, an original |
| 42 // input frame height of max 360 * kMaxCropFactor pixels is accepted. |
| 43 const int kMaxCropFactor = 2; |
| 44 |
| 39 // Returns true if |constraint| is fulfilled. |format| can be changed | 45 // Returns true if |constraint| is fulfilled. |format| can be changed |
| 40 // changed by a constraint. Ie - the frame rate can be changed by setting | 46 // changed by a constraint. Ie - the frame rate can be changed by setting |
| 41 // maxFrameRate. | 47 // maxFrameRate. |
| 42 bool UpdateFormatForConstraint( | 48 bool UpdateFormatForConstraint( |
| 43 const blink::WebMediaConstraint& constraint, | 49 const blink::WebMediaConstraint& constraint, |
| 44 bool mandatory, | 50 bool mandatory, |
| 45 media::VideoCaptureFormat* format) { | 51 media::VideoCaptureFormat* format) { |
| 46 DCHECK(format != NULL); | 52 DCHECK(format != NULL); |
| 47 | 53 |
| 48 if (!format->IsValid()) | 54 if (!format->IsValid()) |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 89 | 95 |
| 90 int value; | 96 int value; |
| 91 if (!base::StringToInt(constraint_value, &value)) { | 97 if (!base::StringToInt(constraint_value, &value)) { |
| 92 DLOG(WARNING) << "Can't parse MediaStream constraint. Name:" | 98 DLOG(WARNING) << "Can't parse MediaStream constraint. Name:" |
| 93 << constraint_name << " Value:" << constraint_value; | 99 << constraint_name << " Value:" << constraint_value; |
| 94 return false; | 100 return false; |
| 95 } | 101 } |
| 96 if (constraint_name == MediaStreamVideoSource::kMinWidth) { | 102 if (constraint_name == MediaStreamVideoSource::kMinWidth) { |
| 97 return (value <= format->frame_size.width()); | 103 return (value <= format->frame_size.width()); |
| 98 } else if (constraint_name == MediaStreamVideoSource::kMaxWidth) { | 104 } else if (constraint_name == MediaStreamVideoSource::kMaxWidth) { |
| 99 return (value >= format->frame_size.width()); | 105 return (value * kMaxCropFactor >= format->frame_size.width()); |
| 100 } else if (constraint_name == MediaStreamVideoSource::kMinHeight) { | 106 } else if (constraint_name == MediaStreamVideoSource::kMinHeight) { |
| 101 return (value <= format->frame_size.height()); | 107 return (value <= format->frame_size.height()); |
| 102 } else if (constraint_name == MediaStreamVideoSource::kMaxHeight) { | 108 } else if (constraint_name == MediaStreamVideoSource::kMaxHeight) { |
| 103 return (value >= format->frame_size.height()); | 109 return (value * kMaxCropFactor >= format->frame_size.height()); |
| 104 } else if (constraint_name == MediaStreamVideoSource::kMinFrameRate) { | 110 } else if (constraint_name == MediaStreamVideoSource::kMinFrameRate) { |
| 105 return (value <= format->frame_rate); | 111 return (value <= format->frame_rate); |
| 106 } else if (constraint_name == MediaStreamVideoSource::kMaxFrameRate) { | 112 } else if (constraint_name == MediaStreamVideoSource::kMaxFrameRate) { |
| 107 if (value == 0) { | 113 if (value == 0) { |
| 108 // The frame rate is set by constraint. | 114 // The frame rate is set by constraint. |
| 109 // Don't allow 0 as frame rate if it is a mandatory constraint. | 115 // Don't allow 0 as frame rate if it is a mandatory constraint. |
| 110 // Set the frame rate to 1 if it is not mandatory. | 116 // Set the frame rate to 1 if it is not mandatory. |
| 111 if (mandatory) { | 117 if (mandatory) { |
| 112 return false; | 118 return false; |
| 113 } else { | 119 } else { |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 FilterFormatsByConstraint(optional[i], false, ¤t_candidates); | 184 FilterFormatsByConstraint(optional[i], false, ¤t_candidates); |
| 179 if (!current_candidates.empty()) { | 185 if (!current_candidates.empty()) { |
| 180 candidates = current_candidates; | 186 candidates = current_candidates; |
| 181 } | 187 } |
| 182 } | 188 } |
| 183 | 189 |
| 184 // We have done as good as we can to filter the supported resolutions. | 190 // We have done as good as we can to filter the supported resolutions. |
| 185 return candidates; | 191 return candidates; |
| 186 } | 192 } |
| 187 | 193 |
| 188 // Find the format that best matches the default video size. | 194 bool GetConstraintValue(const blink::WebMediaConstraints& constraints, |
| 189 // This algorithm is chosen since a resolution must be picked even if no | 195 bool mandatory, const blink::WebString& name, |
| 190 // constraints are provided. We don't just select the maximum supported | 196 int* value) { |
| 191 // resolution since higher resolution cost more in terms of complexity and | 197 blink::WebString value_str; |
| 192 // many cameras perform worse at its maximum supported resolution. | 198 bool ret = mandatory ? |
| 193 const media::VideoCaptureFormat& GetBestCaptureFormat( | 199 constraints.getMandatoryConstraintValue(name, value_str) : |
| 194 const media::VideoCaptureFormats& formats) { | 200 constraints.getOptionalConstraintValue(name, value_str); |
| 195 DCHECK(!formats.empty()); | 201 if (ret) |
| 202 base::StringToInt(value_str.utf8(), value); |
| 203 return ret; |
| 204 } |
| 196 | 205 |
| 197 int default_area = | 206 // Retrieve the desired max width and height from |constraints|. |
| 198 MediaStreamVideoSource::kDefaultWidth * | 207 void GetDesiredMaxWidthAndHeight(const blink::WebMediaConstraints& constraints, |
| 199 MediaStreamVideoSource::kDefaultHeight; | 208 int* desired_width, int* desired_height) { |
| 209 bool mandatory = GetConstraintValue(constraints, true, |
| 210 MediaStreamVideoSource::kMaxWidth, |
| 211 desired_width); |
| 212 mandatory |= GetConstraintValue(constraints, true, |
| 213 MediaStreamVideoSource::kMaxHeight, |
| 214 desired_height); |
| 215 if (mandatory) |
| 216 return; |
| 200 | 217 |
| 218 GetConstraintValue(constraints, false, MediaStreamVideoSource::kMaxWidth, |
| 219 desired_width); |
| 220 GetConstraintValue(constraints, false, MediaStreamVideoSource::kMaxHeight, |
| 221 desired_height); |
| 222 } |
| 223 |
| 224 const media::VideoCaptureFormat& GetBestFormatBasedOnArea( |
| 225 const media::VideoCaptureFormats& formats, |
| 226 int area) { |
| 201 media::VideoCaptureFormats::const_iterator it = formats.begin(); | 227 media::VideoCaptureFormats::const_iterator it = formats.begin(); |
| 202 media::VideoCaptureFormats::const_iterator best_it = formats.begin(); | 228 media::VideoCaptureFormats::const_iterator best_it = formats.begin(); |
| 203 int best_diff = std::numeric_limits<int>::max(); | 229 int best_diff = std::numeric_limits<int>::max(); |
| 204 for (; it != formats.end(); ++it) { | 230 for (; it != formats.end(); ++it) { |
| 205 int diff = abs(default_area - | 231 int diff = abs(area - it->frame_size.width() * it->frame_size.height()); |
| 206 it->frame_size.width() * it->frame_size.height()); | |
| 207 if (diff < best_diff) { | 232 if (diff < best_diff) { |
| 208 best_diff = diff; | 233 best_diff = diff; |
| 209 best_it = it; | 234 best_it = it; |
| 210 } | 235 } |
| 211 } | 236 } |
| 212 return *best_it; | 237 return *best_it; |
| 213 } | 238 } |
| 214 | 239 |
| 240 // Find the format that best matches the default video size. |
| 241 // This algorithm is chosen since a resolution must be picked even if no |
| 242 // constraints are provided. We don't just select the maximum supported |
| 243 // resolution since higher resolutions cost more in terms of complexity and |
| 244 // many cameras have lower frame rate and have more noise in the image at |
| 245 // their maximum supported resolution. |
| 246 void GetBestCaptureFormat( |
| 247 const media::VideoCaptureFormats& formats, |
| 248 const blink::WebMediaConstraints& constraints, |
| 249 media::VideoCaptureFormat* capture_format, |
| 250 gfx::Size* frame_output_size) { |
| 251 DCHECK(!formats.empty()); |
| 252 DCHECK(frame_output_size); |
| 253 |
| 254 int max_width = std::numeric_limits<int>::max(); |
| 255 int max_height = std::numeric_limits<int>::max();; |
| 256 GetDesiredMaxWidthAndHeight(constraints, &max_width, &max_height); |
| 257 |
| 258 *capture_format = GetBestFormatBasedOnArea( |
| 259 formats, |
| 260 std::min(max_width, MediaStreamVideoSource::kDefaultWidth) * |
| 261 std::min(max_height, MediaStreamVideoSource::kDefaultHeight)); |
| 262 |
| 263 *frame_output_size = capture_format->frame_size; |
| 264 if (max_width < frame_output_size->width()) |
| 265 frame_output_size->set_width(max_width); |
| 266 if (max_height < frame_output_size->height()) |
| 267 frame_output_size->set_height(max_height); |
| 268 } |
| 269 |
| 270 // Empty method used for keeping a reference to the original media::VideoFrame |
| 271 // in MediaStreamVideoSource::DeliverVideoFrame if cropping is needed. |
| 272 // The reference to |frame| is kept in the closure that calls this method. |
| 273 void ReleaseOriginalFrame( |
| 274 const scoped_refptr<media::VideoFrame>& frame) { |
| 275 } |
| 276 |
| 215 } // anonymous namespace | 277 } // anonymous namespace |
| 216 | 278 |
| 217 MediaStreamVideoSource::MediaStreamVideoSource( | 279 MediaStreamVideoSource::MediaStreamVideoSource( |
| 218 MediaStreamDependencyFactory* factory) | 280 MediaStreamDependencyFactory* factory) |
| 219 : state_(NEW), | 281 : state_(NEW), |
| 220 factory_(factory), | 282 factory_(factory), |
| 221 capture_adapter_(NULL) { | 283 capture_adapter_(NULL) { |
| 222 DCHECK(factory_); | 284 DCHECK(factory_); |
| 223 } | 285 } |
| 224 | 286 |
| 225 MediaStreamVideoSource::~MediaStreamVideoSource() { | 287 MediaStreamVideoSource::~MediaStreamVideoSource() { |
| 226 } | 288 } |
| 227 | 289 |
| 228 void MediaStreamVideoSource::AddTrack( | 290 void MediaStreamVideoSource::AddTrack( |
| 229 const blink::WebMediaStreamTrack& track, | 291 const blink::WebMediaStreamTrack& track, |
| 230 const blink::WebMediaConstraints& constraints, | 292 const blink::WebMediaConstraints& constraints, |
| 231 const ConstraintsCallback& callback) { | 293 const ConstraintsCallback& callback) { |
| 232 DCHECK(CalledOnValidThread()); | 294 DCHECK(CalledOnValidThread()); |
| 233 requested_constraints_.push_back(RequestedConstraints(constraints, | 295 requested_constraints_.push_back(RequestedConstraints(constraints, |
| 234 callback)); | 296 callback)); |
| 235 switch (state_) { | 297 switch (state_) { |
| 236 case NEW: { | 298 case NEW: { |
| 237 // Tab capture and Screen capture needs the maximum requested height | 299 // Tab capture and Screen capture needs the maximum requested height |
| 238 // and width to decide on the resolution. | 300 // and width to decide on the resolution. |
| 239 blink::WebString max_width; | |
| 240 int max_requested_width = 0; | 301 int max_requested_width = 0; |
| 241 if (constraints.getMandatoryConstraintValue(kMaxWidth, max_width)) | 302 GetConstraintValue(constraints, true, kMaxWidth, &max_requested_width); |
| 242 base::StringToInt(max_width.utf8(), &max_requested_width); | |
| 243 | 303 |
| 244 int max_requested_height = 0; | 304 int max_requested_height = 0; |
| 245 blink::WebString max_height; | 305 GetConstraintValue(constraints, true, kMaxHeight, &max_requested_height); |
| 246 if (constraints.getMandatoryConstraintValue(kMaxHeight, max_height)) | |
| 247 base::StringToInt(max_height.utf8(), &max_requested_height); | |
| 248 | 306 |
| 249 state_ = RETRIEVING_CAPABILITIES; | 307 state_ = RETRIEVING_CAPABILITIES; |
| 250 GetCurrentSupportedFormats(max_requested_width, | 308 GetCurrentSupportedFormats(max_requested_width, |
| 251 max_requested_height); | 309 max_requested_height); |
| 252 | 310 |
| 253 break; | 311 break; |
| 254 } | 312 } |
| 255 case STARTING: | 313 case STARTING: |
| 256 case RETRIEVING_CAPABILITIES: { | 314 case RETRIEVING_CAPABILITIES: { |
| 257 // The |callback| will be triggered once the delegate has started or | 315 // The |callback| will be triggered once the delegate has started or |
| 258 // the capabilitites has been retrieved. | 316 // the capabilities have been retrieved. |
| 259 break; | 317 break; |
| 260 } | 318 } |
| 261 case ENDED: | 319 case ENDED: |
| 262 case STARTED: { | 320 case STARTED: { |
| 263 // Currently, reconfiguring the source is not supported. | 321 // Currently, reconfiguring the source is not supported. |
| 264 FinalizeAddTrack(); | 322 FinalizeAddTrack(); |
| 265 } | 323 } |
| 266 } | 324 } |
| 267 } | 325 } |
| 268 | 326 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 295 } | 353 } |
| 296 | 354 |
| 297 void MediaStreamVideoSource::DoStopSource() { | 355 void MediaStreamVideoSource::DoStopSource() { |
| 298 DVLOG(3) << "DoStopSource()"; | 356 DVLOG(3) << "DoStopSource()"; |
| 299 StopSourceImpl(); | 357 StopSourceImpl(); |
| 300 state_ = ENDED; | 358 state_ = ENDED; |
| 301 } | 359 } |
| 302 | 360 |
| 303 void MediaStreamVideoSource::DeliverVideoFrame( | 361 void MediaStreamVideoSource::DeliverVideoFrame( |
| 304 const scoped_refptr<media::VideoFrame>& frame) { | 362 const scoped_refptr<media::VideoFrame>& frame) { |
| 305 if (capture_adapter_) | 363 scoped_refptr<media::VideoFrame> video_frame(frame); |
| 306 capture_adapter_->OnFrameCaptured(frame); | 364 |
| 365 if (frame->visible_rect().size() != frame_output_size_) { |
| 366 // If |frame| is not the size that is expected, we need to crop it by |
| 367 // providing a new |visible_rect|. The new visible rect must be within the |
| 368 // original |visible_rect|. |
| 369 const int visible_width = std::min(frame_output_size_.width(), |
| 370 frame->visible_rect().width()); |
| 371 |
| 372 // TODO(perkj): horiz_crop and vert_crop must be 0 until local |
| 373 // rendering can support offsets on media::VideoFrame::visible_rect(). |
| 374 // crbug/349450. The effect of this is that the cropped frame originates |
| 375 // from the top left of the original frame instead of being centered. |
| 376 // Find a new horizontal offset within |frame|. |
| 377 // const int horiz_crop = frame->visible_rect().x() + |
| 378 // ((frame->visible_rect().width() - visible_width) / 2); |
| 379 // Find a new vertical offset within |frame|. |
| 380 // const int vert_crop = frame->visible_rect().y() + |
| 381 // ((frame->visible_rect().height() - visible_height) / 2); |
| 382 const int horiz_crop = 0; |
| 383 const int vert_crop = 0; |
| 384 |
| 385 const int visible_height = std::min(frame_output_size_.height(), |
| 386 frame->visible_rect().height()); |
| 387 |
| 388 gfx::Rect rect(horiz_crop, vert_crop, visible_width, visible_height); |
| 389 video_frame = media::VideoFrame::WrapVideoFrame( |
| 390 frame, rect, base::Bind(&ReleaseOriginalFrame, frame)); |
| 391 } |
| 392 |
| 393 if ((frame->format() == media::VideoFrame::I420 || |
| 394 frame->format() == media::VideoFrame::YV12) && |
| 395 capture_adapter_) { |
| 396 capture_adapter_->OnFrameCaptured(video_frame); |
| 397 } |
| 307 } | 398 } |
| 308 | 399 |
| 309 void MediaStreamVideoSource::OnSupportedFormats( | 400 void MediaStreamVideoSource::OnSupportedFormats( |
| 310 const media::VideoCaptureFormats& formats) { | 401 const media::VideoCaptureFormats& formats) { |
| 311 DCHECK(CalledOnValidThread()); | 402 DCHECK(CalledOnValidThread()); |
| 312 DCHECK_EQ(RETRIEVING_CAPABILITIES, state_); | 403 DCHECK_EQ(RETRIEVING_CAPABILITIES, state_); |
| 313 | 404 |
| 314 supported_formats_ = formats; | 405 supported_formats_ = formats; |
| 315 if (!FindBestFormatWithConstraints(supported_formats_, ¤t_format_, | 406 if (!FindBestFormatWithConstraints(supported_formats_, |
| 316 ¤t_constraints_)) { | 407 ¤t_format_, |
| 408 &frame_output_size_, |
| 409 ¤t_constraints_)) { |
| 317 FinalizeAddTrack(); | 410 FinalizeAddTrack(); |
| 318 SetReadyState(blink::WebMediaStreamSource::ReadyStateEnded); | 411 SetReadyState(blink::WebMediaStreamSource::ReadyStateEnded); |
| 319 return; | 412 return; |
| 320 } | 413 } |
| 321 | 414 |
| 322 state_ = STARTING; | 415 state_ = STARTING; |
| 323 DVLOG(3) << "Starting the capturer with" | 416 DVLOG(3) << "Starting the capturer with" |
| 324 << " width = " << current_format_.frame_size.width() | 417 << " width = " << current_format_.frame_size.width() |
| 325 << " height = " << current_format_.frame_size.height() | 418 << " height = " << current_format_.frame_size.height() |
| 326 << " frame rate = " << current_format_.frame_rate; | 419 << " frame rate = " << current_format_.frame_rate; |
| 327 | 420 |
| 328 media::VideoCaptureParams params; | 421 media::VideoCaptureParams params; |
| 329 params.requested_format = current_format_; | 422 params.requested_format = current_format_; |
| 330 StartSourceImpl(params); | 423 StartSourceImpl(params); |
| 331 } | 424 } |
| 332 | 425 |
| 333 bool MediaStreamVideoSource::FindBestFormatWithConstraints( | 426 bool MediaStreamVideoSource::FindBestFormatWithConstraints( |
| 334 const media::VideoCaptureFormats& formats, | 427 const media::VideoCaptureFormats& formats, |
| 335 media::VideoCaptureFormat* best_format, | 428 media::VideoCaptureFormat* best_format, |
| 429 gfx::Size* frame_output_size, |
| 336 blink::WebMediaConstraints* resulting_constraints) { | 430 blink::WebMediaConstraints* resulting_constraints) { |
| 337 // Find the first constraints that we can fulfilled. | 431 // Find the first constraints that we can fulfill. |
| 338 for (std::vector<RequestedConstraints>::iterator request_it = | 432 for (std::vector<RequestedConstraints>::iterator request_it = |
| 339 requested_constraints_.begin(); | 433 requested_constraints_.begin(); |
| 340 request_it != requested_constraints_.end(); ++request_it) { | 434 request_it != requested_constraints_.end(); ++request_it) { |
| 341 const blink::WebMediaConstraints& requested_constraints = | 435 const blink::WebMediaConstraints& requested_constraints = |
| 342 request_it->constraints; | 436 request_it->constraints; |
| 343 | 437 |
| 344 media::VideoCaptureFormats filtered_formats = | 438 media::VideoCaptureFormats filtered_formats = |
| 345 FilterFormats(requested_constraints, formats); | 439 FilterFormats(requested_constraints, formats); |
| 346 if (filtered_formats.size() > 0) { | 440 if (filtered_formats.size() > 0) { |
| 347 // A request with constraints that can be fulfilled. | 441 // A request with constraints that can be fulfilled. |
| 348 *best_format = GetBestCaptureFormat(filtered_formats); | 442 GetBestCaptureFormat(filtered_formats, |
| 443 requested_constraints, |
| 444 best_format, |
| 445 frame_output_size); |
| 349 *resulting_constraints= requested_constraints; | 446 *resulting_constraints= requested_constraints; |
| 350 return true; | 447 return true; |
| 351 } | 448 } |
| 352 } | 449 } |
| 353 return false; | 450 return false; |
| 354 } | 451 } |
| 355 | 452 |
| 356 void MediaStreamVideoSource::OnStartDone(bool success) { | 453 void MediaStreamVideoSource::OnStartDone(bool success) { |
| 357 DCHECK(CalledOnValidThread()); | 454 DCHECK(CalledOnValidThread()); |
| 358 DVLOG(3) << "OnStartDone({success =" << success << "})"; | 455 DVLOG(3) << "OnStartDone({success =" << success << "})"; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 396 MediaStreamVideoSource::RequestedConstraints::RequestedConstraints( | 493 MediaStreamVideoSource::RequestedConstraints::RequestedConstraints( |
| 397 const blink::WebMediaConstraints& constraints, | 494 const blink::WebMediaConstraints& constraints, |
| 398 const ConstraintsCallback& callback) | 495 const ConstraintsCallback& callback) |
| 399 : constraints(constraints), callback(callback) { | 496 : constraints(constraints), callback(callback) { |
| 400 } | 497 } |
| 401 | 498 |
| 402 MediaStreamVideoSource::RequestedConstraints::~RequestedConstraints() { | 499 MediaStreamVideoSource::RequestedConstraints::~RequestedConstraints() { |
| 403 } | 500 } |
| 404 | 501 |
| 405 } // namespace content | 502 } // namespace content |
| OLD | NEW |