Clean up check for dependency_info.
[chromium-blink-merge.git] / media / blink / video_frame_compositor.cc
blob073ec97462a889e2c6e0c35f3597070ba9eb3a74
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 PIXEL_FORMAT_UNKNOWN:
23 case PIXEL_FORMAT_I420:
24 case PIXEL_FORMAT_YV12:
25 case PIXEL_FORMAT_YV16:
26 case PIXEL_FORMAT_YV24:
27 case PIXEL_FORMAT_NV12:
28 case PIXEL_FORMAT_NV21:
29 case PIXEL_FORMAT_UYVY:
30 case PIXEL_FORMAT_YUY2:
31 case PIXEL_FORMAT_XRGB:
32 case PIXEL_FORMAT_RGB24:
33 case PIXEL_FORMAT_MJPEG:
34 case PIXEL_FORMAT_MT21:
35 return true;
36 case PIXEL_FORMAT_YV12A:
37 case PIXEL_FORMAT_ARGB:
38 case PIXEL_FORMAT_RGB32:
39 break;
41 return false;
44 VideoFrameCompositor::VideoFrameCompositor(
45 const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
46 const base::Callback<void(gfx::Size)>& natural_size_changed_cb,
47 const base::Callback<void(bool)>& opacity_changed_cb)
48 : compositor_task_runner_(compositor_task_runner),
49 tick_clock_(new base::DefaultTickClock()),
50 natural_size_changed_cb_(natural_size_changed_cb),
51 opacity_changed_cb_(opacity_changed_cb),
52 background_rendering_enabled_(true),
53 background_rendering_timer_(
54 FROM_HERE,
55 base::TimeDelta::FromMilliseconds(kBackgroundRenderingTimeoutMs),
56 base::Bind(&VideoFrameCompositor::BackgroundRender,
57 base::Unretained(this)),
58 // Task is not repeating, CallRender() will reset the task as needed.
59 false),
60 client_(nullptr),
61 rendering_(false),
62 rendered_last_frame_(false),
63 is_background_rendering_(false),
64 new_background_frame_(false),
65 // Assume 60Hz before the first UpdateCurrentFrame() call.
66 last_interval_(base::TimeDelta::FromSecondsD(1.0 / 60)),
67 callback_(nullptr) {
68 background_rendering_timer_.SetTaskRunner(compositor_task_runner_);
71 VideoFrameCompositor::~VideoFrameCompositor() {
72 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
73 DCHECK(!callback_);
74 DCHECK(!rendering_);
75 if (client_)
76 client_->StopUsingProvider();
79 void VideoFrameCompositor::OnRendererStateUpdate(bool new_state) {
80 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
81 DCHECK_NE(rendering_, new_state);
82 rendering_ = new_state;
84 if (rendering_) {
85 // Always start playback in background rendering mode, if |client_| kicks
86 // in right away it's okay.
87 BackgroundRender();
88 } else if (background_rendering_enabled_) {
89 background_rendering_timer_.Stop();
90 } else {
91 DCHECK(!background_rendering_timer_.IsRunning());
94 if (!client_)
95 return;
97 if (rendering_)
98 client_->StartRendering();
99 else
100 client_->StopRendering();
103 void VideoFrameCompositor::SetVideoFrameProviderClient(
104 cc::VideoFrameProvider::Client* client) {
105 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
106 if (client_)
107 client_->StopUsingProvider();
108 client_ = client;
110 // |client_| may now be null, so verify before calling it.
111 if (rendering_ && client_)
112 client_->StartRendering();
115 scoped_refptr<VideoFrame> VideoFrameCompositor::GetCurrentFrame() {
116 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
117 return current_frame_;
120 void VideoFrameCompositor::PutCurrentFrame() {
121 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
122 rendered_last_frame_ = true;
125 bool VideoFrameCompositor::UpdateCurrentFrame(base::TimeTicks deadline_min,
126 base::TimeTicks deadline_max) {
127 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
128 return CallRender(deadline_min, deadline_max, false);
131 bool VideoFrameCompositor::HasCurrentFrame() {
132 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
133 return current_frame_;
136 void VideoFrameCompositor::Start(RenderCallback* callback) {
137 TRACE_EVENT0("media", "VideoFrameCompositor::Start");
139 // Called from the media thread, so acquire the callback under lock before
140 // returning in case a Stop() call comes in before the PostTask is processed.
141 base::AutoLock lock(lock_);
142 DCHECK(!callback_);
143 callback_ = callback;
144 compositor_task_runner_->PostTask(
145 FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate,
146 base::Unretained(this), true));
149 void VideoFrameCompositor::Stop() {
150 TRACE_EVENT0("media", "VideoFrameCompositor::Stop");
152 // Called from the media thread, so release the callback under lock before
153 // returning to avoid a pending UpdateCurrentFrame() call occurring before
154 // the PostTask is processed.
155 base::AutoLock lock(lock_);
156 DCHECK(callback_);
157 callback_ = nullptr;
158 compositor_task_runner_->PostTask(
159 FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate,
160 base::Unretained(this), false));
163 void VideoFrameCompositor::PaintFrameUsingOldRenderingPath(
164 const scoped_refptr<VideoFrame>& frame) {
165 if (!compositor_task_runner_->BelongsToCurrentThread()) {
166 compositor_task_runner_->PostTask(
167 FROM_HERE,
168 base::Bind(&VideoFrameCompositor::PaintFrameUsingOldRenderingPath,
169 base::Unretained(this), frame));
170 return;
173 if (ProcessNewFrame(frame) && client_)
174 client_->DidReceiveFrame();
177 scoped_refptr<VideoFrame>
178 VideoFrameCompositor::GetCurrentFrameAndUpdateIfStale() {
179 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
180 if (client_ || !rendering_ || !is_background_rendering_)
181 return current_frame_;
183 DCHECK(!last_background_render_.is_null());
185 const base::TimeTicks now = tick_clock_->NowTicks();
186 const base::TimeDelta interval = now - last_background_render_;
188 // Cap updates to 250Hz which should be more than enough for everyone.
189 if (interval < base::TimeDelta::FromMilliseconds(4))
190 return current_frame_;
192 // Update the interval based on the time between calls and call background
193 // render which will give this information to the client.
194 last_interval_ = interval;
195 BackgroundRender();
197 return current_frame_;
200 bool VideoFrameCompositor::ProcessNewFrame(
201 const scoped_refptr<VideoFrame>& frame) {
202 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
204 if (frame == current_frame_)
205 return false;
207 // Set the flag indicating that the current frame is unrendered, if we get a
208 // subsequent PutCurrentFrame() call it will mark it as rendered.
209 rendered_last_frame_ = false;
211 if (current_frame_ &&
212 current_frame_->natural_size() != frame->natural_size()) {
213 natural_size_changed_cb_.Run(frame->natural_size());
216 if (!current_frame_ || IsOpaque(current_frame_) != IsOpaque(frame))
217 opacity_changed_cb_.Run(IsOpaque(frame));
219 current_frame_ = frame;
220 return true;
223 void VideoFrameCompositor::BackgroundRender() {
224 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
225 const base::TimeTicks now = tick_clock_->NowTicks();
226 last_background_render_ = now;
227 bool new_frame = CallRender(now, now + last_interval_, true);
228 if (new_frame && client_)
229 client_->DidReceiveFrame();
232 bool VideoFrameCompositor::CallRender(base::TimeTicks deadline_min,
233 base::TimeTicks deadline_max,
234 bool background_rendering) {
235 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
237 base::AutoLock lock(lock_);
238 if (!callback_) {
239 // Even if we no longer have a callback, return true if we have a frame
240 // which |client_| hasn't seen before.
241 return !rendered_last_frame_ && current_frame_;
244 DCHECK(rendering_);
246 // If the previous frame was never rendered and we're not in background
247 // rendering mode (nor have just exited it), let the client know.
248 if (!rendered_last_frame_ && current_frame_ && !background_rendering &&
249 !is_background_rendering_) {
250 callback_->OnFrameDropped();
253 const bool new_frame = ProcessNewFrame(
254 callback_->Render(deadline_min, deadline_max, background_rendering));
256 // We may create a new frame here with background rendering, but the provider
257 // has no way of knowing that a new frame had been processed, so keep track of
258 // the new frame, and return true on the next call to |CallRender|.
259 const bool had_new_background_frame = new_background_frame_;
260 new_background_frame_ = background_rendering && new_frame;
262 is_background_rendering_ = background_rendering;
263 last_interval_ = deadline_max - deadline_min;
265 // Restart the background rendering timer whether we're background rendering
266 // or not; in either case we should wait for |kBackgroundRenderingTimeoutMs|.
267 if (background_rendering_enabled_)
268 background_rendering_timer_.Reset();
269 return new_frame || had_new_background_frame;
272 } // namespace media