Returning scoped_ptr instead of raw pointer in QuicInfoToValue in net/
[chromium-blink-merge.git] / media / renderers / video_renderer_impl.cc
blob507599d890ba1613e62b04d0f23742dd4ff5d761
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/time/default_tick_clock.h"
16 #include "base/trace_event/trace_event.h"
17 #include "media/base/bind_to_current_loop.h"
18 #include "media/base/buffers.h"
19 #include "media/base/limits.h"
20 #include "media/base/media_switches.h"
21 #include "media/base/pipeline.h"
22 #include "media/base/video_frame.h"
23 #include "media/renderers/gpu_video_accelerator_factories.h"
24 #include "media/video/gpu_memory_buffer_video_frame_pool.h"
26 namespace media {
28 // TODO(dalecurtis): This experiment is temporary and should be removed once we
29 // have enough data to support the primacy of the new video rendering path; see
30 // http://crbug.com/485699 for details.
31 static bool ShouldUseVideoRenderingPath() {
32 // Note: It's important to query the field trial state first, to ensure that
33 // UMA reports the correct group.
34 const std::string group_name =
35 base::FieldTrialList::FindFullName("NewVideoRendererTrial");
36 const bool disabled_via_cli =
37 base::CommandLine::ForCurrentProcess()->HasSwitch(
38 switches::kDisableNewVideoRenderer);
39 return !disabled_via_cli && !StartsWithASCII(group_name, "Disabled", true);
42 VideoRendererImpl::VideoRendererImpl(
43 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
44 VideoRendererSink* sink,
45 ScopedVector<VideoDecoder> decoders,
46 bool drop_frames,
47 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
48 const scoped_refptr<MediaLog>& media_log)
49 : task_runner_(task_runner),
50 use_new_video_renderering_path_(ShouldUseVideoRenderingPath()),
51 sink_(sink),
52 sink_started_(false),
53 video_frame_stream_(
54 new VideoFrameStream(task_runner, decoders.Pass(), media_log)),
55 gpu_memory_buffer_pool_(
56 new GpuMemoryBufferVideoFramePool(task_runner, gpu_factories)),
57 low_delay_(false),
58 received_end_of_stream_(false),
59 rendered_end_of_stream_(false),
60 frame_available_(&lock_),
61 state_(kUninitialized),
62 thread_(),
63 pending_read_(false),
64 drop_frames_(drop_frames),
65 buffering_state_(BUFFERING_HAVE_NOTHING),
66 frames_decoded_(0),
67 frames_dropped_(0),
68 is_shutting_down_(false),
69 tick_clock_(new base::DefaultTickClock()),
70 was_background_rendering_(false),
71 time_progressing_(false),
72 render_first_frame_and_stop_(false),
73 posted_maybe_stop_after_first_paint_(false),
74 weak_factory_(this) {
77 VideoRendererImpl::~VideoRendererImpl() {
78 DCHECK(task_runner_->BelongsToCurrentThread());
80 if (!use_new_video_renderering_path_) {
81 base::AutoLock auto_lock(lock_);
82 is_shutting_down_ = true;
83 frame_available_.Signal();
86 if (!thread_.is_null())
87 base::PlatformThread::Join(thread_);
89 if (!init_cb_.is_null())
90 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT);
92 if (!flush_cb_.is_null())
93 base::ResetAndReturn(&flush_cb_).Run();
95 if (use_new_video_renderering_path_ && sink_started_)
96 StopSink();
99 void VideoRendererImpl::Flush(const base::Closure& callback) {
100 DVLOG(1) << __FUNCTION__;
101 DCHECK(task_runner_->BelongsToCurrentThread());
103 if (use_new_video_renderering_path_ && sink_started_)
104 StopSink();
106 base::AutoLock auto_lock(lock_);
107 DCHECK_EQ(state_, kPlaying);
108 flush_cb_ = callback;
109 state_ = kFlushing;
111 // This is necessary if the |video_frame_stream_| has already seen an end of
112 // stream and needs to drain it before flushing it.
113 ready_frames_.clear();
114 if (buffering_state_ != BUFFERING_HAVE_NOTHING) {
115 buffering_state_ = BUFFERING_HAVE_NOTHING;
116 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
118 received_end_of_stream_ = false;
119 rendered_end_of_stream_ = false;
121 if (use_new_video_renderering_path_)
122 algorithm_->Reset();
124 video_frame_stream_->Reset(
125 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone,
126 weak_factory_.GetWeakPtr()));
129 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) {
130 DVLOG(1) << __FUNCTION__ << "(" << timestamp.InMicroseconds() << ")";
131 DCHECK(task_runner_->BelongsToCurrentThread());
132 base::AutoLock auto_lock(lock_);
133 DCHECK_EQ(state_, kFlushed);
134 DCHECK(!pending_read_);
135 DCHECK(ready_frames_.empty());
136 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
138 state_ = kPlaying;
139 start_timestamp_ = timestamp;
140 AttemptRead_Locked();
143 void VideoRendererImpl::Initialize(
144 DemuxerStream* stream,
145 const PipelineStatusCB& init_cb,
146 const SetDecryptorReadyCB& set_decryptor_ready_cb,
147 const StatisticsCB& statistics_cb,
148 const BufferingStateCB& buffering_state_cb,
149 const base::Closure& ended_cb,
150 const PipelineStatusCB& error_cb,
151 const TimeSource::WallClockTimeCB& wall_clock_time_cb,
152 const base::Closure& waiting_for_decryption_key_cb) {
153 DCHECK(task_runner_->BelongsToCurrentThread());
154 base::AutoLock auto_lock(lock_);
155 DCHECK(stream);
156 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO);
157 DCHECK(!init_cb.is_null());
158 DCHECK(!statistics_cb.is_null());
159 DCHECK(!buffering_state_cb.is_null());
160 DCHECK(!ended_cb.is_null());
161 DCHECK(!wall_clock_time_cb.is_null());
162 DCHECK_EQ(kUninitialized, state_);
163 DCHECK(!render_first_frame_and_stop_);
164 DCHECK(!posted_maybe_stop_after_first_paint_);
165 DCHECK(!was_background_rendering_);
166 DCHECK(!time_progressing_);
168 low_delay_ = (stream->liveness() == DemuxerStream::LIVENESS_LIVE);
170 // Always post |init_cb_| because |this| could be destroyed if initialization
171 // failed.
172 init_cb_ = BindToCurrentLoop(init_cb);
174 statistics_cb_ = statistics_cb;
175 buffering_state_cb_ = buffering_state_cb;
176 paint_cb_ = base::Bind(&VideoRendererSink::PaintFrameUsingOldRenderingPath,
177 base::Unretained(sink_));
178 ended_cb_ = ended_cb;
179 error_cb_ = error_cb;
180 wall_clock_time_cb_ = wall_clock_time_cb;
181 state_ = kInitializing;
183 video_frame_stream_->Initialize(
184 stream, base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized,
185 weak_factory_.GetWeakPtr()),
186 set_decryptor_ready_cb, statistics_cb, waiting_for_decryption_key_cb);
189 scoped_refptr<VideoFrame> VideoRendererImpl::Render(
190 base::TimeTicks deadline_min,
191 base::TimeTicks deadline_max,
192 bool background_rendering) {
193 base::AutoLock auto_lock(lock_);
194 DCHECK(use_new_video_renderering_path_);
195 DCHECK_EQ(state_, kPlaying);
197 size_t frames_dropped = 0;
198 scoped_refptr<VideoFrame> result =
199 algorithm_->Render(deadline_min, deadline_max, &frames_dropped);
201 // Due to how the |algorithm_| holds frames, this should never be null if
202 // we've had a proper startup sequence.
203 DCHECK(result);
205 // Declare HAVE_NOTHING if we reach a state where we can't progress playback
206 // any further. We don't want to do this if we've already done so, reached
207 // end of stream, or have frames available. We also don't want to do this in
208 // background rendering mode unless this isn't the first background render
209 // tick and we haven't seen any decoded frames since the last one.
210 const size_t effective_frames = MaybeFireEndedCallback();
211 if (buffering_state_ == BUFFERING_HAVE_ENOUGH && !received_end_of_stream_ &&
212 !effective_frames && (!background_rendering ||
213 (!frames_decoded_ && was_background_rendering_))) {
214 // Do not set |buffering_state_| here as the lock in FrameReady() may be
215 // held already and it fire the state changes in the wrong order.
216 task_runner_->PostTask(
217 FROM_HERE, base::Bind(&VideoRendererImpl::TransitionToHaveNothing,
218 weak_factory_.GetWeakPtr()));
221 // We don't count dropped frames in the background to avoid skewing the count
222 // and impacting JavaScript visible metrics used by web developers.
224 // Just after resuming from background rendering, we also don't count the
225 // dropped frames since they are likely just dropped due to being too old.
226 if (!background_rendering && !was_background_rendering_)
227 frames_dropped_ += frames_dropped;
228 UpdateStatsAndWait_Locked(base::TimeDelta());
229 was_background_rendering_ = background_rendering;
231 // After painting the first frame, if playback hasn't started, we post a
232 // delayed task to request that the sink be stopped. The task is delayed to
233 // give videos with autoplay time to start.
235 // OnTimeStateChanged() will clear this flag if time starts before we get here
236 // and MaybeStopSinkAfterFirstPaint() will ignore this request if time starts
237 // before the call executes.
238 if (render_first_frame_and_stop_ && !posted_maybe_stop_after_first_paint_) {
239 posted_maybe_stop_after_first_paint_ = true;
240 task_runner_->PostDelayedTask(
241 FROM_HERE, base::Bind(&VideoRendererImpl::MaybeStopSinkAfterFirstPaint,
242 weak_factory_.GetWeakPtr()),
243 base::TimeDelta::FromMilliseconds(250));
246 // Always post this task, it will acquire new frames if necessary and since it
247 // happens on another thread, even if we don't have room in the queue now, by
248 // the time it runs (may be delayed up to 50ms for complex decodes!) we might.
249 task_runner_->PostTask(FROM_HERE, base::Bind(&VideoRendererImpl::AttemptRead,
250 weak_factory_.GetWeakPtr()));
252 return result;
255 void VideoRendererImpl::OnFrameDropped() {
256 base::AutoLock auto_lock(lock_);
257 DCHECK(use_new_video_renderering_path_);
258 algorithm_->OnLastFrameDropped();
261 void VideoRendererImpl::CreateVideoThread() {
262 // This may fail and cause a crash if there are too many threads created in
263 // the current process. See http://crbug.com/443291
264 CHECK(base::PlatformThread::Create(0, this, &thread_));
266 #if defined(OS_WIN)
267 // Bump up our priority so our sleeping is more accurate.
268 // TODO(scherkus): find out if this is necessary, but it seems to help.
269 ::SetThreadPriority(thread_.platform_handle(), THREAD_PRIORITY_ABOVE_NORMAL);
270 #endif // defined(OS_WIN)
273 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) {
274 DCHECK(task_runner_->BelongsToCurrentThread());
275 base::AutoLock auto_lock(lock_);
276 DCHECK_EQ(state_, kInitializing);
278 if (!success) {
279 state_ = kUninitialized;
280 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED);
281 return;
284 // We're all good! Consider ourselves flushed. (ThreadMain() should never
285 // see us in the kUninitialized state).
286 // Since we had an initial Preroll(), we consider ourself flushed, because we
287 // have not populated any buffers yet.
288 state_ = kFlushed;
290 if (use_new_video_renderering_path_) {
291 algorithm_.reset(new VideoRendererAlgorithm(wall_clock_time_cb_));
292 if (!drop_frames_)
293 algorithm_->disable_frame_dropping();
294 } else {
295 CreateVideoThread();
298 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
301 // PlatformThread::Delegate implementation.
302 void VideoRendererImpl::ThreadMain() {
303 DCHECK(!use_new_video_renderering_path_);
304 base::PlatformThread::SetName("CrVideoRenderer");
306 // The number of milliseconds to idle when we do not have anything to do.
307 // Nothing special about the value, other than we're being more OS-friendly
308 // than sleeping for 1 millisecond.
310 // TODO(scherkus): switch to pure event-driven frame timing instead of this
311 // kIdleTimeDelta business http://crbug.com/106874
312 const base::TimeDelta kIdleTimeDelta =
313 base::TimeDelta::FromMilliseconds(10);
315 for (;;) {
316 base::AutoLock auto_lock(lock_);
318 // Thread exit condition.
319 if (is_shutting_down_)
320 return;
322 // Remain idle as long as we're not playing.
323 if (state_ != kPlaying || buffering_state_ != BUFFERING_HAVE_ENOUGH) {
324 UpdateStatsAndWait_Locked(kIdleTimeDelta);
325 continue;
328 base::TimeTicks now = tick_clock_->NowTicks();
330 // Remain idle until we have the next frame ready for rendering.
331 if (ready_frames_.empty()) {
332 base::TimeDelta wait_time = kIdleTimeDelta;
333 if (received_end_of_stream_) {
334 if (!rendered_end_of_stream_) {
335 rendered_end_of_stream_ = true;
336 task_runner_->PostTask(FROM_HERE, ended_cb_);
338 } else if (now >= latest_possible_paint_time_) {
339 // Declare HAVE_NOTHING if we don't have another frame by the time we
340 // are ready to paint the next one.
341 buffering_state_ = BUFFERING_HAVE_NOTHING;
342 task_runner_->PostTask(
343 FROM_HERE, base::Bind(buffering_state_cb_, BUFFERING_HAVE_NOTHING));
344 } else {
345 wait_time = std::min(kIdleTimeDelta, latest_possible_paint_time_ - now);
348 UpdateStatsAndWait_Locked(wait_time);
349 continue;
352 base::TimeTicks target_paint_time =
353 ConvertMediaTimestamp(ready_frames_.front()->timestamp());
355 // If media time has stopped, don't attempt to paint any more frames.
356 if (target_paint_time.is_null()) {
357 UpdateStatsAndWait_Locked(kIdleTimeDelta);
358 continue;
361 // Deadline is defined as the duration between this frame and the next
362 // frame, using the delta between this frame and the previous frame as the
363 // assumption for frame duration.
365 // TODO(scherkus): This can be vastly improved. Use a histogram to measure
366 // the accuracy of our frame timing code. http://crbug.com/149829
367 if (last_media_time_.is_null()) {
368 latest_possible_paint_time_ = now;
369 } else {
370 base::TimeDelta duration = target_paint_time - last_media_time_;
371 latest_possible_paint_time_ = target_paint_time + duration;
374 // Remain idle until we've reached our target paint window.
375 if (now < target_paint_time) {
376 UpdateStatsAndWait_Locked(
377 std::min(target_paint_time - now, kIdleTimeDelta));
378 continue;
381 if (ready_frames_.size() > 1 && now > latest_possible_paint_time_ &&
382 drop_frames_) {
383 DropNextReadyFrame_Locked();
384 continue;
387 // Congratulations! You've made it past the video frame timing gauntlet.
389 // At this point enough time has passed that the next frame that ready for
390 // rendering.
391 PaintNextReadyFrame_Locked();
395 void VideoRendererImpl::SetTickClockForTesting(
396 scoped_ptr<base::TickClock> tick_clock) {
397 tick_clock_.swap(tick_clock);
400 void VideoRendererImpl::OnTimeStateChanged(bool time_progressing) {
401 DCHECK(task_runner_->BelongsToCurrentThread());
402 time_progressing_ = time_progressing;
404 // WARNING: Do not attempt to use |lock_| here as this may be a reentrant call
405 // in response to callbacks firing above.
407 if (!use_new_video_renderering_path_ || sink_started_ == time_progressing_)
408 return;
410 if (time_progressing_) {
411 // If only an EOS frame came in after a seek, the renderer may not have
412 // received the ended event yet though we've posted it.
413 if (!rendered_end_of_stream_)
414 StartSink();
415 } else {
416 StopSink();
420 void VideoRendererImpl::PaintNextReadyFrame_Locked() {
421 DCHECK(!use_new_video_renderering_path_);
422 lock_.AssertAcquired();
424 scoped_refptr<VideoFrame> next_frame = ready_frames_.front();
425 ready_frames_.pop_front();
427 last_media_time_ = ConvertMediaTimestamp(next_frame->timestamp());
429 paint_cb_.Run(next_frame);
431 task_runner_->PostTask(
432 FROM_HERE,
433 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
436 void VideoRendererImpl::DropNextReadyFrame_Locked() {
437 DCHECK(!use_new_video_renderering_path_);
438 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped");
440 lock_.AssertAcquired();
442 last_media_time_ = ConvertMediaTimestamp(ready_frames_.front()->timestamp());
444 ready_frames_.pop_front();
445 frames_dropped_++;
447 task_runner_->PostTask(
448 FROM_HERE,
449 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
452 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
453 const scoped_refptr<VideoFrame>& frame) {
454 DCHECK(task_runner_->BelongsToCurrentThread());
455 bool start_sink = false;
457 base::AutoLock auto_lock(lock_);
458 DCHECK_NE(state_, kUninitialized);
459 DCHECK_NE(state_, kFlushed);
461 CHECK(pending_read_);
462 pending_read_ = false;
464 if (status == VideoFrameStream::DECODE_ERROR ||
465 status == VideoFrameStream::DECRYPT_ERROR) {
466 DCHECK(!frame.get());
467 PipelineStatus error = PIPELINE_ERROR_DECODE;
468 if (status == VideoFrameStream::DECRYPT_ERROR)
469 error = PIPELINE_ERROR_DECRYPT;
470 task_runner_->PostTask(FROM_HERE, base::Bind(error_cb_, error));
471 return;
474 // Already-queued VideoFrameStream ReadCB's can fire after various state
475 // transitions have happened; in that case just drop those frames
476 // immediately.
477 if (state_ == kFlushing)
478 return;
480 DCHECK_EQ(state_, kPlaying);
482 // Can happen when demuxers are preparing for a new Seek().
483 if (!frame.get()) {
484 DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED);
485 return;
488 if (frame->end_of_stream()) {
489 DCHECK(!received_end_of_stream_);
490 received_end_of_stream_ = true;
492 // See if we can fire EOS immediately instead of waiting for Render().
493 if (use_new_video_renderering_path_)
494 MaybeFireEndedCallback();
495 } else {
496 // Maintain the latest frame decoded so the correct frame is displayed
497 // after prerolling has completed.
498 if (frame->timestamp() <= start_timestamp_) {
499 if (use_new_video_renderering_path_)
500 algorithm_->Reset();
501 ready_frames_.clear();
503 AddReadyFrame_Locked(frame);
506 // Signal buffering state if we've met our conditions for having enough
507 // data.
508 if (buffering_state_ != BUFFERING_HAVE_ENOUGH && HaveEnoughData_Locked()) {
509 TransitionToHaveEnough_Locked();
510 if (use_new_video_renderering_path_ && !sink_started_ &&
511 !rendered_end_of_stream_) {
512 start_sink = true;
513 render_first_frame_and_stop_ = true;
514 posted_maybe_stop_after_first_paint_ = false;
518 // Background rendering updates may not be ticking fast enough by itself to
519 // remove expired frames, so give it a boost here by ensuring we don't exit
520 // the decoding cycle too early.
521 if (was_background_rendering_) {
522 DCHECK(use_new_video_renderering_path_);
523 algorithm_->RemoveExpiredFrames(tick_clock_->NowTicks());
526 // Always request more decoded video if we have capacity. This serves two
527 // purposes:
528 // 1) Prerolling while paused
529 // 2) Keeps decoding going if video rendering thread starts falling behind
530 AttemptRead_Locked();
533 // If time is progressing, the sink has already been started; this may be true
534 // if we have previously underflowed, yet weren't stopped because of audio.
535 if (use_new_video_renderering_path_ && start_sink) {
536 DCHECK(!sink_started_);
537 StartSink();
541 bool VideoRendererImpl::HaveEnoughData_Locked() {
542 DCHECK_EQ(state_, kPlaying);
544 if (received_end_of_stream_ || !video_frame_stream_->CanReadWithoutStalling())
545 return true;
547 if (HaveReachedBufferingCap())
548 return true;
550 if (use_new_video_renderering_path_ && was_background_rendering_ &&
551 frames_decoded_) {
552 return true;
555 if (!low_delay_)
556 return false;
558 return ready_frames_.size() > 0 ||
559 (use_new_video_renderering_path_ && algorithm_->frames_queued() > 0);
562 void VideoRendererImpl::TransitionToHaveEnough_Locked() {
563 DCHECK(task_runner_->BelongsToCurrentThread());
564 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
566 if (!ready_frames_.empty()) {
567 DCHECK(!use_new_video_renderering_path_);
568 // Because the clock might remain paused in for an undetermined amount
569 // of time (e.g., seeking while paused), paint the first frame.
570 PaintNextReadyFrame_Locked();
573 buffering_state_ = BUFFERING_HAVE_ENOUGH;
574 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH);
577 void VideoRendererImpl::TransitionToHaveNothing() {
578 DCHECK(task_runner_->BelongsToCurrentThread());
580 base::AutoLock auto_lock(lock_);
581 if (buffering_state_ != BUFFERING_HAVE_ENOUGH || HaveEnoughData_Locked())
582 return;
584 buffering_state_ = BUFFERING_HAVE_NOTHING;
585 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
588 void VideoRendererImpl::AddReadyFrame_Locked(
589 const scoped_refptr<VideoFrame>& frame) {
590 DCHECK(task_runner_->BelongsToCurrentThread());
591 lock_.AssertAcquired();
592 DCHECK(!frame->end_of_stream());
594 frames_decoded_++;
596 if (use_new_video_renderering_path_) {
597 algorithm_->EnqueueFrame(frame);
598 return;
601 ready_frames_.push_back(frame);
602 DCHECK_LE(ready_frames_.size(),
603 static_cast<size_t>(limits::kMaxVideoFrames));
605 // Avoid needlessly waking up |thread_| unless playing.
606 if (state_ == kPlaying)
607 frame_available_.Signal();
610 void VideoRendererImpl::AttemptRead() {
611 base::AutoLock auto_lock(lock_);
612 AttemptRead_Locked();
615 void VideoRendererImpl::AttemptRead_Locked() {
616 DCHECK(task_runner_->BelongsToCurrentThread());
617 lock_.AssertAcquired();
619 if (pending_read_ || received_end_of_stream_)
620 return;
622 if (HaveReachedBufferingCap())
623 return;
625 switch (state_) {
626 case kPlaying:
627 pending_read_ = true;
628 video_frame_stream_->Read(base::Bind(&VideoRendererImpl::FrameReady,
629 weak_factory_.GetWeakPtr()));
630 return;
632 case kUninitialized:
633 case kInitializing:
634 case kFlushing:
635 case kFlushed:
636 return;
640 void VideoRendererImpl::OnVideoFrameStreamResetDone() {
641 base::AutoLock auto_lock(lock_);
642 DCHECK_EQ(kFlushing, state_);
643 DCHECK(!pending_read_);
644 DCHECK(ready_frames_.empty());
645 DCHECK(!received_end_of_stream_);
646 DCHECK(!rendered_end_of_stream_);
647 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
649 state_ = kFlushed;
650 latest_possible_paint_time_ = last_media_time_ = base::TimeTicks();
651 base::ResetAndReturn(&flush_cb_).Run();
654 void VideoRendererImpl::UpdateStatsAndWait_Locked(
655 base::TimeDelta wait_duration) {
656 lock_.AssertAcquired();
657 DCHECK_GE(frames_decoded_, 0);
658 DCHECK_GE(frames_dropped_, 0);
660 if (frames_decoded_ || frames_dropped_) {
661 PipelineStatistics statistics;
662 statistics.video_frames_decoded = frames_decoded_;
663 statistics.video_frames_dropped = frames_dropped_;
664 task_runner_->PostTask(FROM_HERE, base::Bind(statistics_cb_, statistics));
666 frames_decoded_ = 0;
667 frames_dropped_ = 0;
670 if (wait_duration > base::TimeDelta())
671 frame_available_.TimedWait(wait_duration);
674 void VideoRendererImpl::MaybeStopSinkAfterFirstPaint() {
675 DCHECK(task_runner_->BelongsToCurrentThread());
676 DCHECK(use_new_video_renderering_path_);
679 base::AutoLock auto_lock(lock_);
680 render_first_frame_and_stop_ = false;
683 if (!time_progressing_ && sink_started_)
684 StopSink();
687 bool VideoRendererImpl::HaveReachedBufferingCap() {
688 DCHECK(task_runner_->BelongsToCurrentThread());
689 const size_t kMaxVideoFrames = limits::kMaxVideoFrames;
691 if (use_new_video_renderering_path_) {
692 // When the display rate is less than the frame rate, the effective frames
693 // queued may be much smaller than the actual number of frames queued. Here
694 // we ensure that frames_queued() doesn't get excessive.
695 return algorithm_->EffectiveFramesQueued() >= kMaxVideoFrames ||
696 algorithm_->frames_queued() >= 3 * kMaxVideoFrames;
699 return ready_frames_.size() >= kMaxVideoFrames;
702 void VideoRendererImpl::StartSink() {
703 DCHECK(task_runner_->BelongsToCurrentThread());
704 DCHECK_GT(algorithm_->frames_queued(), 0u);
705 sink_->Start(this);
706 sink_started_ = true;
707 was_background_rendering_ = false;
710 void VideoRendererImpl::StopSink() {
711 DCHECK(task_runner_->BelongsToCurrentThread());
712 sink_->Stop();
713 sink_started_ = false;
714 was_background_rendering_ = false;
717 size_t VideoRendererImpl::MaybeFireEndedCallback() {
718 // If there's only one frame in the video or Render() was never called, the
719 // algorithm will have one frame linger indefinitely. So in cases where the
720 // frame duration is unknown and we've received EOS, fire it once we get down
721 // to a single frame.
722 const size_t effective_frames = algorithm_->EffectiveFramesQueued();
724 // Don't fire ended if we haven't received EOS or have already done so.
725 if (!received_end_of_stream_ || rendered_end_of_stream_)
726 return effective_frames;
728 // Don't fire ended if time isn't moving and we have frames.
729 if (!time_progressing_ && algorithm_->frames_queued())
730 return effective_frames;
732 // Fire ended if we have no more effective frames or only ever had one frame.
733 if (!effective_frames ||
734 (algorithm_->frames_queued() == 1u &&
735 algorithm_->average_frame_duration() == base::TimeDelta())) {
736 rendered_end_of_stream_ = true;
737 task_runner_->PostTask(FROM_HERE, ended_cb_);
740 return effective_frames;
743 base::TimeTicks VideoRendererImpl::ConvertMediaTimestamp(
744 base::TimeDelta media_time) {
745 std::vector<base::TimeDelta> media_times(1, media_time);
746 std::vector<base::TimeTicks> wall_clock_times;
747 if (!wall_clock_time_cb_.Run(media_times, &wall_clock_times))
748 return base::TimeTicks();
749 return wall_clock_times[0];
752 } // namespace media