diff options
author | halliwell <halliwell@chromium.org> | 2015-07-10 13:18:24 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-10 20:19:10 +0000 |
commit | eae1a9bbc57f3e7c53ae6fa94ad86c240cfd5237 (patch) | |
tree | 073954a4e4f20951253c294ae32ab06e54034f9f | |
parent | ab1a2dc33561303b1aab4a6e77a47bff749e58ce (diff) | |
download | chromium_src-eae1a9bbc57f3e7c53ae6fa94ad86c240cfd5237.zip chromium_src-eae1a9bbc57f3e7c53ae6fa94ad86c240cfd5237.tar.gz chromium_src-eae1a9bbc57f3e7c53ae6fa94ad86c240cfd5237.tar.bz2 |
Limit rate of posted VideoPlane::SetGeometry calls from Cast overlays
Some VideoPlane::SetGeometry implementations are slow (on the order of
100ms). The overlay path can end up posting a task as fast as the
graphics system is producing frames. So at 20fps, the media thread
ends up backing up with SetGeometry tasks.
The new implementation measures the time taken by SetGeometry to
adaptively determine a reasonable rate at which to call it. It then
coalesces multiple SetGeometry calls within this frequency into one.
BUG=469374
Review URL: https://codereview.chromium.org/1228423003
Cr-Commit-Position: refs/heads/master@{#338343}
-rw-r--r-- | ui/ozone/platform/cast/overlay_manager_cast.cc | 116 |
1 files changed, 105 insertions, 11 deletions
diff --git a/ui/ozone/platform/cast/overlay_manager_cast.cc b/ui/ozone/platform/cast/overlay_manager_cast.cc index 9d17300..a46bb55 100644 --- a/ui/ozone/platform/cast/overlay_manager_cast.cc +++ b/ui/ozone/platform/cast/overlay_manager_cast.cc @@ -18,15 +18,105 @@ namespace ui { namespace { -void SetVideoPlaneGeometry( - const chromecast::RectF& display_rect, - chromecast::media::VideoPlane::CoordinateType coordinate_type, - chromecast::media::VideoPlane::Transform transform) { - chromecast::media::VideoPlane* video_plane = - chromecast::media::CastMediaShlib::GetVideoPlane(); - CHECK(video_plane); - video_plane->SetGeometry(display_rect, coordinate_type, transform); -} +// Helper class for calling VideoPlane::SetGeometry with rate-limiting. +// SetGeometry can take on the order of 100ms to run in some implementations +// and can be called on the order of 20x / second (as fast as graphics frames +// are produced). This creates an ever-growing backlog of tasks on the media +// thread. +// This class measures the time taken to run SetGeometry to determine a +// reasonable frequency at which to call it. Excess calls are coalesced +// to just set the most recent geometry. +class RateLimitedSetVideoPlaneGeometry + : public base::RefCountedThreadSafe<RateLimitedSetVideoPlaneGeometry> { + public: + RateLimitedSetVideoPlaneGeometry( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) + : pending_display_rect_(0, 0, 0, 0), + pending_set_geometry_(false), + min_calling_interval_ms_(0), + sample_counter_(0), + task_runner_(task_runner) {} + + void SetGeometry( + const chromecast::RectF& display_rect, + chromecast::media::VideoPlane::CoordinateType coordinate_type, + chromecast::media::VideoPlane::Transform transform) { + DCHECK(task_runner_->BelongsToCurrentThread()); + + base::TimeTicks now = base::TimeTicks::Now(); + base::TimeDelta elapsed = now - last_set_geometry_time_; + + if (elapsed < base::TimeDelta::FromMilliseconds(min_calling_interval_ms_)) { + if (!pending_set_geometry_) { + pending_set_geometry_ = true; + + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind( + &RateLimitedSetVideoPlaneGeometry::ApplyPendingSetGeometry, + this), + base::TimeDelta::FromMilliseconds(2 * min_calling_interval_ms_)); + } + + pending_display_rect_ = display_rect; + pending_coordinate_type_ = coordinate_type; + pending_transform_ = transform; + return; + } + last_set_geometry_time_ = now; + + chromecast::media::VideoPlane* video_plane = + chromecast::media::CastMediaShlib::GetVideoPlane(); + CHECK(video_plane); + base::TimeTicks start = base::TimeTicks::Now(); + video_plane->SetGeometry(display_rect, coordinate_type, transform); + + base::TimeDelta set_geometry_time = base::TimeTicks::Now() - start; + UpdateAverageTime(set_geometry_time.InMilliseconds()); + } + + private: + friend class base::RefCountedThreadSafe<RateLimitedSetVideoPlaneGeometry>; + ~RateLimitedSetVideoPlaneGeometry() {} + + void UpdateAverageTime(int64 sample) { + const size_t kSampleCount = 5; + if (samples_.size() < kSampleCount) + samples_.push_back(sample); + else + samples_[sample_counter_++ % kSampleCount] = sample; + int64 total = 0; + for (int64 s : samples_) + total += s; + min_calling_interval_ms_ = 2 * total / samples_.size(); + } + + void ApplyPendingSetGeometry() { + if (pending_set_geometry_) { + pending_set_geometry_ = false; + SetGeometry(pending_display_rect_, pending_coordinate_type_, + pending_transform_); + } + } + + chromecast::RectF pending_display_rect_; + chromecast::media::VideoPlane::CoordinateType pending_coordinate_type_; + chromecast::media::VideoPlane::Transform pending_transform_; + bool pending_set_geometry_; + base::TimeTicks last_set_geometry_time_; + + // Don't call SetGeometry faster than this interval. + int64 min_calling_interval_ms_; + + // Min calling interval is computed as double average of last few time samples + // (i.e. allow at least as much time between calls as the call itself takes). + std::vector<int64> samples_; + size_t sample_counter_; + + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + + DISALLOW_COPY_AND_ASSIGN(RateLimitedSetVideoPlaneGeometry); +}; // Translates a gfx::OverlayTransform into a VideoPlane::Transform. // Could be just a lookup table once we have unit tests for this code @@ -63,7 +153,9 @@ class OverlayCandidatesCast : public OverlayCandidatesOzone { : media_task_runner_( chromecast::media::MediaMessageLoop::GetTaskRunner()), transform_(gfx::OVERLAY_TRANSFORM_INVALID), - display_rect_(0, 0, 0, 0) {} + display_rect_(0, 0, 0, 0), + video_plane_wrapper_( + new RateLimitedSetVideoPlaneGeometry(media_task_runner_)) {} void CheckOverlaySupport(OverlaySurfaceCandidateList* surfaces) override; @@ -71,6 +163,7 @@ class OverlayCandidatesCast : public OverlayCandidatesOzone { scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; gfx::OverlayTransform transform_; chromecast::RectF display_rect_; + scoped_refptr<RateLimitedSetVideoPlaneGeometry> video_plane_wrapper_; }; void OverlayCandidatesCast::CheckOverlaySupport( @@ -99,7 +192,8 @@ void OverlayCandidatesCast::CheckOverlaySupport( media_task_runner_->PostTask( FROM_HERE, base::Bind( - &SetVideoPlaneGeometry, display_rect, + &RateLimitedSetVideoPlaneGeometry::SetGeometry, + video_plane_wrapper_, display_rect, chromecast::media::VideoPlane::COORDINATE_TYPE_GRAPHICS_PLANE, ConvertTransform(candidate.transform))); } |