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 26b0b5a2db222571ef2b224381dfcfe0a0da842d..8608311925e2da90c5fd5e5f12c60d47bd44d7af 100644 |
--- a/content/renderer/media/media_stream_video_source.cc |
+++ b/content/renderer/media/media_stream_video_source.cc |
@@ -14,7 +14,7 @@ |
#include "content/child/child_process.h" |
#include "content/renderer/media/media_stream_constraints_util.h" |
#include "content/renderer/media/media_stream_video_track.h" |
-#include "content/renderer/media/video_frame_deliverer.h" |
+#include "content/renderer/media/video_track_adapter.h" |
namespace content { |
@@ -54,6 +54,75 @@ const char kGooglePrefix[] = "goog"; |
// input frame height of max 360 * kMaxCropFactor pixels is accepted. |
const int kMaxCropFactor = 2; |
+// Returns true if |constraint| has mandatory constraints. |
+bool HasMandatoryConstraints(const blink::WebMediaConstraints& constraints) { |
+ blink::WebVector<blink::WebMediaConstraint> mandatory_constraints; |
+ constraints.getMandatoryConstraints(mandatory_constraints); |
+ return !mandatory_constraints.isEmpty(); |
+} |
+ |
+// Retrieve the desired max width and height from |constraints|. If not set, |
+// the |desired_width| and |desired_height| are set to |
+// std::numeric_limits<int>::max(); |
+// If either max width or height is set as a mandatory constraint, the optional |
+// constraints are not checked. |
+void GetDesiredMaxWidthAndHeight(const blink::WebMediaConstraints& constraints, |
+ int* desired_width, int* desired_height) { |
+ *desired_width = std::numeric_limits<int>::max(); |
+ *desired_height = std::numeric_limits<int>::max(); |
+ |
+ bool mandatory = GetMandatoryConstraintValueAsInteger( |
+ constraints, |
+ MediaStreamVideoSource::kMaxWidth, |
+ desired_width); |
+ mandatory |= GetMandatoryConstraintValueAsInteger( |
+ constraints, |
+ MediaStreamVideoSource::kMaxHeight, |
+ desired_height); |
+ if (mandatory) |
+ return; |
+ |
+ GetOptionalConstraintValueAsInteger(constraints, |
+ MediaStreamVideoSource::kMaxWidth, |
+ desired_width); |
+ GetOptionalConstraintValueAsInteger(constraints, |
+ MediaStreamVideoSource::kMaxHeight, |
+ desired_height); |
+} |
+ |
+// Retrieve the desired max and min aspect ratio from |constraints|. If not set, |
+// the |min_aspect_ratio| is set to 0 and |max_aspect_ratio| is set to |
+// std::numeric_limits<double>::max(); |
+// If either min or max aspect ratio is set as a mandatory constraint, the |
+// optional constraints are not checked. |
+void GetDesiredMinAndMaxAspectRatio( |
+ const blink::WebMediaConstraints& constraints, |
+ double* min_aspect_ratio, |
+ double* max_aspect_ratio) { |
+ *min_aspect_ratio = 0; |
+ *max_aspect_ratio = std::numeric_limits<double>::max(); |
+ |
+ bool mandatory = GetMandatoryConstraintValueAsDouble( |
+ constraints, |
+ MediaStreamVideoSource::kMinAspectRatio, |
+ min_aspect_ratio); |
+ mandatory |= GetMandatoryConstraintValueAsDouble( |
+ constraints, |
+ MediaStreamVideoSource::kMaxAspectRatio, |
+ max_aspect_ratio); |
+ if (mandatory) |
+ return; |
+ |
+ GetOptionalConstraintValueAsDouble( |
+ constraints, |
+ MediaStreamVideoSource::kMinAspectRatio, |
+ min_aspect_ratio); |
+ GetOptionalConstraintValueAsDouble( |
+ constraints, |
+ MediaStreamVideoSource::kMaxAspectRatio, |
+ max_aspect_ratio); |
+} |
+ |
// Returns true if |constraint| is fulfilled. |format| can be changed |
// changed by a constraint. Ie - the frame rate can be changed by setting |
// maxFrameRate. |
@@ -87,22 +156,10 @@ bool UpdateFormatForConstraint( |
if (constraint_name == MediaStreamVideoSource::kMinAspectRatio || |
constraint_name == MediaStreamVideoSource::kMaxAspectRatio) { |
- double double_value = 0; |
- base::StringToDouble(constraint_value, &double_value); |
- |
- // The aspect ratio in |constraint.m_value| has been converted to a string |
- // and back to a double, so it may have a rounding error. |
- // E.g if the value 1/3 is converted to a string, the string will not have |
- // infinite length. |
- // We add a margin of 0.0005 which is high enough to detect the same aspect |
- // ratio but small enough to avoid matching wrong aspect ratios. |
- const double kRoundingTruncation = 0.0005; |
- double ratio = static_cast<double>(format->frame_size.width()) / |
- format->frame_size.height(); |
- if (constraint_name == MediaStreamVideoSource::kMinAspectRatio) |
- return (double_value <= ratio + kRoundingTruncation); |
- // Subtract 0.0005 to avoid rounding problems. Same as above. |
- return (double_value >= ratio - kRoundingTruncation); |
+ // These constraints are handled by cropping if the camera outputs the wrong |
+ // aspect ratio. |
+ double value; |
+ return base::StringToDouble(constraint_value, &value); |
} |
int value; |
@@ -172,13 +229,37 @@ media::VideoCaptureFormats FilterFormats( |
return supported_formats; |
} |
+ double max_aspect_ratio; |
+ double min_aspect_ratio; |
+ GetDesiredMinAndMaxAspectRatio(constraints, |
+ &min_aspect_ratio, |
+ &max_aspect_ratio); |
+ |
+ if (min_aspect_ratio > max_aspect_ratio || max_aspect_ratio < 0.05f) { |
+ DLOG(WARNING) << "Wrong requested aspect ratio."; |
+ return media::VideoCaptureFormats(); |
+ } |
+ |
+ int min_width = 0; |
+ GetMandatoryConstraintValueAsInteger(constraints, |
+ MediaStreamVideoSource::kMinWidth, |
+ &min_width); |
+ int min_height = 0; |
+ GetMandatoryConstraintValueAsInteger(constraints, |
+ MediaStreamVideoSource::kMinHeight, |
+ &min_height); |
+ int max_width; |
+ int max_height; |
+ GetDesiredMaxWidthAndHeight(constraints, &max_width, &max_height); |
+ |
+ if (min_width > max_width || min_height > max_height) |
+ return media::VideoCaptureFormats(); |
+ |
blink::WebVector<blink::WebMediaConstraint> mandatory; |
blink::WebVector<blink::WebMediaConstraint> optional; |
constraints.getMandatoryConstraints(mandatory); |
constraints.getOptionalConstraints(optional); |
- |
media::VideoCaptureFormats candidates = supported_formats; |
- |
for (size_t i = 0; i < mandatory.size(); ++i) |
FilterFormatsByConstraint(mandatory[i], true, &candidates); |
@@ -203,33 +284,6 @@ media::VideoCaptureFormats FilterFormats( |
return candidates; |
} |
-// Returns true if |constraint| has mandatory constraints. |
-bool HasMandatoryConstraints(const blink::WebMediaConstraints& constraints) { |
- blink::WebVector<blink::WebMediaConstraint> mandatory_constraints; |
- constraints.getMandatoryConstraints(mandatory_constraints); |
- return !mandatory_constraints.isEmpty(); |
-} |
- |
-// Retrieve the desired max width and height from |constraints|. |
-void GetDesiredMaxWidthAndHeight(const blink::WebMediaConstraints& constraints, |
- int* desired_width, int* desired_height) { |
- bool mandatory = GetMandatoryConstraintValueAsInteger( |
- constraints, MediaStreamVideoSource::kMaxWidth, desired_width); |
- mandatory |= GetMandatoryConstraintValueAsInteger( |
- constraints, MediaStreamVideoSource::kMaxHeight, desired_height); |
- // Skip the optional constraints if any of the mandatory constraint is |
- // specified. |
- if (mandatory) |
- return; |
- |
- GetOptionalConstraintValueAsInteger(constraints, |
- MediaStreamVideoSource::kMaxWidth, |
- desired_width); |
- GetOptionalConstraintValueAsInteger(constraints, |
- MediaStreamVideoSource::kMaxHeight, |
- desired_height); |
-} |
- |
const media::VideoCaptureFormat& GetBestFormatBasedOnArea( |
const media::VideoCaptureFormats& formats, |
int area) { |
@@ -255,109 +309,21 @@ const media::VideoCaptureFormat& GetBestFormatBasedOnArea( |
void GetBestCaptureFormat( |
const media::VideoCaptureFormats& formats, |
const blink::WebMediaConstraints& constraints, |
- media::VideoCaptureFormat* capture_format, |
- gfx::Size* max_frame_output_size) { |
+ media::VideoCaptureFormat* capture_format) { |
DCHECK(!formats.empty()); |
- DCHECK(max_frame_output_size); |
- int max_width = std::numeric_limits<int>::max(); |
- int max_height = std::numeric_limits<int>::max();; |
+ int max_width; |
+ int max_height; |
GetDesiredMaxWidthAndHeight(constraints, &max_width, &max_height); |
*capture_format = GetBestFormatBasedOnArea( |
formats, |
std::min(max_width, MediaStreamVideoSource::kDefaultWidth) * |
std::min(max_height, MediaStreamVideoSource::kDefaultHeight)); |
- |
- max_frame_output_size->set_width(max_width); |
- max_frame_output_size->set_height(max_height); |
-} |
- |
-// Empty method used for keeping a reference to the original media::VideoFrame |
-// in MediaStreamVideoSource::FrameDeliverer::DeliverFrameOnIO 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 |
-// Helper class used for delivering video frames to all registered tracks |
-// on the IO-thread. |
-class MediaStreamVideoSource::FrameDeliverer : public VideoFrameDeliverer { |
- public: |
- FrameDeliverer( |
- const scoped_refptr<base::MessageLoopProxy>& io_message_loop) |
- : VideoFrameDeliverer(io_message_loop) { |
- } |
- |
- // Register |callback| to receive video frames of max size |
- // |max_frame_output_size| on the IO thread. |
- // TODO(perkj): Currently |max_frame_output_size| must be the same for all |
- // |callbacks|. |
- void AddCallback(void* id, |
- const VideoCaptureDeliverFrameCB& callback, |
- const gfx::Size& max_frame_output_size) { |
- DCHECK(thread_checker().CalledOnValidThread()); |
- io_message_loop()->PostTask( |
- FROM_HERE, |
- base::Bind( |
- &FrameDeliverer::AddCallbackWithResolutionOnIO, |
- this, id, callback, max_frame_output_size)); |
- } |
- |
- virtual void DeliverFrameOnIO( |
- const scoped_refptr<media::VideoFrame>& frame, |
- const media::VideoCaptureFormat& format) OVERRIDE { |
- DCHECK(io_message_loop()->BelongsToCurrentThread()); |
- TRACE_EVENT0("video", "MediaStreamVideoSource::DeliverFrameOnIO"); |
- if (max_output_size_.IsEmpty()) |
- return; // Frame received before the output has been decided. |
- |
- scoped_refptr<media::VideoFrame> video_frame(frame); |
- const gfx::Size& visible_size = frame->visible_rect().size(); |
- if (visible_size.width() > max_output_size_.width() || |
- visible_size.height() > max_output_size_.height()) { |
- // 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|. |
- gfx::Rect output_rect = frame->visible_rect(); |
- output_rect.ClampToCenteredSize(max_output_size_); |
- // TODO(perkj): Allow cropping of textures once http://crbug/362521 is |
- // fixed. |
- if (frame->format() != media::VideoFrame::NATIVE_TEXTURE) { |
- video_frame = media::VideoFrame::WrapVideoFrame( |
- frame, |
- output_rect, |
- output_rect.size(), |
- base::Bind(&ReleaseOriginalFrame, frame)); |
- } |
- } |
- VideoFrameDeliverer::DeliverFrameOnIO(video_frame, format); |
- } |
- |
- protected: |
- virtual ~FrameDeliverer() { |
- } |
- |
- void AddCallbackWithResolutionOnIO( |
- void* id, |
- const VideoCaptureDeliverFrameCB& callback, |
- const gfx::Size& max_frame_output_size) { |
- DCHECK(io_message_loop()->BelongsToCurrentThread()); |
- // Currently we only support one frame output size. |
- DCHECK(!max_frame_output_size.IsEmpty() && |
- (max_output_size_.IsEmpty() || |
- max_output_size_ == max_frame_output_size)); |
- max_output_size_ = max_frame_output_size; |
- VideoFrameDeliverer::AddCallbackOnIO(id, callback); |
- } |
- |
- private: |
- gfx::Size max_output_size_; |
-}; |
- |
// static |
MediaStreamVideoSource* MediaStreamVideoSource::GetVideoSource( |
const blink::WebMediaStreamSource& source) { |
@@ -375,9 +341,8 @@ bool MediaStreamVideoSource::IsConstraintSupported(const std::string& name) { |
MediaStreamVideoSource::MediaStreamVideoSource() |
: state_(NEW), |
- frame_deliverer_( |
- new MediaStreamVideoSource::FrameDeliverer( |
- ChildProcess::current()->io_message_loop_proxy())), |
+ track_adapter_(new VideoTrackAdapter( |
+ ChildProcess::current()->io_message_loop_proxy())), |
weak_factory_(this) { |
} |
@@ -440,9 +405,20 @@ void MediaStreamVideoSource::RemoveTrack(MediaStreamVideoTrack* video_track) { |
std::find(tracks_.begin(), tracks_.end(), video_track); |
DCHECK(it != tracks_.end()); |
tracks_.erase(it); |
- // Call |RemoveCallback| here even if adding the track has failed and |
- // frame_deliverer_->AddCallback has not been called. |
- frame_deliverer_->RemoveCallback(video_track); |
+ |
+ // Check if |video_track| is waiting for applying new constraints and remove |
+ // the request in that case. |
+ for (std::vector<RequestedConstraints>::iterator it = |
+ requested_constraints_.begin(); |
+ it != requested_constraints_.end(); ++it) { |
+ if (it->track == video_track) { |
+ requested_constraints_.erase(it); |
+ break; |
+ } |
+ } |
+ // Call |frame_adapter_->RemoveTrack| here even if adding the track has |
+ // failed and |frame_adapter_->AddCallback| has not been called. |
+ track_adapter_->RemoveTrack(video_track); |
if (tracks_.empty()) |
StopSource(); |
@@ -450,7 +426,8 @@ void MediaStreamVideoSource::RemoveTrack(MediaStreamVideoTrack* video_track) { |
const scoped_refptr<base::MessageLoopProxy>& |
MediaStreamVideoSource::io_message_loop() const { |
- return frame_deliverer_->io_message_loop(); |
+ DCHECK(CalledOnValidThread()); |
+ return track_adapter_->io_message_loop(); |
} |
void MediaStreamVideoSource::DoStopSource() { |
@@ -470,9 +447,7 @@ void MediaStreamVideoSource::OnSupportedFormats( |
supported_formats_ = formats; |
if (!FindBestFormatWithConstraints(supported_formats_, |
- ¤t_format_, |
- &max_frame_output_size_, |
- ¤t_constraints_)) { |
+ ¤t_format_)) { |
SetReadyState(blink::WebMediaStreamSource::ReadyStateEnded); |
// This object can be deleted after calling FinalizeAddTrack. See comment |
// in the header file. |
@@ -490,15 +465,12 @@ void MediaStreamVideoSource::OnSupportedFormats( |
params.requested_format = current_format_; |
StartSourceImpl( |
params, |
- base::Bind(&MediaStreamVideoSource::FrameDeliverer::DeliverFrameOnIO, |
- frame_deliverer_)); |
+ base::Bind(&VideoTrackAdapter::DeliverFrameOnIO, track_adapter_)); |
} |
bool MediaStreamVideoSource::FindBestFormatWithConstraints( |
const media::VideoCaptureFormats& formats, |
- media::VideoCaptureFormat* best_format, |
- gfx::Size* max_frame_output_size, |
- blink::WebMediaConstraints* resulting_constraints) { |
+ media::VideoCaptureFormat* best_format) { |
// Find the first constraints that we can fulfill. |
for (std::vector<RequestedConstraints>::iterator request_it = |
requested_constraints_.begin(); |
@@ -511,9 +483,6 @@ bool MediaStreamVideoSource::FindBestFormatWithConstraints( |
// we will start with whatever format is native to the source. |
if (formats.empty() && !HasMandatoryConstraints(requested_constraints)) { |
*best_format = media::VideoCaptureFormat(); |
- *resulting_constraints = requested_constraints; |
- *max_frame_output_size = gfx::Size(std::numeric_limits<int>::max(), |
- std::numeric_limits<int>::max()); |
return true; |
} |
media::VideoCaptureFormats filtered_formats = |
@@ -522,9 +491,7 @@ bool MediaStreamVideoSource::FindBestFormatWithConstraints( |
// A request with constraints that can be fulfilled. |
GetBestCaptureFormat(filtered_formats, |
requested_constraints, |
- best_format, |
- max_frame_output_size); |
- *resulting_constraints= requested_constraints; |
+ best_format); |
return true; |
} |
} |
@@ -566,11 +533,23 @@ void MediaStreamVideoSource::FinalizeAddTrack() { |
((!current_format_.IsValid() && !HasMandatoryConstraints( |
it->constraints)) || |
!FilterFormats(it->constraints, formats).empty()); |
+ |
if (success) { |
- frame_deliverer_->AddCallback(it->track, it->frame_callback, |
- max_frame_output_size_); |
+ int max_width; |
+ int max_height; |
+ GetDesiredMaxWidthAndHeight(it->constraints, &max_width, &max_height); |
+ double max_aspect_ratio; |
+ double min_aspect_ratio; |
+ GetDesiredMinAndMaxAspectRatio(it->constraints, |
+ &min_aspect_ratio, |
+ &max_aspect_ratio); |
+ track_adapter_->AddTrack(it->track,it->frame_callback, |
+ max_width, max_height, |
+ min_aspect_ratio, max_aspect_ratio); |
} |
+ |
DVLOG(3) << "FinalizeAddTrack() success " << success; |
+ |
if (!it->callback.is_null()) |
it->callback.Run(this, success); |
} |