Update broken references to image assets
[chromium-blink-merge.git] / media / blink / video_frame_compositor.cc
blobcc6d2072c4bd1270258a41a555295094b86d998c
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_YV12:
24 case PIXEL_FORMAT_I420:
25 case PIXEL_FORMAT_YV16:
26 case PIXEL_FORMAT_YV24:
27 case PIXEL_FORMAT_NV12:
28 case PIXEL_FORMAT_XRGB:
29 case PIXEL_FORMAT_UYVY:
30 return true;
31 case PIXEL_FORMAT_YV12A:
32 case PIXEL_FORMAT_ARGB:
33 break;
35 return false;
38 VideoFrameCompositor::VideoFrameCompositor(
39 const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
40 const base::Callback<void(gfx::Size)>& natural_size_changed_cb,
41 const base::Callback<void(bool)>& opacity_changed_cb)
42 : compositor_task_runner_(compositor_task_runner),
43 tick_clock_(new base::DefaultTickClock()),
44 natural_size_changed_cb_(natural_size_changed_cb),
45 opacity_changed_cb_(opacity_changed_cb),
46 background_rendering_enabled_(true),
47 background_rendering_timer_(
48 FROM_HERE,
49 base::TimeDelta::FromMilliseconds(kBackgroundRenderingTimeoutMs),
50 base::Bind(&VideoFrameCompositor::BackgroundRender,
51 base::Unretained(this)),
52 // Task is not repeating, CallRender() will reset the task as needed.
53 false),
54 client_(nullptr),
55 rendering_(false),
56 rendered_last_frame_(false),
57 is_background_rendering_(false),
58 new_background_frame_(false),
59 // Assume 60Hz before the first UpdateCurrentFrame() call.
60 last_interval_(base::TimeDelta::FromSecondsD(1.0 / 60)),
61 callback_(nullptr) {
62 background_rendering_timer_.SetTaskRunner(compositor_task_runner_);
65 VideoFrameCompositor::~VideoFrameCompositor() {
66 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
67 DCHECK(!callback_);
68 DCHECK(!rendering_);
69 if (client_)
70 client_->StopUsingProvider();
73 void VideoFrameCompositor::OnRendererStateUpdate(bool new_state) {
74 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
75 DCHECK_NE(rendering_, new_state);
76 rendering_ = new_state;
78 if (rendering_) {
79 // Always start playback in background rendering mode, if |client_| kicks
80 // in right away it's okay.
81 BackgroundRender();
82 } else if (background_rendering_enabled_) {
83 background_rendering_timer_.Stop();
84 } else {
85 DCHECK(!background_rendering_timer_.IsRunning());
88 if (!client_)
89 return;
91 if (rendering_)
92 client_->StartRendering();
93 else
94 client_->StopRendering();
97 void VideoFrameCompositor::SetVideoFrameProviderClient(
98 cc::VideoFrameProvider::Client* client) {
99 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
100 if (client_)
101 client_->StopUsingProvider();
102 client_ = client;
104 // |client_| may now be null, so verify before calling it.
105 if (rendering_ && client_)
106 client_->StartRendering();
109 scoped_refptr<VideoFrame> VideoFrameCompositor::GetCurrentFrame() {
110 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
111 return current_frame_;
114 void VideoFrameCompositor::PutCurrentFrame() {
115 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
116 rendered_last_frame_ = true;
119 bool VideoFrameCompositor::UpdateCurrentFrame(base::TimeTicks deadline_min,
120 base::TimeTicks deadline_max) {
121 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
122 return CallRender(deadline_min, deadline_max, false);
125 bool VideoFrameCompositor::HasCurrentFrame() {
126 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
127 return current_frame_;
130 void VideoFrameCompositor::Start(RenderCallback* callback) {
131 TRACE_EVENT0("media", "VideoFrameCompositor::Start");
133 // Called from the media thread, so acquire the callback under lock before
134 // returning in case a Stop() call comes in before the PostTask is processed.
135 base::AutoLock lock(lock_);
136 DCHECK(!callback_);
137 callback_ = callback;
138 compositor_task_runner_->PostTask(
139 FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate,
140 base::Unretained(this), true));
143 void VideoFrameCompositor::Stop() {
144 TRACE_EVENT0("media", "VideoFrameCompositor::Stop");
146 // Called from the media thread, so release the callback under lock before
147 // returning to avoid a pending UpdateCurrentFrame() call occurring before
148 // the PostTask is processed.
149 base::AutoLock lock(lock_);
150 DCHECK(callback_);
151 callback_ = nullptr;
152 compositor_task_runner_->PostTask(
153 FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate,
154 base::Unretained(this), false));
157 void VideoFrameCompositor::PaintFrameUsingOldRenderingPath(
158 const scoped_refptr<VideoFrame>& frame) {
159 if (!compositor_task_runner_->BelongsToCurrentThread()) {
160 compositor_task_runner_->PostTask(
161 FROM_HERE,
162 base::Bind(&VideoFrameCompositor::PaintFrameUsingOldRenderingPath,
163 base::Unretained(this), frame));
164 return;
167 if (ProcessNewFrame(frame) && client_)
168 client_->DidReceiveFrame();
171 scoped_refptr<VideoFrame>
172 VideoFrameCompositor::GetCurrentFrameAndUpdateIfStale() {
173 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
174 if (client_ || !rendering_ || !is_background_rendering_)
175 return current_frame_;
177 DCHECK(!last_background_render_.is_null());
179 const base::TimeTicks now = tick_clock_->NowTicks();
180 const base::TimeDelta interval = now - last_background_render_;
182 // Cap updates to 250Hz which should be more than enough for everyone.
183 if (interval < base::TimeDelta::FromMilliseconds(4))
184 return current_frame_;
186 // Update the interval based on the time between calls and call background
187 // render which will give this information to the client.
188 last_interval_ = interval;
189 BackgroundRender();
191 return current_frame_;
194 bool VideoFrameCompositor::ProcessNewFrame(
195 const scoped_refptr<VideoFrame>& frame) {
196 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
198 if (frame == current_frame_)
199 return false;
201 // Set the flag indicating that the current frame is unrendered, if we get a
202 // subsequent PutCurrentFrame() call it will mark it as rendered.
203 rendered_last_frame_ = false;
205 if (current_frame_ &&
206 current_frame_->natural_size() != frame->natural_size()) {
207 natural_size_changed_cb_.Run(frame->natural_size());
210 if (!current_frame_ || IsOpaque(current_frame_) != IsOpaque(frame))
211 opacity_changed_cb_.Run(IsOpaque(frame));
213 current_frame_ = frame;
214 return true;
217 void VideoFrameCompositor::BackgroundRender() {
218 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
219 const base::TimeTicks now = tick_clock_->NowTicks();
220 last_background_render_ = now;
221 bool new_frame = CallRender(now, now + last_interval_, true);
222 if (new_frame && client_)
223 client_->DidReceiveFrame();
226 bool VideoFrameCompositor::CallRender(base::TimeTicks deadline_min,
227 base::TimeTicks deadline_max,
228 bool background_rendering) {
229 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
231 base::AutoLock lock(lock_);
232 if (!callback_) {
233 // Even if we no longer have a callback, return true if we have a frame
234 // which |client_| hasn't seen before.
235 return !rendered_last_frame_ && current_frame_;
238 DCHECK(rendering_);
240 // If the previous frame was never rendered and we're not in background
241 // rendering mode (nor have just exited it), let the client know.
242 if (!rendered_last_frame_ && current_frame_ && !background_rendering &&
243 !is_background_rendering_) {
244 callback_->OnFrameDropped();
247 const bool new_frame = ProcessNewFrame(
248 callback_->Render(deadline_min, deadline_max, background_rendering));
250 // We may create a new frame here with background rendering, but the provider
251 // has no way of knowing that a new frame had been processed, so keep track of
252 // the new frame, and return true on the next call to |CallRender|.
253 const bool had_new_background_frame = new_background_frame_;
254 new_background_frame_ = background_rendering && new_frame;
256 is_background_rendering_ = background_rendering;
257 last_interval_ = deadline_max - deadline_min;
259 // Restart the background rendering timer whether we're background rendering
260 // or not; in either case we should wait for |kBackgroundRenderingTimeoutMs|.
261 if (background_rendering_enabled_)
262 background_rendering_timer_.Reset();
263 return new_frame || had_new_background_frame;
266 } // namespace media