1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/ozone/platform/cast/overlay_manager_cast.h"
8 #include "base/location.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/single_thread_task_runner.h"
11 #include "chromecast/media/base/media_message_loop.h"
12 #include "chromecast/public/cast_media_shlib.h"
13 #include "chromecast/public/graphics_types.h"
14 #include "chromecast/public/video_plane.h"
15 #include "ui/gfx/geometry/rect_conversions.h"
16 #include "ui/ozone/public/overlay_candidates_ozone.h"
21 // Helper class for calling VideoPlane::SetGeometry with rate-limiting.
22 // SetGeometry can take on the order of 100ms to run in some implementations
23 // and can be called on the order of 20x / second (as fast as graphics frames
24 // are produced). This creates an ever-growing backlog of tasks on the media
26 // This class measures the time taken to run SetGeometry to determine a
27 // reasonable frequency at which to call it. Excess calls are coalesced
28 // to just set the most recent geometry.
29 class RateLimitedSetVideoPlaneGeometry
30 : public base::RefCountedThreadSafe
<RateLimitedSetVideoPlaneGeometry
> {
32 RateLimitedSetVideoPlaneGeometry(
33 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
)
34 : pending_display_rect_(0, 0, 0, 0),
35 pending_set_geometry_(false),
36 min_calling_interval_ms_(0),
38 task_runner_(task_runner
) {}
41 const chromecast::RectF
& display_rect
,
42 chromecast::media::VideoPlane::CoordinateType coordinate_type
,
43 chromecast::media::VideoPlane::Transform transform
) {
44 DCHECK(task_runner_
->BelongsToCurrentThread());
46 base::TimeTicks now
= base::TimeTicks::Now();
47 base::TimeDelta elapsed
= now
- last_set_geometry_time_
;
49 if (elapsed
< base::TimeDelta::FromMilliseconds(min_calling_interval_ms_
)) {
50 if (!pending_set_geometry_
) {
51 pending_set_geometry_
= true;
53 task_runner_
->PostDelayedTask(
56 &RateLimitedSetVideoPlaneGeometry::ApplyPendingSetGeometry
,
58 base::TimeDelta::FromMilliseconds(2 * min_calling_interval_ms_
));
61 pending_display_rect_
= display_rect
;
62 pending_coordinate_type_
= coordinate_type
;
63 pending_transform_
= transform
;
66 last_set_geometry_time_
= now
;
68 chromecast::media::VideoPlane
* video_plane
=
69 chromecast::media::CastMediaShlib::GetVideoPlane();
71 base::TimeTicks start
= base::TimeTicks::Now();
72 video_plane
->SetGeometry(display_rect
, coordinate_type
, transform
);
74 base::TimeDelta set_geometry_time
= base::TimeTicks::Now() - start
;
75 UpdateAverageTime(set_geometry_time
.InMilliseconds());
79 friend class base::RefCountedThreadSafe
<RateLimitedSetVideoPlaneGeometry
>;
80 ~RateLimitedSetVideoPlaneGeometry() {}
82 void UpdateAverageTime(int64 sample
) {
83 const size_t kSampleCount
= 5;
84 if (samples_
.size() < kSampleCount
)
85 samples_
.push_back(sample
);
87 samples_
[sample_counter_
++ % kSampleCount
] = sample
;
89 for (int64 s
: samples_
)
91 min_calling_interval_ms_
= 2 * total
/ samples_
.size();
94 void ApplyPendingSetGeometry() {
95 if (pending_set_geometry_
) {
96 pending_set_geometry_
= false;
97 SetGeometry(pending_display_rect_
, pending_coordinate_type_
,
102 chromecast::RectF pending_display_rect_
;
103 chromecast::media::VideoPlane::CoordinateType pending_coordinate_type_
;
104 chromecast::media::VideoPlane::Transform pending_transform_
;
105 bool pending_set_geometry_
;
106 base::TimeTicks last_set_geometry_time_
;
108 // Don't call SetGeometry faster than this interval.
109 int64 min_calling_interval_ms_
;
111 // Min calling interval is computed as double average of last few time samples
112 // (i.e. allow at least as much time between calls as the call itself takes).
113 std::vector
<int64
> samples_
;
114 size_t sample_counter_
;
116 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
118 DISALLOW_COPY_AND_ASSIGN(RateLimitedSetVideoPlaneGeometry
);
121 // Translates a gfx::OverlayTransform into a VideoPlane::Transform.
122 // Could be just a lookup table once we have unit tests for this code
123 // to ensure it stays in sync with OverlayTransform.
124 chromecast::media::VideoPlane::Transform
ConvertTransform(
125 gfx::OverlayTransform transform
) {
127 case gfx::OVERLAY_TRANSFORM_NONE
:
128 return chromecast::media::VideoPlane::TRANSFORM_NONE
;
129 case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL
:
130 return chromecast::media::VideoPlane::FLIP_HORIZONTAL
;
131 case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL
:
132 return chromecast::media::VideoPlane::FLIP_VERTICAL
;
133 case gfx::OVERLAY_TRANSFORM_ROTATE_90
:
134 return chromecast::media::VideoPlane::ROTATE_90
;
135 case gfx::OVERLAY_TRANSFORM_ROTATE_180
:
136 return chromecast::media::VideoPlane::ROTATE_180
;
137 case gfx::OVERLAY_TRANSFORM_ROTATE_270
:
138 return chromecast::media::VideoPlane::ROTATE_270
;
141 return chromecast::media::VideoPlane::TRANSFORM_NONE
;
145 bool ExactlyEqual(const chromecast::RectF
& r1
, const chromecast::RectF
& r2
) {
146 return r1
.x
== r2
.x
&& r1
.y
== r2
.y
&& r1
.width
== r2
.width
&&
147 r1
.height
== r2
.height
;
150 class OverlayCandidatesCast
: public OverlayCandidatesOzone
{
152 OverlayCandidatesCast()
153 : media_task_runner_(
154 chromecast::media::MediaMessageLoop::GetTaskRunner()),
155 transform_(gfx::OVERLAY_TRANSFORM_INVALID
),
156 display_rect_(0, 0, 0, 0),
157 video_plane_wrapper_(
158 new RateLimitedSetVideoPlaneGeometry(media_task_runner_
)) {}
160 void CheckOverlaySupport(OverlaySurfaceCandidateList
* surfaces
) override
;
163 scoped_refptr
<base::SingleThreadTaskRunner
> media_task_runner_
;
164 gfx::OverlayTransform transform_
;
165 chromecast::RectF display_rect_
;
166 scoped_refptr
<RateLimitedSetVideoPlaneGeometry
> video_plane_wrapper_
;
169 void OverlayCandidatesCast::CheckOverlaySupport(
170 OverlaySurfaceCandidateList
* surfaces
) {
171 for (auto& candidate
: *surfaces
) {
172 if (candidate
.plane_z_order
!= -1)
175 candidate
.overlay_handled
= true;
177 // Compositor requires all overlay rectangles to have integer coords.
178 candidate
.display_rect
=
179 gfx::RectF(gfx::ToEnclosedRect(candidate
.display_rect
));
181 chromecast::RectF
display_rect(
182 candidate
.display_rect
.x(), candidate
.display_rect
.y(),
183 candidate
.display_rect
.width(), candidate
.display_rect
.height());
185 // Update video plane geometry + transform to match compositor quad.
186 // This must be done on media thread - and no point doing if it hasn't
188 if (candidate
.transform
!= transform_
||
189 !ExactlyEqual(display_rect
, display_rect_
)) {
190 transform_
= candidate
.transform
;
191 display_rect_
= display_rect
;
193 media_task_runner_
->PostTask(
196 &RateLimitedSetVideoPlaneGeometry::SetGeometry
,
197 video_plane_wrapper_
, display_rect
,
198 chromecast::media::VideoPlane::COORDINATE_TYPE_GRAPHICS_PLANE
,
199 ConvertTransform(candidate
.transform
)));
207 OverlayManagerCast::OverlayManagerCast() {
210 OverlayManagerCast::~OverlayManagerCast() {
213 scoped_ptr
<OverlayCandidatesOzone
> OverlayManagerCast::CreateOverlayCandidates(
214 gfx::AcceleratedWidget w
) {
215 return make_scoped_ptr(new OverlayCandidatesCast());
218 bool OverlayManagerCast::CanShowPrimaryPlaneAsOverlay() {