hunspell: Cleanup to fix the header include guards under google/ directory.
[chromium-blink-merge.git] / media / renderers / video_renderer_impl.cc
blobd19399197acd391464c3e0e6f0f2c57c30bfd999
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/metrics/histogram_macros.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/string_util.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_log.h"
22 #include "media/base/media_switches.h"
23 #include "media/base/pipeline.h"
24 #include "media/base/video_frame.h"
25 #include "media/renderers/gpu_video_accelerator_factories.h"
26 #include "media/video/gpu_memory_buffer_video_frame_pool.h"
28 namespace media {
30 // TODO(dalecurtis): This experiment is temporary and should be removed once we
31 // have enough data to support the primacy of the new video rendering path; see
32 // http://crbug.com/485699 for details.
33 static bool ShouldUseVideoRenderingPath() {
34 // Note: It's important to query the field trial state first, to ensure that
35 // UMA reports the correct group.
36 const std::string group_name =
37 base::FieldTrialList::FindFullName("NewVideoRendererTrial");
38 const bool disabled_via_cli =
39 base::CommandLine::ForCurrentProcess()->HasSwitch(
40 switches::kDisableNewVideoRenderer);
41 return !disabled_via_cli &&
42 !base::StartsWith(group_name, "Disabled",
43 base::CompareCase::SENSITIVE);
46 VideoRendererImpl::VideoRendererImpl(
47 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
48 VideoRendererSink* sink,
49 ScopedVector<VideoDecoder> decoders,
50 bool drop_frames,
51 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
52 const scoped_refptr<MediaLog>& media_log)
53 : task_runner_(task_runner),
54 use_new_video_renderering_path_(ShouldUseVideoRenderingPath()),
55 sink_(sink),
56 sink_started_(false),
57 video_frame_stream_(
58 new VideoFrameStream(task_runner, decoders.Pass(), media_log)),
59 gpu_memory_buffer_pool_(
60 new GpuMemoryBufferVideoFramePool(task_runner, gpu_factories)),
61 media_log_(media_log),
62 low_delay_(false),
63 received_end_of_stream_(false),
64 rendered_end_of_stream_(false),
65 frame_available_(&lock_),
66 state_(kUninitialized),
67 thread_(),
68 pending_read_(false),
69 drop_frames_(drop_frames),
70 buffering_state_(BUFFERING_HAVE_NOTHING),
71 frames_decoded_(0),
72 frames_dropped_(0),
73 is_shutting_down_(false),
74 tick_clock_(new base::DefaultTickClock()),
75 was_background_rendering_(false),
76 time_progressing_(false),
77 render_first_frame_and_stop_(false),
78 posted_maybe_stop_after_first_paint_(false),
79 weak_factory_(this) {
82 VideoRendererImpl::~VideoRendererImpl() {
83 DCHECK(task_runner_->BelongsToCurrentThread());
85 if (!use_new_video_renderering_path_) {
86 base::AutoLock auto_lock(lock_);
87 is_shutting_down_ = true;
88 frame_available_.Signal();
91 if (!thread_.is_null())
92 base::PlatformThread::Join(thread_);
94 if (!init_cb_.is_null())
95 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT);
97 if (!flush_cb_.is_null())
98 base::ResetAndReturn(&flush_cb_).Run();
100 if (use_new_video_renderering_path_ && sink_started_)
101 StopSink();
104 void VideoRendererImpl::Flush(const base::Closure& callback) {
105 DVLOG(1) << __FUNCTION__;
106 DCHECK(task_runner_->BelongsToCurrentThread());
108 if (use_new_video_renderering_path_ && sink_started_)
109 StopSink();
111 base::AutoLock auto_lock(lock_);
112 DCHECK_EQ(state_, kPlaying);
113 flush_cb_ = callback;
114 state_ = kFlushing;
116 // This is necessary if the |video_frame_stream_| has already seen an end of
117 // stream and needs to drain it before flushing it.
118 ready_frames_.clear();
119 if (buffering_state_ != BUFFERING_HAVE_NOTHING) {
120 buffering_state_ = BUFFERING_HAVE_NOTHING;
121 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
123 received_end_of_stream_ = false;
124 rendered_end_of_stream_ = false;
126 if (use_new_video_renderering_path_)
127 algorithm_->Reset();
129 video_frame_stream_->Reset(
130 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone,
131 weak_factory_.GetWeakPtr()));
134 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) {
135 DVLOG(1) << __FUNCTION__ << "(" << timestamp.InMicroseconds() << ")";
136 DCHECK(task_runner_->BelongsToCurrentThread());
137 base::AutoLock auto_lock(lock_);
138 DCHECK_EQ(state_, kFlushed);
139 DCHECK(!pending_read_);
140 DCHECK(ready_frames_.empty());
141 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
143 state_ = kPlaying;
144 start_timestamp_ = timestamp;
145 AttemptRead_Locked();
148 void VideoRendererImpl::Initialize(
149 DemuxerStream* stream,
150 const PipelineStatusCB& init_cb,
151 const SetDecryptorReadyCB& set_decryptor_ready_cb,
152 const StatisticsCB& statistics_cb,
153 const BufferingStateCB& buffering_state_cb,
154 const base::Closure& ended_cb,
155 const PipelineStatusCB& error_cb,
156 const TimeSource::WallClockTimeCB& wall_clock_time_cb,
157 const base::Closure& waiting_for_decryption_key_cb) {
158 DCHECK(task_runner_->BelongsToCurrentThread());
159 base::AutoLock auto_lock(lock_);
160 DCHECK(stream);
161 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO);
162 DCHECK(!init_cb.is_null());
163 DCHECK(!statistics_cb.is_null());
164 DCHECK(!buffering_state_cb.is_null());
165 DCHECK(!ended_cb.is_null());
166 DCHECK(!wall_clock_time_cb.is_null());
167 DCHECK_EQ(kUninitialized, state_);
168 DCHECK(!render_first_frame_and_stop_);
169 DCHECK(!posted_maybe_stop_after_first_paint_);
170 DCHECK(!was_background_rendering_);
171 DCHECK(!time_progressing_);
173 low_delay_ = (stream->liveness() == DemuxerStream::LIVENESS_LIVE);
174 UMA_HISTOGRAM_BOOLEAN("Media.VideoRenderer.LowDelay", low_delay_);
175 if (low_delay_)
176 MEDIA_LOG(DEBUG, media_log_) << "Video rendering in low delay mode.";
178 // Always post |init_cb_| because |this| could be destroyed if initialization
179 // failed.
180 init_cb_ = BindToCurrentLoop(init_cb);
182 // Always post |buffering_state_cb_| because it may otherwise invoke reentrant
183 // calls to OnTimeStateChanged() under lock, which can deadlock the compositor
184 // and media threads.
185 buffering_state_cb_ = BindToCurrentLoop(buffering_state_cb);
187 statistics_cb_ = statistics_cb;
188 paint_cb_ = base::Bind(&VideoRendererSink::PaintFrameUsingOldRenderingPath,
189 base::Unretained(sink_));
190 ended_cb_ = ended_cb;
191 error_cb_ = error_cb;
192 wall_clock_time_cb_ = wall_clock_time_cb;
193 state_ = kInitializing;
195 video_frame_stream_->Initialize(
196 stream, base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized,
197 weak_factory_.GetWeakPtr()),
198 set_decryptor_ready_cb, statistics_cb, waiting_for_decryption_key_cb);
201 scoped_refptr<VideoFrame> VideoRendererImpl::Render(
202 base::TimeTicks deadline_min,
203 base::TimeTicks deadline_max,
204 bool background_rendering) {
205 base::AutoLock auto_lock(lock_);
206 DCHECK(use_new_video_renderering_path_);
207 DCHECK_EQ(state_, kPlaying);
209 size_t frames_dropped = 0;
210 scoped_refptr<VideoFrame> result =
211 algorithm_->Render(deadline_min, deadline_max, &frames_dropped);
213 // Due to how the |algorithm_| holds frames, this should never be null if
214 // we've had a proper startup sequence.
215 DCHECK(result);
217 // Declare HAVE_NOTHING if we reach a state where we can't progress playback
218 // any further. We don't want to do this if we've already done so, reached
219 // end of stream, or have frames available. We also don't want to do this in
220 // background rendering mode unless this isn't the first background render
221 // tick and we haven't seen any decoded frames since the last one.
223 // We use the inverse of |render_first_frame_and_stop_| as a proxy for the
224 // value of |time_progressing_| here since we can't access it from the
225 // compositor thread. If we're here (in Render()) the sink must have been
226 // started -- but if it was started only to render the first frame and stop,
227 // then |time_progressing_| is likely false. If we're still in Render() when
228 // |render_first_frame_and_stop_| is false, then |time_progressing_| is true.
229 // If |time_progressing_| is actually true when |render_first_frame_and_stop_|
230 // is also true, then the ended callback will be harmlessly delayed until
231 // MaybeStopSinkAfterFirstPaint() runs and the next Render() call comes in.
232 const size_t effective_frames =
233 MaybeFireEndedCallback_Locked(!render_first_frame_and_stop_);
234 if (buffering_state_ == BUFFERING_HAVE_ENOUGH && !received_end_of_stream_ &&
235 !effective_frames && (!background_rendering ||
236 (!frames_decoded_ && was_background_rendering_))) {
237 // Do not set |buffering_state_| here as the lock in FrameReady() may be
238 // held already and it fire the state changes in the wrong order.
239 task_runner_->PostTask(
240 FROM_HERE, base::Bind(&VideoRendererImpl::TransitionToHaveNothing,
241 weak_factory_.GetWeakPtr()));
244 // We don't count dropped frames in the background to avoid skewing the count
245 // and impacting JavaScript visible metrics used by web developers.
247 // Just after resuming from background rendering, we also don't count the
248 // dropped frames since they are likely just dropped due to being too old.
249 if (!background_rendering && !was_background_rendering_)
250 frames_dropped_ += frames_dropped;
251 UpdateStatsAndWait_Locked(base::TimeDelta());
252 was_background_rendering_ = background_rendering;
254 // After painting the first frame, if playback hasn't started, we post a
255 // delayed task to request that the sink be stopped. The task is delayed to
256 // give videos with autoplay time to start.
258 // OnTimeStateChanged() will clear this flag if time starts before we get here
259 // and MaybeStopSinkAfterFirstPaint() will ignore this request if time starts
260 // before the call executes.
261 if (render_first_frame_and_stop_ && !posted_maybe_stop_after_first_paint_) {
262 posted_maybe_stop_after_first_paint_ = true;
263 task_runner_->PostDelayedTask(
264 FROM_HERE, base::Bind(&VideoRendererImpl::MaybeStopSinkAfterFirstPaint,
265 weak_factory_.GetWeakPtr()),
266 base::TimeDelta::FromMilliseconds(250));
269 // Always post this task, it will acquire new frames if necessary and since it
270 // happens on another thread, even if we don't have room in the queue now, by
271 // the time it runs (may be delayed up to 50ms for complex decodes!) we might.
272 task_runner_->PostTask(FROM_HERE, base::Bind(&VideoRendererImpl::AttemptRead,
273 weak_factory_.GetWeakPtr()));
275 return result;
278 void VideoRendererImpl::OnFrameDropped() {
279 base::AutoLock auto_lock(lock_);
280 DCHECK(use_new_video_renderering_path_);
281 algorithm_->OnLastFrameDropped();
284 void VideoRendererImpl::CreateVideoThread() {
285 // This may fail and cause a crash if there are too many threads created in
286 // the current process. See http://crbug.com/443291
287 const base::ThreadPriority priority =
288 #if defined(OS_WIN)
289 // Bump up our priority so our sleeping is more accurate.
290 // TODO(scherkus): find out if this is necessary, but it seems to help.
291 base::ThreadPriority::DISPLAY;
292 #else
293 base::ThreadPriority::NORMAL;
294 #endif
295 CHECK(base::PlatformThread::CreateWithPriority(0, this, &thread_, priority));
298 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) {
299 DCHECK(task_runner_->BelongsToCurrentThread());
300 base::AutoLock auto_lock(lock_);
301 DCHECK_EQ(state_, kInitializing);
303 if (!success) {
304 state_ = kUninitialized;
305 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED);
306 return;
309 // We're all good! Consider ourselves flushed. (ThreadMain() should never
310 // see us in the kUninitialized state).
311 // Since we had an initial Preroll(), we consider ourself flushed, because we
312 // have not populated any buffers yet.
313 state_ = kFlushed;
315 if (use_new_video_renderering_path_) {
316 algorithm_.reset(new VideoRendererAlgorithm(wall_clock_time_cb_));
317 if (!drop_frames_)
318 algorithm_->disable_frame_dropping();
319 } else {
320 CreateVideoThread();
323 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
326 // PlatformThread::Delegate implementation.
327 void VideoRendererImpl::ThreadMain() {
328 DCHECK(!use_new_video_renderering_path_);
329 base::PlatformThread::SetName("CrVideoRenderer");
331 // The number of milliseconds to idle when we do not have anything to do.
332 // Nothing special about the value, other than we're being more OS-friendly
333 // than sleeping for 1 millisecond.
335 // TODO(scherkus): switch to pure event-driven frame timing instead of this
336 // kIdleTimeDelta business http://crbug.com/106874
337 const base::TimeDelta kIdleTimeDelta =
338 base::TimeDelta::FromMilliseconds(10);
340 for (;;) {
341 base::AutoLock auto_lock(lock_);
343 // Thread exit condition.
344 if (is_shutting_down_)
345 return;
347 // Remain idle as long as we're not playing.
348 if (state_ != kPlaying || buffering_state_ != BUFFERING_HAVE_ENOUGH) {
349 UpdateStatsAndWait_Locked(kIdleTimeDelta);
350 continue;
353 base::TimeTicks now = tick_clock_->NowTicks();
355 // Remain idle until we have the next frame ready for rendering.
356 if (ready_frames_.empty()) {
357 base::TimeDelta wait_time = kIdleTimeDelta;
358 if (received_end_of_stream_) {
359 if (!rendered_end_of_stream_) {
360 rendered_end_of_stream_ = true;
361 task_runner_->PostTask(FROM_HERE, ended_cb_);
363 } else if (now >= latest_possible_paint_time_) {
364 // Declare HAVE_NOTHING if we don't have another frame by the time we
365 // are ready to paint the next one.
366 buffering_state_ = BUFFERING_HAVE_NOTHING;
367 task_runner_->PostTask(
368 FROM_HERE, base::Bind(buffering_state_cb_, BUFFERING_HAVE_NOTHING));
369 } else {
370 wait_time = std::min(kIdleTimeDelta, latest_possible_paint_time_ - now);
373 UpdateStatsAndWait_Locked(wait_time);
374 continue;
377 base::TimeTicks target_paint_time =
378 ConvertMediaTimestamp(ready_frames_.front()->timestamp());
380 // If media time has stopped, don't attempt to paint any more frames.
381 if (target_paint_time.is_null()) {
382 UpdateStatsAndWait_Locked(kIdleTimeDelta);
383 continue;
386 // Deadline is defined as the duration between this frame and the next
387 // frame, using the delta between this frame and the previous frame as the
388 // assumption for frame duration.
390 // TODO(scherkus): This can be vastly improved. Use a histogram to measure
391 // the accuracy of our frame timing code. http://crbug.com/149829
392 if (last_media_time_.is_null()) {
393 latest_possible_paint_time_ = now;
394 } else {
395 base::TimeDelta duration = target_paint_time - last_media_time_;
396 latest_possible_paint_time_ = target_paint_time + duration;
399 // Remain idle until we've reached our target paint window.
400 if (now < target_paint_time) {
401 UpdateStatsAndWait_Locked(
402 std::min(target_paint_time - now, kIdleTimeDelta));
403 continue;
406 if (ready_frames_.size() > 1 && now > latest_possible_paint_time_ &&
407 drop_frames_) {
408 DropNextReadyFrame_Locked();
409 continue;
412 // Congratulations! You've made it past the video frame timing gauntlet.
414 // At this point enough time has passed that the next frame that ready for
415 // rendering.
416 PaintNextReadyFrame_Locked();
420 void VideoRendererImpl::SetTickClockForTesting(
421 scoped_ptr<base::TickClock> tick_clock) {
422 tick_clock_.swap(tick_clock);
425 void VideoRendererImpl::OnTimeStateChanged(bool time_progressing) {
426 DCHECK(task_runner_->BelongsToCurrentThread());
427 time_progressing_ = time_progressing;
429 // WARNING: Do not attempt to use |lock_| here as this may be a reentrant call
430 // in response to callbacks firing above.
432 if (!use_new_video_renderering_path_ || sink_started_ == time_progressing_)
433 return;
435 if (time_progressing_) {
436 // If only an EOS frame came in after a seek, the renderer may not have
437 // received the ended event yet though we've posted it.
438 if (!rendered_end_of_stream_)
439 StartSink();
440 } else {
441 StopSink();
445 void VideoRendererImpl::PaintNextReadyFrame_Locked() {
446 DCHECK(!use_new_video_renderering_path_);
447 lock_.AssertAcquired();
449 scoped_refptr<VideoFrame> next_frame = ready_frames_.front();
450 ready_frames_.pop_front();
452 last_media_time_ = ConvertMediaTimestamp(next_frame->timestamp());
454 paint_cb_.Run(next_frame);
456 task_runner_->PostTask(
457 FROM_HERE,
458 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
461 void VideoRendererImpl::DropNextReadyFrame_Locked() {
462 DCHECK(!use_new_video_renderering_path_);
463 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped");
465 lock_.AssertAcquired();
467 last_media_time_ = ConvertMediaTimestamp(ready_frames_.front()->timestamp());
469 ready_frames_.pop_front();
470 frames_dropped_++;
472 task_runner_->PostTask(
473 FROM_HERE,
474 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
477 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
478 const scoped_refptr<VideoFrame>& frame) {
479 DCHECK(task_runner_->BelongsToCurrentThread());
480 bool start_sink = false;
482 base::AutoLock auto_lock(lock_);
483 DCHECK_NE(state_, kUninitialized);
484 DCHECK_NE(state_, kFlushed);
486 CHECK(pending_read_);
487 pending_read_ = false;
489 if (status == VideoFrameStream::DECODE_ERROR) {
490 DCHECK(!frame.get());
491 PipelineStatus error = PIPELINE_ERROR_DECODE;
492 task_runner_->PostTask(FROM_HERE, base::Bind(error_cb_, error));
493 return;
496 // Already-queued VideoFrameStream ReadCB's can fire after various state
497 // transitions have happened; in that case just drop those frames
498 // immediately.
499 if (state_ == kFlushing)
500 return;
502 DCHECK_EQ(state_, kPlaying);
504 // Can happen when demuxers are preparing for a new Seek().
505 if (!frame.get()) {
506 DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED);
507 return;
510 // In low delay mode, don't accumulate frames that's earlier than the start
511 // time. Otherwise we could declare HAVE_ENOUGH_DATA and start playback
512 // prematurely.
513 if (low_delay_ &&
514 !frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM) &&
515 frame->timestamp() < start_timestamp_) {
516 AttemptRead_Locked();
517 return;
520 if (frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) {
521 DCHECK(!received_end_of_stream_);
522 received_end_of_stream_ = true;
524 // See if we can fire EOS immediately instead of waiting for Render().
525 if (use_new_video_renderering_path_)
526 MaybeFireEndedCallback_Locked(time_progressing_);
527 } else {
528 // Maintain the latest frame decoded so the correct frame is displayed
529 // after prerolling has completed.
530 if (frame->timestamp() <= start_timestamp_) {
531 if (use_new_video_renderering_path_)
532 algorithm_->Reset();
533 ready_frames_.clear();
535 AddReadyFrame_Locked(frame);
538 // Background rendering updates may not be ticking fast enough by itself to
539 // remove expired frames, so give it a boost here by ensuring we don't exit
540 // the decoding cycle too early.
542 // Similarly, if we've paused for underflow, remove all frames which are
543 // before the current media time.
544 const bool have_nothing = buffering_state_ != BUFFERING_HAVE_ENOUGH;
545 const bool have_nothing_and_paused = have_nothing && !sink_started_;
546 if (was_background_rendering_ ||
547 (use_new_video_renderering_path_ && have_nothing_and_paused &&
548 drop_frames_)) {
549 base::TimeTicks expiry_time;
550 if (have_nothing_and_paused) {
551 // Use the current media wall clock time plus the frame duration since
552 // RemoveExpiredFrames() is expecting the end point of an interval (it
553 // will subtract from the given value).
554 std::vector<base::TimeTicks> current_time;
555 wall_clock_time_cb_.Run(std::vector<base::TimeDelta>(), &current_time);
556 expiry_time = current_time[0] + algorithm_->average_frame_duration();
557 } else {
558 expiry_time = tick_clock_->NowTicks();
561 // Prior to rendering the first frame, |have_nothing_and_paused| will be
562 // true, correspondingly the |expiry_time| will be null; in this case
563 // there's no reason to try and remove any frames.
564 if (!expiry_time.is_null()) {
565 const size_t removed_frames =
566 algorithm_->RemoveExpiredFrames(expiry_time);
568 // Frames removed during underflow should be counted as dropped.
569 if (have_nothing_and_paused && removed_frames)
570 frames_dropped_ += removed_frames;
574 // Signal buffering state if we've met our conditions for having enough
575 // data.
576 if (have_nothing && HaveEnoughData_Locked()) {
577 TransitionToHaveEnough_Locked();
578 if (use_new_video_renderering_path_ && !sink_started_ &&
579 !rendered_end_of_stream_) {
580 start_sink = true;
581 render_first_frame_and_stop_ = true;
582 posted_maybe_stop_after_first_paint_ = false;
586 // Always request more decoded video if we have capacity. This serves two
587 // purposes:
588 // 1) Prerolling while paused
589 // 2) Keeps decoding going if video rendering thread starts falling behind
590 AttemptRead_Locked();
593 // If time is progressing, the sink has already been started; this may be true
594 // if we have previously underflowed, yet weren't stopped because of audio.
595 if (use_new_video_renderering_path_ && start_sink) {
596 DCHECK(!sink_started_);
597 StartSink();
601 bool VideoRendererImpl::HaveEnoughData_Locked() {
602 DCHECK_EQ(state_, kPlaying);
604 if (received_end_of_stream_ || !video_frame_stream_->CanReadWithoutStalling())
605 return true;
607 if (HaveReachedBufferingCap())
608 return true;
610 if (use_new_video_renderering_path_ && was_background_rendering_ &&
611 frames_decoded_) {
612 return true;
615 if (!low_delay_)
616 return false;
618 return ready_frames_.size() > 0 ||
619 (use_new_video_renderering_path_ && algorithm_->frames_queued() > 0);
622 void VideoRendererImpl::TransitionToHaveEnough_Locked() {
623 DCHECK(task_runner_->BelongsToCurrentThread());
624 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
626 if (!ready_frames_.empty()) {
627 DCHECK(!use_new_video_renderering_path_);
628 // Because the clock might remain paused in for an undetermined amount
629 // of time (e.g., seeking while paused), paint the first frame.
630 PaintNextReadyFrame_Locked();
633 buffering_state_ = BUFFERING_HAVE_ENOUGH;
634 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH);
637 void VideoRendererImpl::TransitionToHaveNothing() {
638 DCHECK(task_runner_->BelongsToCurrentThread());
640 base::AutoLock auto_lock(lock_);
641 if (buffering_state_ != BUFFERING_HAVE_ENOUGH || HaveEnoughData_Locked())
642 return;
644 buffering_state_ = BUFFERING_HAVE_NOTHING;
645 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
648 void VideoRendererImpl::AddReadyFrame_Locked(
649 const scoped_refptr<VideoFrame>& frame) {
650 DCHECK(task_runner_->BelongsToCurrentThread());
651 lock_.AssertAcquired();
652 DCHECK(!frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM));
654 frames_decoded_++;
656 if (use_new_video_renderering_path_) {
657 algorithm_->EnqueueFrame(frame);
658 return;
661 ready_frames_.push_back(frame);
662 DCHECK_LE(ready_frames_.size(),
663 static_cast<size_t>(limits::kMaxVideoFrames));
665 // Avoid needlessly waking up |thread_| unless playing.
666 if (state_ == kPlaying)
667 frame_available_.Signal();
670 void VideoRendererImpl::AttemptRead() {
671 base::AutoLock auto_lock(lock_);
672 AttemptRead_Locked();
675 void VideoRendererImpl::AttemptRead_Locked() {
676 DCHECK(task_runner_->BelongsToCurrentThread());
677 lock_.AssertAcquired();
679 if (pending_read_ || received_end_of_stream_)
680 return;
682 if (HaveReachedBufferingCap())
683 return;
685 switch (state_) {
686 case kPlaying:
687 pending_read_ = true;
688 video_frame_stream_->Read(base::Bind(&VideoRendererImpl::FrameReady,
689 weak_factory_.GetWeakPtr()));
690 return;
692 case kUninitialized:
693 case kInitializing:
694 case kFlushing:
695 case kFlushed:
696 return;
700 void VideoRendererImpl::OnVideoFrameStreamResetDone() {
701 base::AutoLock auto_lock(lock_);
702 DCHECK_EQ(kFlushing, state_);
703 DCHECK(!pending_read_);
704 DCHECK(ready_frames_.empty());
705 DCHECK(!received_end_of_stream_);
706 DCHECK(!rendered_end_of_stream_);
707 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
709 state_ = kFlushed;
710 latest_possible_paint_time_ = last_media_time_ = base::TimeTicks();
711 base::ResetAndReturn(&flush_cb_).Run();
714 void VideoRendererImpl::UpdateStatsAndWait_Locked(
715 base::TimeDelta wait_duration) {
716 lock_.AssertAcquired();
717 DCHECK_GE(frames_decoded_, 0);
718 DCHECK_GE(frames_dropped_, 0);
720 if (frames_decoded_ || frames_dropped_) {
721 PipelineStatistics statistics;
722 statistics.video_frames_decoded = frames_decoded_;
723 statistics.video_frames_dropped = frames_dropped_;
724 task_runner_->PostTask(FROM_HERE, base::Bind(statistics_cb_, statistics));
726 frames_decoded_ = 0;
727 frames_dropped_ = 0;
730 if (wait_duration > base::TimeDelta())
731 frame_available_.TimedWait(wait_duration);
734 void VideoRendererImpl::MaybeStopSinkAfterFirstPaint() {
735 DCHECK(task_runner_->BelongsToCurrentThread());
736 DCHECK(use_new_video_renderering_path_);
738 if (!time_progressing_ && sink_started_)
739 StopSink();
741 base::AutoLock auto_lock(lock_);
742 render_first_frame_and_stop_ = false;
745 bool VideoRendererImpl::HaveReachedBufferingCap() {
746 DCHECK(task_runner_->BelongsToCurrentThread());
747 const size_t kMaxVideoFrames = limits::kMaxVideoFrames;
749 if (use_new_video_renderering_path_) {
750 // When the display rate is less than the frame rate, the effective frames
751 // queued may be much smaller than the actual number of frames queued. Here
752 // we ensure that frames_queued() doesn't get excessive.
753 return algorithm_->EffectiveFramesQueued() >= kMaxVideoFrames ||
754 algorithm_->frames_queued() >= 3 * kMaxVideoFrames;
757 return ready_frames_.size() >= kMaxVideoFrames;
760 void VideoRendererImpl::StartSink() {
761 DCHECK(task_runner_->BelongsToCurrentThread());
762 DCHECK_GT(algorithm_->frames_queued(), 0u);
763 sink_started_ = true;
764 was_background_rendering_ = false;
765 sink_->Start(this);
768 void VideoRendererImpl::StopSink() {
769 DCHECK(task_runner_->BelongsToCurrentThread());
770 sink_->Stop();
771 algorithm_->set_time_stopped();
772 sink_started_ = false;
773 was_background_rendering_ = false;
776 size_t VideoRendererImpl::MaybeFireEndedCallback_Locked(bool time_progressing) {
777 lock_.AssertAcquired();
779 // If there's only one frame in the video or Render() was never called, the
780 // algorithm will have one frame linger indefinitely. So in cases where the
781 // frame duration is unknown and we've received EOS, fire it once we get down
782 // to a single frame.
783 const size_t effective_frames = algorithm_->EffectiveFramesQueued();
785 // Don't fire ended if we haven't received EOS or have already done so.
786 if (!received_end_of_stream_ || rendered_end_of_stream_)
787 return effective_frames;
789 // Don't fire ended if time isn't moving and we have frames.
790 if (!time_progressing && algorithm_->frames_queued())
791 return effective_frames;
793 // Fire ended if we have no more effective frames or only ever had one frame.
794 if (!effective_frames ||
795 (algorithm_->frames_queued() == 1u &&
796 algorithm_->average_frame_duration() == base::TimeDelta())) {
797 rendered_end_of_stream_ = true;
798 task_runner_->PostTask(FROM_HERE, ended_cb_);
801 return effective_frames;
804 base::TimeTicks VideoRendererImpl::ConvertMediaTimestamp(
805 base::TimeDelta media_time) {
806 std::vector<base::TimeDelta> media_times(1, media_time);
807 std::vector<base::TimeTicks> wall_clock_times;
808 if (!wall_clock_time_cb_.Run(media_times, &wall_clock_times))
809 return base::TimeTicks();
810 return wall_clock_times[0];
813 } // namespace media