Sync defines with the GYP build.
[chromium-blink-merge.git] / media / blink / video_frame_compositor.cc
blob3e19254f67bc5ed4229dd09798b8028516393466
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 static bool IsOpaque(const scoped_refptr<VideoFrame>& frame) {
20 switch (frame->format()) {
21 case VideoFrame::UNKNOWN:
22 case VideoFrame::YV12:
23 case VideoFrame::YV12J:
24 case VideoFrame::YV12HD:
25 case VideoFrame::YV16:
26 case VideoFrame::I420:
27 case VideoFrame::YV24:
28 case VideoFrame::NV12:
29 return true;
31 case VideoFrame::YV12A:
32 #if defined(VIDEO_HOLE)
33 case VideoFrame::HOLE:
34 #endif // defined(VIDEO_HOLE)
35 case VideoFrame::NATIVE_TEXTURE:
36 case VideoFrame::ARGB:
37 break;
39 return false;
42 VideoFrameCompositor::VideoFrameCompositor(
43 const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
44 const base::Callback<void(gfx::Size)>& natural_size_changed_cb,
45 const base::Callback<void(bool)>& opacity_changed_cb)
46 : compositor_task_runner_(compositor_task_runner),
47 tick_clock_(new base::DefaultTickClock()),
48 natural_size_changed_cb_(natural_size_changed_cb),
49 opacity_changed_cb_(opacity_changed_cb),
50 background_rendering_enabled_(true),
51 background_rendering_timer_(
52 FROM_HERE,
53 base::TimeDelta::FromMilliseconds(kBackgroundRenderingTimeoutMs),
54 base::Bind(&VideoFrameCompositor::BackgroundRender,
55 base::Unretained(this)),
56 // Task is not repeating, CallRender() will reset the task as needed.
57 false),
58 client_(nullptr),
59 rendering_(false),
60 rendered_last_frame_(false),
61 // Assume 60Hz before the first UpdateCurrentFrame() call.
62 last_interval_(base::TimeDelta::FromSecondsD(1.0 / 60)),
63 callback_(nullptr) {
64 background_rendering_timer_.SetTaskRunner(compositor_task_runner_);
67 VideoFrameCompositor::~VideoFrameCompositor() {
68 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
69 DCHECK(!callback_);
70 DCHECK(!rendering_);
71 if (client_)
72 client_->StopUsingProvider();
75 void VideoFrameCompositor::OnRendererStateUpdate(bool new_state) {
76 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
77 DCHECK_NE(rendering_, new_state);
78 rendering_ = new_state;
80 if (rendering_) {
81 // Always start playback in background rendering mode, if |client_| kicks
82 // in right away it's okay.
83 BackgroundRender();
84 } else if (background_rendering_enabled_) {
85 background_rendering_timer_.Stop();
86 } else {
87 DCHECK(!background_rendering_timer_.IsRunning());
90 if (!client_)
91 return;
93 if (rendering_)
94 client_->StartRendering();
95 else
96 client_->StopRendering();
99 void VideoFrameCompositor::SetVideoFrameProviderClient(
100 cc::VideoFrameProvider::Client* client) {
101 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
102 if (client_)
103 client_->StopUsingProvider();
104 client_ = client;
106 // |client_| may now be null, so verify before calling it.
107 if (rendering_ && client_)
108 client_->StartRendering();
111 scoped_refptr<VideoFrame> VideoFrameCompositor::GetCurrentFrame() {
112 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
113 return current_frame_;
116 void VideoFrameCompositor::PutCurrentFrame() {
117 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
118 rendered_last_frame_ = true;
121 bool VideoFrameCompositor::UpdateCurrentFrame(base::TimeTicks deadline_min,
122 base::TimeTicks deadline_max) {
123 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
124 return CallRender(deadline_min, deadline_max, false);
127 void VideoFrameCompositor::Start(RenderCallback* callback) {
128 TRACE_EVENT0("media", "VideoFrameCompositor::Start");
130 // Called from the media thread, so acquire the callback under lock before
131 // returning in case a Stop() call comes in before the PostTask is processed.
132 base::AutoLock lock(lock_);
133 DCHECK(!callback_);
134 callback_ = callback;
135 compositor_task_runner_->PostTask(
136 FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate,
137 base::Unretained(this), true));
140 void VideoFrameCompositor::Stop() {
141 TRACE_EVENT0("media", "VideoFrameCompositor::Stop");
143 // Called from the media thread, so release the callback under lock before
144 // returning to avoid a pending UpdateCurrentFrame() call occurring before
145 // the PostTask is processed.
146 base::AutoLock lock(lock_);
147 DCHECK(callback_);
148 callback_ = nullptr;
149 compositor_task_runner_->PostTask(
150 FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate,
151 base::Unretained(this), false));
154 void VideoFrameCompositor::PaintFrameUsingOldRenderingPath(
155 const scoped_refptr<VideoFrame>& frame) {
156 if (!compositor_task_runner_->BelongsToCurrentThread()) {
157 compositor_task_runner_->PostTask(
158 FROM_HERE,
159 base::Bind(&VideoFrameCompositor::PaintFrameUsingOldRenderingPath,
160 base::Unretained(this), frame));
161 return;
164 if (ProcessNewFrame(frame) && client_)
165 client_->DidReceiveFrame();
168 scoped_refptr<VideoFrame>
169 VideoFrameCompositor::GetCurrentFrameAndUpdateIfStale() {
170 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
171 if (client_ || !rendering_ || !is_background_rendering_)
172 return current_frame_;
174 DCHECK(!last_background_render_.is_null());
176 const base::TimeTicks now = tick_clock_->NowTicks();
177 const base::TimeDelta interval = now - last_background_render_;
179 // Cap updates to 250Hz which should be more than enough for everyone.
180 if (interval < base::TimeDelta::FromMilliseconds(4))
181 return current_frame_;
183 // Update the interval based on the time between calls and call background
184 // render which will give this information to the client.
185 last_interval_ = interval;
186 BackgroundRender();
188 return current_frame_;
191 bool VideoFrameCompositor::ProcessNewFrame(
192 const scoped_refptr<VideoFrame>& frame) {
193 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
195 if (frame == current_frame_)
196 return false;
198 // Set the flag indicating that the current frame is unrendered, if we get a
199 // subsequent PutCurrentFrame() call it will mark it as rendered.
200 rendered_last_frame_ = false;
202 if (current_frame_ &&
203 current_frame_->natural_size() != frame->natural_size()) {
204 natural_size_changed_cb_.Run(frame->natural_size());
207 if (!current_frame_ || IsOpaque(current_frame_) != IsOpaque(frame))
208 opacity_changed_cb_.Run(IsOpaque(frame));
210 current_frame_ = frame;
211 return true;
214 void VideoFrameCompositor::BackgroundRender() {
215 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
216 const base::TimeTicks now = tick_clock_->NowTicks();
217 last_background_render_ = now;
218 CallRender(now, now + last_interval_, true);
221 bool VideoFrameCompositor::CallRender(base::TimeTicks deadline_min,
222 base::TimeTicks deadline_max,
223 bool background_rendering) {
224 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
226 base::AutoLock lock(lock_);
227 if (!callback_) {
228 // Even if we no longer have a callback, return true if we have a frame
229 // which |client_| hasn't seen before.
230 return !rendered_last_frame_ && current_frame_;
233 DCHECK(rendering_);
235 // If the previous frame was never rendered and we're not in background
236 // rendering mode (nor have just exited it), let the client know.
237 if (!rendered_last_frame_ && current_frame_ && !background_rendering &&
238 !is_background_rendering_) {
239 callback_->OnFrameDropped();
242 const bool new_frame = ProcessNewFrame(
243 callback_->Render(deadline_min, deadline_max, background_rendering));
245 is_background_rendering_ = background_rendering;
246 last_interval_ = deadline_max - deadline_min;
248 // Restart the background rendering timer whether we're background rendering
249 // or not; in either case we should wait for |kBackgroundRenderingTimeoutMs|.
250 if (background_rendering_enabled_)
251 background_rendering_timer_.Reset();
252 return new_frame;
255 } // namespace media