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

Side by Side Diff: media/renderers/video_renderer_impl.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: Remove NullVideoSink 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 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/renderers/video_renderer_impl.h" 5 #include "media/renderers/video_renderer_impl.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/callback.h" 8 #include "base/callback.h"
9 #include "base/callback_helpers.h" 9 #include "base/callback_helpers.h"
10 #include "base/command_line.h"
10 #include "base/location.h" 11 #include "base/location.h"
11 #include "base/single_thread_task_runner.h" 12 #include "base/single_thread_task_runner.h"
12 #include "base/threading/platform_thread.h" 13 #include "base/threading/platform_thread.h"
13 #include "base/time/default_tick_clock.h" 14 #include "base/time/default_tick_clock.h"
14 #include "base/trace_event/trace_event.h" 15 #include "base/trace_event/trace_event.h"
15 #include "media/base/bind_to_current_loop.h" 16 #include "media/base/bind_to_current_loop.h"
16 #include "media/base/buffers.h" 17 #include "media/base/buffers.h"
17 #include "media/base/limits.h" 18 #include "media/base/limits.h"
19 #include "media/base/media_switches.h"
18 #include "media/base/pipeline.h" 20 #include "media/base/pipeline.h"
19 #include "media/base/video_frame.h" 21 #include "media/base/video_frame.h"
20 22
21 namespace media { 23 namespace media {
22 24
25 // Wait a quarter of a second for Render() callbacks before starting background
26 // rendering to keep the decode pump moving.
27 static const int kBackgroundRenderingTimeoutMs = 250;
xhwang 2015/04/29 06:51:54 nit: s/static//
DaleCurtis 2015/04/30 03:49:37 Done.
28
23 VideoRendererImpl::VideoRendererImpl( 29 VideoRendererImpl::VideoRendererImpl(
24 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, 30 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
25 VideoRendererSink* sink, 31 VideoRendererSink* sink,
26 ScopedVector<VideoDecoder> decoders, 32 ScopedVector<VideoDecoder> decoders,
27 bool drop_frames, 33 bool drop_frames,
28 const scoped_refptr<MediaLog>& media_log) 34 const scoped_refptr<MediaLog>& media_log)
29 : task_runner_(task_runner), 35 : task_runner_(task_runner),
36 use_new_video_renderering_path_(
37 base::CommandLine::ForCurrentProcess()->HasSwitch(
38 switches::kEnableNewVideoRenderer)),
30 sink_(sink), 39 sink_(sink),
31 video_frame_stream_( 40 video_frame_stream_(
32 new VideoFrameStream(task_runner, decoders.Pass(), media_log)), 41 new VideoFrameStream(task_runner, decoders.Pass(), media_log)),
33 low_delay_(false), 42 low_delay_(false),
34 received_end_of_stream_(false), 43 received_end_of_stream_(false),
35 rendered_end_of_stream_(false), 44 rendered_end_of_stream_(false),
36 frame_available_(&lock_), 45 frame_available_(&lock_),
37 state_(kUninitialized), 46 state_(kUninitialized),
38 thread_(), 47 thread_(),
39 pending_read_(false), 48 pending_read_(false),
40 drop_frames_(drop_frames), 49 drop_frames_(drop_frames),
41 buffering_state_(BUFFERING_HAVE_NOTHING), 50 buffering_state_(BUFFERING_HAVE_NOTHING),
42 frames_decoded_(0), 51 frames_decoded_(0),
43 frames_dropped_(0), 52 frames_dropped_(0),
44 is_shutting_down_(false), 53 is_shutting_down_(false),
45 tick_clock_(new base::DefaultTickClock()), 54 tick_clock_(new base::DefaultTickClock()),
55 is_background_rendering_(false),
56 should_use_background_renderering_(true),
57 time_progressing_(false),
58 render_first_frame_and_stop_(false),
59 background_rendering_timeout_(
60 base::TimeDelta::FromMilliseconds(kBackgroundRenderingTimeoutMs)),
46 weak_factory_(this) { 61 weak_factory_(this) {
47 } 62 }
48 63
49 VideoRendererImpl::~VideoRendererImpl() { 64 VideoRendererImpl::~VideoRendererImpl() {
50 DCHECK(task_runner_->BelongsToCurrentThread()); 65 DCHECK(task_runner_->BelongsToCurrentThread());
51 66
52 { 67 if (!use_new_video_renderering_path_) {
53 base::AutoLock auto_lock(lock_); 68 base::AutoLock auto_lock(lock_);
54 is_shutting_down_ = true; 69 is_shutting_down_ = true;
55 frame_available_.Signal(); 70 frame_available_.Signal();
56 } 71 }
57 72
58 if (!thread_.is_null()) 73 if (!thread_.is_null())
59 base::PlatformThread::Join(thread_); 74 base::PlatformThread::Join(thread_);
60 75
61 if (!init_cb_.is_null()) 76 if (!init_cb_.is_null())
62 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT); 77 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT);
63 78
64 if (!flush_cb_.is_null()) 79 if (!flush_cb_.is_null())
65 base::ResetAndReturn(&flush_cb_).Run(); 80 base::ResetAndReturn(&flush_cb_).Run();
81
82 if (use_new_video_renderering_path_ &&
83 (time_progressing_ || render_first_frame_and_stop_)) {
84 StopSink();
85 }
66 } 86 }
67 87
68 void VideoRendererImpl::Flush(const base::Closure& callback) { 88 void VideoRendererImpl::Flush(const base::Closure& callback) {
69 DVLOG(1) << __FUNCTION__; 89 DVLOG(1) << __FUNCTION__;
70 DCHECK(task_runner_->BelongsToCurrentThread()); 90 DCHECK(task_runner_->BelongsToCurrentThread());
71 base::AutoLock auto_lock(lock_); 91 base::AutoLock auto_lock(lock_);
72 DCHECK_EQ(state_, kPlaying); 92 DCHECK_EQ(state_, kPlaying);
73 flush_cb_ = callback; 93 flush_cb_ = callback;
74 state_ = kFlushing; 94 state_ = kFlushing;
75 95
76 // This is necessary if the |video_frame_stream_| has already seen an end of 96 // This is necessary if the |video_frame_stream_| has already seen an end of
77 // stream and needs to drain it before flushing it. 97 // stream and needs to drain it before flushing it.
78 ready_frames_.clear(); 98 ready_frames_.clear();
79 if (buffering_state_ != BUFFERING_HAVE_NOTHING) { 99 if (buffering_state_ != BUFFERING_HAVE_NOTHING) {
80 buffering_state_ = BUFFERING_HAVE_NOTHING; 100 buffering_state_ = BUFFERING_HAVE_NOTHING;
81 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING); 101 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
82 } 102 }
83 received_end_of_stream_ = false; 103 received_end_of_stream_ = false;
84 rendered_end_of_stream_ = false; 104 rendered_end_of_stream_ = false;
85 105
106 if (use_new_video_renderering_path_)
107 algorithm_->Reset();
108
86 video_frame_stream_->Reset( 109 video_frame_stream_->Reset(
87 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone, 110 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone,
88 weak_factory_.GetWeakPtr())); 111 weak_factory_.GetWeakPtr()));
89 } 112 }
90 113
91 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) { 114 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) {
92 DVLOG(1) << __FUNCTION__ << "(" << timestamp.InMicroseconds() << ")"; 115 DVLOG(1) << __FUNCTION__ << "(" << timestamp.InMicroseconds() << ")";
93 DCHECK(task_runner_->BelongsToCurrentThread()); 116 DCHECK(task_runner_->BelongsToCurrentThread());
94 base::AutoLock auto_lock(lock_); 117 base::AutoLock auto_lock(lock_);
95 DCHECK_EQ(state_, kFlushed); 118 DCHECK_EQ(state_, kFlushed);
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
130 init_cb_ = BindToCurrentLoop(init_cb); 153 init_cb_ = BindToCurrentLoop(init_cb);
131 154
132 statistics_cb_ = statistics_cb; 155 statistics_cb_ = statistics_cb;
133 buffering_state_cb_ = buffering_state_cb; 156 buffering_state_cb_ = buffering_state_cb;
134 paint_cb_ = base::Bind(&VideoRendererSink::PaintFrameUsingOldRenderingPath, 157 paint_cb_ = base::Bind(&VideoRendererSink::PaintFrameUsingOldRenderingPath,
135 base::Unretained(sink_)); 158 base::Unretained(sink_));
136 ended_cb_ = ended_cb; 159 ended_cb_ = ended_cb;
137 error_cb_ = error_cb; 160 error_cb_ = error_cb;
138 wall_clock_time_cb_ = wall_clock_time_cb; 161 wall_clock_time_cb_ = wall_clock_time_cb;
139 state_ = kInitializing; 162 state_ = kInitializing;
163 render_first_frame_and_stop_ = false;
164 is_background_rendering_ = false;
165 time_progressing_ = false;
xhwang 2015/04/29 06:51:55 Can we DCHECK these three?
DaleCurtis 2015/04/30 03:49:37 Done.
140 166
141 video_frame_stream_->Initialize( 167 video_frame_stream_->Initialize(
142 stream, base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized, 168 stream, base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized,
143 weak_factory_.GetWeakPtr()), 169 weak_factory_.GetWeakPtr()),
144 set_decryptor_ready_cb, statistics_cb, waiting_for_decryption_key_cb); 170 set_decryptor_ready_cb, statistics_cb, waiting_for_decryption_key_cb);
145 } 171 }
146 172
147 scoped_refptr<VideoFrame> VideoRendererImpl::Render( 173 scoped_refptr<VideoFrame> VideoRendererImpl::Render(
148 base::TimeTicks deadline_min, 174 base::TimeTicks deadline_min,
149 base::TimeTicks deadline_max) { 175 base::TimeTicks deadline_max) {
150 // TODO(dalecurtis): Hook this up to the new VideoRendererAlgorithm. 176 base::AutoLock auto_lock(lock_);
151 NOTIMPLEMENTED(); 177 DCHECK(use_new_video_renderering_path_);
152 return nullptr; 178
179 size_t frames_dropped = 0;
180 scoped_refptr<VideoFrame> result =
181 algorithm_->Render(deadline_min, deadline_max, &frames_dropped);
182
183 // Due to how the |algorithm_| holds frames, this should never be null if
184 // we've had a proper startup sequence.
185 DCHECK(result);
186
187 // See if it's time to fire the ended callback.
188 const size_t effective_frames = MaybeFireEndedCallback();
189
190 // Declare HAVE_NOTHING if we have no more effective frames.
191 if (!effective_frames && !rendered_end_of_stream_ &&
192 buffering_state_ == BUFFERING_HAVE_ENOUGH) {
193 buffering_state_ = BUFFERING_HAVE_NOTHING;
194 task_runner_->PostTask(
195 FROM_HERE, base::Bind(buffering_state_cb_, BUFFERING_HAVE_NOTHING));
196 }
197
198 // As we resume from background rendering, don't count the initial batch of
199 // dropped frames since they are likely just dropped due to being too old.
200 if (!is_background_rendering_) {
201 frames_dropped_ += frames_dropped;
202 UpdateStatsAndWait_Locked(base::TimeDelta());
203 }
204 is_background_rendering_ = false;
205
206 // After painting the first frame, if playback hasn't started, we request that
207 // the sink be stopped. OnTimeStateChanged() will clear this flag if time
208 // starts before we get here and MaybeStopSinkAfterFirstPaint() will ignore
209 // this request if time starts before the call executes.
210 if (render_first_frame_and_stop_) {
211 task_runner_->PostTask(
212 FROM_HERE, base::Bind(&VideoRendererImpl::MaybeStopSinkAfterFirstPaint,
213 weak_factory_.GetWeakPtr()));
214 }
215
216 // Always post this task, it will acquire new frames if necessary, reset the
217 // background rendering timer, and more.
218 task_runner_->PostTask(FROM_HERE, base::Bind(&VideoRendererImpl::AttemptRead,
219 weak_factory_.GetWeakPtr()));
220
221 return result;
153 } 222 }
154 223
155 void VideoRendererImpl::OnFrameDropped() { 224 void VideoRendererImpl::OnFrameDropped() {
156 // TODO(dalecurtis): Hook this up to the new VideoRendererAlgorithm. 225 base::AutoLock auto_lock(lock_);
157 NOTIMPLEMENTED(); 226 DCHECK(use_new_video_renderering_path_);
227 algorithm_->OnLastFrameDropped();
228 }
229
230 void VideoRendererImpl::SetBackgroundRenderingForTesting(
231 bool enabled,
232 base::TimeDelta timeout) {
233 should_use_background_renderering_ = enabled;
234 background_rendering_timeout_ = timeout;
158 } 235 }
159 236
160 void VideoRendererImpl::CreateVideoThread() { 237 void VideoRendererImpl::CreateVideoThread() {
161 // This may fail and cause a crash if there are too many threads created in 238 // This may fail and cause a crash if there are too many threads created in
162 // the current process. See http://crbug.com/443291 239 // the current process. See http://crbug.com/443291
163 CHECK(base::PlatformThread::Create(0, this, &thread_)); 240 CHECK(base::PlatformThread::Create(0, this, &thread_));
164 241
165 #if defined(OS_WIN) 242 #if defined(OS_WIN)
166 // Bump up our priority so our sleeping is more accurate. 243 // Bump up our priority so our sleeping is more accurate.
167 // TODO(scherkus): find out if this is necessary, but it seems to help. 244 // TODO(scherkus): find out if this is necessary, but it seems to help.
(...skipping 11 matching lines...) Expand all
179 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); 256 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED);
180 return; 257 return;
181 } 258 }
182 259
183 // We're all good! Consider ourselves flushed. (ThreadMain() should never 260 // We're all good! Consider ourselves flushed. (ThreadMain() should never
184 // see us in the kUninitialized state). 261 // see us in the kUninitialized state).
185 // Since we had an initial Preroll(), we consider ourself flushed, because we 262 // Since we had an initial Preroll(), we consider ourself flushed, because we
186 // have not populated any buffers yet. 263 // have not populated any buffers yet.
187 state_ = kFlushed; 264 state_ = kFlushed;
188 265
189 CreateVideoThread(); 266 if (use_new_video_renderering_path_) {
267 algorithm_.reset(new VideoRendererAlgorithm(wall_clock_time_cb_));
268 if (!drop_frames_)
269 algorithm_->disable_frame_dropping();
270 } else {
271 CreateVideoThread();
272 }
190 273
191 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); 274 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
192 } 275 }
193 276
194 // PlatformThread::Delegate implementation. 277 // PlatformThread::Delegate implementation.
195 void VideoRendererImpl::ThreadMain() { 278 void VideoRendererImpl::ThreadMain() {
279 DCHECK(!use_new_video_renderering_path_);
196 base::PlatformThread::SetName("CrVideoRenderer"); 280 base::PlatformThread::SetName("CrVideoRenderer");
197 281
198 // The number of milliseconds to idle when we do not have anything to do. 282 // The number of milliseconds to idle when we do not have anything to do.
199 // Nothing special about the value, other than we're being more OS-friendly 283 // Nothing special about the value, other than we're being more OS-friendly
200 // than sleeping for 1 millisecond. 284 // than sleeping for 1 millisecond.
201 // 285 //
202 // TODO(scherkus): switch to pure event-driven frame timing instead of this 286 // TODO(scherkus): switch to pure event-driven frame timing instead of this
203 // kIdleTimeDelta business http://crbug.com/106874 287 // kIdleTimeDelta business http://crbug.com/106874
204 const base::TimeDelta kIdleTimeDelta = 288 const base::TimeDelta kIdleTimeDelta =
205 base::TimeDelta::FromMilliseconds(10); 289 base::TimeDelta::FromMilliseconds(10);
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
283 PaintNextReadyFrame_Locked(); 367 PaintNextReadyFrame_Locked();
284 } 368 }
285 } 369 }
286 370
287 void VideoRendererImpl::SetTickClockForTesting( 371 void VideoRendererImpl::SetTickClockForTesting(
288 scoped_ptr<base::TickClock> tick_clock) { 372 scoped_ptr<base::TickClock> tick_clock) {
289 tick_clock_.swap(tick_clock); 373 tick_clock_.swap(tick_clock);
290 } 374 }
291 375
292 void VideoRendererImpl::OnTimeStateChanged(bool time_progressing) { 376 void VideoRendererImpl::OnTimeStateChanged(bool time_progressing) {
293 // TODO(dalecurtis): Wire up to the VideoRendererSink once it's implemented. 377 DCHECK(task_runner_->BelongsToCurrentThread());
378 const bool was_progressing = time_progressing_;
379 time_progressing_ = time_progressing;
380
381 // WARNING: Do not attempt to use |lock_| here as this may be a reentrant call
382 // in response to callbacks firing above.
383
384 if (!use_new_video_renderering_path_ ||
385 was_progressing == time_progressing_ || render_first_frame_and_stop_) {
386 return;
387 }
388
389 if (time_progressing_) {
390 StartSink();
391 return;
392 }
393
394 StopSink();
294 } 395 }
295 396
296 void VideoRendererImpl::PaintNextReadyFrame_Locked() { 397 void VideoRendererImpl::PaintNextReadyFrame_Locked() {
398 DCHECK(!use_new_video_renderering_path_);
297 lock_.AssertAcquired(); 399 lock_.AssertAcquired();
298 400
299 scoped_refptr<VideoFrame> next_frame = ready_frames_.front(); 401 scoped_refptr<VideoFrame> next_frame = ready_frames_.front();
300 ready_frames_.pop_front(); 402 ready_frames_.pop_front();
301 frames_decoded_++; 403 frames_decoded_++;
302 404
303 last_media_time_ = wall_clock_time_cb_.Run(next_frame->timestamp()); 405 last_media_time_ = wall_clock_time_cb_.Run(next_frame->timestamp());
304 406
305 paint_cb_.Run(next_frame); 407 paint_cb_.Run(next_frame);
306 408
307 task_runner_->PostTask( 409 task_runner_->PostTask(
308 FROM_HERE, 410 FROM_HERE,
309 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr())); 411 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
310 } 412 }
311 413
312 void VideoRendererImpl::DropNextReadyFrame_Locked() { 414 void VideoRendererImpl::DropNextReadyFrame_Locked() {
415 DCHECK(!use_new_video_renderering_path_);
313 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped"); 416 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped");
314 417
315 lock_.AssertAcquired(); 418 lock_.AssertAcquired();
316 419
317 last_media_time_ = 420 last_media_time_ =
318 wall_clock_time_cb_.Run(ready_frames_.front()->timestamp()); 421 wall_clock_time_cb_.Run(ready_frames_.front()->timestamp());
319 422
320 ready_frames_.pop_front(); 423 ready_frames_.pop_front();
321 frames_decoded_++; 424 frames_decoded_++;
322 frames_dropped_++; 425 frames_dropped_++;
323 426
324 task_runner_->PostTask( 427 task_runner_->PostTask(
325 FROM_HERE, 428 FROM_HERE,
326 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr())); 429 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
327 } 430 }
328 431
329 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, 432 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
330 const scoped_refptr<VideoFrame>& frame) { 433 const scoped_refptr<VideoFrame>& frame) {
331 DCHECK(task_runner_->BelongsToCurrentThread()); 434 DCHECK(task_runner_->BelongsToCurrentThread());
332 base::AutoLock auto_lock(lock_); 435 bool start_sink = false;
333 DCHECK_NE(state_, kUninitialized); 436 {
334 DCHECK_NE(state_, kFlushed); 437 base::AutoLock auto_lock(lock_);
438 DCHECK_NE(state_, kUninitialized);
439 DCHECK_NE(state_, kFlushed);
335 440
336 CHECK(pending_read_); 441 CHECK(pending_read_);
337 pending_read_ = false; 442 pending_read_ = false;
338 443
339 if (status == VideoFrameStream::DECODE_ERROR || 444 if (status == VideoFrameStream::DECODE_ERROR ||
340 status == VideoFrameStream::DECRYPT_ERROR) { 445 status == VideoFrameStream::DECRYPT_ERROR) {
341 DCHECK(!frame.get()); 446 DCHECK(!frame.get());
342 PipelineStatus error = PIPELINE_ERROR_DECODE; 447 PipelineStatus error = PIPELINE_ERROR_DECODE;
343 if (status == VideoFrameStream::DECRYPT_ERROR) 448 if (status == VideoFrameStream::DECRYPT_ERROR)
344 error = PIPELINE_ERROR_DECRYPT; 449 error = PIPELINE_ERROR_DECRYPT;
345 task_runner_->PostTask(FROM_HERE, base::Bind(error_cb_, error)); 450 task_runner_->PostTask(FROM_HERE, base::Bind(error_cb_, error));
346 return; 451 return;
452 }
453
454 // Already-queued VideoFrameStream ReadCB's can fire after various state
455 // transitions have happened; in that case just drop those frames
456 // immediately.
457 if (state_ == kFlushing)
458 return;
459
460 DCHECK_EQ(state_, kPlaying);
461
462 // Can happen when demuxers are preparing for a new Seek().
463 if (!frame.get()) {
464 DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED);
465 return;
466 }
467
468 if (frame->end_of_stream()) {
469 DCHECK(!received_end_of_stream_);
470 received_end_of_stream_ = true;
471
472 // See if we can fire EOS immediately instead of waiting for Render() or
473 // BackgroundRender() to tick.
474 if (use_new_video_renderering_path_ && time_progressing_)
475 MaybeFireEndedCallback();
476 } else {
477 // Maintain the latest frame decoded so the correct frame is displayed
478 // after prerolling has completed.
479 if (frame->timestamp() <= start_timestamp_) {
480 if (use_new_video_renderering_path_)
481 algorithm_->Reset();
482 ready_frames_.clear();
483 }
484 AddReadyFrame_Locked(frame);
485 }
486
487 // Signal buffering state if we've met our conditions for having enough
488 // data.
489 if (buffering_state_ != BUFFERING_HAVE_ENOUGH && HaveEnoughData_Locked()) {
490 TransitionToHaveEnough_Locked();
491 start_sink = true;
492 render_first_frame_and_stop_ = true;
493 }
494
495 // BackgroundRender may not be ticking fast enough by itself to remove
496 // expired frames, so give it a boost here by ensuring we don't exit the
497 // decoding cycle too early.
498 if (is_background_rendering_) {
499 DCHECK(use_new_video_renderering_path_);
500 BackgroundRender_Locked();
501 }
502
503 // Always request more decoded video if we have capacity. This serves two
504 // purposes:
505 // 1) Prerolling while paused
506 // 2) Keeps decoding going if video rendering thread starts falling behind
507 AttemptRead_Locked();
347 } 508 }
348 509
349 // Already-queued VideoFrameStream ReadCB's can fire after various state 510 // If time is progressing, the sink has already been started; this may be true
350 // transitions have happened; in that case just drop those frames immediately. 511 // if we have previously underflowed, yet weren't stopped because of audio.
351 if (state_ == kFlushing) 512 if (use_new_video_renderering_path_ && !time_progressing_ && start_sink)
352 return; 513 StartSink();
353
354 DCHECK_EQ(state_, kPlaying);
355
356 // Can happen when demuxers are preparing for a new Seek().
357 if (!frame.get()) {
358 DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED);
359 return;
360 }
361
362 if (frame->end_of_stream()) {
363 DCHECK(!received_end_of_stream_);
364 received_end_of_stream_ = true;
365 } else {
366 // Maintain the latest frame decoded so the correct frame is displayed after
367 // prerolling has completed.
368 if (frame->timestamp() <= start_timestamp_)
369 ready_frames_.clear();
370 AddReadyFrame_Locked(frame);
371 }
372
373 // Signal buffering state if we've met our conditions for having enough data.
374 if (buffering_state_ != BUFFERING_HAVE_ENOUGH && HaveEnoughData_Locked())
375 TransitionToHaveEnough_Locked();
376
377 // Always request more decoded video if we have capacity. This serves two
378 // purposes:
379 // 1) Prerolling while paused
380 // 2) Keeps decoding going if video rendering thread starts falling behind
381 AttemptRead_Locked();
382 } 514 }
383 515
384 bool VideoRendererImpl::HaveEnoughData_Locked() { 516 bool VideoRendererImpl::HaveEnoughData_Locked() {
385 DCHECK_EQ(state_, kPlaying); 517 DCHECK_EQ(state_, kPlaying);
386 return received_end_of_stream_ || 518
387 !video_frame_stream_->CanReadWithoutStalling() || 519 if (received_end_of_stream_ || !video_frame_stream_->CanReadWithoutStalling())
388 ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames) || 520 return true;
389 (low_delay_ && ready_frames_.size() > 0); 521
522 if (HaveReachedBufferingCap())
523 return true;
524
525 if (!low_delay_)
526 return false;
527
528 return ready_frames_.size() > 0 ||
529 (use_new_video_renderering_path_ && algorithm_->frames_queued() > 0);
390 } 530 }
391 531
392 void VideoRendererImpl::TransitionToHaveEnough_Locked() { 532 void VideoRendererImpl::TransitionToHaveEnough_Locked() {
393 DCHECK(task_runner_->BelongsToCurrentThread()); 533 DCHECK(task_runner_->BelongsToCurrentThread());
394 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); 534 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
395 535
396 if (!ready_frames_.empty()) { 536 if (!ready_frames_.empty()) {
537 DCHECK(!use_new_video_renderering_path_);
397 // Because the clock might remain paused in for an undetermined amount 538 // Because the clock might remain paused in for an undetermined amount
398 // of time (e.g., seeking while paused), paint the first frame. 539 // of time (e.g., seeking while paused), paint the first frame.
399 PaintNextReadyFrame_Locked(); 540 PaintNextReadyFrame_Locked();
400 } 541 }
401 542
402 buffering_state_ = BUFFERING_HAVE_ENOUGH; 543 buffering_state_ = BUFFERING_HAVE_ENOUGH;
403 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH); 544 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH);
404 } 545 }
405 546
406 void VideoRendererImpl::AddReadyFrame_Locked( 547 void VideoRendererImpl::AddReadyFrame_Locked(
407 const scoped_refptr<VideoFrame>& frame) { 548 const scoped_refptr<VideoFrame>& frame) {
408 DCHECK(task_runner_->BelongsToCurrentThread()); 549 DCHECK(task_runner_->BelongsToCurrentThread());
409 lock_.AssertAcquired(); 550 lock_.AssertAcquired();
410 DCHECK(!frame->end_of_stream()); 551 DCHECK(!frame->end_of_stream());
411 552
553 if (use_new_video_renderering_path_) {
554 algorithm_->EnqueueFrame(frame);
555 return;
556 }
557
412 ready_frames_.push_back(frame); 558 ready_frames_.push_back(frame);
413 DCHECK_LE(ready_frames_.size(), 559 DCHECK_LE(ready_frames_.size(),
414 static_cast<size_t>(limits::kMaxVideoFrames)); 560 static_cast<size_t>(limits::kMaxVideoFrames));
415 561
416 // Avoid needlessly waking up |thread_| unless playing. 562 // Avoid needlessly waking up |thread_| unless playing.
417 if (state_ == kPlaying) 563 if (state_ == kPlaying)
418 frame_available_.Signal(); 564 frame_available_.Signal();
419 } 565 }
420 566
421 void VideoRendererImpl::AttemptRead() { 567 void VideoRendererImpl::AttemptRead() {
422 base::AutoLock auto_lock(lock_); 568 base::AutoLock auto_lock(lock_);
423 AttemptRead_Locked(); 569 AttemptRead_Locked();
424 } 570 }
425 571
426 void VideoRendererImpl::AttemptRead_Locked() { 572 void VideoRendererImpl::AttemptRead_Locked() {
427 DCHECK(task_runner_->BelongsToCurrentThread()); 573 DCHECK(task_runner_->BelongsToCurrentThread());
428 lock_.AssertAcquired(); 574 lock_.AssertAcquired();
429 575
430 if (pending_read_ || received_end_of_stream_ || 576 if (pending_read_ || received_end_of_stream_)
431 ready_frames_.size() == static_cast<size_t>(limits::kMaxVideoFrames)) {
432 return; 577 return;
433 } 578
579 if (use_new_video_renderering_path_ && time_progressing_)
580 RestartBackgroundRenderTimer();
xhwang 2015/04/29 06:51:54 It's a bit unclear to me when we should call this.
DaleCurtis 2015/04/30 03:49:37 Per offline discussion replaced with a repeating t
581
582 if (HaveReachedBufferingCap())
583 return;
434 584
435 switch (state_) { 585 switch (state_) {
436 case kPlaying: 586 case kPlaying:
437 pending_read_ = true; 587 pending_read_ = true;
438 video_frame_stream_->Read(base::Bind(&VideoRendererImpl::FrameReady, 588 video_frame_stream_->Read(base::Bind(&VideoRendererImpl::FrameReady,
439 weak_factory_.GetWeakPtr())); 589 weak_factory_.GetWeakPtr()));
440 return; 590 return;
441 591
442 case kUninitialized: 592 case kUninitialized:
443 case kInitializing: 593 case kInitializing:
444 case kFlushing: 594 case kFlushing:
445 case kFlushed: 595 case kFlushed:
446 return; 596 return;
447 } 597 }
448 } 598 }
449 599
450 void VideoRendererImpl::OnVideoFrameStreamResetDone() { 600 void VideoRendererImpl::OnVideoFrameStreamResetDone() {
451 base::AutoLock auto_lock(lock_); 601 base::AutoLock auto_lock(lock_);
452 DCHECK_EQ(kFlushing, state_); 602 DCHECK_EQ(kFlushing, state_);
453 DCHECK(!pending_read_); 603 DCHECK(!pending_read_);
454 DCHECK(ready_frames_.empty()); 604 DCHECK(ready_frames_.empty());
455 DCHECK(!received_end_of_stream_); 605 DCHECK(!received_end_of_stream_);
456 DCHECK(!rendered_end_of_stream_); 606 DCHECK(!rendered_end_of_stream_);
457 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); 607 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
458 608
459 state_ = kFlushed; 609 state_ = kFlushed;
460 latest_possible_paint_time_ = last_media_time_ = base::TimeTicks(); 610 latest_possible_paint_time_ = last_media_time_ = base::TimeTicks();
611
461 base::ResetAndReturn(&flush_cb_).Run(); 612 base::ResetAndReturn(&flush_cb_).Run();
462 } 613 }
463 614
464 void VideoRendererImpl::UpdateStatsAndWait_Locked( 615 void VideoRendererImpl::UpdateStatsAndWait_Locked(
465 base::TimeDelta wait_duration) { 616 base::TimeDelta wait_duration) {
466 lock_.AssertAcquired(); 617 lock_.AssertAcquired();
467 DCHECK_GE(frames_decoded_, 0); 618 DCHECK_GE(frames_decoded_, 0);
468 DCHECK_LE(frames_dropped_, frames_decoded_); 619 DCHECK_GE(frames_dropped_, 0);
469 620
470 if (frames_decoded_) { 621 if (frames_decoded_ || frames_dropped_) {
471 PipelineStatistics statistics; 622 PipelineStatistics statistics;
472 statistics.video_frames_decoded = frames_decoded_; 623 statistics.video_frames_decoded = frames_decoded_;
473 statistics.video_frames_dropped = frames_dropped_; 624 statistics.video_frames_dropped = frames_dropped_;
474 task_runner_->PostTask(FROM_HERE, base::Bind(statistics_cb_, statistics)); 625 task_runner_->PostTask(FROM_HERE, base::Bind(statistics_cb_, statistics));
475 626
476 frames_decoded_ = 0; 627 frames_decoded_ = 0;
477 frames_dropped_ = 0; 628 frames_dropped_ = 0;
478 } 629 }
479 630
480 frame_available_.TimedWait(wait_duration); 631 if (wait_duration > base::TimeDelta())
632 frame_available_.TimedWait(wait_duration);
633 }
634
635 void VideoRendererImpl::MaybeStopSinkAfterFirstPaint() {
636 DCHECK(task_runner_->BelongsToCurrentThread());
637 DCHECK(use_new_video_renderering_path_);
638
639 {
640 base::AutoLock auto_lock(lock_);
641 render_first_frame_and_stop_ = false;
642 }
643
644 if (!time_progressing_)
645 StopSink();
646 }
647
648 void VideoRendererImpl::RestartBackgroundRenderTimer() {
649 DCHECK(use_new_video_renderering_path_);
650 if (!drop_frames_ || !should_use_background_renderering_)
651 return;
652
653 // This controls the amount of time allowed between Render() callbacks before
654 // we assume they have timed out and it's necessary to start expiring
655 // unrendered frames.
656 background_rendering_timer_.Start(
657 FROM_HERE, background_rendering_timeout_,
658 base::Bind(&VideoRendererImpl::BackgroundRender,
659 weak_factory_.GetWeakPtr()));
660 }
661
662 void VideoRendererImpl::BackgroundRender() {
663 base::AutoLock auto_lock(lock_);
664 BackgroundRender_Locked();
665 }
666
667 void VideoRendererImpl::BackgroundRender_Locked() {
668 DCHECK(task_runner_->BelongsToCurrentThread());
669 lock_.AssertAcquired();
670
671 // If a Render() call never occurs after starting playback for the first frame
672 // we need to carry out the duties of Render() and stop the sink. We don't
673 // call MaybeStopSinkAfterFirstPaint() since it may need to mutate |sink_|,
674 // which can't be done under lock.
675 if (render_first_frame_and_stop_) {
676 task_runner_->PostTask(
677 FROM_HERE, base::Bind(&VideoRendererImpl::MaybeStopSinkAfterFirstPaint,
678 weak_factory_.GetWeakPtr()));
679
680 // MaybeStopSinkAfterFirstPaint isn't going to stop the sink if time is
681 // currently progressing, so only bail out if necessary.
682 if (!time_progressing_) {
683 RestartBackgroundRenderTimer();
684 return;
685 }
686 }
687
688 // First clear as many expired frames as we can.
689 algorithm_->RemoveExpiredFrames(tick_clock_->NowTicks());
690 is_background_rendering_ = true;
691
692 // See if we've run out of frames and need to fire the ended callback.
693 MaybeFireEndedCallback();
694 if (rendered_end_of_stream_)
695 return;
696
697 // Start queuing new frames and scheduled this process again otherwise.
698 AttemptRead_Locked();
699 UpdateStatsAndWait_Locked(base::TimeDelta());
700 RestartBackgroundRenderTimer();
701 }
702
703 bool VideoRendererImpl::HaveReachedBufferingCap() {
704 DCHECK(task_runner_->BelongsToCurrentThread());
705 if (use_new_video_renderering_path_) {
706 // When the display rate is less than the frame rate, the effective frames
707 // queued may be much smaller than the actual number of frames queued. Here
708 // we ensure that frames_queued() doesn't get excessive.
709 return algorithm_->EffectiveFramesQueued() >=
710 static_cast<size_t>(limits::kMaxVideoFrames) ||
711 algorithm_->frames_queued() >=
712 static_cast<size_t>(3 * limits::kMaxVideoFrames);
713 }
714
715 return ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames);
716 }
717
718 void VideoRendererImpl::StartSink() {
719 DCHECK(task_runner_->BelongsToCurrentThread());
720 sink_->Start(this);
721 RestartBackgroundRenderTimer();
722 }
723
724 void VideoRendererImpl::StopSink() {
725 DCHECK(task_runner_->BelongsToCurrentThread());
726 sink_->Stop();
727 background_rendering_timer_.Stop();
728 is_background_rendering_ = false;
729 }
730
731 size_t VideoRendererImpl::MaybeFireEndedCallback() {
732 // If there's only one frame in the video or Render() was never called, the
733 // algorithm will have one frame linger indefinitely. So in cases where the
734 // frame duration is unknown nad we've recieved EOS, fire it once we get down
xhwang 2015/04/29 06:51:54 s/nad/and/
DaleCurtis 2015/04/30 03:49:37 Done.
735 // to a single frame.
736 const size_t effective_frames = algorithm_->EffectiveFramesQueued();
737
738 if ((!effective_frames ||
739 (algorithm_->frames_queued() == 1u &&
740 algorithm_->average_frame_duration() == base::TimeDelta())) &&
741 received_end_of_stream_ && !rendered_end_of_stream_) {
742 rendered_end_of_stream_ = true;
743 task_runner_->PostTask(FROM_HERE, ended_cb_);
744 }
745
746 return effective_frames;
481 } 747 }
482 748
483 } // namespace media 749 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698