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

Side by Side Diff: media/filters/video_renderer_impl.cc

Issue 237353007: Refactor VideoRendererImpl to use VideoFrameScheduler. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: pretty much done Created 6 years, 8 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 | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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/filters/video_renderer_impl.h" 5 #include "media/filters/video_renderer_impl.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/callback_helpers.h" 8 #include "base/callback_helpers.h"
10 #include "base/debug/trace_event.h"
11 #include "base/location.h"
12 #include "base/single_thread_task_runner.h" 9 #include "base/single_thread_task_runner.h"
13 #include "base/threading/platform_thread.h" 10 #include "base/time/default_tick_clock.h"
14 #include "media/base/buffers.h"
15 #include "media/base/limits.h" 11 #include "media/base/limits.h"
16 #include "media/base/pipeline.h"
17 #include "media/base/video_frame.h" 12 #include "media/base/video_frame.h"
18 13
19 namespace media { 14 namespace media {
20 15
21 VideoRendererImpl::VideoRendererImpl( 16 VideoRendererImpl::VideoRendererImpl(
22 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, 17 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
18 scoped_ptr<VideoFrameScheduler> scheduler,
23 ScopedVector<VideoDecoder> decoders, 19 ScopedVector<VideoDecoder> decoders,
24 const SetDecryptorReadyCB& set_decryptor_ready_cb, 20 const SetDecryptorReadyCB& set_decryptor_ready_cb)
25 const PaintCB& paint_cb,
26 bool drop_frames)
27 : task_runner_(task_runner), 21 : task_runner_(task_runner),
22 scheduler_(scheduler.Pass()),
23 tick_clock_(new base::DefaultTickClock()),
28 video_frame_stream_(task_runner, decoders.Pass(), set_decryptor_ready_cb), 24 video_frame_stream_(task_runner, decoders.Pass(), set_decryptor_ready_cb),
29 received_end_of_stream_(false), 25 received_end_of_stream_(false),
30 frame_available_(&lock_),
31 state_(kUninitialized), 26 state_(kUninitialized),
32 thread_(),
33 pending_read_(false), 27 pending_read_(false),
34 drop_frames_(drop_frames),
35 playback_rate_(0), 28 playback_rate_(0),
36 paint_cb_(paint_cb),
37 last_timestamp_(kNoTimestamp()),
38 frames_decoded_(0), 29 frames_decoded_(0),
39 frames_dropped_(0), 30 frames_dropped_(0),
40 weak_factory_(this) { 31 weak_factory_(this) {
41 DCHECK(!paint_cb_.is_null());
42 } 32 }
43 33
44 VideoRendererImpl::~VideoRendererImpl() { 34 VideoRendererImpl::~VideoRendererImpl() {
45 base::AutoLock auto_lock(lock_);
46 CHECK(state_ == kStopped || state_ == kUninitialized) << state_; 35 CHECK(state_ == kStopped || state_ == kUninitialized) << state_;
47 CHECK(thread_.is_null());
48 } 36 }
49 37
50 void VideoRendererImpl::Play(const base::Closure& callback) { 38 void VideoRendererImpl::Play(const base::Closure& callback) {
51 DCHECK(task_runner_->BelongsToCurrentThread()); 39 DCHECK(task_runner_->BelongsToCurrentThread());
52 base::AutoLock auto_lock(lock_);
53 DCHECK_EQ(kPrerolled, state_); 40 DCHECK_EQ(kPrerolled, state_);
54 state_ = kPlaying; 41 state_ = kPlaying;
42
43 ScheduleVideoFrames();
44 AttemptRead();
45
46 // Changing from non-playing to playing state can trigger ended.
47 if (ShouldTransitionToEnded())
48 TransitionToEnded();
49
55 callback.Run(); 50 callback.Run();
56 } 51 }
57 52
58 void VideoRendererImpl::Pause(const base::Closure& callback) { 53 void VideoRendererImpl::Pause(const base::Closure& callback) {
59 DCHECK(task_runner_->BelongsToCurrentThread()); 54 DCHECK(task_runner_->BelongsToCurrentThread());
60 base::AutoLock auto_lock(lock_);
61 DCHECK(state_ != kUninitialized || state_ == kError); 55 DCHECK(state_ != kUninitialized || state_ == kError);
acolwell GONE FROM CHROMIUM 2014/04/24 16:43:59 nit: Add != kPausing ?
scherkus (not reviewing) 2014/04/25 02:04:47 kPausing is no more!
62 state_ = kPaused; 56
63 callback.Run(); 57 state_ = kPausing;
58 pause_cb_ = callback;
59 scheduler_->Reset();
60
61 if (ShouldTransitionToPaused())
62 TransitionToPaused();
64 } 63 }
65 64
66 void VideoRendererImpl::Flush(const base::Closure& callback) { 65 void VideoRendererImpl::Flush(const base::Closure& callback) {
67 DCHECK(task_runner_->BelongsToCurrentThread()); 66 DCHECK(task_runner_->BelongsToCurrentThread());
68 base::AutoLock auto_lock(lock_);
69 DCHECK_EQ(state_, kPaused); 67 DCHECK_EQ(state_, kPaused);
68 DCHECK_EQ(scheduled_frames_.size(), 0u);
70 flush_cb_ = callback; 69 flush_cb_ = callback;
71 state_ = kFlushing; 70 state_ = kFlushing;
72 71
73 // This is necessary if the |video_frame_stream_| has already seen an end of 72 // This is necessary if the |video_frame_stream_| has already seen an end of
74 // stream and needs to drain it before flushing it. 73 // stream and needs to drain it before flushing it.
acolwell GONE FROM CHROMIUM 2014/04/24 16:43:59 nit: Is this comment still correct? It confuses me
scherkus (not reviewing) 2014/04/25 02:04:47 That's my understanding. I feel the comment might
xhwang 2014/04/25 05:44:47 If you look at the history, it existed even before
scherkus (not reviewing) 2014/04/25 18:24:25 Removed.
75 ready_frames_.clear(); 74 unscheduled_frames_.clear();
75
76 received_end_of_stream_ = false; 76 received_end_of_stream_ = false;
77 video_frame_stream_.Reset( 77 video_frame_stream_.Reset(
78 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone, 78 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone,
79 weak_factory_.GetWeakPtr())); 79 weak_factory_.GetWeakPtr()));
80 } 80 }
81 81
82 void VideoRendererImpl::Stop(const base::Closure& callback) { 82 void VideoRendererImpl::Stop(const base::Closure& callback) {
83 DCHECK(task_runner_->BelongsToCurrentThread()); 83 DCHECK(task_runner_->BelongsToCurrentThread());
84 base::AutoLock auto_lock(lock_);
85 if (state_ == kUninitialized || state_ == kStopped) { 84 if (state_ == kUninitialized || state_ == kStopped) {
86 callback.Run(); 85 callback.Run();
87 return; 86 return;
88 } 87 }
89 88
90 // TODO(scherkus): Consider invalidating |weak_factory_| and replacing 89 // TODO(scherkus): Consider invalidating |weak_factory_| and replacing
91 // task-running guards that check |state_| with DCHECK(). 90 // task-running guards that check |state_| with DCHECK().
92 91
93 state_ = kStopped; 92 state_ = kStopped;
94 93
95 statistics_cb_.Reset(); 94 statistics_cb_.Reset();
96 max_time_cb_.Reset(); 95 max_time_cb_.Reset();
97 DoStopOrError_Locked();
98 96
99 // Clean up our thread if present. 97 // Leave |scheduled_frames_| untouched. If we get the frames back -- great --
100 base::PlatformThreadHandle thread_to_join = base::PlatformThreadHandle(); 98 // but it's more likely we'll be destroyed before that.
101 if (!thread_.is_null()) { 99 unscheduled_frames_.clear();
102 // Signal the thread since it's possible to get stopped with the video 100 scheduler_->Reset();
103 // thread waiting for a read to complete.
104 frame_available_.Signal();
105 std::swap(thread_, thread_to_join);
106 }
107
108 if (!thread_to_join.is_null()) {
109 base::AutoUnlock auto_unlock(lock_);
110 base::PlatformThread::Join(thread_to_join);
111 }
112 101
113 video_frame_stream_.Stop(callback); 102 video_frame_stream_.Stop(callback);
114 } 103 }
115 104
116 void VideoRendererImpl::SetPlaybackRate(float playback_rate) { 105 void VideoRendererImpl::SetPlaybackRate(float playback_rate) {
117 DCHECK(task_runner_->BelongsToCurrentThread()); 106 DCHECK(task_runner_->BelongsToCurrentThread());
118 base::AutoLock auto_lock(lock_); 107 if (playback_rate_ == playback_rate)
108 return;
109
119 playback_rate_ = playback_rate; 110 playback_rate_ = playback_rate;
111
112 scheduler_->Reset();
113 if (state_ == kPlaying)
114 ScheduleVideoFrames();
120 } 115 }
121 116
122 void VideoRendererImpl::Preroll(base::TimeDelta time, 117 void VideoRendererImpl::Preroll(base::TimeDelta time,
123 const PipelineStatusCB& cb) { 118 const PipelineStatusCB& cb) {
124 DCHECK(task_runner_->BelongsToCurrentThread()); 119 DCHECK(task_runner_->BelongsToCurrentThread());
125 base::AutoLock auto_lock(lock_);
126 DCHECK(!cb.is_null()); 120 DCHECK(!cb.is_null());
127 DCHECK(preroll_cb_.is_null()); 121 DCHECK(preroll_cb_.is_null());
128 DCHECK(state_ == kFlushed || state_== kPaused) << "state_ " << state_; 122 DCHECK(state_ == kFlushed || state_ == kPaused) << "state_ " << state_;
129 123
130 if (state_ == kFlushed) { 124 if (state_ == kFlushed) {
131 DCHECK(time != kNoTimestamp()); 125 DCHECK(time != kNoTimestamp());
132 DCHECK(!pending_read_); 126 DCHECK(!pending_read_);
133 DCHECK(ready_frames_.empty()); 127 DCHECK(unscheduled_frames_.empty());
128 DCHECK(scheduled_frames_.empty());
134 } else { 129 } else {
130 // Prerolling without a timestamp is used for rebuffering. No painting
131 // is required so skip ahead one state.
135 DCHECK(time == kNoTimestamp()); 132 DCHECK(time == kNoTimestamp());
136 } 133 }
137 134
138 state_ = kPrerolling; 135 state_ = kPrerolling;
139 preroll_cb_ = cb; 136 preroll_cb_ = cb;
140 preroll_timestamp_ = time; 137 preroll_timestamp_ = time;
141 138
142 if (ShouldTransitionToPrerolled_Locked()) { 139 if (ShouldTransitionToPrerolled()) {
143 TransitionToPrerolled_Locked(); 140 TransitionToPrerolled();
144 return; 141 return;
145 } 142 }
146 143
147 AttemptRead_Locked(); 144 AttemptRead();
148 } 145 }
149 146
150 void VideoRendererImpl::Initialize(DemuxerStream* stream, 147 void VideoRendererImpl::Initialize(DemuxerStream* stream,
151 const PipelineStatusCB& init_cb, 148 const PipelineStatusCB& init_cb,
152 const StatisticsCB& statistics_cb, 149 const StatisticsCB& statistics_cb,
153 const TimeCB& max_time_cb, 150 const TimeCB& max_time_cb,
154 const base::Closure& ended_cb, 151 const base::Closure& ended_cb,
155 const PipelineStatusCB& error_cb, 152 const PipelineStatusCB& error_cb,
156 const TimeDeltaCB& get_time_cb, 153 const TimeDeltaCB& get_time_cb,
157 const TimeDeltaCB& get_duration_cb) { 154 const TimeDeltaCB& get_duration_cb) {
158 DCHECK(task_runner_->BelongsToCurrentThread()); 155 DCHECK(task_runner_->BelongsToCurrentThread());
159 base::AutoLock auto_lock(lock_);
160 DCHECK(stream); 156 DCHECK(stream);
161 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); 157 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO);
162 DCHECK(!init_cb.is_null()); 158 DCHECK(!init_cb.is_null());
163 DCHECK(!statistics_cb.is_null()); 159 DCHECK(!statistics_cb.is_null());
164 DCHECK(!max_time_cb.is_null()); 160 DCHECK(!max_time_cb.is_null());
165 DCHECK(!ended_cb.is_null()); 161 DCHECK(!ended_cb.is_null());
166 DCHECK(!get_time_cb.is_null()); 162 DCHECK(!get_time_cb.is_null());
167 DCHECK(!get_duration_cb.is_null()); 163 DCHECK(!get_duration_cb.is_null());
168 DCHECK_EQ(kUninitialized, state_); 164 DCHECK_EQ(kUninitialized, state_);
169 165
170 init_cb_ = init_cb; 166 init_cb_ = init_cb;
171 statistics_cb_ = statistics_cb; 167 statistics_cb_ = statistics_cb;
172 max_time_cb_ = max_time_cb; 168 max_time_cb_ = max_time_cb;
173 ended_cb_ = ended_cb; 169 ended_cb_ = ended_cb;
174 error_cb_ = error_cb; 170 error_cb_ = error_cb;
175 get_time_cb_ = get_time_cb; 171 get_time_cb_ = get_time_cb;
176 get_duration_cb_ = get_duration_cb; 172 get_duration_cb_ = get_duration_cb;
177 state_ = kInitializing; 173 state_ = kInitializing;
178 174
179 video_frame_stream_.Initialize( 175 video_frame_stream_.Initialize(
180 stream, 176 stream,
181 statistics_cb, 177 statistics_cb,
182 base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized, 178 base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized,
183 weak_factory_.GetWeakPtr())); 179 weak_factory_.GetWeakPtr()));
184 } 180 }
185 181
186 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) { 182 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) {
187 DCHECK(task_runner_->BelongsToCurrentThread()); 183 DCHECK(task_runner_->BelongsToCurrentThread());
188 base::AutoLock auto_lock(lock_);
189 184
190 if (state_ == kStopped) 185 if (state_ == kStopped)
191 return; 186 return;
192 187
193 DCHECK_EQ(state_, kInitializing); 188 DCHECK_EQ(state_, kInitializing);
194 189
195 if (!success) { 190 if (!success) {
196 state_ = kUninitialized; 191 state_ = kUninitialized;
197 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); 192 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED);
198 return; 193 return;
199 } 194 }
200 195
201 // We're all good! Consider ourselves flushed. (ThreadMain() should never 196 // We're all good! Consider ourselves flushed. (ThreadMain() should never
202 // see us in the kUninitialized state). 197 // see us in the kUninitialized state).
203 // Since we had an initial Preroll(), we consider ourself flushed, because we 198 // Since we had an initial Preroll(), we consider ourself flushed, because we
204 // have not populated any buffers yet. 199 // have not populated any buffers yet.
205 state_ = kFlushed; 200 state_ = kFlushed;
206 201
207 // Create our video thread.
208 if (!base::PlatformThread::Create(0, this, &thread_)) {
209 NOTREACHED() << "Video thread creation failed";
210 state_ = kError;
211 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED);
212 return;
213 }
214
215 #if defined(OS_WIN)
216 // Bump up our priority so our sleeping is more accurate.
217 // TODO(scherkus): find out if this is necessary, but it seems to help.
218 ::SetThreadPriority(thread_.platform_handle(), THREAD_PRIORITY_ABOVE_NORMAL);
219 #endif // defined(OS_WIN)
220 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); 202 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
221 } 203 }
222 204
223 // PlatformThread::Delegate implementation.
224 void VideoRendererImpl::ThreadMain() {
225 base::PlatformThread::SetName("CrVideoRenderer");
226
227 // The number of milliseconds to idle when we do not have anything to do.
228 // Nothing special about the value, other than we're being more OS-friendly
229 // than sleeping for 1 millisecond.
230 //
231 // TODO(scherkus): switch to pure event-driven frame timing instead of this
232 // kIdleTimeDelta business http://crbug.com/106874
233 const base::TimeDelta kIdleTimeDelta =
234 base::TimeDelta::FromMilliseconds(10);
235
236 for (;;) {
237 base::AutoLock auto_lock(lock_);
238
239 // Thread exit condition.
240 if (state_ == kStopped)
241 return;
242
243 // Remain idle as long as we're not playing.
244 if (state_ != kPlaying || playback_rate_ == 0) {
245 UpdateStatsAndWait_Locked(kIdleTimeDelta);
246 continue;
247 }
248
249 // Remain idle until we have the next frame ready for rendering.
250 if (ready_frames_.empty()) {
251 if (received_end_of_stream_) {
252 state_ = kEnded;
253 ended_cb_.Run();
254
255 // No need to sleep here as we idle when |state_ != kPlaying|.
256 continue;
257 }
258
259 UpdateStatsAndWait_Locked(kIdleTimeDelta);
260 continue;
261 }
262
263 base::TimeDelta remaining_time =
264 CalculateSleepDuration(ready_frames_.front(), playback_rate_);
265
266 // Sleep up to a maximum of our idle time until we're within the time to
267 // render the next frame.
268 if (remaining_time.InMicroseconds() > 0) {
269 remaining_time = std::min(remaining_time, kIdleTimeDelta);
270 UpdateStatsAndWait_Locked(remaining_time);
271 continue;
272 }
273
274 // Deadline is defined as the midpoint between this frame and the next
275 // frame, using the delta between this frame and the previous frame as the
276 // assumption for frame duration.
277 //
278 // TODO(scherkus): An improvement over midpoint might be selecting the
279 // minimum and/or maximum between the midpoint and some constants. As a
280 // thought experiment, consider what would be better than the midpoint
281 // for both the 1fps case and 120fps case.
282 //
283 // TODO(scherkus): This can be vastly improved. Use a histogram to measure
284 // the accuracy of our frame timing code. http://crbug.com/149829
285 if (drop_frames_ && last_timestamp_ != kNoTimestamp()) {
286 base::TimeDelta now = get_time_cb_.Run();
287 base::TimeDelta deadline = ready_frames_.front()->timestamp() +
288 (ready_frames_.front()->timestamp() - last_timestamp_) / 2;
289
290 if (now > deadline) {
291 DropNextReadyFrame_Locked();
292 continue;
293 }
294 }
295
296 // Congratulations! You've made it past the video frame timing gauntlet.
297 //
298 // At this point enough time has passed that the next frame that ready for
299 // rendering.
300 PaintNextReadyFrame_Locked();
301 }
302 }
303
304 void VideoRendererImpl::PaintNextReadyFrame_Locked() {
305 lock_.AssertAcquired();
306
307 scoped_refptr<VideoFrame> next_frame = ready_frames_.front();
308 ready_frames_.pop_front();
309 frames_decoded_++;
310
311 last_timestamp_ = next_frame->timestamp();
312
313 paint_cb_.Run(next_frame);
314
315 task_runner_->PostTask(
316 FROM_HERE,
317 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
318 }
319
320 void VideoRendererImpl::DropNextReadyFrame_Locked() {
321 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped");
322
323 lock_.AssertAcquired();
324
325 last_timestamp_ = ready_frames_.front()->timestamp();
326 ready_frames_.pop_front();
327 frames_decoded_++;
328 frames_dropped_++;
329
330 task_runner_->PostTask(
331 FROM_HERE,
332 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
333 }
334
335 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, 205 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
336 const scoped_refptr<VideoFrame>& frame) { 206 const scoped_refptr<VideoFrame>& frame) {
337 base::AutoLock auto_lock(lock_);
338 DCHECK_NE(state_, kUninitialized); 207 DCHECK_NE(state_, kUninitialized);
339 DCHECK_NE(state_, kFlushed); 208 DCHECK_NE(state_, kFlushed);
340 209
341 CHECK(pending_read_); 210 CHECK(pending_read_);
342 pending_read_ = false; 211 pending_read_ = false;
343 212
344 if (status == VideoFrameStream::DECODE_ERROR || 213 if (status == VideoFrameStream::DECODE_ERROR ||
345 status == VideoFrameStream::DECRYPT_ERROR) { 214 status == VideoFrameStream::DECRYPT_ERROR) {
346 DCHECK(!frame.get()); 215 DCHECK(!frame.get());
347 PipelineStatus error = PIPELINE_ERROR_DECODE; 216 PipelineStatus error = PIPELINE_ERROR_DECODE;
(...skipping 12 matching lines...) Expand all
360 // Already-queued VideoFrameStream ReadCB's can fire after various state 229 // Already-queued VideoFrameStream ReadCB's can fire after various state
361 // transitions have happened; in that case just drop those frames immediately. 230 // transitions have happened; in that case just drop those frames immediately.
362 if (state_ == kStopped || state_ == kError || state_ == kFlushing) 231 if (state_ == kStopped || state_ == kError || state_ == kFlushing)
363 return; 232 return;
364 233
365 if (!frame.get()) { 234 if (!frame.get()) {
366 // Abort preroll early for a NULL frame because we won't get more frames. 235 // Abort preroll early for a NULL frame because we won't get more frames.
367 // A new preroll will be requested after this one completes so there is no 236 // A new preroll will be requested after this one completes so there is no
368 // point trying to collect more frames. 237 // point trying to collect more frames.
369 if (state_ == kPrerolling) 238 if (state_ == kPrerolling)
370 TransitionToPrerolled_Locked(); 239 TransitionToPrerolled();
371 240
372 return; 241 return;
373 } 242 }
374 243
375 if (frame->end_of_stream()) { 244 if (frame->end_of_stream()) {
376 DCHECK(!received_end_of_stream_); 245 DCHECK(!received_end_of_stream_);
377 received_end_of_stream_ = true; 246 received_end_of_stream_ = true;
378 max_time_cb_.Run(get_duration_cb_.Run()); 247 max_time_cb_.Run(get_duration_cb_.Run());
379 248
380 if (state_ == kPrerolling) 249 if (state_ == kPrerolling)
381 TransitionToPrerolled_Locked(); 250 TransitionToPrerolled();
251
252 if (ShouldTransitionToEnded())
253 TransitionToEnded();
382 254
383 return; 255 return;
384 } 256 }
385 257
386 // Maintain the latest frame decoded so the correct frame is displayed after 258 // Maintain the latest frame decoded so the correct frame is displayed after
387 // prerolling has completed. 259 // prerolling has completed.
388 if (state_ == kPrerolling && preroll_timestamp_ != kNoTimestamp() && 260 if (state_ == kPrerolling && preroll_timestamp_ != kNoTimestamp() &&
389 frame->timestamp() <= preroll_timestamp_) { 261 frame->timestamp() <= preroll_timestamp_) {
390 ready_frames_.clear(); 262 unscheduled_frames_.clear();
391 } 263 }
392 264
393 AddReadyFrame_Locked(frame);
394
395 if (ShouldTransitionToPrerolled_Locked())
396 TransitionToPrerolled_Locked();
397
398 // Always request more decoded video if we have capacity. This serves two
399 // purposes:
400 // 1) Prerolling while paused
401 // 2) Keeps decoding going if video rendering thread starts falling behind
402 AttemptRead_Locked();
403 }
404
405 bool VideoRendererImpl::ShouldTransitionToPrerolled_Locked() {
406 return state_ == kPrerolling &&
407 (!video_frame_stream_.CanReadWithoutStalling() ||
408 ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames));
409 }
410
411 void VideoRendererImpl::AddReadyFrame_Locked(
412 const scoped_refptr<VideoFrame>& frame) {
413 lock_.AssertAcquired();
414 DCHECK(!frame->end_of_stream());
415
416 // Adjust the incoming frame if its rendering stop time is past the duration 265 // Adjust the incoming frame if its rendering stop time is past the duration
417 // of the video itself. This is typically the last frame of the video and 266 // of the video itself. This is typically the last frame of the video and
418 // occurs if the container specifies a duration that isn't a multiple of the 267 // occurs if the container specifies a duration that isn't a multiple of the
419 // frame rate. Another way for this to happen is for the container to state 268 // frame rate. Another way for this to happen is for the container to state
420 // a smaller duration than the largest packet timestamp. 269 // a smaller duration than the largest packet timestamp.
421 base::TimeDelta duration = get_duration_cb_.Run(); 270 base::TimeDelta duration = get_duration_cb_.Run();
422 if (frame->timestamp() > duration) { 271 if (frame->timestamp() > duration) {
423 frame->set_timestamp(duration); 272 frame->set_timestamp(duration);
424 } 273 }
425 274
426 ready_frames_.push_back(frame); 275 unscheduled_frames_.push_back(frame);
427 DCHECK_LE(ready_frames_.size(), 276 DCHECK_LE(video_frame_count(), limits::kMaxVideoFrames);
428 static_cast<size_t>(limits::kMaxVideoFrames));
429 277
430 max_time_cb_.Run(frame->timestamp()); 278 max_time_cb_.Run(frame->timestamp());
431 279
432 // Avoid needlessly waking up |thread_| unless playing. 280 if (ShouldTransitionToPrerolled())
281 TransitionToPrerolled();
282
433 if (state_ == kPlaying) 283 if (state_ == kPlaying)
434 frame_available_.Signal(); 284 ScheduleVideoFrames();
285
286 if (ShouldTransitionToEnded())
287 TransitionToEnded();
288
289 // Always request more decoded video if we have capacity. This serves two
290 // purposes:
291 // 1) Prerolling while paused
292 // 2) Keeps decoding going if video rendering thread starts falling behind
293 AttemptRead();
294 }
295
296 bool VideoRendererImpl::ShouldTransitionToPrerolled() {
297 return state_ == kPrerolling &&
298 (!video_frame_stream_.CanReadWithoutStalling() ||
299 video_frame_count() >= limits::kMaxVideoFrames);
300 }
301
302 bool VideoRendererImpl::ShouldTransitionToPaused() {
303 return state_ == kPausing && scheduled_frames_.empty();
304 }
305
306 void VideoRendererImpl::TransitionToPaused() {
307 DCHECK(ShouldTransitionToPaused());
308 state_ = kPaused;
309 base::ResetAndReturn(&pause_cb_).Run();
310 }
xhwang 2014/04/24 18:48:44 nit: I found it's easier to understand if we just
scherkus (not reviewing) 2014/04/25 02:04:47 yeah ... the biggest thing I wanted out of these t
311
312 bool VideoRendererImpl::ShouldTransitionToEnded() {
313 return state_ == kPlaying && unscheduled_frames_.empty() &&
314 scheduled_frames_.empty() && received_end_of_stream_;
315 }
316
317 void VideoRendererImpl::TransitionToEnded() {
318 DCHECK(ShouldTransitionToEnded());
319 state_ = kEnded;
320 ended_cb_.Run();
435 } 321 }
436 322
437 void VideoRendererImpl::AttemptRead() { 323 void VideoRendererImpl::AttemptRead() {
438 base::AutoLock auto_lock(lock_);
439 AttemptRead_Locked();
440 }
441
442 void VideoRendererImpl::AttemptRead_Locked() {
443 DCHECK(task_runner_->BelongsToCurrentThread()); 324 DCHECK(task_runner_->BelongsToCurrentThread());
444 lock_.AssertAcquired();
445 325
446 if (pending_read_ || received_end_of_stream_ || 326 if (pending_read_ || received_end_of_stream_ ||
447 ready_frames_.size() == static_cast<size_t>(limits::kMaxVideoFrames)) { 327 video_frame_count() == limits::kMaxVideoFrames) {
448 return; 328 return;
449 } 329 }
450 330
451 switch (state_) { 331 switch (state_) {
332 case kPausing:
452 case kPaused: 333 case kPaused:
453 case kPrerolling: 334 case kPrerolling:
454 case kPlaying: 335 case kPlaying:
455 pending_read_ = true; 336 pending_read_ = true;
456 video_frame_stream_.Read(base::Bind(&VideoRendererImpl::FrameReady, 337 video_frame_stream_.Read(base::Bind(&VideoRendererImpl::FrameReady,
457 weak_factory_.GetWeakPtr())); 338 weak_factory_.GetWeakPtr()));
458 return; 339 return;
459 340
460 case kUninitialized: 341 case kUninitialized:
461 case kInitializing: 342 case kInitializing:
462 case kPrerolled: 343 case kPrerolled:
463 case kFlushing: 344 case kFlushing:
464 case kFlushed: 345 case kFlushed:
465 case kEnded: 346 case kEnded:
466 case kStopped: 347 case kStopped:
467 case kError: 348 case kError:
468 return; 349 return;
469 } 350 }
470 } 351 }
471 352
472 void VideoRendererImpl::OnVideoFrameStreamResetDone() { 353 void VideoRendererImpl::OnVideoFrameStreamResetDone() {
473 base::AutoLock auto_lock(lock_);
474 if (state_ == kStopped) 354 if (state_ == kStopped)
475 return; 355 return;
476 356
477 DCHECK_EQ(kFlushing, state_); 357 DCHECK_EQ(kFlushing, state_);
478 DCHECK(!pending_read_); 358 DCHECK(!pending_read_);
479 DCHECK(ready_frames_.empty()); 359 DCHECK(unscheduled_frames_.empty());
360 DCHECK(scheduled_frames_.empty());
480 DCHECK(!received_end_of_stream_); 361 DCHECK(!received_end_of_stream_);
481 362
482 state_ = kFlushed; 363 state_ = kFlushed;
483 last_timestamp_ = kNoTimestamp();
484 base::ResetAndReturn(&flush_cb_).Run(); 364 base::ResetAndReturn(&flush_cb_).Run();
485 } 365 }
486 366
487 base::TimeDelta VideoRendererImpl::CalculateSleepDuration( 367 void VideoRendererImpl::TransitionToPrerolled() {
488 const scoped_refptr<VideoFrame>& next_frame,
489 float playback_rate) {
490 // Determine the current and next presentation timestamps.
491 base::TimeDelta now = get_time_cb_.Run();
492 base::TimeDelta next_pts = next_frame->timestamp();
493
494 // Scale our sleep based on the playback rate.
495 base::TimeDelta sleep = next_pts - now;
496 return base::TimeDelta::FromMicroseconds(
497 static_cast<int64>(sleep.InMicroseconds() / playback_rate));
498 }
499
500 void VideoRendererImpl::DoStopOrError_Locked() {
501 lock_.AssertAcquired();
502 last_timestamp_ = kNoTimestamp();
503 ready_frames_.clear();
504 }
505
506 void VideoRendererImpl::TransitionToPrerolled_Locked() {
507 lock_.AssertAcquired();
508 DCHECK_EQ(state_, kPrerolling); 368 DCHECK_EQ(state_, kPrerolling);
509 369
510 state_ = kPrerolled; 370 state_ = kPrerolled;
511 371
512 // Because we might remain in the prerolled state for an undetermined amount 372 // Because we might remain in the prerolled state for an undetermined amount
513 // of time (e.g., we seeked while paused), we'll paint the first prerolled 373 // of time (e.g., we seeked while paused), we'll paint the first prerolled
514 // frame. 374 // frame.
515 if (!ready_frames_.empty()) 375 if (!unscheduled_frames_.empty()) {
516 PaintNextReadyFrame_Locked(); 376 ScheduleFirstFrameForImmediateDisplay();
377 AttemptRead();
378 }
517 379
518 base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK); 380 base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK);
519 } 381 }
520 382
521 void VideoRendererImpl::UpdateStatsAndWait_Locked( 383 void VideoRendererImpl::ScheduleVideoFrames() {
522 base::TimeDelta wait_duration) { 384 DCHECK_EQ(state_, kPlaying);
523 lock_.AssertAcquired();
524 DCHECK_GE(frames_decoded_, 0);
525 DCHECK_LE(frames_dropped_, frames_decoded_);
526 385
527 if (frames_decoded_) { 386 if (playback_rate_ == 0)
528 PipelineStatistics statistics; 387 return;
529 statistics.video_frames_decoded = frames_decoded_;
530 statistics.video_frames_dropped = frames_dropped_;
531 statistics_cb_.Run(statistics);
532 388
533 frames_decoded_ = 0; 389 base::TimeDelta current_media_time = get_time_cb_.Run();
534 frames_dropped_ = 0; 390 base::TimeTicks current_wall_ticks = tick_clock_->NowTicks();
acolwell GONE FROM CHROMIUM 2014/04/24 16:43:59 hmm.. ISTM that pipeline's Clock should be the sol
scherkus (not reviewing) 2014/04/25 02:04:47 I agree -- we're not quite done with how we want c
391
392 while (!unscheduled_frames_.empty()) {
393 base::TimeDelta delta =
394 (unscheduled_frames_.front()->timestamp() - current_media_time) /
395 playback_rate_;
xhwang 2014/04/24 18:48:44 So even if delta < 0 we still ScheduleVideoFrame()
scherkus (not reviewing) 2014/04/25 02:04:47 Done. It's up to the scheduler to decide whether
396 base::TimeTicks target_wall_ticks = current_wall_ticks + delta;
397
398 scheduled_frames_.push_back(unscheduled_frames_.front());
399 unscheduled_frames_.pop_front();
400
401 scheduler_->ScheduleVideoFrame(
402 scheduled_frames_.back(),
403 target_wall_ticks,
404 base::Bind(&VideoRendererImpl::OnVideoFrameFinished,
405 weak_factory_.GetWeakPtr()));
406 }
407 }
408
409 void VideoRendererImpl::ScheduleFirstFrameForImmediateDisplay() {
xhwang 2014/04/24 18:48:44 DCHECK_EQ(state_, kPrerolled);
scherkus (not reviewing) 2014/04/25 02:04:47 Done.
410 scheduled_frames_.push_back(unscheduled_frames_.front());
411 unscheduled_frames_.pop_front();
412
413 scheduler_->ScheduleVideoFrame(
414 scheduled_frames_.back(),
415 tick_clock_->NowTicks(),
416 base::Bind(&VideoRendererImpl::OnVideoFrameFinished,
417 weak_factory_.GetWeakPtr()));
418 }
xhwang 2014/04/24 18:48:44 Probably we can have a helper function like Sche
scherkus (not reviewing) 2014/04/25 02:04:47 I tried it out but didn't like how ScheduleAllFram
419
420 void VideoRendererImpl::OnVideoFrameFinished(
421 const scoped_refptr<VideoFrame>& frame,
422 VideoFrameScheduler::Reason reason) {
423 VideoFrameQueue::iterator iter =
424 std::find(scheduled_frames_.begin(), scheduled_frames_.end(), frame);
425 CHECK(iter != scheduled_frames_.end());
426
427 switch (reason) {
428 case VideoFrameScheduler::DISPLAYED: {
429 ++frames_decoded_;
430 scheduled_frames_.erase(iter);
acolwell GONE FROM CHROMIUM 2014/04/24 16:43:59 nit: An reason not to do this before the switch si
scherkus (not reviewing) 2014/04/25 02:04:47 Done.
431
432 PipelineStatistics stats;
433 stats.video_frames_decoded = frames_decoded_;
434 stats.video_frames_dropped = frames_dropped_;
435 statistics_cb_.Run(stats);
436 break;
437 }
438
439 case VideoFrameScheduler::DROPPED: {
440 ++frames_decoded_;
441 ++frames_dropped_;
442 scheduled_frames_.erase(iter);
443
444 PipelineStatistics stats;
acolwell GONE FROM CHROMIUM 2014/04/24 16:43:59 nit: Move this below w/ a bool guard so we don't h
scherkus (not reviewing) 2014/04/25 02:04:47 With change in Reset() behaviour this function cle
445 stats.video_frames_decoded = frames_decoded_;
446 stats.video_frames_dropped = frames_dropped_;
447 statistics_cb_.Run(stats);
448 break;
449 }
450
451 case VideoFrameScheduler::RESET: {
452 unscheduled_frames_.push_back(*iter);
xhwang 2014/04/24 18:48:44 ha, It surprised me a bit that the "reset" frames
scherkus (not reviewing) 2014/04/25 02:04:47 out of curiosity, what did you think "reset" was f
453 scheduled_frames_.erase(iter);
454 break;
455 }
535 } 456 }
536 457
537 frame_available_.TimedWait(wait_duration); 458 if (ShouldTransitionToPaused()) {
459 TransitionToPaused();
460 return;
461 }
462
463 if (ShouldTransitionToEnded()) {
464 TransitionToEnded();
465 return;
466 }
467
468 AttemptRead();
469 }
470
471 void VideoRendererImpl::SetTickClockForTesting(
472 scoped_ptr<base::TickClock> tick_clock) {
473 tick_clock_.swap(tick_clock);
538 } 474 }
539 475
540 } // namespace media 476 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698