Port Chromium's blink_tests target to GN.
[chromium-blink-merge.git] / media / renderers / video_renderer_impl.cc
blob722438681254ddc4fa26a895a6f64e0540c15e68
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
3 // found in the LICENSE file.
5 #include "media/renderers/video_renderer_impl.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/callback_helpers.h"
10 #include "base/command_line.h"
11 #include "base/location.h"
12 #include "base/metrics/field_trial.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/strings/string_util.h"
15 #include "base/threading/platform_thread.h"
16 #include "base/time/default_tick_clock.h"
17 #include "base/trace_event/trace_event.h"
18 #include "media/base/bind_to_current_loop.h"
19 #include "media/base/buffers.h"
20 #include "media/base/limits.h"
21 #include "media/base/media_switches.h"
22 #include "media/base/pipeline.h"
23 #include "media/base/video_frame.h"
25 namespace media {
27 // TODO(dalecurtis): This experiment is temporary and should be removed once we
28 // have enough data to support the primacy of the new video rendering path; see
29 // http://crbug.com/485699 for details.
30 static bool ShouldUseVideoRenderingPath() {
31 // Note: It's important to query the field trial state first, to ensure that
32 // UMA reports the correct group.
33 const std::string group_name =
34 base::FieldTrialList::FindFullName("NewVideoRendererTrial");
35 const bool disabled_via_cli =
36 base::CommandLine::ForCurrentProcess()->HasSwitch(
37 switches::kDisableNewVideoRenderer);
38 return !disabled_via_cli && !StartsWithASCII(group_name, "Disabled", true);
41 VideoRendererImpl::VideoRendererImpl(
42 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
43 VideoRendererSink* sink,
44 ScopedVector<VideoDecoder> decoders,
45 bool drop_frames,
46 const scoped_refptr<MediaLog>& media_log)
47 : task_runner_(task_runner),
48 use_new_video_renderering_path_(ShouldUseVideoRenderingPath()),
49 sink_(sink),
50 sink_started_(false),
51 video_frame_stream_(
52 new VideoFrameStream(task_runner, decoders.Pass(), media_log)),
53 low_delay_(false),
54 received_end_of_stream_(false),
55 rendered_end_of_stream_(false),
56 frame_available_(&lock_),
57 state_(kUninitialized),
58 thread_(),
59 pending_read_(false),
60 drop_frames_(drop_frames),
61 buffering_state_(BUFFERING_HAVE_NOTHING),
62 frames_decoded_(0),
63 frames_dropped_(0),
64 is_shutting_down_(false),
65 tick_clock_(new base::DefaultTickClock()),
66 was_background_rendering_(false),
67 time_progressing_(false),
68 render_first_frame_and_stop_(false),
69 posted_maybe_stop_after_first_paint_(false),
70 weak_factory_(this) {
73 VideoRendererImpl::~VideoRendererImpl() {
74 DCHECK(task_runner_->BelongsToCurrentThread());
76 if (!use_new_video_renderering_path_) {
77 base::AutoLock auto_lock(lock_);
78 is_shutting_down_ = true;
79 frame_available_.Signal();
82 if (!thread_.is_null())
83 base::PlatformThread::Join(thread_);
85 if (!init_cb_.is_null())
86 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT);
88 if (!flush_cb_.is_null())
89 base::ResetAndReturn(&flush_cb_).Run();
91 if (use_new_video_renderering_path_ && sink_started_)
92 StopSink();
95 void VideoRendererImpl::Flush(const base::Closure& callback) {
96 DVLOG(1) << __FUNCTION__;
97 DCHECK(task_runner_->BelongsToCurrentThread());
99 if (use_new_video_renderering_path_ && sink_started_)
100 StopSink();
102 base::AutoLock auto_lock(lock_);
103 DCHECK_EQ(state_, kPlaying);
104 flush_cb_ = callback;
105 state_ = kFlushing;
107 // This is necessary if the |video_frame_stream_| has already seen an end of
108 // stream and needs to drain it before flushing it.
109 ready_frames_.clear();
110 if (buffering_state_ != BUFFERING_HAVE_NOTHING) {
111 buffering_state_ = BUFFERING_HAVE_NOTHING;
112 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
114 received_end_of_stream_ = false;
115 rendered_end_of_stream_ = false;
117 if (use_new_video_renderering_path_)
118 algorithm_->Reset();
120 video_frame_stream_->Reset(
121 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone,
122 weak_factory_.GetWeakPtr()));
125 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) {
126 DVLOG(1) << __FUNCTION__ << "(" << timestamp.InMicroseconds() << ")";
127 DCHECK(task_runner_->BelongsToCurrentThread());
128 base::AutoLock auto_lock(lock_);
129 DCHECK_EQ(state_, kFlushed);
130 DCHECK(!pending_read_);
131 DCHECK(ready_frames_.empty());
132 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
134 state_ = kPlaying;
135 start_timestamp_ = timestamp;
136 AttemptRead_Locked();
139 void VideoRendererImpl::Initialize(
140 DemuxerStream* stream,
141 const PipelineStatusCB& init_cb,
142 const SetDecryptorReadyCB& set_decryptor_ready_cb,
143 const StatisticsCB& statistics_cb,
144 const BufferingStateCB& buffering_state_cb,
145 const base::Closure& ended_cb,
146 const PipelineStatusCB& error_cb,
147 const WallClockTimeCB& wall_clock_time_cb,
148 const base::Closure& waiting_for_decryption_key_cb) {
149 DCHECK(task_runner_->BelongsToCurrentThread());
150 base::AutoLock auto_lock(lock_);
151 DCHECK(stream);
152 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO);
153 DCHECK(!init_cb.is_null());
154 DCHECK(!statistics_cb.is_null());
155 DCHECK(!buffering_state_cb.is_null());
156 DCHECK(!ended_cb.is_null());
157 DCHECK(!wall_clock_time_cb.is_null());
158 DCHECK_EQ(kUninitialized, state_);
159 DCHECK(!render_first_frame_and_stop_);
160 DCHECK(!posted_maybe_stop_after_first_paint_);
161 DCHECK(!was_background_rendering_);
162 DCHECK(!time_progressing_);
164 low_delay_ = (stream->liveness() == DemuxerStream::LIVENESS_LIVE);
166 // Always post |init_cb_| because |this| could be destroyed if initialization
167 // failed.
168 init_cb_ = BindToCurrentLoop(init_cb);
170 statistics_cb_ = statistics_cb;
171 buffering_state_cb_ = buffering_state_cb;
172 paint_cb_ = base::Bind(&VideoRendererSink::PaintFrameUsingOldRenderingPath,
173 base::Unretained(sink_));
174 ended_cb_ = ended_cb;
175 error_cb_ = error_cb;
176 wall_clock_time_cb_ = wall_clock_time_cb;
177 state_ = kInitializing;
179 video_frame_stream_->Initialize(
180 stream, base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized,
181 weak_factory_.GetWeakPtr()),
182 set_decryptor_ready_cb, statistics_cb, waiting_for_decryption_key_cb);
185 scoped_refptr<VideoFrame> VideoRendererImpl::Render(
186 base::TimeTicks deadline_min,
187 base::TimeTicks deadline_max,
188 bool background_rendering) {
189 base::AutoLock auto_lock(lock_);
190 DCHECK(use_new_video_renderering_path_);
191 DCHECK_EQ(state_, kPlaying);
193 size_t frames_dropped = 0;
194 scoped_refptr<VideoFrame> result =
195 algorithm_->Render(deadline_min, deadline_max, &frames_dropped);
197 // Due to how the |algorithm_| holds frames, this should never be null if
198 // we've had a proper startup sequence.
199 DCHECK(result);
201 // Declare HAVE_NOTHING if we reach a state where we can't progress playback
202 // any further. We don't want to do this if we've already done so, reached
203 // end of stream, or have frames available. We also don't want to do this in
204 // background rendering mode unless this isn't the first background render
205 // tick and we haven't seen any decoded frames since the last one.
206 const size_t effective_frames = MaybeFireEndedCallback();
207 if (buffering_state_ == BUFFERING_HAVE_ENOUGH && !received_end_of_stream_ &&
208 !effective_frames && (!background_rendering ||
209 (!frames_decoded_ && was_background_rendering_))) {
210 // Do not set |buffering_state_| here as the lock in FrameReady() may be
211 // held already and it fire the state changes in the wrong order.
212 task_runner_->PostTask(
213 FROM_HERE, base::Bind(&VideoRendererImpl::TransitionToHaveNothing,
214 weak_factory_.GetWeakPtr()));
217 // We don't count dropped frames in the background to avoid skewing the count
218 // and impacting JavaScript visible metrics used by web developers.
220 // Just after resuming from background rendering, we also don't count the
221 // dropped frames since they are likely just dropped due to being too old.
222 if (!background_rendering && !was_background_rendering_)
223 frames_dropped_ += frames_dropped;
224 UpdateStatsAndWait_Locked(base::TimeDelta());
225 was_background_rendering_ = background_rendering;
227 // After painting the first frame, if playback hasn't started, we post a
228 // delayed task to request that the sink be stopped. The task is delayed to
229 // give videos with autoplay time to start.
231 // OnTimeStateChanged() will clear this flag if time starts before we get here
232 // and MaybeStopSinkAfterFirstPaint() will ignore this request if time starts
233 // before the call executes.
234 if (render_first_frame_and_stop_ && !posted_maybe_stop_after_first_paint_) {
235 posted_maybe_stop_after_first_paint_ = true;
236 task_runner_->PostDelayedTask(
237 FROM_HERE, base::Bind(&VideoRendererImpl::MaybeStopSinkAfterFirstPaint,
238 weak_factory_.GetWeakPtr()),
239 base::TimeDelta::FromMilliseconds(250));
242 // Always post this task, it will acquire new frames if necessary and since it
243 // happens on another thread, even if we don't have room in the queue now, by
244 // the time it runs (may be delayed up to 50ms for complex decodes!) we might.
245 task_runner_->PostTask(FROM_HERE, base::Bind(&VideoRendererImpl::AttemptRead,
246 weak_factory_.GetWeakPtr()));
248 return result;
251 void VideoRendererImpl::OnFrameDropped() {
252 base::AutoLock auto_lock(lock_);
253 DCHECK(use_new_video_renderering_path_);
254 algorithm_->OnLastFrameDropped();
257 void VideoRendererImpl::CreateVideoThread() {
258 // This may fail and cause a crash if there are too many threads created in
259 // the current process. See http://crbug.com/443291
260 CHECK(base::PlatformThread::Create(0, this, &thread_));
262 #if defined(OS_WIN)
263 // Bump up our priority so our sleeping is more accurate.
264 // TODO(scherkus): find out if this is necessary, but it seems to help.
265 ::SetThreadPriority(thread_.platform_handle(), THREAD_PRIORITY_ABOVE_NORMAL);
266 #endif // defined(OS_WIN)
269 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) {
270 DCHECK(task_runner_->BelongsToCurrentThread());
271 base::AutoLock auto_lock(lock_);
272 DCHECK_EQ(state_, kInitializing);
274 if (!success) {
275 state_ = kUninitialized;
276 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED);
277 return;
280 // We're all good! Consider ourselves flushed. (ThreadMain() should never
281 // see us in the kUninitialized state).
282 // Since we had an initial Preroll(), we consider ourself flushed, because we
283 // have not populated any buffers yet.
284 state_ = kFlushed;
286 if (use_new_video_renderering_path_) {
287 algorithm_.reset(new VideoRendererAlgorithm(wall_clock_time_cb_));
288 if (!drop_frames_)
289 algorithm_->disable_frame_dropping();
290 } else {
291 CreateVideoThread();
294 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
297 // PlatformThread::Delegate implementation.
298 void VideoRendererImpl::ThreadMain() {
299 DCHECK(!use_new_video_renderering_path_);
300 base::PlatformThread::SetName("CrVideoRenderer");
302 // The number of milliseconds to idle when we do not have anything to do.
303 // Nothing special about the value, other than we're being more OS-friendly
304 // than sleeping for 1 millisecond.
306 // TODO(scherkus): switch to pure event-driven frame timing instead of this
307 // kIdleTimeDelta business http://crbug.com/106874
308 const base::TimeDelta kIdleTimeDelta =
309 base::TimeDelta::FromMilliseconds(10);
311 for (;;) {
312 base::AutoLock auto_lock(lock_);
314 // Thread exit condition.
315 if (is_shutting_down_)
316 return;
318 // Remain idle as long as we're not playing.
319 if (state_ != kPlaying || buffering_state_ != BUFFERING_HAVE_ENOUGH) {
320 UpdateStatsAndWait_Locked(kIdleTimeDelta);
321 continue;
324 base::TimeTicks now = tick_clock_->NowTicks();
326 // Remain idle until we have the next frame ready for rendering.
327 if (ready_frames_.empty()) {
328 base::TimeDelta wait_time = kIdleTimeDelta;
329 if (received_end_of_stream_) {
330 if (!rendered_end_of_stream_) {
331 rendered_end_of_stream_ = true;
332 task_runner_->PostTask(FROM_HERE, ended_cb_);
334 } else if (now >= latest_possible_paint_time_) {
335 // Declare HAVE_NOTHING if we don't have another frame by the time we
336 // are ready to paint the next one.
337 buffering_state_ = BUFFERING_HAVE_NOTHING;
338 task_runner_->PostTask(
339 FROM_HERE, base::Bind(buffering_state_cb_, BUFFERING_HAVE_NOTHING));
340 } else {
341 wait_time = std::min(kIdleTimeDelta, latest_possible_paint_time_ - now);
344 UpdateStatsAndWait_Locked(wait_time);
345 continue;
348 base::TimeTicks target_paint_time =
349 wall_clock_time_cb_.Run(ready_frames_.front()->timestamp());
351 // If media time has stopped, don't attempt to paint any more frames.
352 if (target_paint_time.is_null()) {
353 UpdateStatsAndWait_Locked(kIdleTimeDelta);
354 continue;
357 // Deadline is defined as the duration between this frame and the next
358 // frame, using the delta between this frame and the previous frame as the
359 // assumption for frame duration.
361 // TODO(scherkus): This can be vastly improved. Use a histogram to measure
362 // the accuracy of our frame timing code. http://crbug.com/149829
363 if (last_media_time_.is_null()) {
364 latest_possible_paint_time_ = now;
365 } else {
366 base::TimeDelta duration = target_paint_time - last_media_time_;
367 latest_possible_paint_time_ = target_paint_time + duration;
370 // Remain idle until we've reached our target paint window.
371 if (now < target_paint_time) {
372 UpdateStatsAndWait_Locked(
373 std::min(target_paint_time - now, kIdleTimeDelta));
374 continue;
377 if (ready_frames_.size() > 1 && now > latest_possible_paint_time_ &&
378 drop_frames_) {
379 DropNextReadyFrame_Locked();
380 continue;
383 // Congratulations! You've made it past the video frame timing gauntlet.
385 // At this point enough time has passed that the next frame that ready for
386 // rendering.
387 PaintNextReadyFrame_Locked();
391 void VideoRendererImpl::SetTickClockForTesting(
392 scoped_ptr<base::TickClock> tick_clock) {
393 tick_clock_.swap(tick_clock);
396 void VideoRendererImpl::OnTimeStateChanged(bool time_progressing) {
397 DCHECK(task_runner_->BelongsToCurrentThread());
398 time_progressing_ = time_progressing;
400 // WARNING: Do not attempt to use |lock_| here as this may be a reentrant call
401 // in response to callbacks firing above.
403 if (!use_new_video_renderering_path_ || sink_started_ == time_progressing_)
404 return;
406 if (time_progressing_) {
407 // If only an EOS frame came in after a seek, the renderer may not have
408 // received the ended event yet though we've posted it.
409 if (!rendered_end_of_stream_)
410 StartSink();
411 } else {
412 StopSink();
416 void VideoRendererImpl::PaintNextReadyFrame_Locked() {
417 DCHECK(!use_new_video_renderering_path_);
418 lock_.AssertAcquired();
420 scoped_refptr<VideoFrame> next_frame = ready_frames_.front();
421 ready_frames_.pop_front();
423 last_media_time_ = wall_clock_time_cb_.Run(next_frame->timestamp());
425 paint_cb_.Run(next_frame);
427 task_runner_->PostTask(
428 FROM_HERE,
429 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
432 void VideoRendererImpl::DropNextReadyFrame_Locked() {
433 DCHECK(!use_new_video_renderering_path_);
434 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped");
436 lock_.AssertAcquired();
438 last_media_time_ =
439 wall_clock_time_cb_.Run(ready_frames_.front()->timestamp());
441 ready_frames_.pop_front();
442 frames_dropped_++;
444 task_runner_->PostTask(
445 FROM_HERE,
446 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
449 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
450 const scoped_refptr<VideoFrame>& frame) {
451 DCHECK(task_runner_->BelongsToCurrentThread());
452 bool start_sink = false;
454 base::AutoLock auto_lock(lock_);
455 DCHECK_NE(state_, kUninitialized);
456 DCHECK_NE(state_, kFlushed);
458 CHECK(pending_read_);
459 pending_read_ = false;
461 if (status == VideoFrameStream::DECODE_ERROR ||
462 status == VideoFrameStream::DECRYPT_ERROR) {
463 DCHECK(!frame.get());
464 PipelineStatus error = PIPELINE_ERROR_DECODE;
465 if (status == VideoFrameStream::DECRYPT_ERROR)
466 error = PIPELINE_ERROR_DECRYPT;
467 task_runner_->PostTask(FROM_HERE, base::Bind(error_cb_, error));
468 return;
471 // Already-queued VideoFrameStream ReadCB's can fire after various state
472 // transitions have happened; in that case just drop those frames
473 // immediately.
474 if (state_ == kFlushing)
475 return;
477 DCHECK_EQ(state_, kPlaying);
479 // Can happen when demuxers are preparing for a new Seek().
480 if (!frame.get()) {
481 DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED);
482 return;
485 if (frame->end_of_stream()) {
486 DCHECK(!received_end_of_stream_);
487 received_end_of_stream_ = true;
489 // See if we can fire EOS immediately instead of waiting for Render().
490 if (use_new_video_renderering_path_)
491 MaybeFireEndedCallback();
492 } else {
493 // Maintain the latest frame decoded so the correct frame is displayed
494 // after prerolling has completed.
495 if (frame->timestamp() <= start_timestamp_) {
496 if (use_new_video_renderering_path_)
497 algorithm_->Reset();
498 ready_frames_.clear();
500 AddReadyFrame_Locked(frame);
503 // Signal buffering state if we've met our conditions for having enough
504 // data.
505 if (buffering_state_ != BUFFERING_HAVE_ENOUGH && HaveEnoughData_Locked()) {
506 TransitionToHaveEnough_Locked();
507 if (use_new_video_renderering_path_ && !sink_started_ &&
508 !rendered_end_of_stream_) {
509 start_sink = true;
510 render_first_frame_and_stop_ = true;
511 posted_maybe_stop_after_first_paint_ = false;
515 // Background rendering updates may not be ticking fast enough by itself to
516 // remove expired frames, so give it a boost here by ensuring we don't exit
517 // the decoding cycle too early.
518 if (was_background_rendering_) {
519 DCHECK(use_new_video_renderering_path_);
520 algorithm_->RemoveExpiredFrames(tick_clock_->NowTicks());
523 // Always request more decoded video if we have capacity. This serves two
524 // purposes:
525 // 1) Prerolling while paused
526 // 2) Keeps decoding going if video rendering thread starts falling behind
527 AttemptRead_Locked();
530 // If time is progressing, the sink has already been started; this may be true
531 // if we have previously underflowed, yet weren't stopped because of audio.
532 if (use_new_video_renderering_path_ && start_sink) {
533 DCHECK(!sink_started_);
534 StartSink();
538 bool VideoRendererImpl::HaveEnoughData_Locked() {
539 DCHECK_EQ(state_, kPlaying);
541 if (received_end_of_stream_ || !video_frame_stream_->CanReadWithoutStalling())
542 return true;
544 if (HaveReachedBufferingCap())
545 return true;
547 if (use_new_video_renderering_path_ && was_background_rendering_ &&
548 frames_decoded_) {
549 return true;
552 if (!low_delay_)
553 return false;
555 return ready_frames_.size() > 0 ||
556 (use_new_video_renderering_path_ && algorithm_->frames_queued() > 0);
559 void VideoRendererImpl::TransitionToHaveEnough_Locked() {
560 DCHECK(task_runner_->BelongsToCurrentThread());
561 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
563 if (!ready_frames_.empty()) {
564 DCHECK(!use_new_video_renderering_path_);
565 // Because the clock might remain paused in for an undetermined amount
566 // of time (e.g., seeking while paused), paint the first frame.
567 PaintNextReadyFrame_Locked();
570 buffering_state_ = BUFFERING_HAVE_ENOUGH;
571 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH);
574 void VideoRendererImpl::TransitionToHaveNothing() {
575 DCHECK(task_runner_->BelongsToCurrentThread());
577 base::AutoLock auto_lock(lock_);
578 if (buffering_state_ != BUFFERING_HAVE_ENOUGH || HaveEnoughData_Locked())
579 return;
581 buffering_state_ = BUFFERING_HAVE_NOTHING;
582 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
585 void VideoRendererImpl::AddReadyFrame_Locked(
586 const scoped_refptr<VideoFrame>& frame) {
587 DCHECK(task_runner_->BelongsToCurrentThread());
588 lock_.AssertAcquired();
589 DCHECK(!frame->end_of_stream());
591 frames_decoded_++;
593 if (use_new_video_renderering_path_) {
594 algorithm_->EnqueueFrame(frame);
595 return;
598 ready_frames_.push_back(frame);
599 DCHECK_LE(ready_frames_.size(),
600 static_cast<size_t>(limits::kMaxVideoFrames));
602 // Avoid needlessly waking up |thread_| unless playing.
603 if (state_ == kPlaying)
604 frame_available_.Signal();
607 void VideoRendererImpl::AttemptRead() {
608 base::AutoLock auto_lock(lock_);
609 AttemptRead_Locked();
612 void VideoRendererImpl::AttemptRead_Locked() {
613 DCHECK(task_runner_->BelongsToCurrentThread());
614 lock_.AssertAcquired();
616 if (pending_read_ || received_end_of_stream_)
617 return;
619 if (HaveReachedBufferingCap())
620 return;
622 switch (state_) {
623 case kPlaying:
624 pending_read_ = true;
625 video_frame_stream_->Read(base::Bind(&VideoRendererImpl::FrameReady,
626 weak_factory_.GetWeakPtr()));
627 return;
629 case kUninitialized:
630 case kInitializing:
631 case kFlushing:
632 case kFlushed:
633 return;
637 void VideoRendererImpl::OnVideoFrameStreamResetDone() {
638 base::AutoLock auto_lock(lock_);
639 DCHECK_EQ(kFlushing, state_);
640 DCHECK(!pending_read_);
641 DCHECK(ready_frames_.empty());
642 DCHECK(!received_end_of_stream_);
643 DCHECK(!rendered_end_of_stream_);
644 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
646 state_ = kFlushed;
647 latest_possible_paint_time_ = last_media_time_ = base::TimeTicks();
648 base::ResetAndReturn(&flush_cb_).Run();
651 void VideoRendererImpl::UpdateStatsAndWait_Locked(
652 base::TimeDelta wait_duration) {
653 lock_.AssertAcquired();
654 DCHECK_GE(frames_decoded_, 0);
655 DCHECK_GE(frames_dropped_, 0);
657 if (frames_decoded_ || frames_dropped_) {
658 PipelineStatistics statistics;
659 statistics.video_frames_decoded = frames_decoded_;
660 statistics.video_frames_dropped = frames_dropped_;
661 task_runner_->PostTask(FROM_HERE, base::Bind(statistics_cb_, statistics));
663 frames_decoded_ = 0;
664 frames_dropped_ = 0;
667 if (wait_duration > base::TimeDelta())
668 frame_available_.TimedWait(wait_duration);
671 void VideoRendererImpl::MaybeStopSinkAfterFirstPaint() {
672 DCHECK(task_runner_->BelongsToCurrentThread());
673 DCHECK(use_new_video_renderering_path_);
676 base::AutoLock auto_lock(lock_);
677 render_first_frame_and_stop_ = false;
680 if (!time_progressing_ && sink_started_)
681 StopSink();
684 bool VideoRendererImpl::HaveReachedBufferingCap() {
685 DCHECK(task_runner_->BelongsToCurrentThread());
686 const size_t kMaxVideoFrames = limits::kMaxVideoFrames;
688 if (use_new_video_renderering_path_) {
689 // When the display rate is less than the frame rate, the effective frames
690 // queued may be much smaller than the actual number of frames queued. Here
691 // we ensure that frames_queued() doesn't get excessive.
692 return algorithm_->EffectiveFramesQueued() >= kMaxVideoFrames ||
693 algorithm_->frames_queued() >= 3 * kMaxVideoFrames;
696 return ready_frames_.size() >= kMaxVideoFrames;
699 void VideoRendererImpl::StartSink() {
700 DCHECK(task_runner_->BelongsToCurrentThread());
701 sink_->Start(this);
702 sink_started_ = true;
703 was_background_rendering_ = false;
706 void VideoRendererImpl::StopSink() {
707 DCHECK(task_runner_->BelongsToCurrentThread());
708 sink_->Stop();
709 sink_started_ = false;
710 was_background_rendering_ = false;
713 size_t VideoRendererImpl::MaybeFireEndedCallback() {
714 // If there's only one frame in the video or Render() was never called, the
715 // algorithm will have one frame linger indefinitely. So in cases where the
716 // frame duration is unknown and we've received EOS, fire it once we get down
717 // to a single frame.
718 const size_t effective_frames = algorithm_->EffectiveFramesQueued();
720 // Don't fire ended if we haven't received EOS or have already done so.
721 if (!received_end_of_stream_ || rendered_end_of_stream_)
722 return effective_frames;
724 // Don't fire ended if time isn't moving and we have frames.
725 if (!time_progressing_ && algorithm_->frames_queued())
726 return effective_frames;
728 // Fire ended if we have no more effective frames or only ever had one frame.
729 if (!effective_frames ||
730 (algorithm_->frames_queued() == 1u &&
731 algorithm_->average_frame_duration() == base::TimeDelta())) {
732 rendered_end_of_stream_ = true;
733 task_runner_->PostTask(FROM_HERE, ended_cb_);
736 return effective_frames;
739 } // namespace media