Remove INJECT_EVENTS permissions from test APKs.
[chromium-blink-merge.git] / media / blink / video_frame_compositor.cc
blobce8ba2b82ca6287d97fdac62ec0a0879028afb42
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
3 // found in the LICENSE file.
5 #include "media/blink/video_frame_compositor.h"
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/time/default_tick_clock.h"
10 #include "base/trace_event/trace_event.h"
11 #include "media/base/video_frame.h"
13 namespace media {
15 // Amount of time to wait between UpdateCurrentFrame() callbacks before starting
16 // background rendering to keep the Render() callbacks moving.
17 const int kBackgroundRenderingTimeoutMs = 250;
19 // Returns true if the format has no Alpha channel (hence is always opaque).
20 static bool IsOpaque(const scoped_refptr<VideoFrame>& frame) {
21 switch (frame->format()) {
22 case VideoFrame::UNKNOWN:
23 case VideoFrame::YV12:
24 case VideoFrame::I420:
25 case VideoFrame::YV16:
26 case VideoFrame::YV24:
27 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
28 case VideoFrame::NV12:
29 #endif
30 case VideoFrame::XRGB:
31 return true;
32 case VideoFrame::YV12A:
33 case VideoFrame::ARGB:
34 break;
36 return false;
39 VideoFrameCompositor::VideoFrameCompositor(
40 const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
41 const base::Callback<void(gfx::Size)>& natural_size_changed_cb,
42 const base::Callback<void(bool)>& opacity_changed_cb)
43 : compositor_task_runner_(compositor_task_runner),
44 tick_clock_(new base::DefaultTickClock()),
45 natural_size_changed_cb_(natural_size_changed_cb),
46 opacity_changed_cb_(opacity_changed_cb),
47 background_rendering_enabled_(true),
48 background_rendering_timer_(
49 FROM_HERE,
50 base::TimeDelta::FromMilliseconds(kBackgroundRenderingTimeoutMs),
51 base::Bind(&VideoFrameCompositor::BackgroundRender,
52 base::Unretained(this)),
53 // Task is not repeating, CallRender() will reset the task as needed.
54 false),
55 client_(nullptr),
56 rendering_(false),
57 rendered_last_frame_(false),
58 is_background_rendering_(false),
59 new_background_frame_(false),
60 // Assume 60Hz before the first UpdateCurrentFrame() call.
61 last_interval_(base::TimeDelta::FromSecondsD(1.0 / 60)),
62 callback_(nullptr) {
63 background_rendering_timer_.SetTaskRunner(compositor_task_runner_);
66 VideoFrameCompositor::~VideoFrameCompositor() {
67 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
68 DCHECK(!callback_);
69 DCHECK(!rendering_);
70 if (client_)
71 client_->StopUsingProvider();
74 void VideoFrameCompositor::OnRendererStateUpdate(bool new_state) {
75 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
76 DCHECK_NE(rendering_, new_state);
77 rendering_ = new_state;
79 if (rendering_) {
80 // Always start playback in background rendering mode, if |client_| kicks
81 // in right away it's okay.
82 BackgroundRender();
83 } else if (background_rendering_enabled_) {
84 background_rendering_timer_.Stop();
85 } else {
86 DCHECK(!background_rendering_timer_.IsRunning());
89 if (!client_)
90 return;
92 if (rendering_)
93 client_->StartRendering();
94 else
95 client_->StopRendering();
98 void VideoFrameCompositor::SetVideoFrameProviderClient(
99 cc::VideoFrameProvider::Client* client) {
100 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
101 if (client_)
102 client_->StopUsingProvider();
103 client_ = client;
105 // |client_| may now be null, so verify before calling it.
106 if (rendering_ && client_)
107 client_->StartRendering();
110 scoped_refptr<VideoFrame> VideoFrameCompositor::GetCurrentFrame() {
111 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
112 return current_frame_;
115 void VideoFrameCompositor::PutCurrentFrame() {
116 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
117 rendered_last_frame_ = true;
120 bool VideoFrameCompositor::UpdateCurrentFrame(base::TimeTicks deadline_min,
121 base::TimeTicks deadline_max) {
122 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
123 return CallRender(deadline_min, deadline_max, false);
126 bool VideoFrameCompositor::HasCurrentFrame() {
127 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
128 return current_frame_;
131 void VideoFrameCompositor::Start(RenderCallback* callback) {
132 TRACE_EVENT0("media", "VideoFrameCompositor::Start");
134 // Called from the media thread, so acquire the callback under lock before
135 // returning in case a Stop() call comes in before the PostTask is processed.
136 base::AutoLock lock(lock_);
137 DCHECK(!callback_);
138 callback_ = callback;
139 compositor_task_runner_->PostTask(
140 FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate,
141 base::Unretained(this), true));
144 void VideoFrameCompositor::Stop() {
145 TRACE_EVENT0("media", "VideoFrameCompositor::Stop");
147 // Called from the media thread, so release the callback under lock before
148 // returning to avoid a pending UpdateCurrentFrame() call occurring before
149 // the PostTask is processed.
150 base::AutoLock lock(lock_);
151 DCHECK(callback_);
152 callback_ = nullptr;
153 compositor_task_runner_->PostTask(
154 FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate,
155 base::Unretained(this), false));
158 void VideoFrameCompositor::PaintFrameUsingOldRenderingPath(
159 const scoped_refptr<VideoFrame>& frame) {
160 if (!compositor_task_runner_->BelongsToCurrentThread()) {
161 compositor_task_runner_->PostTask(
162 FROM_HERE,
163 base::Bind(&VideoFrameCompositor::PaintFrameUsingOldRenderingPath,
164 base::Unretained(this), frame));
165 return;
168 if (ProcessNewFrame(frame) && client_)
169 client_->DidReceiveFrame();
172 scoped_refptr<VideoFrame>
173 VideoFrameCompositor::GetCurrentFrameAndUpdateIfStale() {
174 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
175 if (client_ || !rendering_ || !is_background_rendering_)
176 return current_frame_;
178 DCHECK(!last_background_render_.is_null());
180 const base::TimeTicks now = tick_clock_->NowTicks();
181 const base::TimeDelta interval = now - last_background_render_;
183 // Cap updates to 250Hz which should be more than enough for everyone.
184 if (interval < base::TimeDelta::FromMilliseconds(4))
185 return current_frame_;
187 // Update the interval based on the time between calls and call background
188 // render which will give this information to the client.
189 last_interval_ = interval;
190 BackgroundRender();
192 return current_frame_;
195 bool VideoFrameCompositor::ProcessNewFrame(
196 const scoped_refptr<VideoFrame>& frame) {
197 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
199 if (frame == current_frame_)
200 return false;
202 // Set the flag indicating that the current frame is unrendered, if we get a
203 // subsequent PutCurrentFrame() call it will mark it as rendered.
204 rendered_last_frame_ = false;
206 if (current_frame_ &&
207 current_frame_->natural_size() != frame->natural_size()) {
208 natural_size_changed_cb_.Run(frame->natural_size());
211 if (!current_frame_ || IsOpaque(current_frame_) != IsOpaque(frame))
212 opacity_changed_cb_.Run(IsOpaque(frame));
214 current_frame_ = frame;
215 return true;
218 void VideoFrameCompositor::BackgroundRender() {
219 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
220 const base::TimeTicks now = tick_clock_->NowTicks();
221 last_background_render_ = now;
222 CallRender(now, now + last_interval_, true);
225 bool VideoFrameCompositor::CallRender(base::TimeTicks deadline_min,
226 base::TimeTicks deadline_max,
227 bool background_rendering) {
228 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
230 base::AutoLock lock(lock_);
231 if (!callback_) {
232 // Even if we no longer have a callback, return true if we have a frame
233 // which |client_| hasn't seen before.
234 return !rendered_last_frame_ && current_frame_;
237 DCHECK(rendering_);
239 // If the previous frame was never rendered and we're not in background
240 // rendering mode (nor have just exited it), let the client know.
241 if (!rendered_last_frame_ && current_frame_ && !background_rendering &&
242 !is_background_rendering_) {
243 callback_->OnFrameDropped();
246 const bool new_frame = ProcessNewFrame(
247 callback_->Render(deadline_min, deadline_max, background_rendering));
249 // We may create a new frame here with background rendering, but the provider
250 // has no way of knowing that a new frame had been processed, so keep track of
251 // the new frame, and return true on the next call to |CallRender|.
252 const bool had_new_background_frame = new_background_frame_;
253 new_background_frame_ = background_rendering && new_frame;
255 is_background_rendering_ = background_rendering;
256 last_interval_ = deadline_max - deadline_min;
258 // Restart the background rendering timer whether we're background rendering
259 // or not; in either case we should wait for |kBackgroundRenderingTimeoutMs|.
260 if (background_rendering_enabled_)
261 background_rendering_timer_.Reset();
262 return new_frame || had_new_background_frame;
265 } // namespace media