Switch GetWallClockTime to using vectors for input and output.
[chromium-blink-merge.git] / media / renderers / video_renderer_impl.cc
blob0e3e28e5bd608ddf84dc40160596b84c3048e60e
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 TimeSource::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 ConvertMediaTimestamp(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_ = ConvertMediaTimestamp(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_ = ConvertMediaTimestamp(ready_frames_.front()->timestamp());
440 ready_frames_.pop_front();
441 frames_dropped_++;
443 task_runner_->PostTask(
444 FROM_HERE,
445 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
448 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
449 const scoped_refptr<VideoFrame>& frame) {
450 DCHECK(task_runner_->BelongsToCurrentThread());
451 bool start_sink = false;
453 base::AutoLock auto_lock(lock_);
454 DCHECK_NE(state_, kUninitialized);
455 DCHECK_NE(state_, kFlushed);
457 CHECK(pending_read_);
458 pending_read_ = false;
460 if (status == VideoFrameStream::DECODE_ERROR ||
461 status == VideoFrameStream::DECRYPT_ERROR) {
462 DCHECK(!frame.get());
463 PipelineStatus error = PIPELINE_ERROR_DECODE;
464 if (status == VideoFrameStream::DECRYPT_ERROR)
465 error = PIPELINE_ERROR_DECRYPT;
466 task_runner_->PostTask(FROM_HERE, base::Bind(error_cb_, error));
467 return;
470 // Already-queued VideoFrameStream ReadCB's can fire after various state
471 // transitions have happened; in that case just drop those frames
472 // immediately.
473 if (state_ == kFlushing)
474 return;
476 DCHECK_EQ(state_, kPlaying);
478 // Can happen when demuxers are preparing for a new Seek().
479 if (!frame.get()) {
480 DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED);
481 return;
484 if (frame->end_of_stream()) {
485 DCHECK(!received_end_of_stream_);
486 received_end_of_stream_ = true;
488 // See if we can fire EOS immediately instead of waiting for Render().
489 if (use_new_video_renderering_path_)
490 MaybeFireEndedCallback();
491 } else {
492 // Maintain the latest frame decoded so the correct frame is displayed
493 // after prerolling has completed.
494 if (frame->timestamp() <= start_timestamp_) {
495 if (use_new_video_renderering_path_)
496 algorithm_->Reset();
497 ready_frames_.clear();
499 AddReadyFrame_Locked(frame);
502 // Signal buffering state if we've met our conditions for having enough
503 // data.
504 if (buffering_state_ != BUFFERING_HAVE_ENOUGH && HaveEnoughData_Locked()) {
505 TransitionToHaveEnough_Locked();
506 if (use_new_video_renderering_path_ && !sink_started_ &&
507 !rendered_end_of_stream_) {
508 start_sink = true;
509 render_first_frame_and_stop_ = true;
510 posted_maybe_stop_after_first_paint_ = false;
514 // Background rendering updates may not be ticking fast enough by itself to
515 // remove expired frames, so give it a boost here by ensuring we don't exit
516 // the decoding cycle too early.
517 if (was_background_rendering_) {
518 DCHECK(use_new_video_renderering_path_);
519 algorithm_->RemoveExpiredFrames(tick_clock_->NowTicks());
522 // Always request more decoded video if we have capacity. This serves two
523 // purposes:
524 // 1) Prerolling while paused
525 // 2) Keeps decoding going if video rendering thread starts falling behind
526 AttemptRead_Locked();
529 // If time is progressing, the sink has already been started; this may be true
530 // if we have previously underflowed, yet weren't stopped because of audio.
531 if (use_new_video_renderering_path_ && start_sink) {
532 DCHECK(!sink_started_);
533 StartSink();
537 bool VideoRendererImpl::HaveEnoughData_Locked() {
538 DCHECK_EQ(state_, kPlaying);
540 if (received_end_of_stream_ || !video_frame_stream_->CanReadWithoutStalling())
541 return true;
543 if (HaveReachedBufferingCap())
544 return true;
546 if (use_new_video_renderering_path_ && was_background_rendering_ &&
547 frames_decoded_) {
548 return true;
551 if (!low_delay_)
552 return false;
554 return ready_frames_.size() > 0 ||
555 (use_new_video_renderering_path_ && algorithm_->frames_queued() > 0);
558 void VideoRendererImpl::TransitionToHaveEnough_Locked() {
559 DCHECK(task_runner_->BelongsToCurrentThread());
560 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
562 if (!ready_frames_.empty()) {
563 DCHECK(!use_new_video_renderering_path_);
564 // Because the clock might remain paused in for an undetermined amount
565 // of time (e.g., seeking while paused), paint the first frame.
566 PaintNextReadyFrame_Locked();
569 buffering_state_ = BUFFERING_HAVE_ENOUGH;
570 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH);
573 void VideoRendererImpl::TransitionToHaveNothing() {
574 DCHECK(task_runner_->BelongsToCurrentThread());
576 base::AutoLock auto_lock(lock_);
577 if (buffering_state_ != BUFFERING_HAVE_ENOUGH || HaveEnoughData_Locked())
578 return;
580 buffering_state_ = BUFFERING_HAVE_NOTHING;
581 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
584 void VideoRendererImpl::AddReadyFrame_Locked(
585 const scoped_refptr<VideoFrame>& frame) {
586 DCHECK(task_runner_->BelongsToCurrentThread());
587 lock_.AssertAcquired();
588 DCHECK(!frame->end_of_stream());
590 frames_decoded_++;
592 if (use_new_video_renderering_path_) {
593 algorithm_->EnqueueFrame(frame);
594 return;
597 ready_frames_.push_back(frame);
598 DCHECK_LE(ready_frames_.size(),
599 static_cast<size_t>(limits::kMaxVideoFrames));
601 // Avoid needlessly waking up |thread_| unless playing.
602 if (state_ == kPlaying)
603 frame_available_.Signal();
606 void VideoRendererImpl::AttemptRead() {
607 base::AutoLock auto_lock(lock_);
608 AttemptRead_Locked();
611 void VideoRendererImpl::AttemptRead_Locked() {
612 DCHECK(task_runner_->BelongsToCurrentThread());
613 lock_.AssertAcquired();
615 if (pending_read_ || received_end_of_stream_)
616 return;
618 if (HaveReachedBufferingCap())
619 return;
621 switch (state_) {
622 case kPlaying:
623 pending_read_ = true;
624 video_frame_stream_->Read(base::Bind(&VideoRendererImpl::FrameReady,
625 weak_factory_.GetWeakPtr()));
626 return;
628 case kUninitialized:
629 case kInitializing:
630 case kFlushing:
631 case kFlushed:
632 return;
636 void VideoRendererImpl::OnVideoFrameStreamResetDone() {
637 base::AutoLock auto_lock(lock_);
638 DCHECK_EQ(kFlushing, state_);
639 DCHECK(!pending_read_);
640 DCHECK(ready_frames_.empty());
641 DCHECK(!received_end_of_stream_);
642 DCHECK(!rendered_end_of_stream_);
643 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
645 state_ = kFlushed;
646 latest_possible_paint_time_ = last_media_time_ = base::TimeTicks();
647 base::ResetAndReturn(&flush_cb_).Run();
650 void VideoRendererImpl::UpdateStatsAndWait_Locked(
651 base::TimeDelta wait_duration) {
652 lock_.AssertAcquired();
653 DCHECK_GE(frames_decoded_, 0);
654 DCHECK_GE(frames_dropped_, 0);
656 if (frames_decoded_ || frames_dropped_) {
657 PipelineStatistics statistics;
658 statistics.video_frames_decoded = frames_decoded_;
659 statistics.video_frames_dropped = frames_dropped_;
660 task_runner_->PostTask(FROM_HERE, base::Bind(statistics_cb_, statistics));
662 frames_decoded_ = 0;
663 frames_dropped_ = 0;
666 if (wait_duration > base::TimeDelta())
667 frame_available_.TimedWait(wait_duration);
670 void VideoRendererImpl::MaybeStopSinkAfterFirstPaint() {
671 DCHECK(task_runner_->BelongsToCurrentThread());
672 DCHECK(use_new_video_renderering_path_);
675 base::AutoLock auto_lock(lock_);
676 render_first_frame_and_stop_ = false;
679 if (!time_progressing_ && sink_started_)
680 StopSink();
683 bool VideoRendererImpl::HaveReachedBufferingCap() {
684 DCHECK(task_runner_->BelongsToCurrentThread());
685 const size_t kMaxVideoFrames = limits::kMaxVideoFrames;
687 if (use_new_video_renderering_path_) {
688 // When the display rate is less than the frame rate, the effective frames
689 // queued may be much smaller than the actual number of frames queued. Here
690 // we ensure that frames_queued() doesn't get excessive.
691 return algorithm_->EffectiveFramesQueued() >= kMaxVideoFrames ||
692 algorithm_->frames_queued() >= 3 * kMaxVideoFrames;
695 return ready_frames_.size() >= kMaxVideoFrames;
698 void VideoRendererImpl::StartSink() {
699 DCHECK(task_runner_->BelongsToCurrentThread());
700 sink_->Start(this);
701 sink_started_ = true;
702 was_background_rendering_ = false;
705 void VideoRendererImpl::StopSink() {
706 DCHECK(task_runner_->BelongsToCurrentThread());
707 sink_->Stop();
708 sink_started_ = false;
709 was_background_rendering_ = false;
712 size_t VideoRendererImpl::MaybeFireEndedCallback() {
713 // If there's only one frame in the video or Render() was never called, the
714 // algorithm will have one frame linger indefinitely. So in cases where the
715 // frame duration is unknown and we've received EOS, fire it once we get down
716 // to a single frame.
717 const size_t effective_frames = algorithm_->EffectiveFramesQueued();
719 // Don't fire ended if we haven't received EOS or have already done so.
720 if (!received_end_of_stream_ || rendered_end_of_stream_)
721 return effective_frames;
723 // Don't fire ended if time isn't moving and we have frames.
724 if (!time_progressing_ && algorithm_->frames_queued())
725 return effective_frames;
727 // Fire ended if we have no more effective frames or only ever had one frame.
728 if (!effective_frames ||
729 (algorithm_->frames_queued() == 1u &&
730 algorithm_->average_frame_duration() == base::TimeDelta())) {
731 rendered_end_of_stream_ = true;
732 task_runner_->PostTask(FROM_HERE, ended_cb_);
735 return effective_frames;
738 base::TimeTicks VideoRendererImpl::ConvertMediaTimestamp(
739 base::TimeDelta media_time) {
740 std::vector<base::TimeDelta> media_times(1, media_time);
741 std::vector<base::TimeTicks> wall_clock_times;
742 if (!wall_clock_time_cb_.Run(media_times, &wall_clock_times))
743 return base::TimeTicks();
744 return wall_clock_times[0];
747 } // namespace media