Update V8 to version 4.7.24.
[chromium-blink-merge.git] / media / renderers / video_renderer_impl.cc
blobc921f6a2f9c229ff6855e217d312f2d6c331ea74
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>& media_task_runner,
48 const scoped_refptr<base::TaskRunner>& worker_task_runner,
49 VideoRendererSink* sink,
50 ScopedVector<VideoDecoder> decoders,
51 bool drop_frames,
52 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
53 const scoped_refptr<MediaLog>& media_log)
54 : task_runner_(media_task_runner),
55 use_new_video_renderering_path_(ShouldUseVideoRenderingPath()),
56 sink_(sink),
57 sink_started_(false),
58 video_frame_stream_(
59 new VideoFrameStream(media_task_runner, decoders.Pass(), media_log)),
60 gpu_memory_buffer_pool_(nullptr),
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) {
80 if (gpu_factories &&
81 gpu_factories->ShouldUseGpuMemoryBuffersForVideoFrames()) {
82 gpu_memory_buffer_pool_.reset(new GpuMemoryBufferVideoFramePool(
83 media_task_runner, worker_task_runner, gpu_factories));
84 frame_ready_cb_ =
85 base::Bind(&VideoRendererImpl::FrameReadyForCopyingToGpuMemoryBuffers,
86 weak_factory_.GetWeakPtr());
87 } else {
88 frame_ready_cb_ =
89 base::Bind(&VideoRendererImpl::FrameReady, weak_factory_.GetWeakPtr());
93 VideoRendererImpl::~VideoRendererImpl() {
94 DCHECK(task_runner_->BelongsToCurrentThread());
96 if (!use_new_video_renderering_path_) {
97 base::AutoLock auto_lock(lock_);
98 is_shutting_down_ = true;
99 frame_available_.Signal();
102 if (!thread_.is_null())
103 base::PlatformThread::Join(thread_);
105 if (!init_cb_.is_null())
106 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT);
108 if (!flush_cb_.is_null())
109 base::ResetAndReturn(&flush_cb_).Run();
111 if (use_new_video_renderering_path_ && sink_started_)
112 StopSink();
115 void VideoRendererImpl::Flush(const base::Closure& callback) {
116 DVLOG(1) << __FUNCTION__;
117 DCHECK(task_runner_->BelongsToCurrentThread());
119 if (use_new_video_renderering_path_ && sink_started_)
120 StopSink();
122 base::AutoLock auto_lock(lock_);
123 DCHECK_EQ(state_, kPlaying);
124 flush_cb_ = callback;
125 state_ = kFlushing;
127 // This is necessary if the |video_frame_stream_| has already seen an end of
128 // stream and needs to drain it before flushing it.
129 ready_frames_.clear();
130 if (buffering_state_ != BUFFERING_HAVE_NOTHING) {
131 buffering_state_ = BUFFERING_HAVE_NOTHING;
132 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
134 received_end_of_stream_ = false;
135 rendered_end_of_stream_ = false;
137 if (use_new_video_renderering_path_)
138 algorithm_->Reset();
140 video_frame_stream_->Reset(
141 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone,
142 weak_factory_.GetWeakPtr()));
145 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) {
146 DVLOG(1) << __FUNCTION__ << "(" << timestamp.InMicroseconds() << ")";
147 DCHECK(task_runner_->BelongsToCurrentThread());
148 base::AutoLock auto_lock(lock_);
149 DCHECK_EQ(state_, kFlushed);
150 DCHECK(!pending_read_);
151 DCHECK(ready_frames_.empty());
152 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
154 state_ = kPlaying;
155 start_timestamp_ = timestamp;
156 AttemptRead_Locked();
159 void VideoRendererImpl::Initialize(
160 DemuxerStream* stream,
161 const PipelineStatusCB& init_cb,
162 const SetDecryptorReadyCB& set_decryptor_ready_cb,
163 const StatisticsCB& statistics_cb,
164 const BufferingStateCB& buffering_state_cb,
165 const base::Closure& ended_cb,
166 const PipelineStatusCB& error_cb,
167 const TimeSource::WallClockTimeCB& wall_clock_time_cb,
168 const base::Closure& waiting_for_decryption_key_cb) {
169 DCHECK(task_runner_->BelongsToCurrentThread());
170 base::AutoLock auto_lock(lock_);
171 DCHECK(stream);
172 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO);
173 DCHECK(!init_cb.is_null());
174 DCHECK(!statistics_cb.is_null());
175 DCHECK(!buffering_state_cb.is_null());
176 DCHECK(!ended_cb.is_null());
177 DCHECK(!wall_clock_time_cb.is_null());
178 DCHECK_EQ(kUninitialized, state_);
179 DCHECK(!render_first_frame_and_stop_);
180 DCHECK(!posted_maybe_stop_after_first_paint_);
181 DCHECK(!was_background_rendering_);
182 DCHECK(!time_progressing_);
184 low_delay_ = (stream->liveness() == DemuxerStream::LIVENESS_LIVE);
185 UMA_HISTOGRAM_BOOLEAN("Media.VideoRenderer.LowDelay", low_delay_);
186 if (low_delay_)
187 MEDIA_LOG(DEBUG, media_log_) << "Video rendering in low delay mode.";
189 // Always post |init_cb_| because |this| could be destroyed if initialization
190 // failed.
191 init_cb_ = BindToCurrentLoop(init_cb);
193 // Always post |buffering_state_cb_| because it may otherwise invoke reentrant
194 // calls to OnTimeStateChanged() under lock, which can deadlock the compositor
195 // and media threads.
196 buffering_state_cb_ = BindToCurrentLoop(buffering_state_cb);
198 statistics_cb_ = statistics_cb;
199 paint_cb_ = base::Bind(&VideoRendererSink::PaintFrameUsingOldRenderingPath,
200 base::Unretained(sink_));
201 ended_cb_ = ended_cb;
202 error_cb_ = error_cb;
203 wall_clock_time_cb_ = wall_clock_time_cb;
204 state_ = kInitializing;
206 video_frame_stream_->Initialize(
207 stream, base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized,
208 weak_factory_.GetWeakPtr()),
209 set_decryptor_ready_cb, statistics_cb, waiting_for_decryption_key_cb);
212 scoped_refptr<VideoFrame> VideoRendererImpl::Render(
213 base::TimeTicks deadline_min,
214 base::TimeTicks deadline_max,
215 bool background_rendering) {
216 base::AutoLock auto_lock(lock_);
217 DCHECK(use_new_video_renderering_path_);
218 DCHECK_EQ(state_, kPlaying);
220 size_t frames_dropped = 0;
221 scoped_refptr<VideoFrame> result =
222 algorithm_->Render(deadline_min, deadline_max, &frames_dropped);
224 // Due to how the |algorithm_| holds frames, this should never be null if
225 // we've had a proper startup sequence.
226 DCHECK(result);
228 // Declare HAVE_NOTHING if we reach a state where we can't progress playback
229 // any further. We don't want to do this if we've already done so, reached
230 // end of stream, or have frames available. We also don't want to do this in
231 // background rendering mode unless this isn't the first background render
232 // tick and we haven't seen any decoded frames since the last one.
234 // We use the inverse of |render_first_frame_and_stop_| as a proxy for the
235 // value of |time_progressing_| here since we can't access it from the
236 // compositor thread. If we're here (in Render()) the sink must have been
237 // started -- but if it was started only to render the first frame and stop,
238 // then |time_progressing_| is likely false. If we're still in Render() when
239 // |render_first_frame_and_stop_| is false, then |time_progressing_| is true.
240 // If |time_progressing_| is actually true when |render_first_frame_and_stop_|
241 // is also true, then the ended callback will be harmlessly delayed until
242 // MaybeStopSinkAfterFirstPaint() runs and the next Render() call comes in.
243 const size_t effective_frames =
244 MaybeFireEndedCallback_Locked(!render_first_frame_and_stop_);
245 if (buffering_state_ == BUFFERING_HAVE_ENOUGH && !received_end_of_stream_ &&
246 !effective_frames && (!background_rendering ||
247 (!frames_decoded_ && was_background_rendering_))) {
248 // Do not set |buffering_state_| here as the lock in FrameReady() may be
249 // held already and it fire the state changes in the wrong order.
250 task_runner_->PostTask(
251 FROM_HERE, base::Bind(&VideoRendererImpl::TransitionToHaveNothing,
252 weak_factory_.GetWeakPtr()));
255 // We don't count dropped frames in the background to avoid skewing the count
256 // and impacting JavaScript visible metrics used by web developers.
258 // Just after resuming from background rendering, we also don't count the
259 // dropped frames since they are likely just dropped due to being too old.
260 if (!background_rendering && !was_background_rendering_)
261 frames_dropped_ += frames_dropped;
262 UpdateStatsAndWait_Locked(base::TimeDelta());
263 was_background_rendering_ = background_rendering;
265 // After painting the first frame, if playback hasn't started, we post a
266 // delayed task to request that the sink be stopped. The task is delayed to
267 // give videos with autoplay time to start.
269 // OnTimeStateChanged() will clear this flag if time starts before we get here
270 // and MaybeStopSinkAfterFirstPaint() will ignore this request if time starts
271 // before the call executes.
272 if (render_first_frame_and_stop_ && !posted_maybe_stop_after_first_paint_) {
273 posted_maybe_stop_after_first_paint_ = true;
274 task_runner_->PostDelayedTask(
275 FROM_HERE, base::Bind(&VideoRendererImpl::MaybeStopSinkAfterFirstPaint,
276 weak_factory_.GetWeakPtr()),
277 base::TimeDelta::FromMilliseconds(250));
280 // Always post this task, it will acquire new frames if necessary and since it
281 // happens on another thread, even if we don't have room in the queue now, by
282 // the time it runs (may be delayed up to 50ms for complex decodes!) we might.
283 task_runner_->PostTask(FROM_HERE, base::Bind(&VideoRendererImpl::AttemptRead,
284 weak_factory_.GetWeakPtr()));
286 return result;
289 void VideoRendererImpl::OnFrameDropped() {
290 base::AutoLock auto_lock(lock_);
291 DCHECK(use_new_video_renderering_path_);
292 algorithm_->OnLastFrameDropped();
295 void VideoRendererImpl::CreateVideoThread() {
296 // This may fail and cause a crash if there are too many threads created in
297 // the current process. See http://crbug.com/443291
298 const base::ThreadPriority priority =
299 #if defined(OS_WIN)
300 // Bump up our priority so our sleeping is more accurate.
301 // TODO(scherkus): find out if this is necessary, but it seems to help.
302 base::ThreadPriority::DISPLAY;
303 #else
304 base::ThreadPriority::NORMAL;
305 #endif
306 CHECK(base::PlatformThread::CreateWithPriority(0, this, &thread_, priority));
309 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) {
310 DCHECK(task_runner_->BelongsToCurrentThread());
311 base::AutoLock auto_lock(lock_);
312 DCHECK_EQ(state_, kInitializing);
314 if (!success) {
315 state_ = kUninitialized;
316 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED);
317 return;
320 // We're all good! Consider ourselves flushed. (ThreadMain() should never
321 // see us in the kUninitialized state).
322 // Since we had an initial Preroll(), we consider ourself flushed, because we
323 // have not populated any buffers yet.
324 state_ = kFlushed;
326 if (use_new_video_renderering_path_) {
327 algorithm_.reset(new VideoRendererAlgorithm(wall_clock_time_cb_));
328 if (!drop_frames_)
329 algorithm_->disable_frame_dropping();
330 } else {
331 CreateVideoThread();
334 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
337 // PlatformThread::Delegate implementation.
338 void VideoRendererImpl::ThreadMain() {
339 DCHECK(!use_new_video_renderering_path_);
340 base::PlatformThread::SetName("CrVideoRenderer");
342 // The number of milliseconds to idle when we do not have anything to do.
343 // Nothing special about the value, other than we're being more OS-friendly
344 // than sleeping for 1 millisecond.
346 // TODO(scherkus): switch to pure event-driven frame timing instead of this
347 // kIdleTimeDelta business http://crbug.com/106874
348 const base::TimeDelta kIdleTimeDelta =
349 base::TimeDelta::FromMilliseconds(10);
351 for (;;) {
352 base::AutoLock auto_lock(lock_);
354 // Thread exit condition.
355 if (is_shutting_down_)
356 return;
358 // Remain idle as long as we're not playing.
359 if (state_ != kPlaying || buffering_state_ != BUFFERING_HAVE_ENOUGH) {
360 UpdateStatsAndWait_Locked(kIdleTimeDelta);
361 continue;
364 base::TimeTicks now = tick_clock_->NowTicks();
366 // Remain idle until we have the next frame ready for rendering.
367 if (ready_frames_.empty()) {
368 base::TimeDelta wait_time = kIdleTimeDelta;
369 if (received_end_of_stream_) {
370 if (!rendered_end_of_stream_) {
371 rendered_end_of_stream_ = true;
372 task_runner_->PostTask(FROM_HERE, ended_cb_);
374 } else if (now >= latest_possible_paint_time_) {
375 // Declare HAVE_NOTHING if we don't have another frame by the time we
376 // are ready to paint the next one.
377 buffering_state_ = BUFFERING_HAVE_NOTHING;
378 task_runner_->PostTask(
379 FROM_HERE, base::Bind(buffering_state_cb_, BUFFERING_HAVE_NOTHING));
380 } else {
381 wait_time = std::min(kIdleTimeDelta, latest_possible_paint_time_ - now);
384 UpdateStatsAndWait_Locked(wait_time);
385 continue;
388 base::TimeTicks target_paint_time =
389 ConvertMediaTimestamp(ready_frames_.front()->timestamp());
391 // If media time has stopped, don't attempt to paint any more frames.
392 if (target_paint_time.is_null()) {
393 UpdateStatsAndWait_Locked(kIdleTimeDelta);
394 continue;
397 // Deadline is defined as the duration between this frame and the next
398 // frame, using the delta between this frame and the previous frame as the
399 // assumption for frame duration.
401 // TODO(scherkus): This can be vastly improved. Use a histogram to measure
402 // the accuracy of our frame timing code. http://crbug.com/149829
403 if (last_media_time_.is_null()) {
404 latest_possible_paint_time_ = now;
405 } else {
406 base::TimeDelta duration = target_paint_time - last_media_time_;
407 latest_possible_paint_time_ = target_paint_time + duration;
410 // Remain idle until we've reached our target paint window.
411 if (now < target_paint_time) {
412 UpdateStatsAndWait_Locked(
413 std::min(target_paint_time - now, kIdleTimeDelta));
414 continue;
417 if (ready_frames_.size() > 1 && now > latest_possible_paint_time_ &&
418 drop_frames_) {
419 DropNextReadyFrame_Locked();
420 continue;
423 // Congratulations! You've made it past the video frame timing gauntlet.
425 // At this point enough time has passed that the next frame that ready for
426 // rendering.
427 PaintNextReadyFrame_Locked();
431 void VideoRendererImpl::SetTickClockForTesting(
432 scoped_ptr<base::TickClock> tick_clock) {
433 tick_clock_.swap(tick_clock);
436 void VideoRendererImpl::OnTimeStateChanged(bool time_progressing) {
437 DCHECK(task_runner_->BelongsToCurrentThread());
438 time_progressing_ = time_progressing;
440 // WARNING: Do not attempt to use |lock_| here as this may be a reentrant call
441 // in response to callbacks firing above.
443 if (!use_new_video_renderering_path_ || sink_started_ == time_progressing_)
444 return;
446 if (time_progressing_) {
447 // If only an EOS frame came in after a seek, the renderer may not have
448 // received the ended event yet though we've posted it.
449 if (!rendered_end_of_stream_)
450 StartSink();
451 } else {
452 StopSink();
456 void VideoRendererImpl::PaintNextReadyFrame_Locked() {
457 DCHECK(!use_new_video_renderering_path_);
458 lock_.AssertAcquired();
460 scoped_refptr<VideoFrame> next_frame = ready_frames_.front();
461 ready_frames_.pop_front();
463 last_media_time_ = ConvertMediaTimestamp(next_frame->timestamp());
465 paint_cb_.Run(next_frame);
467 task_runner_->PostTask(
468 FROM_HERE,
469 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
472 void VideoRendererImpl::DropNextReadyFrame_Locked() {
473 DCHECK(!use_new_video_renderering_path_);
474 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped");
476 lock_.AssertAcquired();
478 last_media_time_ = ConvertMediaTimestamp(ready_frames_.front()->timestamp());
480 ready_frames_.pop_front();
481 frames_dropped_++;
483 task_runner_->PostTask(
484 FROM_HERE,
485 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
488 void VideoRendererImpl::FrameReadyForCopyingToGpuMemoryBuffers(
489 VideoFrameStream::Status status,
490 const scoped_refptr<VideoFrame>& frame) {
491 if (status != VideoFrameStream::OK || start_timestamp_ > frame->timestamp()) {
492 VideoRendererImpl::FrameReady(status, frame);
493 return;
496 DCHECK(frame);
497 gpu_memory_buffer_pool_->MaybeCreateHardwareFrame(
498 frame, base::Bind(&VideoRendererImpl::FrameReady,
499 weak_factory_.GetWeakPtr(), status));
502 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
503 const scoped_refptr<VideoFrame>& frame) {
504 DCHECK(task_runner_->BelongsToCurrentThread());
505 bool start_sink = false;
507 base::AutoLock auto_lock(lock_);
508 DCHECK_NE(state_, kUninitialized);
509 DCHECK_NE(state_, kFlushed);
511 CHECK(pending_read_);
512 pending_read_ = false;
514 if (status == VideoFrameStream::DECODE_ERROR) {
515 DCHECK(!frame.get());
516 PipelineStatus error = PIPELINE_ERROR_DECODE;
517 task_runner_->PostTask(FROM_HERE, base::Bind(error_cb_, error));
518 return;
521 // Already-queued VideoFrameStream ReadCB's can fire after various state
522 // transitions have happened; in that case just drop those frames
523 // immediately.
524 if (state_ == kFlushing)
525 return;
527 DCHECK_EQ(state_, kPlaying);
529 // Can happen when demuxers are preparing for a new Seek().
530 if (!frame.get()) {
531 DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED);
532 return;
535 // In low delay mode, don't accumulate frames that's earlier than the start
536 // time. Otherwise we could declare HAVE_ENOUGH_DATA and start playback
537 // prematurely.
538 if (low_delay_ &&
539 !frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM) &&
540 frame->timestamp() < start_timestamp_) {
541 AttemptRead_Locked();
542 return;
545 if (frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) {
546 DCHECK(!received_end_of_stream_);
547 received_end_of_stream_ = true;
549 // See if we can fire EOS immediately instead of waiting for Render().
550 if (use_new_video_renderering_path_)
551 MaybeFireEndedCallback_Locked(time_progressing_);
552 } else {
553 // Maintain the latest frame decoded so the correct frame is displayed
554 // after prerolling has completed.
555 if (frame->timestamp() <= start_timestamp_) {
556 if (use_new_video_renderering_path_)
557 algorithm_->Reset();
558 ready_frames_.clear();
560 AddReadyFrame_Locked(frame);
563 // Background rendering updates may not be ticking fast enough by itself to
564 // remove expired frames, so give it a boost here by ensuring we don't exit
565 // the decoding cycle too early.
567 // Similarly, if we've paused for underflow, remove all frames which are
568 // before the current media time.
569 const bool have_nothing = buffering_state_ != BUFFERING_HAVE_ENOUGH;
570 const bool have_nothing_and_paused = have_nothing && !sink_started_;
571 if (was_background_rendering_ ||
572 (use_new_video_renderering_path_ && have_nothing_and_paused &&
573 drop_frames_)) {
574 base::TimeTicks expiry_time;
575 if (have_nothing_and_paused) {
576 // Use the current media wall clock time plus the frame duration since
577 // RemoveExpiredFrames() is expecting the end point of an interval (it
578 // will subtract from the given value).
579 std::vector<base::TimeTicks> current_time;
580 wall_clock_time_cb_.Run(std::vector<base::TimeDelta>(), &current_time);
581 expiry_time = current_time[0] + algorithm_->average_frame_duration();
582 } else {
583 expiry_time = tick_clock_->NowTicks();
586 // Prior to rendering the first frame, |have_nothing_and_paused| will be
587 // true, correspondingly the |expiry_time| will be null; in this case
588 // there's no reason to try and remove any frames.
589 if (!expiry_time.is_null()) {
590 const size_t removed_frames =
591 algorithm_->RemoveExpiredFrames(expiry_time);
593 // Frames removed during underflow should be counted as dropped.
594 if (have_nothing_and_paused && removed_frames)
595 frames_dropped_ += removed_frames;
599 // Signal buffering state if we've met our conditions for having enough
600 // data.
601 if (have_nothing && HaveEnoughData_Locked()) {
602 TransitionToHaveEnough_Locked();
603 if (use_new_video_renderering_path_ && !sink_started_ &&
604 !rendered_end_of_stream_) {
605 start_sink = true;
606 render_first_frame_and_stop_ = true;
607 posted_maybe_stop_after_first_paint_ = false;
611 // Always request more decoded video if we have capacity. This serves two
612 // purposes:
613 // 1) Prerolling while paused
614 // 2) Keeps decoding going if video rendering thread starts falling behind
615 AttemptRead_Locked();
618 // If time is progressing, the sink has already been started; this may be true
619 // if we have previously underflowed, yet weren't stopped because of audio.
620 if (use_new_video_renderering_path_ && start_sink) {
621 DCHECK(!sink_started_);
622 StartSink();
626 bool VideoRendererImpl::HaveEnoughData_Locked() {
627 DCHECK_EQ(state_, kPlaying);
629 if (received_end_of_stream_ || !video_frame_stream_->CanReadWithoutStalling())
630 return true;
632 if (HaveReachedBufferingCap())
633 return true;
635 if (use_new_video_renderering_path_ && was_background_rendering_ &&
636 frames_decoded_) {
637 return true;
640 if (!low_delay_)
641 return false;
643 return ready_frames_.size() > 0 ||
644 (use_new_video_renderering_path_ && algorithm_->frames_queued() > 0);
647 void VideoRendererImpl::TransitionToHaveEnough_Locked() {
648 DCHECK(task_runner_->BelongsToCurrentThread());
649 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
651 if (!ready_frames_.empty()) {
652 DCHECK(!use_new_video_renderering_path_);
653 // Because the clock might remain paused in for an undetermined amount
654 // of time (e.g., seeking while paused), paint the first frame.
655 PaintNextReadyFrame_Locked();
658 buffering_state_ = BUFFERING_HAVE_ENOUGH;
659 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH);
662 void VideoRendererImpl::TransitionToHaveNothing() {
663 DCHECK(task_runner_->BelongsToCurrentThread());
665 base::AutoLock auto_lock(lock_);
666 if (buffering_state_ != BUFFERING_HAVE_ENOUGH || HaveEnoughData_Locked())
667 return;
669 buffering_state_ = BUFFERING_HAVE_NOTHING;
670 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
673 void VideoRendererImpl::AddReadyFrame_Locked(
674 const scoped_refptr<VideoFrame>& frame) {
675 DCHECK(task_runner_->BelongsToCurrentThread());
676 lock_.AssertAcquired();
677 DCHECK(!frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM));
679 frames_decoded_++;
681 if (use_new_video_renderering_path_) {
682 algorithm_->EnqueueFrame(frame);
683 return;
686 ready_frames_.push_back(frame);
687 DCHECK_LE(ready_frames_.size(),
688 static_cast<size_t>(limits::kMaxVideoFrames));
690 // Avoid needlessly waking up |thread_| unless playing.
691 if (state_ == kPlaying)
692 frame_available_.Signal();
695 void VideoRendererImpl::AttemptRead() {
696 base::AutoLock auto_lock(lock_);
697 AttemptRead_Locked();
700 void VideoRendererImpl::AttemptRead_Locked() {
701 DCHECK(task_runner_->BelongsToCurrentThread());
702 lock_.AssertAcquired();
704 if (pending_read_ || received_end_of_stream_)
705 return;
707 if (HaveReachedBufferingCap())
708 return;
710 switch (state_) {
711 case kPlaying:
712 pending_read_ = true;
713 video_frame_stream_->Read(frame_ready_cb_);
714 return;
716 case kUninitialized:
717 case kInitializing:
718 case kFlushing:
719 case kFlushed:
720 return;
724 void VideoRendererImpl::OnVideoFrameStreamResetDone() {
725 base::AutoLock auto_lock(lock_);
726 DCHECK_EQ(kFlushing, state_);
727 DCHECK(!pending_read_);
728 DCHECK(ready_frames_.empty());
729 DCHECK(!received_end_of_stream_);
730 DCHECK(!rendered_end_of_stream_);
731 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
733 state_ = kFlushed;
734 latest_possible_paint_time_ = last_media_time_ = base::TimeTicks();
735 base::ResetAndReturn(&flush_cb_).Run();
738 void VideoRendererImpl::UpdateStatsAndWait_Locked(
739 base::TimeDelta wait_duration) {
740 lock_.AssertAcquired();
741 DCHECK_GE(frames_decoded_, 0);
742 DCHECK_GE(frames_dropped_, 0);
744 if (frames_decoded_ || frames_dropped_) {
745 PipelineStatistics statistics;
746 statistics.video_frames_decoded = frames_decoded_;
747 statistics.video_frames_dropped = frames_dropped_;
748 task_runner_->PostTask(FROM_HERE, base::Bind(statistics_cb_, statistics));
750 frames_decoded_ = 0;
751 frames_dropped_ = 0;
754 if (wait_duration > base::TimeDelta())
755 frame_available_.TimedWait(wait_duration);
758 void VideoRendererImpl::MaybeStopSinkAfterFirstPaint() {
759 DCHECK(task_runner_->BelongsToCurrentThread());
760 DCHECK(use_new_video_renderering_path_);
762 if (!time_progressing_ && sink_started_)
763 StopSink();
765 base::AutoLock auto_lock(lock_);
766 render_first_frame_and_stop_ = false;
769 bool VideoRendererImpl::HaveReachedBufferingCap() {
770 DCHECK(task_runner_->BelongsToCurrentThread());
771 const size_t kMaxVideoFrames = limits::kMaxVideoFrames;
773 if (use_new_video_renderering_path_) {
774 // When the display rate is less than the frame rate, the effective frames
775 // queued may be much smaller than the actual number of frames queued. Here
776 // we ensure that frames_queued() doesn't get excessive.
777 return algorithm_->EffectiveFramesQueued() >= kMaxVideoFrames ||
778 algorithm_->frames_queued() >= 3 * kMaxVideoFrames;
781 return ready_frames_.size() >= kMaxVideoFrames;
784 void VideoRendererImpl::StartSink() {
785 DCHECK(task_runner_->BelongsToCurrentThread());
786 DCHECK_GT(algorithm_->frames_queued(), 0u);
787 sink_started_ = true;
788 was_background_rendering_ = false;
789 sink_->Start(this);
792 void VideoRendererImpl::StopSink() {
793 DCHECK(task_runner_->BelongsToCurrentThread());
794 sink_->Stop();
795 algorithm_->set_time_stopped();
796 sink_started_ = false;
797 was_background_rendering_ = false;
800 size_t VideoRendererImpl::MaybeFireEndedCallback_Locked(bool time_progressing) {
801 lock_.AssertAcquired();
803 // If there's only one frame in the video or Render() was never called, the
804 // algorithm will have one frame linger indefinitely. So in cases where the
805 // frame duration is unknown and we've received EOS, fire it once we get down
806 // to a single frame.
807 const size_t effective_frames = algorithm_->EffectiveFramesQueued();
809 // Don't fire ended if we haven't received EOS or have already done so.
810 if (!received_end_of_stream_ || rendered_end_of_stream_)
811 return effective_frames;
813 // Don't fire ended if time isn't moving and we have frames.
814 if (!time_progressing && algorithm_->frames_queued())
815 return effective_frames;
817 // Fire ended if we have no more effective frames or only ever had one frame.
818 if (!effective_frames ||
819 (algorithm_->frames_queued() == 1u &&
820 algorithm_->average_frame_duration() == base::TimeDelta())) {
821 rendered_end_of_stream_ = true;
822 task_runner_->PostTask(FROM_HERE, ended_cb_);
825 return effective_frames;
828 base::TimeTicks VideoRendererImpl::ConvertMediaTimestamp(
829 base::TimeDelta media_time) {
830 std::vector<base::TimeDelta> media_times(1, media_time);
831 std::vector<base::TimeTicks> wall_clock_times;
832 if (!wall_clock_time_cb_.Run(media_times, &wall_clock_times))
833 return base::TimeTicks();
834 return wall_clock_times[0];
837 } // namespace media