Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(399)

Side by Side Diff: media/blink/video_frame_compositor.cc

Issue 1083383005: Connect the new video rendering path to the compositor. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@vra
Patch Set: Comments. Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/blink/video_frame_compositor.h" 5 #include "media/blink/video_frame_compositor.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.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"
9 #include "media/base/video_frame.h" 11 #include "media/base/video_frame.h"
10 12
11 namespace media { 13 namespace media {
12 14
15 // The maximum time we'll allow to elapse between Render() callbacks if there is
16 // an external caller requesting frames via GetCurrentFrame(); i.e. there is a
17 // canvas which frames are being copied into.
18 const int kStaleFrameThresholdMs = 250;
19
13 static bool IsOpaque(const scoped_refptr<VideoFrame>& frame) { 20 static bool IsOpaque(const scoped_refptr<VideoFrame>& frame) {
14 switch (frame->format()) { 21 switch (frame->format()) {
15 case VideoFrame::UNKNOWN: 22 case VideoFrame::UNKNOWN:
16 case VideoFrame::YV12: 23 case VideoFrame::YV12:
17 case VideoFrame::YV12J: 24 case VideoFrame::YV12J:
18 case VideoFrame::YV12HD: 25 case VideoFrame::YV12HD:
19 case VideoFrame::YV16: 26 case VideoFrame::YV16:
20 case VideoFrame::I420: 27 case VideoFrame::I420:
21 case VideoFrame::YV24: 28 case VideoFrame::YV24:
22 case VideoFrame::NV12: 29 case VideoFrame::NV12:
23 return true; 30 return true;
24 31
25 case VideoFrame::YV12A: 32 case VideoFrame::YV12A:
26 #if defined(VIDEO_HOLE) 33 #if defined(VIDEO_HOLE)
27 case VideoFrame::HOLE: 34 case VideoFrame::HOLE:
28 #endif // defined(VIDEO_HOLE) 35 #endif // defined(VIDEO_HOLE)
29 case VideoFrame::NATIVE_TEXTURE: 36 case VideoFrame::NATIVE_TEXTURE:
30 case VideoFrame::ARGB: 37 case VideoFrame::ARGB:
31 break; 38 break;
32 } 39 }
33 return false; 40 return false;
34 } 41 }
35 42
36 VideoFrameCompositor::VideoFrameCompositor( 43 VideoFrameCompositor::VideoFrameCompositor(
37 const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner, 44 const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
38 const base::Callback<void(gfx::Size)>& natural_size_changed_cb, 45 const base::Callback<void(gfx::Size)>& natural_size_changed_cb,
39 const base::Callback<void(bool)>& opacity_changed_cb) 46 const base::Callback<void(bool)>& opacity_changed_cb)
40 : compositor_task_runner_(compositor_task_runner), 47 : compositor_task_runner_(compositor_task_runner),
48 tick_clock_(new base::DefaultTickClock()),
41 natural_size_changed_cb_(natural_size_changed_cb), 49 natural_size_changed_cb_(natural_size_changed_cb),
42 opacity_changed_cb_(opacity_changed_cb), 50 opacity_changed_cb_(opacity_changed_cb),
51 stale_frame_threshold_(
52 base::TimeDelta::FromMilliseconds(kStaleFrameThresholdMs)),
43 client_(nullptr), 53 client_(nullptr),
44 rendering_(false), 54 rendering_(false),
45 callback_(nullptr) { 55 rendered_last_frame_(false),
56 callback_(nullptr),
57 // Assume 60Hz before the first UpdateCurrentFrame() call.
58 last_interval_(base::TimeDelta::FromSecondsD(1.0 / 60)) {
46 } 59 }
47 60
48 VideoFrameCompositor::~VideoFrameCompositor() { 61 VideoFrameCompositor::~VideoFrameCompositor() {
49 DCHECK(compositor_task_runner_->BelongsToCurrentThread()); 62 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
50 DCHECK(!callback_); 63 DCHECK(!callback_);
51 DCHECK(!rendering_); 64 DCHECK(!rendering_);
52 if (client_) 65 if (client_)
53 client_->StopUsingProvider(); 66 client_->StopUsingProvider();
54 } 67 }
55 68
56 void VideoFrameCompositor::OnRendererStateUpdate() { 69 void VideoFrameCompositor::OnRendererStateUpdate(bool new_state) {
57 DCHECK(compositor_task_runner_->BelongsToCurrentThread()); 70 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
71 DCHECK_NE(rendering_, new_state);
72 rendering_ = new_state;
73 last_frame_update_time_ = base::TimeTicks();
74
58 if (!client_) 75 if (!client_)
59 return; 76 return;
60 77
61 base::AutoLock lock(lock_); 78 if (rendering_)
62 if (callback_) { 79 client_->StartRendering();
63 if (rendering_) 80 else
64 client_->StartRendering();
65
66 // TODO(dalecurtis): This will need to request the first frame so we have
67 // something to show, even if playback hasn't started yet.
68 } else if (rendering_) {
69 client_->StopRendering(); 81 client_->StopRendering();
70 }
71 } 82 }
72 83
73 scoped_refptr<VideoFrame> 84 scoped_refptr<VideoFrame>
74 VideoFrameCompositor::GetCurrentFrameAndUpdateIfStale() { 85 VideoFrameCompositor::GetCurrentFrameAndUpdateIfStale() {
75 // TODO(dalecurtis): Implement frame refresh when stale.
76 DCHECK(compositor_task_runner_->BelongsToCurrentThread()); 86 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
87 if (rendering_) {
88 const base::TimeTicks now = tick_clock_->NowTicks();
89 if (now - last_frame_update_time_ > stale_frame_threshold_)
90 UpdateCurrentFrame(now, now + last_interval_);
91 }
92
77 return GetCurrentFrame(); 93 return GetCurrentFrame();
78 } 94 }
79 95
80 void VideoFrameCompositor::SetVideoFrameProviderClient( 96 void VideoFrameCompositor::SetVideoFrameProviderClient(
81 cc::VideoFrameProvider::Client* client) { 97 cc::VideoFrameProvider::Client* client) {
82 DCHECK(compositor_task_runner_->BelongsToCurrentThread()); 98 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
83 if (client_) 99 if (client_)
84 client_->StopUsingProvider(); 100 client_->StopUsingProvider();
85 client_ = client; 101 client_ = client;
86 OnRendererStateUpdate(); 102
103 if (rendering_)
104 client_->StartRendering();
87 } 105 }
88 106
89 scoped_refptr<VideoFrame> VideoFrameCompositor::GetCurrentFrame() { 107 scoped_refptr<VideoFrame> VideoFrameCompositor::GetCurrentFrame() {
90 DCHECK(compositor_task_runner_->BelongsToCurrentThread()); 108 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
91 return current_frame_; 109 return current_frame_;
92 } 110 }
93 111
94 void VideoFrameCompositor::PutCurrentFrame() { 112 void VideoFrameCompositor::PutCurrentFrame() {
95 DCHECK(compositor_task_runner_->BelongsToCurrentThread()); 113 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
96 // TODO(dalecurtis): Wire up a flag for RenderCallback::OnFrameDropped(). 114 rendered_last_frame_ = true;
97 } 115 }
98 116
99 bool VideoFrameCompositor::UpdateCurrentFrame(base::TimeTicks deadline_min, 117 bool VideoFrameCompositor::UpdateCurrentFrame(base::TimeTicks deadline_min,
100 base::TimeTicks deadline_max) { 118 base::TimeTicks deadline_max) {
101 // TODO(dalecurtis): Wire this up to RenderCallback::Render(). 119 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
102 base::AutoLock lock(lock_); 120 base::AutoLock lock(lock_);
103 return false; 121 if (!callback_) {
122 LOG(ERROR) << "Got begin frame callback... but had no callback...";
xhwang 2015/04/30 16:39:20 In what case can this happen? Should this be a DCH
DaleCurtis 2015/04/30 18:06:00 Left over debugging from when I was experimenting
123 return false;
124 }
125
126 DCHECK(rendering_);
127
128 // If the previous frame was never rendered, let the client know.
129 if (!rendered_last_frame_ && current_frame_)
130 callback_->OnFrameDropped();
131
132 last_frame_update_time_ = tick_clock_->NowTicks();
133 last_interval_ = deadline_max - deadline_min;
134 scoped_refptr<VideoFrame> frame =
135 callback_->Render(deadline_min, deadline_max);
136
137 // Do nothing if the current frame has already been rendered.
138 if (current_frame_ == frame)
139 return false;
140
141 ProcessNewFrame(frame);
142 return true;
104 } 143 }
105 144
106 void VideoFrameCompositor::Start(RenderCallback* callback) { 145 void VideoFrameCompositor::Start(RenderCallback* callback) {
107 NOTREACHED(); 146 TRACE_EVENT0("media", __FUNCTION__);
108 147
109 // Called from the media thread, so acquire the callback under lock before 148 // Called from the media thread, so acquire the callback under lock before
110 // returning in case a Stop() call comes in before the PostTask is processed. 149 // returning in case a Stop() call comes in before the PostTask is processed.
111 base::AutoLock lock(lock_); 150 base::AutoLock lock(lock_);
151 DCHECK(!callback_);
112 callback_ = callback; 152 callback_ = callback;
113 rendering_ = true;
114 compositor_task_runner_->PostTask( 153 compositor_task_runner_->PostTask(
115 FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate, 154 FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate,
116 base::Unretained(this))); 155 base::Unretained(this), true));
117 } 156 }
118 157
119 void VideoFrameCompositor::Stop() { 158 void VideoFrameCompositor::Stop() {
120 NOTREACHED(); 159 TRACE_EVENT0("media", __FUNCTION__);
121 160
122 // Called from the media thread, so release the callback under lock before 161 // Called from the media thread, so release the callback under lock before
123 // returning to avoid a pending UpdateCurrentFrame() call occurring before 162 // returning to avoid a pending UpdateCurrentFrame() call occurring before
124 // the PostTask is processed. 163 // the PostTask is processed.
125 base::AutoLock lock(lock_); 164 base::AutoLock lock(lock_);
165 DCHECK(callback_);
166
167 // Fire one more Render() callback if we're more than one render interval away
168 // to ensure that we have a good frame to display if Render() has never been
169 // called, or was called long enough ago that the frame is stale. We must
170 // always have a |current_frame_| to recover from "damage" to the video layer.
171 const base::TimeTicks now = tick_clock_->NowTicks();
172 if (now - last_frame_update_time_ > last_interval_) {
173 compositor_task_runner_->PostTask(
174 FROM_HERE, base::Bind(&VideoFrameCompositor::ProcessNewFrame,
175 base::Unretained(this),
176 callback_->Render(now, now + last_interval_)));
177 }
178
126 callback_ = nullptr; 179 callback_ = nullptr;
127 rendering_ = false;
128 compositor_task_runner_->PostTask( 180 compositor_task_runner_->PostTask(
129 FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate, 181 FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate,
130 base::Unretained(this))); 182 base::Unretained(this), false));
131 } 183 }
132 184
133 void VideoFrameCompositor::PaintFrameUsingOldRenderingPath( 185 void VideoFrameCompositor::PaintFrameUsingOldRenderingPath(
134 const scoped_refptr<VideoFrame>& frame) { 186 const scoped_refptr<VideoFrame>& frame) {
135 if (!compositor_task_runner_->BelongsToCurrentThread()) { 187 if (!compositor_task_runner_->BelongsToCurrentThread()) {
136 compositor_task_runner_->PostTask( 188 compositor_task_runner_->PostTask(
137 FROM_HERE, 189 FROM_HERE,
138 base::Bind(&VideoFrameCompositor::PaintFrameUsingOldRenderingPath, 190 base::Bind(&VideoFrameCompositor::PaintFrameUsingOldRenderingPath,
139 base::Unretained(this), frame)); 191 base::Unretained(this), frame));
140 return; 192 return;
141 } 193 }
142 194
143 if (current_frame_.get() && 195 ProcessNewFrame(frame);
196 if (client_)
197 client_->DidReceiveFrame();
xhwang 2015/04/30 16:39:20 There's a chance that we didn't update the |curren
DaleCurtis 2015/04/30 18:06:00 Whoops yes, rolled this into ProcessNewFrame() and
198 }
199
200 void VideoFrameCompositor::ProcessNewFrame(
201 const scoped_refptr<VideoFrame>& frame) {
202 DCHECK(compositor_task_runner_->BelongsToCurrentThread());
203
204 if (frame == current_frame_)
205 return;
206
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;
210
211 if (current_frame_ &&
144 current_frame_->natural_size() != frame->natural_size()) { 212 current_frame_->natural_size() != frame->natural_size()) {
145 natural_size_changed_cb_.Run(frame->natural_size()); 213 natural_size_changed_cb_.Run(frame->natural_size());
146 } 214 }
147 215
148 if (!current_frame_.get() || IsOpaque(current_frame_) != IsOpaque(frame)) { 216 if (!current_frame_ || IsOpaque(current_frame_) != IsOpaque(frame))
149 opacity_changed_cb_.Run(IsOpaque(frame)); 217 opacity_changed_cb_.Run(IsOpaque(frame));
150 }
151 218
152 current_frame_ = frame; 219 current_frame_ = frame;
153
154 if (client_)
155 client_->DidReceiveFrame();
156 } 220 }
157 221
158 } // namespace media 222 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698