Index: content/renderer/media/media_stream_video_source.cc |
diff --git a/content/renderer/media/media_stream_video_source.cc b/content/renderer/media/media_stream_video_source.cc |
index 118d66e3cddc59faa72ed1fd4c7448c0bc0c377e..a544d5cb5e371d81c7cf5fbf4b2bfca89ddc66df 100644 |
--- a/content/renderer/media/media_stream_video_source.cc |
+++ b/content/renderer/media/media_stream_video_source.cc |
@@ -4,6 +4,7 @@ |
#include "content/renderer/media/media_stream_video_source.h" |
+#include <algorithm> |
#include <limits> |
#include <string> |
@@ -36,6 +37,11 @@ const char kSourceId[] = "sourceId"; |
// are unknown. |
const char kGooglePrefix[] = "goog"; |
+// MediaStreamVideoSource supports cropping of video frames but only up to |
+// kMaxCropFactor. Ie - if a constraint is set to maxHeight 360, an original |
+// input frame height of max 360 * kMaxCropFactor pixels is accepted. |
+const int kMaxCropFactor = 2; |
+ |
// Returns true if |constraint| is fulfilled. |format| can be changed |
// changed by a constraint. Ie - the frame rate can be changed by setting |
// maxFrameRate. |
@@ -96,11 +102,11 @@ bool UpdateFormatForConstraint( |
if (constraint_name == MediaStreamVideoSource::kMinWidth) { |
return (value <= format->frame_size.width()); |
} else if (constraint_name == MediaStreamVideoSource::kMaxWidth) { |
- return (value >= format->frame_size.width()); |
+ return (value * kMaxCropFactor >= format->frame_size.width()); |
} else if (constraint_name == MediaStreamVideoSource::kMinHeight) { |
return (value <= format->frame_size.height()); |
} else if (constraint_name == MediaStreamVideoSource::kMaxHeight) { |
- return (value >= format->frame_size.height()); |
+ return (value * kMaxCropFactor >= format->frame_size.height()); |
} else if (constraint_name == MediaStreamVideoSource::kMinFrameRate) { |
return (value <= format->frame_rate); |
} else if (constraint_name == MediaStreamVideoSource::kMaxFrameRate) { |
@@ -185,25 +191,44 @@ media::VideoCaptureFormats FilterFormats( |
return candidates; |
} |
-// Find the format that best matches the default video size. |
-// This algorithm is chosen since a resolution must be picked even if no |
-// constraints are provided. We don't just select the maximum supported |
-// resolution since higher resolution cost more in terms of complexity and |
-// many cameras perform worse at its maximum supported resolution. |
-const media::VideoCaptureFormat& GetBestCaptureFormat( |
- const media::VideoCaptureFormats& formats) { |
- DCHECK(!formats.empty()); |
+bool GetConstraintValue(const blink::WebMediaConstraints& constraints, |
+ bool mandatory, const blink::WebString& name, |
+ int* value) { |
+ blink::WebString value_str; |
+ bool ret = mandatory ? |
+ constraints.getMandatoryConstraintValue(name, value_str) : |
+ constraints.getOptionalConstraintValue(name, value_str); |
+ if (ret) |
+ base::StringToInt(value_str.utf8(), value); |
+ return ret; |
+} |
- int default_area = |
- MediaStreamVideoSource::kDefaultWidth * |
- MediaStreamVideoSource::kDefaultHeight; |
+// Retrieve the desired max width and height from |constraints|. |
+void GetDesiredMaxWidthAndHeight(const blink::WebMediaConstraints& constraints, |
+ int* desired_width, int* desired_height) { |
+ bool mandatory = GetConstraintValue(constraints, true, |
+ MediaStreamVideoSource::kMaxWidth, |
+ desired_width); |
+ mandatory |= GetConstraintValue(constraints, true, |
+ MediaStreamVideoSource::kMaxHeight, |
+ desired_height); |
+ if (mandatory) |
+ return; |
+ |
+ GetConstraintValue(constraints, false, MediaStreamVideoSource::kMaxWidth, |
+ desired_width); |
+ GetConstraintValue(constraints, false, MediaStreamVideoSource::kMaxHeight, |
+ desired_height); |
+} |
+const media::VideoCaptureFormat& GetBestFormatBasedOnArea( |
+ const media::VideoCaptureFormats& formats, |
+ int area) { |
media::VideoCaptureFormats::const_iterator it = formats.begin(); |
media::VideoCaptureFormats::const_iterator best_it = formats.begin(); |
int best_diff = std::numeric_limits<int>::max(); |
for (; it != formats.end(); ++it) { |
- int diff = abs(default_area - |
- it->frame_size.width() * it->frame_size.height()); |
+ int diff = abs(area - it->frame_size.width() * it->frame_size.height()); |
if (diff < best_diff) { |
best_diff = diff; |
best_it = it; |
@@ -212,6 +237,43 @@ const media::VideoCaptureFormat& GetBestCaptureFormat( |
return *best_it; |
} |
+// Find the format that best matches the default video size. |
+// This algorithm is chosen since a resolution must be picked even if no |
+// constraints are provided. We don't just select the maximum supported |
+// resolution since higher resolutions cost more in terms of complexity and |
+// many cameras have lower frame rate and have more noise in the image at |
+// their maximum supported resolution. |
+void GetBestCaptureFormat( |
+ const media::VideoCaptureFormats& formats, |
+ const blink::WebMediaConstraints& constraints, |
+ media::VideoCaptureFormat* capture_format, |
+ gfx::Size* frame_output_size) { |
+ DCHECK(!formats.empty()); |
+ DCHECK(frame_output_size); |
+ |
+ int max_width = std::numeric_limits<int>::max(); |
+ int max_height = std::numeric_limits<int>::max();; |
+ GetDesiredMaxWidthAndHeight(constraints, &max_width, &max_height); |
+ |
+ *capture_format = GetBestFormatBasedOnArea( |
+ formats, |
+ std::min(max_width, MediaStreamVideoSource::kDefaultWidth) * |
+ std::min(max_height, MediaStreamVideoSource::kDefaultHeight)); |
+ |
+ *frame_output_size = capture_format->frame_size; |
+ if (max_width < frame_output_size->width()) |
+ frame_output_size->set_width(max_width); |
+ if (max_height < frame_output_size->height()) |
+ frame_output_size->set_height(max_height); |
+} |
+ |
+// Empty method used for keeping a reference to the original media::VideoFrame |
+// in MediaStreamVideoSource::DeliverVideoFrame if cropping is needed. |
+// The reference to |frame| is kept in the closure that calls this method. |
+void ReleaseOriginalFrame( |
+ const scoped_refptr<media::VideoFrame>& frame) { |
+} |
+ |
} // anonymous namespace |
MediaStreamVideoSource::MediaStreamVideoSource( |
@@ -236,15 +298,11 @@ void MediaStreamVideoSource::AddTrack( |
case NEW: { |
// Tab capture and Screen capture needs the maximum requested height |
// and width to decide on the resolution. |
- blink::WebString max_width; |
int max_requested_width = 0; |
- if (constraints.getMandatoryConstraintValue(kMaxWidth, max_width)) |
- base::StringToInt(max_width.utf8(), &max_requested_width); |
+ GetConstraintValue(constraints, true, kMaxWidth, &max_requested_width); |
int max_requested_height = 0; |
- blink::WebString max_height; |
- if (constraints.getMandatoryConstraintValue(kMaxHeight, max_height)) |
- base::StringToInt(max_height.utf8(), &max_requested_height); |
+ GetConstraintValue(constraints, true, kMaxHeight, &max_requested_height); |
state_ = RETRIEVING_CAPABILITIES; |
GetCurrentSupportedFormats(max_requested_width, |
@@ -255,7 +313,7 @@ void MediaStreamVideoSource::AddTrack( |
case STARTING: |
case RETRIEVING_CAPABILITIES: { |
// The |callback| will be triggered once the delegate has started or |
- // the capabilitites has been retrieved. |
+ // the capabilities have been retrieved. |
break; |
} |
case ENDED: |
@@ -302,8 +360,41 @@ void MediaStreamVideoSource::DoStopSource() { |
void MediaStreamVideoSource::DeliverVideoFrame( |
const scoped_refptr<media::VideoFrame>& frame) { |
- if (capture_adapter_) |
- capture_adapter_->OnFrameCaptured(frame); |
+ scoped_refptr<media::VideoFrame> video_frame(frame); |
+ |
+ if (frame->visible_rect().size() != frame_output_size_) { |
+ // If |frame| is not the size that is expected, we need to crop it by |
+ // providing a new |visible_rect|. The new visible rect must be within the |
+ // original |visible_rect|. |
+ const int visible_width = std::min(frame_output_size_.width(), |
+ frame->visible_rect().width()); |
+ |
+ // TODO(perkj): horiz_crop and vert_crop must be 0 until local |
+ // rendering can support offsets on media::VideoFrame::visible_rect(). |
+ // crbug/349450. The effect of this is that the cropped frame originates |
+ // from the top left of the original frame instead of being centered. |
+ // Find a new horizontal offset within |frame|. |
+ // const int horiz_crop = frame->visible_rect().x() + |
+ // ((frame->visible_rect().width() - visible_width) / 2); |
+ // Find a new vertical offset within |frame|. |
+ // const int vert_crop = frame->visible_rect().y() + |
+ // ((frame->visible_rect().height() - visible_height) / 2); |
+ const int horiz_crop = 0; |
+ const int vert_crop = 0; |
+ |
+ const int visible_height = std::min(frame_output_size_.height(), |
+ frame->visible_rect().height()); |
+ |
+ gfx::Rect rect(horiz_crop, vert_crop, visible_width, visible_height); |
+ video_frame = media::VideoFrame::WrapVideoFrame( |
+ frame, rect, base::Bind(&ReleaseOriginalFrame, frame)); |
+ } |
+ |
+ if ((frame->format() == media::VideoFrame::I420 || |
+ frame->format() == media::VideoFrame::YV12) && |
+ capture_adapter_) { |
+ capture_adapter_->OnFrameCaptured(video_frame); |
+ } |
} |
void MediaStreamVideoSource::OnSupportedFormats( |
@@ -312,8 +403,10 @@ void MediaStreamVideoSource::OnSupportedFormats( |
DCHECK_EQ(RETRIEVING_CAPABILITIES, state_); |
supported_formats_ = formats; |
- if (!FindBestFormatWithConstraints(supported_formats_, ¤t_format_, |
- ¤t_constraints_)) { |
+ if (!FindBestFormatWithConstraints(supported_formats_, |
+ ¤t_format_, |
+ &frame_output_size_, |
+ ¤t_constraints_)) { |
FinalizeAddTrack(); |
SetReadyState(blink::WebMediaStreamSource::ReadyStateEnded); |
return; |
@@ -333,8 +426,9 @@ void MediaStreamVideoSource::OnSupportedFormats( |
bool MediaStreamVideoSource::FindBestFormatWithConstraints( |
const media::VideoCaptureFormats& formats, |
media::VideoCaptureFormat* best_format, |
+ gfx::Size* frame_output_size, |
blink::WebMediaConstraints* resulting_constraints) { |
- // Find the first constraints that we can fulfilled. |
+ // Find the first constraints that we can fulfill. |
for (std::vector<RequestedConstraints>::iterator request_it = |
requested_constraints_.begin(); |
request_it != requested_constraints_.end(); ++request_it) { |
@@ -345,7 +439,10 @@ bool MediaStreamVideoSource::FindBestFormatWithConstraints( |
FilterFormats(requested_constraints, formats); |
if (filtered_formats.size() > 0) { |
// A request with constraints that can be fulfilled. |
- *best_format = GetBestCaptureFormat(filtered_formats); |
+ GetBestCaptureFormat(filtered_formats, |
+ requested_constraints, |
+ best_format, |
+ frame_output_size); |
*resulting_constraints= requested_constraints; |
return true; |
} |