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 |