Gallery: change size of crop overlay to cover entire window.
[chromium-blink-merge.git] / media / renderers / video_renderer_impl.cc
blob9562a5304b7d96e035a135ddbc063d7e053ba930
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 &&
40 !base::StartsWith(group_name, "Disabled",
41 base::CompareCase::SENSITIVE);
44 VideoRendererImpl::VideoRendererImpl(
45 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
46 VideoRendererSink* sink,
47 ScopedVector<VideoDecoder> decoders,
48 bool drop_frames,
49 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
50 const scoped_refptr<MediaLog>& media_log)
51 : task_runner_(task_runner),
52 use_new_video_renderering_path_(ShouldUseVideoRenderingPath()),
53 sink_(sink),
54 sink_started_(false),
55 video_frame_stream_(
56 new VideoFrameStream(task_runner, decoders.Pass(), media_log)),
57 gpu_memory_buffer_pool_(
58 new GpuMemoryBufferVideoFramePool(task_runner, gpu_factories)),
59 low_delay_(false),
60 received_end_of_stream_(false),
61 rendered_end_of_stream_(false),
62 frame_available_(&lock_),
63 state_(kUninitialized),
64 thread_(),
65 pending_read_(false),
66 drop_frames_(drop_frames),
67 buffering_state_(BUFFERING_HAVE_NOTHING),
68 frames_decoded_(0),
69 frames_dropped_(0),
70 is_shutting_down_(false),
71 tick_clock_(new base::DefaultTickClock()),
72 was_background_rendering_(false),
73 time_progressing_(false),
74 render_first_frame_and_stop_(false),
75 posted_maybe_stop_after_first_paint_(false),
76 weak_factory_(this) {
79 VideoRendererImpl::~VideoRendererImpl() {
80 DCHECK(task_runner_->BelongsToCurrentThread());
82 if (!use_new_video_renderering_path_) {
83 base::AutoLock auto_lock(lock_);
84 is_shutting_down_ = true;
85 frame_available_.Signal();
88 if (!thread_.is_null())
89 base::PlatformThread::Join(thread_);
91 if (!init_cb_.is_null())
92 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT);
94 if (!flush_cb_.is_null())
95 base::ResetAndReturn(&flush_cb_).Run();
97 if (use_new_video_renderering_path_ && sink_started_)
98 StopSink();
101 void VideoRendererImpl::Flush(const base::Closure& callback) {
102 DVLOG(1) << __FUNCTION__;
103 DCHECK(task_runner_->BelongsToCurrentThread());
105 if (use_new_video_renderering_path_ && sink_started_)
106 StopSink();
108 base::AutoLock auto_lock(lock_);
109 DCHECK_EQ(state_, kPlaying);
110 flush_cb_ = callback;
111 state_ = kFlushing;
113 // This is necessary if the |video_frame_stream_| has already seen an end of
114 // stream and needs to drain it before flushing it.
115 ready_frames_.clear();
116 if (buffering_state_ != BUFFERING_HAVE_NOTHING) {
117 buffering_state_ = BUFFERING_HAVE_NOTHING;
118 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
120 received_end_of_stream_ = false;
121 rendered_end_of_stream_ = false;
123 if (use_new_video_renderering_path_)
124 algorithm_->Reset();
126 video_frame_stream_->Reset(
127 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone,
128 weak_factory_.GetWeakPtr()));
131 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) {
132 DVLOG(1) << __FUNCTION__ << "(" << timestamp.InMicroseconds() << ")";
133 DCHECK(task_runner_->BelongsToCurrentThread());
134 base::AutoLock auto_lock(lock_);
135 DCHECK_EQ(state_, kFlushed);
136 DCHECK(!pending_read_);
137 DCHECK(ready_frames_.empty());
138 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
140 state_ = kPlaying;
141 start_timestamp_ = timestamp;
142 AttemptRead_Locked();
145 void VideoRendererImpl::Initialize(
146 DemuxerStream* stream,
147 const PipelineStatusCB& init_cb,
148 const SetDecryptorReadyCB& set_decryptor_ready_cb,
149 const StatisticsCB& statistics_cb,
150 const BufferingStateCB& buffering_state_cb,
151 const base::Closure& ended_cb,
152 const PipelineStatusCB& error_cb,
153 const TimeSource::WallClockTimeCB& wall_clock_time_cb,
154 const base::Closure& waiting_for_decryption_key_cb) {
155 DCHECK(task_runner_->BelongsToCurrentThread());
156 base::AutoLock auto_lock(lock_);
157 DCHECK(stream);
158 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO);
159 DCHECK(!init_cb.is_null());
160 DCHECK(!statistics_cb.is_null());
161 DCHECK(!buffering_state_cb.is_null());
162 DCHECK(!ended_cb.is_null());
163 DCHECK(!wall_clock_time_cb.is_null());
164 DCHECK_EQ(kUninitialized, state_);
165 DCHECK(!render_first_frame_and_stop_);
166 DCHECK(!posted_maybe_stop_after_first_paint_);
167 DCHECK(!was_background_rendering_);
168 DCHECK(!time_progressing_);
170 low_delay_ = (stream->liveness() == DemuxerStream::LIVENESS_LIVE);
172 // Always post |init_cb_| because |this| could be destroyed if initialization
173 // failed.
174 init_cb_ = BindToCurrentLoop(init_cb);
176 // Always post |buffering_state_cb_| because it may otherwise invoke reentrant
177 // calls to OnTimeStateChanged() under lock, which can deadlock the compositor
178 // and media threads.
179 buffering_state_cb_ = BindToCurrentLoop(buffering_state_cb);
181 statistics_cb_ = statistics_cb;
182 paint_cb_ = base::Bind(&VideoRendererSink::PaintFrameUsingOldRenderingPath,
183 base::Unretained(sink_));
184 ended_cb_ = ended_cb;
185 error_cb_ = error_cb;
186 wall_clock_time_cb_ = wall_clock_time_cb;
187 state_ = kInitializing;
189 video_frame_stream_->Initialize(
190 stream, base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized,
191 weak_factory_.GetWeakPtr()),
192 set_decryptor_ready_cb, statistics_cb, waiting_for_decryption_key_cb);
195 scoped_refptr<VideoFrame> VideoRendererImpl::Render(
196 base::TimeTicks deadline_min,
197 base::TimeTicks deadline_max,
198 bool background_rendering) {
199 base::AutoLock auto_lock(lock_);
200 DCHECK(use_new_video_renderering_path_);
201 DCHECK_EQ(state_, kPlaying);
203 size_t frames_dropped = 0;
204 scoped_refptr<VideoFrame> result =
205 algorithm_->Render(deadline_min, deadline_max, &frames_dropped);
207 // Due to how the |algorithm_| holds frames, this should never be null if
208 // we've had a proper startup sequence.
209 DCHECK(result);
211 // Declare HAVE_NOTHING if we reach a state where we can't progress playback
212 // any further. We don't want to do this if we've already done so, reached
213 // end of stream, or have frames available. We also don't want to do this in
214 // background rendering mode unless this isn't the first background render
215 // tick and we haven't seen any decoded frames since the last one.
217 // We use the inverse of |render_first_frame_and_stop_| as a proxy for the
218 // value of |time_progressing_| here since we can't access it from the
219 // compositor thread. If we're here (in Render()) the sink must have been
220 // started -- but if it was started only to render the first frame and stop,
221 // then |time_progressing_| is likely false. If we're still in Render() when
222 // |render_first_frame_and_stop_| is false, then |time_progressing_| is true.
223 // If |time_progressing_| is actually true when |render_first_frame_and_stop_|
224 // is also true, then the ended callback will be harmlessly delayed until
225 // MaybeStopSinkAfterFirstPaint() runs and the next Render() call comes in.
226 const size_t effective_frames =
227 MaybeFireEndedCallback_Locked(!render_first_frame_and_stop_);
228 if (buffering_state_ == BUFFERING_HAVE_ENOUGH && !received_end_of_stream_ &&
229 !effective_frames && (!background_rendering ||
230 (!frames_decoded_ && was_background_rendering_))) {
231 // Do not set |buffering_state_| here as the lock in FrameReady() may be
232 // held already and it fire the state changes in the wrong order.
233 task_runner_->PostTask(
234 FROM_HERE, base::Bind(&VideoRendererImpl::TransitionToHaveNothing,
235 weak_factory_.GetWeakPtr()));
238 // We don't count dropped frames in the background to avoid skewing the count
239 // and impacting JavaScript visible metrics used by web developers.
241 // Just after resuming from background rendering, we also don't count the
242 // dropped frames since they are likely just dropped due to being too old.
243 if (!background_rendering && !was_background_rendering_)
244 frames_dropped_ += frames_dropped;
245 UpdateStatsAndWait_Locked(base::TimeDelta());
246 was_background_rendering_ = background_rendering;
248 // After painting the first frame, if playback hasn't started, we post a
249 // delayed task to request that the sink be stopped. The task is delayed to
250 // give videos with autoplay time to start.
252 // OnTimeStateChanged() will clear this flag if time starts before we get here
253 // and MaybeStopSinkAfterFirstPaint() will ignore this request if time starts
254 // before the call executes.
255 if (render_first_frame_and_stop_ && !posted_maybe_stop_after_first_paint_) {
256 posted_maybe_stop_after_first_paint_ = true;
257 task_runner_->PostDelayedTask(
258 FROM_HERE, base::Bind(&VideoRendererImpl::MaybeStopSinkAfterFirstPaint,
259 weak_factory_.GetWeakPtr()),
260 base::TimeDelta::FromMilliseconds(250));
263 // Always post this task, it will acquire new frames if necessary and since it
264 // happens on another thread, even if we don't have room in the queue now, by
265 // the time it runs (may be delayed up to 50ms for complex decodes!) we might.
266 task_runner_->PostTask(FROM_HERE, base::Bind(&VideoRendererImpl::AttemptRead,
267 weak_factory_.GetWeakPtr()));
269 return result;
272 void VideoRendererImpl::OnFrameDropped() {
273 base::AutoLock auto_lock(lock_);
274 DCHECK(use_new_video_renderering_path_);
275 algorithm_->OnLastFrameDropped();
278 void VideoRendererImpl::CreateVideoThread() {
279 // This may fail and cause a crash if there are too many threads created in
280 // the current process. See http://crbug.com/443291
281 const base::ThreadPriority priority =
282 #if defined(OS_WIN)
283 // Bump up our priority so our sleeping is more accurate.
284 // TODO(scherkus): find out if this is necessary, but it seems to help.
285 base::ThreadPriority::DISPLAY;
286 #else
287 base::ThreadPriority::NORMAL;
288 #endif
289 CHECK(base::PlatformThread::CreateWithPriority(0, this, &thread_, priority));
292 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) {
293 DCHECK(task_runner_->BelongsToCurrentThread());
294 base::AutoLock auto_lock(lock_);
295 DCHECK_EQ(state_, kInitializing);
297 if (!success) {
298 state_ = kUninitialized;
299 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED);
300 return;
303 // We're all good! Consider ourselves flushed. (ThreadMain() should never
304 // see us in the kUninitialized state).
305 // Since we had an initial Preroll(), we consider ourself flushed, because we
306 // have not populated any buffers yet.
307 state_ = kFlushed;
309 if (use_new_video_renderering_path_) {
310 algorithm_.reset(new VideoRendererAlgorithm(wall_clock_time_cb_));
311 if (!drop_frames_)
312 algorithm_->disable_frame_dropping();
313 } else {
314 CreateVideoThread();
317 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
320 // PlatformThread::Delegate implementation.
321 void VideoRendererImpl::ThreadMain() {
322 DCHECK(!use_new_video_renderering_path_);
323 base::PlatformThread::SetName("CrVideoRenderer");
325 // The number of milliseconds to idle when we do not have anything to do.
326 // Nothing special about the value, other than we're being more OS-friendly
327 // than sleeping for 1 millisecond.
329 // TODO(scherkus): switch to pure event-driven frame timing instead of this
330 // kIdleTimeDelta business http://crbug.com/106874
331 const base::TimeDelta kIdleTimeDelta =
332 base::TimeDelta::FromMilliseconds(10);
334 for (;;) {
335 base::AutoLock auto_lock(lock_);
337 // Thread exit condition.
338 if (is_shutting_down_)
339 return;
341 // Remain idle as long as we're not playing.
342 if (state_ != kPlaying || buffering_state_ != BUFFERING_HAVE_ENOUGH) {
343 UpdateStatsAndWait_Locked(kIdleTimeDelta);
344 continue;
347 base::TimeTicks now = tick_clock_->NowTicks();
349 // Remain idle until we have the next frame ready for rendering.
350 if (ready_frames_.empty()) {
351 base::TimeDelta wait_time = kIdleTimeDelta;
352 if (received_end_of_stream_) {
353 if (!rendered_end_of_stream_) {
354 rendered_end_of_stream_ = true;
355 task_runner_->PostTask(FROM_HERE, ended_cb_);
357 } else if (now >= latest_possible_paint_time_) {
358 // Declare HAVE_NOTHING if we don't have another frame by the time we
359 // are ready to paint the next one.
360 buffering_state_ = BUFFERING_HAVE_NOTHING;
361 task_runner_->PostTask(
362 FROM_HERE, base::Bind(buffering_state_cb_, BUFFERING_HAVE_NOTHING));
363 } else {
364 wait_time = std::min(kIdleTimeDelta, latest_possible_paint_time_ - now);
367 UpdateStatsAndWait_Locked(wait_time);
368 continue;
371 base::TimeTicks target_paint_time =
372 ConvertMediaTimestamp(ready_frames_.front()->timestamp());
374 // If media time has stopped, don't attempt to paint any more frames.
375 if (target_paint_time.is_null()) {
376 UpdateStatsAndWait_Locked(kIdleTimeDelta);
377 continue;
380 // Deadline is defined as the duration between this frame and the next
381 // frame, using the delta between this frame and the previous frame as the
382 // assumption for frame duration.
384 // TODO(scherkus): This can be vastly improved. Use a histogram to measure
385 // the accuracy of our frame timing code. http://crbug.com/149829
386 if (last_media_time_.is_null()) {
387 latest_possible_paint_time_ = now;
388 } else {
389 base::TimeDelta duration = target_paint_time - last_media_time_;
390 latest_possible_paint_time_ = target_paint_time + duration;
393 // Remain idle until we've reached our target paint window.
394 if (now < target_paint_time) {
395 UpdateStatsAndWait_Locked(
396 std::min(target_paint_time - now, kIdleTimeDelta));
397 continue;
400 if (ready_frames_.size() > 1 && now > latest_possible_paint_time_ &&
401 drop_frames_) {
402 DropNextReadyFrame_Locked();
403 continue;
406 // Congratulations! You've made it past the video frame timing gauntlet.
408 // At this point enough time has passed that the next frame that ready for
409 // rendering.
410 PaintNextReadyFrame_Locked();
414 void VideoRendererImpl::SetTickClockForTesting(
415 scoped_ptr<base::TickClock> tick_clock) {
416 tick_clock_.swap(tick_clock);
419 void VideoRendererImpl::OnTimeStateChanged(bool time_progressing) {
420 DCHECK(task_runner_->BelongsToCurrentThread());
421 time_progressing_ = time_progressing;
423 // WARNING: Do not attempt to use |lock_| here as this may be a reentrant call
424 // in response to callbacks firing above.
426 if (!use_new_video_renderering_path_ || sink_started_ == time_progressing_)
427 return;
429 if (time_progressing_) {
430 // If only an EOS frame came in after a seek, the renderer may not have
431 // received the ended event yet though we've posted it.
432 if (!rendered_end_of_stream_)
433 StartSink();
434 } else {
435 StopSink();
439 void VideoRendererImpl::PaintNextReadyFrame_Locked() {
440 DCHECK(!use_new_video_renderering_path_);
441 lock_.AssertAcquired();
443 scoped_refptr<VideoFrame> next_frame = ready_frames_.front();
444 ready_frames_.pop_front();
446 last_media_time_ = ConvertMediaTimestamp(next_frame->timestamp());
448 paint_cb_.Run(next_frame);
450 task_runner_->PostTask(
451 FROM_HERE,
452 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
455 void VideoRendererImpl::DropNextReadyFrame_Locked() {
456 DCHECK(!use_new_video_renderering_path_);
457 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped");
459 lock_.AssertAcquired();
461 last_media_time_ = ConvertMediaTimestamp(ready_frames_.front()->timestamp());
463 ready_frames_.pop_front();
464 frames_dropped_++;
466 task_runner_->PostTask(
467 FROM_HERE,
468 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
471 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
472 const scoped_refptr<VideoFrame>& frame) {
473 DCHECK(task_runner_->BelongsToCurrentThread());
474 bool start_sink = false;
476 base::AutoLock auto_lock(lock_);
477 DCHECK_NE(state_, kUninitialized);
478 DCHECK_NE(state_, kFlushed);
480 CHECK(pending_read_);
481 pending_read_ = false;
483 if (status == VideoFrameStream::DECODE_ERROR) {
484 DCHECK(!frame.get());
485 PipelineStatus error = PIPELINE_ERROR_DECODE;
486 task_runner_->PostTask(FROM_HERE, base::Bind(error_cb_, error));
487 return;
490 // Already-queued VideoFrameStream ReadCB's can fire after various state
491 // transitions have happened; in that case just drop those frames
492 // immediately.
493 if (state_ == kFlushing)
494 return;
496 DCHECK_EQ(state_, kPlaying);
498 // Can happen when demuxers are preparing for a new Seek().
499 if (!frame.get()) {
500 DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED);
501 return;
504 // In low delay mode, don't accumulate frames that's earlier than the start
505 // time. Otherwise we could declare HAVE_ENOUGH_DATA and start playback
506 // prematurely.
507 if (low_delay_ &&
508 !frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM) &&
509 frame->timestamp() < start_timestamp_) {
510 AttemptRead_Locked();
511 return;
514 if (frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) {
515 DCHECK(!received_end_of_stream_);
516 received_end_of_stream_ = true;
518 // See if we can fire EOS immediately instead of waiting for Render().
519 if (use_new_video_renderering_path_)
520 MaybeFireEndedCallback_Locked(time_progressing_);
521 } else {
522 // Maintain the latest frame decoded so the correct frame is displayed
523 // after prerolling has completed.
524 if (frame->timestamp() <= start_timestamp_) {
525 if (use_new_video_renderering_path_)
526 algorithm_->Reset();
527 ready_frames_.clear();
529 AddReadyFrame_Locked(frame);
532 // Background rendering updates may not be ticking fast enough by itself to
533 // remove expired frames, so give it a boost here by ensuring we don't exit
534 // the decoding cycle too early.
536 // Similarly, if we've paused for underflow, remove all frames which are
537 // before the current media time.
538 const bool have_nothing = buffering_state_ != BUFFERING_HAVE_ENOUGH;
539 const bool have_nothing_and_paused = have_nothing && !sink_started_;
540 if (was_background_rendering_ ||
541 (use_new_video_renderering_path_ && have_nothing_and_paused &&
542 drop_frames_)) {
543 base::TimeTicks expiry_time;
544 if (have_nothing_and_paused) {
545 // Use the current media wall clock time plus the frame duration since
546 // RemoveExpiredFrames() is expecting the end point of an interval (it
547 // will subtract from the given value).
548 std::vector<base::TimeTicks> current_time;
549 wall_clock_time_cb_.Run(std::vector<base::TimeDelta>(), &current_time);
550 expiry_time = current_time[0] + algorithm_->average_frame_duration();
551 } else {
552 expiry_time = tick_clock_->NowTicks();
555 // Prior to rendering the first frame, |have_nothing_and_paused| will be
556 // true, correspondingly the |expiry_time| will be null; in this case
557 // there's no reason to try and remove any frames.
558 if (!expiry_time.is_null()) {
559 const size_t removed_frames =
560 algorithm_->RemoveExpiredFrames(expiry_time);
562 // Frames removed during underflow should be counted as dropped.
563 if (have_nothing_and_paused && removed_frames)
564 frames_dropped_ += removed_frames;
568 // Signal buffering state if we've met our conditions for having enough
569 // data.
570 if (have_nothing && HaveEnoughData_Locked()) {
571 TransitionToHaveEnough_Locked();
572 if (use_new_video_renderering_path_ && !sink_started_ &&
573 !rendered_end_of_stream_) {
574 start_sink = true;
575 render_first_frame_and_stop_ = true;
576 posted_maybe_stop_after_first_paint_ = false;
580 // Always request more decoded video if we have capacity. This serves two
581 // purposes:
582 // 1) Prerolling while paused
583 // 2) Keeps decoding going if video rendering thread starts falling behind
584 AttemptRead_Locked();
587 // If time is progressing, the sink has already been started; this may be true
588 // if we have previously underflowed, yet weren't stopped because of audio.
589 if (use_new_video_renderering_path_ && start_sink) {
590 DCHECK(!sink_started_);
591 StartSink();
595 bool VideoRendererImpl::HaveEnoughData_Locked() {
596 DCHECK_EQ(state_, kPlaying);
598 if (received_end_of_stream_ || !video_frame_stream_->CanReadWithoutStalling())
599 return true;
601 if (HaveReachedBufferingCap())
602 return true;
604 if (use_new_video_renderering_path_ && was_background_rendering_ &&
605 frames_decoded_) {
606 return true;
609 if (!low_delay_)
610 return false;
612 return ready_frames_.size() > 0 ||
613 (use_new_video_renderering_path_ && algorithm_->frames_queued() > 0);
616 void VideoRendererImpl::TransitionToHaveEnough_Locked() {
617 DCHECK(task_runner_->BelongsToCurrentThread());
618 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
620 if (!ready_frames_.empty()) {
621 DCHECK(!use_new_video_renderering_path_);
622 // Because the clock might remain paused in for an undetermined amount
623 // of time (e.g., seeking while paused), paint the first frame.
624 PaintNextReadyFrame_Locked();
627 buffering_state_ = BUFFERING_HAVE_ENOUGH;
628 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH);
631 void VideoRendererImpl::TransitionToHaveNothing() {
632 DCHECK(task_runner_->BelongsToCurrentThread());
634 base::AutoLock auto_lock(lock_);
635 if (buffering_state_ != BUFFERING_HAVE_ENOUGH || HaveEnoughData_Locked())
636 return;
638 buffering_state_ = BUFFERING_HAVE_NOTHING;
639 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
642 void VideoRendererImpl::AddReadyFrame_Locked(
643 const scoped_refptr<VideoFrame>& frame) {
644 DCHECK(task_runner_->BelongsToCurrentThread());
645 lock_.AssertAcquired();
646 DCHECK(!frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM));
648 frames_decoded_++;
650 if (use_new_video_renderering_path_) {
651 algorithm_->EnqueueFrame(frame);
652 return;
655 ready_frames_.push_back(frame);
656 DCHECK_LE(ready_frames_.size(),
657 static_cast<size_t>(limits::kMaxVideoFrames));
659 // Avoid needlessly waking up |thread_| unless playing.
660 if (state_ == kPlaying)
661 frame_available_.Signal();
664 void VideoRendererImpl::AttemptRead() {
665 base::AutoLock auto_lock(lock_);
666 AttemptRead_Locked();
669 void VideoRendererImpl::AttemptRead_Locked() {
670 DCHECK(task_runner_->BelongsToCurrentThread());
671 lock_.AssertAcquired();
673 if (pending_read_ || received_end_of_stream_)
674 return;
676 if (HaveReachedBufferingCap())
677 return;
679 switch (state_) {
680 case kPlaying:
681 pending_read_ = true;
682 video_frame_stream_->Read(base::Bind(&VideoRendererImpl::FrameReady,
683 weak_factory_.GetWeakPtr()));
684 return;
686 case kUninitialized:
687 case kInitializing:
688 case kFlushing:
689 case kFlushed:
690 return;
694 void VideoRendererImpl::OnVideoFrameStreamResetDone() {
695 base::AutoLock auto_lock(lock_);
696 DCHECK_EQ(kFlushing, state_);
697 DCHECK(!pending_read_);
698 DCHECK(ready_frames_.empty());
699 DCHECK(!received_end_of_stream_);
700 DCHECK(!rendered_end_of_stream_);
701 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
703 state_ = kFlushed;
704 latest_possible_paint_time_ = last_media_time_ = base::TimeTicks();
705 base::ResetAndReturn(&flush_cb_).Run();
708 void VideoRendererImpl::UpdateStatsAndWait_Locked(
709 base::TimeDelta wait_duration) {
710 lock_.AssertAcquired();
711 DCHECK_GE(frames_decoded_, 0);
712 DCHECK_GE(frames_dropped_, 0);
714 if (frames_decoded_ || frames_dropped_) {
715 PipelineStatistics statistics;
716 statistics.video_frames_decoded = frames_decoded_;
717 statistics.video_frames_dropped = frames_dropped_;
718 task_runner_->PostTask(FROM_HERE, base::Bind(statistics_cb_, statistics));
720 frames_decoded_ = 0;
721 frames_dropped_ = 0;
724 if (wait_duration > base::TimeDelta())
725 frame_available_.TimedWait(wait_duration);
728 void VideoRendererImpl::MaybeStopSinkAfterFirstPaint() {
729 DCHECK(task_runner_->BelongsToCurrentThread());
730 DCHECK(use_new_video_renderering_path_);
732 if (!time_progressing_ && sink_started_)
733 StopSink();
735 base::AutoLock auto_lock(lock_);
736 render_first_frame_and_stop_ = false;
739 bool VideoRendererImpl::HaveReachedBufferingCap() {
740 DCHECK(task_runner_->BelongsToCurrentThread());
741 const size_t kMaxVideoFrames = limits::kMaxVideoFrames;
743 if (use_new_video_renderering_path_) {
744 // When the display rate is less than the frame rate, the effective frames
745 // queued may be much smaller than the actual number of frames queued. Here
746 // we ensure that frames_queued() doesn't get excessive.
747 return algorithm_->EffectiveFramesQueued() >= kMaxVideoFrames ||
748 algorithm_->frames_queued() >= 3 * kMaxVideoFrames;
751 return ready_frames_.size() >= kMaxVideoFrames;
754 void VideoRendererImpl::StartSink() {
755 DCHECK(task_runner_->BelongsToCurrentThread());
756 DCHECK_GT(algorithm_->frames_queued(), 0u);
757 sink_started_ = true;
758 was_background_rendering_ = false;
759 sink_->Start(this);
762 void VideoRendererImpl::StopSink() {
763 DCHECK(task_runner_->BelongsToCurrentThread());
764 sink_->Stop();
765 algorithm_->set_time_stopped();
766 sink_started_ = false;
767 was_background_rendering_ = false;
770 size_t VideoRendererImpl::MaybeFireEndedCallback_Locked(bool time_progressing) {
771 lock_.AssertAcquired();
773 // If there's only one frame in the video or Render() was never called, the
774 // algorithm will have one frame linger indefinitely. So in cases where the
775 // frame duration is unknown and we've received EOS, fire it once we get down
776 // to a single frame.
777 const size_t effective_frames = algorithm_->EffectiveFramesQueued();
779 // Don't fire ended if we haven't received EOS or have already done so.
780 if (!received_end_of_stream_ || rendered_end_of_stream_)
781 return effective_frames;
783 // Don't fire ended if time isn't moving and we have frames.
784 if (!time_progressing && algorithm_->frames_queued())
785 return effective_frames;
787 // Fire ended if we have no more effective frames or only ever had one frame.
788 if (!effective_frames ||
789 (algorithm_->frames_queued() == 1u &&
790 algorithm_->average_frame_duration() == base::TimeDelta())) {
791 rendered_end_of_stream_ = true;
792 task_runner_->PostTask(FROM_HERE, ended_cb_);
795 return effective_frames;
798 base::TimeTicks VideoRendererImpl::ConvertMediaTimestamp(
799 base::TimeDelta media_time) {
800 std::vector<base::TimeDelta> media_times(1, media_time);
801 std::vector<base::TimeTicks> wall_clock_times;
802 if (!wall_clock_time_cb_.Run(media_times, &wall_clock_times))
803 return base::TimeTicks();
804 return wall_clock_times[0];
807 } // namespace media