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"
8 #include "base/callback.h"
9 #include "base/callback_helpers.h"
10 #include "base/command_line.h"
11 #include "base/location.h"
12 #include "base/metrics/field_trial.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/strings/string_util.h"
15 #include "base/threading/platform_thread.h"
16 #include "base/time/default_tick_clock.h"
17 #include "base/trace_event/trace_event.h"
18 #include "media/base/bind_to_current_loop.h"
19 #include "media/base/buffers.h"
20 #include "media/base/limits.h"
21 #include "media/base/media_switches.h"
22 #include "media/base/pipeline.h"
23 #include "media/base/video_frame.h"
27 // TODO(dalecurtis): This experiment is temporary and should be removed once we
28 // have enough data to support the primacy of the new video rendering path; see
29 // http://crbug.com/485699 for details.
30 static bool ShouldUseVideoRenderingPath() {
31 // Note: It's important to query the field trial state first, to ensure that
32 // UMA reports the correct group.
33 const std::string group_name
=
34 base::FieldTrialList::FindFullName("NewVideoRendererTrial");
35 const bool disabled_via_cli
=
36 base::CommandLine::ForCurrentProcess()->HasSwitch(
37 switches::kDisableNewVideoRenderer
);
38 return !disabled_via_cli
&& !StartsWithASCII(group_name
, "Disabled", true);
41 VideoRendererImpl::VideoRendererImpl(
42 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
,
43 VideoRendererSink
* sink
,
44 ScopedVector
<VideoDecoder
> decoders
,
46 const scoped_refptr
<MediaLog
>& media_log
)
47 : task_runner_(task_runner
),
48 use_new_video_renderering_path_(ShouldUseVideoRenderingPath()),
52 new VideoFrameStream(task_runner
, decoders
.Pass(), media_log
)),
54 received_end_of_stream_(false),
55 rendered_end_of_stream_(false),
56 frame_available_(&lock_
),
57 state_(kUninitialized
),
60 drop_frames_(drop_frames
),
61 buffering_state_(BUFFERING_HAVE_NOTHING
),
64 is_shutting_down_(false),
65 tick_clock_(new base::DefaultTickClock()),
66 was_background_rendering_(false),
67 time_progressing_(false),
68 render_first_frame_and_stop_(false),
69 posted_maybe_stop_after_first_paint_(false),
73 VideoRendererImpl::~VideoRendererImpl() {
74 DCHECK(task_runner_
->BelongsToCurrentThread());
76 if (!use_new_video_renderering_path_
) {
77 base::AutoLock
auto_lock(lock_
);
78 is_shutting_down_
= true;
79 frame_available_
.Signal();
82 if (!thread_
.is_null())
83 base::PlatformThread::Join(thread_
);
85 if (!init_cb_
.is_null())
86 base::ResetAndReturn(&init_cb_
).Run(PIPELINE_ERROR_ABORT
);
88 if (!flush_cb_
.is_null())
89 base::ResetAndReturn(&flush_cb_
).Run();
91 if (use_new_video_renderering_path_
&& sink_started_
)
95 void VideoRendererImpl::Flush(const base::Closure
& callback
) {
96 DVLOG(1) << __FUNCTION__
;
97 DCHECK(task_runner_
->BelongsToCurrentThread());
99 if (use_new_video_renderering_path_
&& sink_started_
)
102 base::AutoLock
auto_lock(lock_
);
103 DCHECK_EQ(state_
, kPlaying
);
104 flush_cb_
= callback
;
107 // This is necessary if the |video_frame_stream_| has already seen an end of
108 // stream and needs to drain it before flushing it.
109 ready_frames_
.clear();
110 if (buffering_state_
!= BUFFERING_HAVE_NOTHING
) {
111 buffering_state_
= BUFFERING_HAVE_NOTHING
;
112 buffering_state_cb_
.Run(BUFFERING_HAVE_NOTHING
);
114 received_end_of_stream_
= false;
115 rendered_end_of_stream_
= false;
117 if (use_new_video_renderering_path_
)
120 video_frame_stream_
->Reset(
121 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone
,
122 weak_factory_
.GetWeakPtr()));
125 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp
) {
126 DVLOG(1) << __FUNCTION__
<< "(" << timestamp
.InMicroseconds() << ")";
127 DCHECK(task_runner_
->BelongsToCurrentThread());
128 base::AutoLock
auto_lock(lock_
);
129 DCHECK_EQ(state_
, kFlushed
);
130 DCHECK(!pending_read_
);
131 DCHECK(ready_frames_
.empty());
132 DCHECK_EQ(buffering_state_
, BUFFERING_HAVE_NOTHING
);
135 start_timestamp_
= timestamp
;
136 AttemptRead_Locked();
139 void VideoRendererImpl::Initialize(
140 DemuxerStream
* stream
,
141 const PipelineStatusCB
& init_cb
,
142 const SetDecryptorReadyCB
& set_decryptor_ready_cb
,
143 const StatisticsCB
& statistics_cb
,
144 const BufferingStateCB
& buffering_state_cb
,
145 const base::Closure
& ended_cb
,
146 const PipelineStatusCB
& error_cb
,
147 const TimeSource::WallClockTimeCB
& wall_clock_time_cb
,
148 const base::Closure
& waiting_for_decryption_key_cb
) {
149 DCHECK(task_runner_
->BelongsToCurrentThread());
150 base::AutoLock
auto_lock(lock_
);
152 DCHECK_EQ(stream
->type(), DemuxerStream::VIDEO
);
153 DCHECK(!init_cb
.is_null());
154 DCHECK(!statistics_cb
.is_null());
155 DCHECK(!buffering_state_cb
.is_null());
156 DCHECK(!ended_cb
.is_null());
157 DCHECK(!wall_clock_time_cb
.is_null());
158 DCHECK_EQ(kUninitialized
, state_
);
159 DCHECK(!render_first_frame_and_stop_
);
160 DCHECK(!posted_maybe_stop_after_first_paint_
);
161 DCHECK(!was_background_rendering_
);
162 DCHECK(!time_progressing_
);
164 low_delay_
= (stream
->liveness() == DemuxerStream::LIVENESS_LIVE
);
166 // Always post |init_cb_| because |this| could be destroyed if initialization
168 init_cb_
= BindToCurrentLoop(init_cb
);
170 statistics_cb_
= statistics_cb
;
171 buffering_state_cb_
= buffering_state_cb
;
172 paint_cb_
= base::Bind(&VideoRendererSink::PaintFrameUsingOldRenderingPath
,
173 base::Unretained(sink_
));
174 ended_cb_
= ended_cb
;
175 error_cb_
= error_cb
;
176 wall_clock_time_cb_
= wall_clock_time_cb
;
177 state_
= kInitializing
;
179 video_frame_stream_
->Initialize(
180 stream
, base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized
,
181 weak_factory_
.GetWeakPtr()),
182 set_decryptor_ready_cb
, statistics_cb
, waiting_for_decryption_key_cb
);
185 scoped_refptr
<VideoFrame
> VideoRendererImpl::Render(
186 base::TimeTicks deadline_min
,
187 base::TimeTicks deadline_max
,
188 bool background_rendering
) {
189 base::AutoLock
auto_lock(lock_
);
190 DCHECK(use_new_video_renderering_path_
);
191 DCHECK_EQ(state_
, kPlaying
);
193 size_t frames_dropped
= 0;
194 scoped_refptr
<VideoFrame
> result
=
195 algorithm_
->Render(deadline_min
, deadline_max
, &frames_dropped
);
197 // Due to how the |algorithm_| holds frames, this should never be null if
198 // we've had a proper startup sequence.
201 // Declare HAVE_NOTHING if we reach a state where we can't progress playback
202 // any further. We don't want to do this if we've already done so, reached
203 // end of stream, or have frames available. We also don't want to do this in
204 // background rendering mode unless this isn't the first background render
205 // tick and we haven't seen any decoded frames since the last one.
206 const size_t effective_frames
= MaybeFireEndedCallback();
207 if (buffering_state_
== BUFFERING_HAVE_ENOUGH
&& !received_end_of_stream_
&&
208 !effective_frames
&& (!background_rendering
||
209 (!frames_decoded_
&& was_background_rendering_
))) {
210 // Do not set |buffering_state_| here as the lock in FrameReady() may be
211 // held already and it fire the state changes in the wrong order.
212 task_runner_
->PostTask(
213 FROM_HERE
, base::Bind(&VideoRendererImpl::TransitionToHaveNothing
,
214 weak_factory_
.GetWeakPtr()));
217 // We don't count dropped frames in the background to avoid skewing the count
218 // and impacting JavaScript visible metrics used by web developers.
220 // Just after resuming from background rendering, we also don't count the
221 // dropped frames since they are likely just dropped due to being too old.
222 if (!background_rendering
&& !was_background_rendering_
)
223 frames_dropped_
+= frames_dropped
;
224 UpdateStatsAndWait_Locked(base::TimeDelta());
225 was_background_rendering_
= background_rendering
;
227 // After painting the first frame, if playback hasn't started, we post a
228 // delayed task to request that the sink be stopped. The task is delayed to
229 // give videos with autoplay time to start.
231 // OnTimeStateChanged() will clear this flag if time starts before we get here
232 // and MaybeStopSinkAfterFirstPaint() will ignore this request if time starts
233 // before the call executes.
234 if (render_first_frame_and_stop_
&& !posted_maybe_stop_after_first_paint_
) {
235 posted_maybe_stop_after_first_paint_
= true;
236 task_runner_
->PostDelayedTask(
237 FROM_HERE
, base::Bind(&VideoRendererImpl::MaybeStopSinkAfterFirstPaint
,
238 weak_factory_
.GetWeakPtr()),
239 base::TimeDelta::FromMilliseconds(250));
242 // Always post this task, it will acquire new frames if necessary and since it
243 // happens on another thread, even if we don't have room in the queue now, by
244 // the time it runs (may be delayed up to 50ms for complex decodes!) we might.
245 task_runner_
->PostTask(FROM_HERE
, base::Bind(&VideoRendererImpl::AttemptRead
,
246 weak_factory_
.GetWeakPtr()));
251 void VideoRendererImpl::OnFrameDropped() {
252 base::AutoLock
auto_lock(lock_
);
253 DCHECK(use_new_video_renderering_path_
);
254 algorithm_
->OnLastFrameDropped();
257 void VideoRendererImpl::CreateVideoThread() {
258 // This may fail and cause a crash if there are too many threads created in
259 // the current process. See http://crbug.com/443291
260 CHECK(base::PlatformThread::Create(0, this, &thread_
));
263 // Bump up our priority so our sleeping is more accurate.
264 // TODO(scherkus): find out if this is necessary, but it seems to help.
265 ::SetThreadPriority(thread_
.platform_handle(), THREAD_PRIORITY_ABOVE_NORMAL
);
266 #endif // defined(OS_WIN)
269 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success
) {
270 DCHECK(task_runner_
->BelongsToCurrentThread());
271 base::AutoLock
auto_lock(lock_
);
272 DCHECK_EQ(state_
, kInitializing
);
275 state_
= kUninitialized
;
276 base::ResetAndReturn(&init_cb_
).Run(DECODER_ERROR_NOT_SUPPORTED
);
280 // We're all good! Consider ourselves flushed. (ThreadMain() should never
281 // see us in the kUninitialized state).
282 // Since we had an initial Preroll(), we consider ourself flushed, because we
283 // have not populated any buffers yet.
286 if (use_new_video_renderering_path_
) {
287 algorithm_
.reset(new VideoRendererAlgorithm(wall_clock_time_cb_
));
289 algorithm_
->disable_frame_dropping();
294 base::ResetAndReturn(&init_cb_
).Run(PIPELINE_OK
);
297 // PlatformThread::Delegate implementation.
298 void VideoRendererImpl::ThreadMain() {
299 DCHECK(!use_new_video_renderering_path_
);
300 base::PlatformThread::SetName("CrVideoRenderer");
302 // The number of milliseconds to idle when we do not have anything to do.
303 // Nothing special about the value, other than we're being more OS-friendly
304 // than sleeping for 1 millisecond.
306 // TODO(scherkus): switch to pure event-driven frame timing instead of this
307 // kIdleTimeDelta business http://crbug.com/106874
308 const base::TimeDelta kIdleTimeDelta
=
309 base::TimeDelta::FromMilliseconds(10);
312 base::AutoLock
auto_lock(lock_
);
314 // Thread exit condition.
315 if (is_shutting_down_
)
318 // Remain idle as long as we're not playing.
319 if (state_
!= kPlaying
|| buffering_state_
!= BUFFERING_HAVE_ENOUGH
) {
320 UpdateStatsAndWait_Locked(kIdleTimeDelta
);
324 base::TimeTicks now
= tick_clock_
->NowTicks();
326 // Remain idle until we have the next frame ready for rendering.
327 if (ready_frames_
.empty()) {
328 base::TimeDelta wait_time
= kIdleTimeDelta
;
329 if (received_end_of_stream_
) {
330 if (!rendered_end_of_stream_
) {
331 rendered_end_of_stream_
= true;
332 task_runner_
->PostTask(FROM_HERE
, ended_cb_
);
334 } else if (now
>= latest_possible_paint_time_
) {
335 // Declare HAVE_NOTHING if we don't have another frame by the time we
336 // are ready to paint the next one.
337 buffering_state_
= BUFFERING_HAVE_NOTHING
;
338 task_runner_
->PostTask(
339 FROM_HERE
, base::Bind(buffering_state_cb_
, BUFFERING_HAVE_NOTHING
));
341 wait_time
= std::min(kIdleTimeDelta
, latest_possible_paint_time_
- now
);
344 UpdateStatsAndWait_Locked(wait_time
);
348 base::TimeTicks target_paint_time
=
349 ConvertMediaTimestamp(ready_frames_
.front()->timestamp());
351 // If media time has stopped, don't attempt to paint any more frames.
352 if (target_paint_time
.is_null()) {
353 UpdateStatsAndWait_Locked(kIdleTimeDelta
);
357 // Deadline is defined as the duration between this frame and the next
358 // frame, using the delta between this frame and the previous frame as the
359 // assumption for frame duration.
361 // TODO(scherkus): This can be vastly improved. Use a histogram to measure
362 // the accuracy of our frame timing code. http://crbug.com/149829
363 if (last_media_time_
.is_null()) {
364 latest_possible_paint_time_
= now
;
366 base::TimeDelta duration
= target_paint_time
- last_media_time_
;
367 latest_possible_paint_time_
= target_paint_time
+ duration
;
370 // Remain idle until we've reached our target paint window.
371 if (now
< target_paint_time
) {
372 UpdateStatsAndWait_Locked(
373 std::min(target_paint_time
- now
, kIdleTimeDelta
));
377 if (ready_frames_
.size() > 1 && now
> latest_possible_paint_time_
&&
379 DropNextReadyFrame_Locked();
383 // Congratulations! You've made it past the video frame timing gauntlet.
385 // At this point enough time has passed that the next frame that ready for
387 PaintNextReadyFrame_Locked();
391 void VideoRendererImpl::SetTickClockForTesting(
392 scoped_ptr
<base::TickClock
> tick_clock
) {
393 tick_clock_
.swap(tick_clock
);
396 void VideoRendererImpl::OnTimeStateChanged(bool time_progressing
) {
397 DCHECK(task_runner_
->BelongsToCurrentThread());
398 time_progressing_
= time_progressing
;
400 // WARNING: Do not attempt to use |lock_| here as this may be a reentrant call
401 // in response to callbacks firing above.
403 if (!use_new_video_renderering_path_
|| sink_started_
== time_progressing_
)
406 if (time_progressing_
) {
407 // If only an EOS frame came in after a seek, the renderer may not have
408 // received the ended event yet though we've posted it.
409 if (!rendered_end_of_stream_
)
416 void VideoRendererImpl::PaintNextReadyFrame_Locked() {
417 DCHECK(!use_new_video_renderering_path_
);
418 lock_
.AssertAcquired();
420 scoped_refptr
<VideoFrame
> next_frame
= ready_frames_
.front();
421 ready_frames_
.pop_front();
423 last_media_time_
= ConvertMediaTimestamp(next_frame
->timestamp());
425 paint_cb_
.Run(next_frame
);
427 task_runner_
->PostTask(
429 base::Bind(&VideoRendererImpl::AttemptRead
, weak_factory_
.GetWeakPtr()));
432 void VideoRendererImpl::DropNextReadyFrame_Locked() {
433 DCHECK(!use_new_video_renderering_path_
);
434 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped");
436 lock_
.AssertAcquired();
438 last_media_time_
= ConvertMediaTimestamp(ready_frames_
.front()->timestamp());
440 ready_frames_
.pop_front();
443 task_runner_
->PostTask(
445 base::Bind(&VideoRendererImpl::AttemptRead
, weak_factory_
.GetWeakPtr()));
448 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status
,
449 const scoped_refptr
<VideoFrame
>& frame
) {
450 DCHECK(task_runner_
->BelongsToCurrentThread());
451 bool start_sink
= false;
453 base::AutoLock
auto_lock(lock_
);
454 DCHECK_NE(state_
, kUninitialized
);
455 DCHECK_NE(state_
, kFlushed
);
457 CHECK(pending_read_
);
458 pending_read_
= false;
460 if (status
== VideoFrameStream::DECODE_ERROR
||
461 status
== VideoFrameStream::DECRYPT_ERROR
) {
462 DCHECK(!frame
.get());
463 PipelineStatus error
= PIPELINE_ERROR_DECODE
;
464 if (status
== VideoFrameStream::DECRYPT_ERROR
)
465 error
= PIPELINE_ERROR_DECRYPT
;
466 task_runner_
->PostTask(FROM_HERE
, base::Bind(error_cb_
, error
));
470 // Already-queued VideoFrameStream ReadCB's can fire after various state
471 // transitions have happened; in that case just drop those frames
473 if (state_
== kFlushing
)
476 DCHECK_EQ(state_
, kPlaying
);
478 // Can happen when demuxers are preparing for a new Seek().
480 DCHECK_EQ(status
, VideoFrameStream::DEMUXER_READ_ABORTED
);
484 if (frame
->end_of_stream()) {
485 DCHECK(!received_end_of_stream_
);
486 received_end_of_stream_
= true;
488 // See if we can fire EOS immediately instead of waiting for Render().
489 if (use_new_video_renderering_path_
)
490 MaybeFireEndedCallback();
492 // Maintain the latest frame decoded so the correct frame is displayed
493 // after prerolling has completed.
494 if (frame
->timestamp() <= start_timestamp_
) {
495 if (use_new_video_renderering_path_
)
497 ready_frames_
.clear();
499 AddReadyFrame_Locked(frame
);
502 // Signal buffering state if we've met our conditions for having enough
504 if (buffering_state_
!= BUFFERING_HAVE_ENOUGH
&& HaveEnoughData_Locked()) {
505 TransitionToHaveEnough_Locked();
506 if (use_new_video_renderering_path_
&& !sink_started_
&&
507 !rendered_end_of_stream_
) {
509 render_first_frame_and_stop_
= true;
510 posted_maybe_stop_after_first_paint_
= false;
514 // Background rendering updates may not be ticking fast enough by itself to
515 // remove expired frames, so give it a boost here by ensuring we don't exit
516 // the decoding cycle too early.
517 if (was_background_rendering_
) {
518 DCHECK(use_new_video_renderering_path_
);
519 algorithm_
->RemoveExpiredFrames(tick_clock_
->NowTicks());
522 // Always request more decoded video if we have capacity. This serves two
524 // 1) Prerolling while paused
525 // 2) Keeps decoding going if video rendering thread starts falling behind
526 AttemptRead_Locked();
529 // If time is progressing, the sink has already been started; this may be true
530 // if we have previously underflowed, yet weren't stopped because of audio.
531 if (use_new_video_renderering_path_
&& start_sink
) {
532 DCHECK(!sink_started_
);
537 bool VideoRendererImpl::HaveEnoughData_Locked() {
538 DCHECK_EQ(state_
, kPlaying
);
540 if (received_end_of_stream_
|| !video_frame_stream_
->CanReadWithoutStalling())
543 if (HaveReachedBufferingCap())
546 if (use_new_video_renderering_path_
&& was_background_rendering_
&&
554 return ready_frames_
.size() > 0 ||
555 (use_new_video_renderering_path_
&& algorithm_
->frames_queued() > 0);
558 void VideoRendererImpl::TransitionToHaveEnough_Locked() {
559 DCHECK(task_runner_
->BelongsToCurrentThread());
560 DCHECK_EQ(buffering_state_
, BUFFERING_HAVE_NOTHING
);
562 if (!ready_frames_
.empty()) {
563 DCHECK(!use_new_video_renderering_path_
);
564 // Because the clock might remain paused in for an undetermined amount
565 // of time (e.g., seeking while paused), paint the first frame.
566 PaintNextReadyFrame_Locked();
569 buffering_state_
= BUFFERING_HAVE_ENOUGH
;
570 buffering_state_cb_
.Run(BUFFERING_HAVE_ENOUGH
);
573 void VideoRendererImpl::TransitionToHaveNothing() {
574 DCHECK(task_runner_
->BelongsToCurrentThread());
576 base::AutoLock
auto_lock(lock_
);
577 if (buffering_state_
!= BUFFERING_HAVE_ENOUGH
|| HaveEnoughData_Locked())
580 buffering_state_
= BUFFERING_HAVE_NOTHING
;
581 buffering_state_cb_
.Run(BUFFERING_HAVE_NOTHING
);
584 void VideoRendererImpl::AddReadyFrame_Locked(
585 const scoped_refptr
<VideoFrame
>& frame
) {
586 DCHECK(task_runner_
->BelongsToCurrentThread());
587 lock_
.AssertAcquired();
588 DCHECK(!frame
->end_of_stream());
592 if (use_new_video_renderering_path_
) {
593 algorithm_
->EnqueueFrame(frame
);
597 ready_frames_
.push_back(frame
);
598 DCHECK_LE(ready_frames_
.size(),
599 static_cast<size_t>(limits::kMaxVideoFrames
));
601 // Avoid needlessly waking up |thread_| unless playing.
602 if (state_
== kPlaying
)
603 frame_available_
.Signal();
606 void VideoRendererImpl::AttemptRead() {
607 base::AutoLock
auto_lock(lock_
);
608 AttemptRead_Locked();
611 void VideoRendererImpl::AttemptRead_Locked() {
612 DCHECK(task_runner_
->BelongsToCurrentThread());
613 lock_
.AssertAcquired();
615 if (pending_read_
|| received_end_of_stream_
)
618 if (HaveReachedBufferingCap())
623 pending_read_
= true;
624 video_frame_stream_
->Read(base::Bind(&VideoRendererImpl::FrameReady
,
625 weak_factory_
.GetWeakPtr()));
636 void VideoRendererImpl::OnVideoFrameStreamResetDone() {
637 base::AutoLock
auto_lock(lock_
);
638 DCHECK_EQ(kFlushing
, state_
);
639 DCHECK(!pending_read_
);
640 DCHECK(ready_frames_
.empty());
641 DCHECK(!received_end_of_stream_
);
642 DCHECK(!rendered_end_of_stream_
);
643 DCHECK_EQ(buffering_state_
, BUFFERING_HAVE_NOTHING
);
646 latest_possible_paint_time_
= last_media_time_
= base::TimeTicks();
647 base::ResetAndReturn(&flush_cb_
).Run();
650 void VideoRendererImpl::UpdateStatsAndWait_Locked(
651 base::TimeDelta wait_duration
) {
652 lock_
.AssertAcquired();
653 DCHECK_GE(frames_decoded_
, 0);
654 DCHECK_GE(frames_dropped_
, 0);
656 if (frames_decoded_
|| frames_dropped_
) {
657 PipelineStatistics statistics
;
658 statistics
.video_frames_decoded
= frames_decoded_
;
659 statistics
.video_frames_dropped
= frames_dropped_
;
660 task_runner_
->PostTask(FROM_HERE
, base::Bind(statistics_cb_
, statistics
));
666 if (wait_duration
> base::TimeDelta())
667 frame_available_
.TimedWait(wait_duration
);
670 void VideoRendererImpl::MaybeStopSinkAfterFirstPaint() {
671 DCHECK(task_runner_
->BelongsToCurrentThread());
672 DCHECK(use_new_video_renderering_path_
);
675 base::AutoLock
auto_lock(lock_
);
676 render_first_frame_and_stop_
= false;
679 if (!time_progressing_
&& sink_started_
)
683 bool VideoRendererImpl::HaveReachedBufferingCap() {
684 DCHECK(task_runner_
->BelongsToCurrentThread());
685 const size_t kMaxVideoFrames
= limits::kMaxVideoFrames
;
687 if (use_new_video_renderering_path_
) {
688 // When the display rate is less than the frame rate, the effective frames
689 // queued may be much smaller than the actual number of frames queued. Here
690 // we ensure that frames_queued() doesn't get excessive.
691 return algorithm_
->EffectiveFramesQueued() >= kMaxVideoFrames
||
692 algorithm_
->frames_queued() >= 3 * kMaxVideoFrames
;
695 return ready_frames_
.size() >= kMaxVideoFrames
;
698 void VideoRendererImpl::StartSink() {
699 DCHECK(task_runner_
->BelongsToCurrentThread());
701 sink_started_
= true;
702 was_background_rendering_
= false;
705 void VideoRendererImpl::StopSink() {
706 DCHECK(task_runner_
->BelongsToCurrentThread());
708 sink_started_
= false;
709 was_background_rendering_
= false;
712 size_t VideoRendererImpl::MaybeFireEndedCallback() {
713 // If there's only one frame in the video or Render() was never called, the
714 // algorithm will have one frame linger indefinitely. So in cases where the
715 // frame duration is unknown and we've received EOS, fire it once we get down
716 // to a single frame.
717 const size_t effective_frames
= algorithm_
->EffectiveFramesQueued();
719 // Don't fire ended if we haven't received EOS or have already done so.
720 if (!received_end_of_stream_
|| rendered_end_of_stream_
)
721 return effective_frames
;
723 // Don't fire ended if time isn't moving and we have frames.
724 if (!time_progressing_
&& algorithm_
->frames_queued())
725 return effective_frames
;
727 // Fire ended if we have no more effective frames or only ever had one frame.
728 if (!effective_frames
||
729 (algorithm_
->frames_queued() == 1u &&
730 algorithm_
->average_frame_duration() == base::TimeDelta())) {
731 rendered_end_of_stream_
= true;
732 task_runner_
->PostTask(FROM_HERE
, ended_cb_
);
735 return effective_frames
;
738 base::TimeTicks
VideoRendererImpl::ConvertMediaTimestamp(
739 base::TimeDelta media_time
) {
740 std::vector
<base::TimeDelta
> media_times(1, media_time
);
741 std::vector
<base::TimeTicks
> wall_clock_times
;
742 if (!wall_clock_time_cb_
.Run(media_times
, &wall_clock_times
))
743 return base::TimeTicks();
744 return wall_clock_times
[0];