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/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"
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
,
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()),
59 new VideoFrameStream(media_task_runner
, decoders
.Pass(), media_log
)),
60 gpu_memory_buffer_pool_(nullptr),
61 media_log_(media_log
),
63 received_end_of_stream_(false),
64 rendered_end_of_stream_(false),
65 frame_available_(&lock_
),
66 state_(kUninitialized
),
70 drop_frames_(drop_frames
),
71 buffering_state_(BUFFERING_HAVE_NOTHING
),
74 is_shutting_down_(false),
75 tick_clock_(new base::DefaultTickClock()),
76 was_background_rendering_(false),
77 time_progressing_(false),
78 render_first_frame_and_stop_(false),
79 posted_maybe_stop_after_first_paint_(false),
82 gpu_factories
->ShouldUseGpuMemoryBuffersForVideoFrames()) {
83 gpu_memory_buffer_pool_
.reset(new GpuMemoryBufferVideoFramePool(
84 media_task_runner
, worker_task_runner
, gpu_factories
));
88 VideoRendererImpl::~VideoRendererImpl() {
89 DCHECK(task_runner_
->BelongsToCurrentThread());
91 if (!use_new_video_renderering_path_
) {
92 base::AutoLock
auto_lock(lock_
);
93 is_shutting_down_
= true;
94 frame_available_
.Signal();
97 if (!thread_
.is_null())
98 base::PlatformThread::Join(thread_
);
100 if (!init_cb_
.is_null())
101 base::ResetAndReturn(&init_cb_
).Run(PIPELINE_ERROR_ABORT
);
103 if (!flush_cb_
.is_null())
104 base::ResetAndReturn(&flush_cb_
).Run();
106 if (use_new_video_renderering_path_
&& sink_started_
)
110 void VideoRendererImpl::Flush(const base::Closure
& callback
) {
111 DVLOG(1) << __FUNCTION__
;
112 DCHECK(task_runner_
->BelongsToCurrentThread());
114 if (use_new_video_renderering_path_
&& sink_started_
)
117 base::AutoLock
auto_lock(lock_
);
118 DCHECK_EQ(state_
, kPlaying
);
119 flush_cb_
= callback
;
122 // This is necessary if the |video_frame_stream_| has already seen an end of
123 // stream and needs to drain it before flushing it.
124 ready_frames_
.clear();
125 if (buffering_state_
!= BUFFERING_HAVE_NOTHING
) {
126 buffering_state_
= BUFFERING_HAVE_NOTHING
;
127 buffering_state_cb_
.Run(BUFFERING_HAVE_NOTHING
);
129 received_end_of_stream_
= false;
130 rendered_end_of_stream_
= false;
132 if (use_new_video_renderering_path_
)
135 video_frame_stream_
->Reset(
136 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone
,
137 weak_factory_
.GetWeakPtr()));
140 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp
) {
141 DVLOG(1) << __FUNCTION__
<< "(" << timestamp
.InMicroseconds() << ")";
142 DCHECK(task_runner_
->BelongsToCurrentThread());
143 base::AutoLock
auto_lock(lock_
);
144 DCHECK_EQ(state_
, kFlushed
);
145 DCHECK(!pending_read_
);
146 DCHECK(ready_frames_
.empty());
147 DCHECK_EQ(buffering_state_
, BUFFERING_HAVE_NOTHING
);
150 start_timestamp_
= timestamp
;
151 AttemptRead_Locked();
154 void VideoRendererImpl::Initialize(
155 DemuxerStream
* stream
,
156 const PipelineStatusCB
& init_cb
,
157 const SetDecryptorReadyCB
& set_decryptor_ready_cb
,
158 const StatisticsCB
& statistics_cb
,
159 const BufferingStateCB
& buffering_state_cb
,
160 const base::Closure
& ended_cb
,
161 const PipelineStatusCB
& error_cb
,
162 const TimeSource::WallClockTimeCB
& wall_clock_time_cb
,
163 const base::Closure
& waiting_for_decryption_key_cb
) {
164 DCHECK(task_runner_
->BelongsToCurrentThread());
165 base::AutoLock
auto_lock(lock_
);
167 DCHECK_EQ(stream
->type(), DemuxerStream::VIDEO
);
168 DCHECK(!init_cb
.is_null());
169 DCHECK(!statistics_cb
.is_null());
170 DCHECK(!buffering_state_cb
.is_null());
171 DCHECK(!ended_cb
.is_null());
172 DCHECK(!wall_clock_time_cb
.is_null());
173 DCHECK_EQ(kUninitialized
, state_
);
174 DCHECK(!render_first_frame_and_stop_
);
175 DCHECK(!posted_maybe_stop_after_first_paint_
);
176 DCHECK(!was_background_rendering_
);
177 DCHECK(!time_progressing_
);
179 low_delay_
= (stream
->liveness() == DemuxerStream::LIVENESS_LIVE
);
180 UMA_HISTOGRAM_BOOLEAN("Media.VideoRenderer.LowDelay", low_delay_
);
182 MEDIA_LOG(DEBUG
, media_log_
) << "Video rendering in low delay mode.";
184 // Always post |init_cb_| because |this| could be destroyed if initialization
186 init_cb_
= BindToCurrentLoop(init_cb
);
188 // Always post |buffering_state_cb_| because it may otherwise invoke reentrant
189 // calls to OnTimeStateChanged() under lock, which can deadlock the compositor
190 // and media threads.
191 buffering_state_cb_
= BindToCurrentLoop(buffering_state_cb
);
193 statistics_cb_
= statistics_cb
;
194 paint_cb_
= base::Bind(&VideoRendererSink::PaintFrameUsingOldRenderingPath
,
195 base::Unretained(sink_
));
196 ended_cb_
= ended_cb
;
197 error_cb_
= error_cb
;
198 wall_clock_time_cb_
= wall_clock_time_cb
;
199 state_
= kInitializing
;
201 video_frame_stream_
->Initialize(
202 stream
, base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized
,
203 weak_factory_
.GetWeakPtr()),
204 set_decryptor_ready_cb
, statistics_cb
, waiting_for_decryption_key_cb
);
207 scoped_refptr
<VideoFrame
> VideoRendererImpl::Render(
208 base::TimeTicks deadline_min
,
209 base::TimeTicks deadline_max
,
210 bool background_rendering
) {
211 base::AutoLock
auto_lock(lock_
);
212 DCHECK(use_new_video_renderering_path_
);
213 DCHECK_EQ(state_
, kPlaying
);
215 size_t frames_dropped
= 0;
216 scoped_refptr
<VideoFrame
> result
=
217 algorithm_
->Render(deadline_min
, deadline_max
, &frames_dropped
);
219 // Due to how the |algorithm_| holds frames, this should never be null if
220 // we've had a proper startup sequence.
223 // Declare HAVE_NOTHING if we reach a state where we can't progress playback
224 // any further. We don't want to do this if we've already done so, reached
225 // end of stream, or have frames available. We also don't want to do this in
226 // background rendering mode unless this isn't the first background render
227 // tick and we haven't seen any decoded frames since the last one.
229 // We use the inverse of |render_first_frame_and_stop_| as a proxy for the
230 // value of |time_progressing_| here since we can't access it from the
231 // compositor thread. If we're here (in Render()) the sink must have been
232 // started -- but if it was started only to render the first frame and stop,
233 // then |time_progressing_| is likely false. If we're still in Render() when
234 // |render_first_frame_and_stop_| is false, then |time_progressing_| is true.
235 // If |time_progressing_| is actually true when |render_first_frame_and_stop_|
236 // is also true, then the ended callback will be harmlessly delayed until
237 // MaybeStopSinkAfterFirstPaint() runs and the next Render() call comes in.
238 const size_t effective_frames
=
239 MaybeFireEndedCallback_Locked(!render_first_frame_and_stop_
);
240 if (buffering_state_
== BUFFERING_HAVE_ENOUGH
&& !received_end_of_stream_
&&
241 !effective_frames
&& (!background_rendering
||
242 (!frames_decoded_
&& was_background_rendering_
))) {
243 // Do not set |buffering_state_| here as the lock in FrameReady() may be
244 // held already and it fire the state changes in the wrong order.
245 task_runner_
->PostTask(
246 FROM_HERE
, base::Bind(&VideoRendererImpl::TransitionToHaveNothing
,
247 weak_factory_
.GetWeakPtr()));
250 // We don't count dropped frames in the background to avoid skewing the count
251 // and impacting JavaScript visible metrics used by web developers.
253 // Just after resuming from background rendering, we also don't count the
254 // dropped frames since they are likely just dropped due to being too old.
255 if (!background_rendering
&& !was_background_rendering_
)
256 frames_dropped_
+= frames_dropped
;
257 UpdateStatsAndWait_Locked(base::TimeDelta());
258 was_background_rendering_
= background_rendering
;
260 // After painting the first frame, if playback hasn't started, we post a
261 // delayed task to request that the sink be stopped. The task is delayed to
262 // give videos with autoplay time to start.
264 // OnTimeStateChanged() will clear this flag if time starts before we get here
265 // and MaybeStopSinkAfterFirstPaint() will ignore this request if time starts
266 // before the call executes.
267 if (render_first_frame_and_stop_
&& !posted_maybe_stop_after_first_paint_
) {
268 posted_maybe_stop_after_first_paint_
= true;
269 task_runner_
->PostDelayedTask(
270 FROM_HERE
, base::Bind(&VideoRendererImpl::MaybeStopSinkAfterFirstPaint
,
271 weak_factory_
.GetWeakPtr()),
272 base::TimeDelta::FromMilliseconds(250));
275 // Always post this task, it will acquire new frames if necessary and since it
276 // happens on another thread, even if we don't have room in the queue now, by
277 // the time it runs (may be delayed up to 50ms for complex decodes!) we might.
278 task_runner_
->PostTask(FROM_HERE
, base::Bind(&VideoRendererImpl::AttemptRead
,
279 weak_factory_
.GetWeakPtr()));
284 void VideoRendererImpl::OnFrameDropped() {
285 base::AutoLock
auto_lock(lock_
);
286 DCHECK(use_new_video_renderering_path_
);
287 algorithm_
->OnLastFrameDropped();
290 void VideoRendererImpl::CreateVideoThread() {
291 // This may fail and cause a crash if there are too many threads created in
292 // the current process. See http://crbug.com/443291
293 const base::ThreadPriority priority
=
295 // Bump up our priority so our sleeping is more accurate.
296 // TODO(scherkus): find out if this is necessary, but it seems to help.
297 base::ThreadPriority::DISPLAY
;
299 base::ThreadPriority::NORMAL
;
301 CHECK(base::PlatformThread::CreateWithPriority(0, this, &thread_
, priority
));
304 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success
) {
305 DCHECK(task_runner_
->BelongsToCurrentThread());
306 base::AutoLock
auto_lock(lock_
);
307 DCHECK_EQ(state_
, kInitializing
);
310 state_
= kUninitialized
;
311 base::ResetAndReturn(&init_cb_
).Run(DECODER_ERROR_NOT_SUPPORTED
);
315 // We're all good! Consider ourselves flushed. (ThreadMain() should never
316 // see us in the kUninitialized state).
317 // Since we had an initial Preroll(), we consider ourself flushed, because we
318 // have not populated any buffers yet.
321 if (use_new_video_renderering_path_
) {
322 algorithm_
.reset(new VideoRendererAlgorithm(wall_clock_time_cb_
));
324 algorithm_
->disable_frame_dropping();
329 base::ResetAndReturn(&init_cb_
).Run(PIPELINE_OK
);
332 // PlatformThread::Delegate implementation.
333 void VideoRendererImpl::ThreadMain() {
334 DCHECK(!use_new_video_renderering_path_
);
335 base::PlatformThread::SetName("CrVideoRenderer");
337 // The number of milliseconds to idle when we do not have anything to do.
338 // Nothing special about the value, other than we're being more OS-friendly
339 // than sleeping for 1 millisecond.
341 // TODO(scherkus): switch to pure event-driven frame timing instead of this
342 // kIdleTimeDelta business http://crbug.com/106874
343 const base::TimeDelta kIdleTimeDelta
=
344 base::TimeDelta::FromMilliseconds(10);
347 base::AutoLock
auto_lock(lock_
);
349 // Thread exit condition.
350 if (is_shutting_down_
)
353 // Remain idle as long as we're not playing.
354 if (state_
!= kPlaying
|| buffering_state_
!= BUFFERING_HAVE_ENOUGH
) {
355 UpdateStatsAndWait_Locked(kIdleTimeDelta
);
359 base::TimeTicks now
= tick_clock_
->NowTicks();
361 // Remain idle until we have the next frame ready for rendering.
362 if (ready_frames_
.empty()) {
363 base::TimeDelta wait_time
= kIdleTimeDelta
;
364 if (received_end_of_stream_
) {
365 if (!rendered_end_of_stream_
) {
366 rendered_end_of_stream_
= true;
367 task_runner_
->PostTask(FROM_HERE
, ended_cb_
);
369 } else if (now
>= latest_possible_paint_time_
) {
370 // Declare HAVE_NOTHING if we don't have another frame by the time we
371 // are ready to paint the next one.
372 buffering_state_
= BUFFERING_HAVE_NOTHING
;
373 task_runner_
->PostTask(
374 FROM_HERE
, base::Bind(buffering_state_cb_
, BUFFERING_HAVE_NOTHING
));
376 wait_time
= std::min(kIdleTimeDelta
, latest_possible_paint_time_
- now
);
379 UpdateStatsAndWait_Locked(wait_time
);
383 base::TimeTicks target_paint_time
=
384 ConvertMediaTimestamp(ready_frames_
.front()->timestamp());
386 // If media time has stopped, don't attempt to paint any more frames.
387 if (target_paint_time
.is_null()) {
388 UpdateStatsAndWait_Locked(kIdleTimeDelta
);
392 // Deadline is defined as the duration between this frame and the next
393 // frame, using the delta between this frame and the previous frame as the
394 // assumption for frame duration.
396 // TODO(scherkus): This can be vastly improved. Use a histogram to measure
397 // the accuracy of our frame timing code. http://crbug.com/149829
398 if (last_media_time_
.is_null()) {
399 latest_possible_paint_time_
= now
;
401 base::TimeDelta duration
= target_paint_time
- last_media_time_
;
402 latest_possible_paint_time_
= target_paint_time
+ duration
;
405 // Remain idle until we've reached our target paint window.
406 if (now
< target_paint_time
) {
407 UpdateStatsAndWait_Locked(
408 std::min(target_paint_time
- now
, kIdleTimeDelta
));
412 if (ready_frames_
.size() > 1 && now
> latest_possible_paint_time_
&&
414 DropNextReadyFrame_Locked();
418 // Congratulations! You've made it past the video frame timing gauntlet.
420 // At this point enough time has passed that the next frame that ready for
422 PaintNextReadyFrame_Locked();
426 void VideoRendererImpl::SetTickClockForTesting(
427 scoped_ptr
<base::TickClock
> tick_clock
) {
428 tick_clock_
.swap(tick_clock
);
431 void VideoRendererImpl::SetGpuMemoryBufferVideoForTesting(
432 scoped_ptr
<GpuMemoryBufferVideoFramePool
> gpu_memory_buffer_pool
) {
433 gpu_memory_buffer_pool_
.swap(gpu_memory_buffer_pool
);
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_
)
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_
)
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(
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();
483 task_runner_
->PostTask(
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(sequence_token_
, status
, frame
);
497 gpu_memory_buffer_pool_
->MaybeCreateHardwareFrame(
498 frame
, base::Bind(&VideoRendererImpl::FrameReady
,
499 weak_factory_
.GetWeakPtr(), sequence_token_
, status
));
502 void VideoRendererImpl::FrameReady(uint32_t sequence_token
,
503 VideoFrameStream::Status status
,
504 const scoped_refptr
<VideoFrame
>& frame
) {
505 DCHECK(task_runner_
->BelongsToCurrentThread());
506 bool start_sink
= false;
508 base::AutoLock
auto_lock(lock_
);
509 // Stream has been reset and this VideoFrame was decoded before the reset
510 // but the async copy finished after.
511 if (sequence_token
!= sequence_token_
)
514 DCHECK_NE(state_
, kUninitialized
);
515 DCHECK_NE(state_
, kFlushed
);
517 CHECK(pending_read_
);
518 pending_read_
= false;
520 if (status
== VideoFrameStream::DECODE_ERROR
) {
521 DCHECK(!frame
.get());
522 PipelineStatus error
= PIPELINE_ERROR_DECODE
;
523 task_runner_
->PostTask(FROM_HERE
, base::Bind(error_cb_
, error
));
527 // Already-queued VideoFrameStream ReadCB's can fire after various state
528 // transitions have happened; in that case just drop those frames
530 if (state_
== kFlushing
)
533 DCHECK_EQ(state_
, kPlaying
);
535 // Can happen when demuxers are preparing for a new Seek().
537 DCHECK_EQ(status
, VideoFrameStream::DEMUXER_READ_ABORTED
);
541 // In low delay mode, don't accumulate frames that's earlier than the start
542 // time. Otherwise we could declare HAVE_ENOUGH_DATA and start playback
545 !frame
->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM
) &&
546 frame
->timestamp() < start_timestamp_
) {
547 AttemptRead_Locked();
551 if (frame
->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM
)) {
552 DCHECK(!received_end_of_stream_
);
553 received_end_of_stream_
= true;
555 // See if we can fire EOS immediately instead of waiting for Render().
556 if (use_new_video_renderering_path_
)
557 MaybeFireEndedCallback_Locked(time_progressing_
);
559 // Maintain the latest frame decoded so the correct frame is displayed
560 // after prerolling has completed.
561 if (frame
->timestamp() <= start_timestamp_
) {
562 if (use_new_video_renderering_path_
)
564 ready_frames_
.clear();
566 AddReadyFrame_Locked(frame
);
569 // Background rendering updates may not be ticking fast enough by itself to
570 // remove expired frames, so give it a boost here by ensuring we don't exit
571 // the decoding cycle too early.
573 // Similarly, if we've paused for underflow, remove all frames which are
574 // before the current media time.
575 const bool have_nothing
= buffering_state_
!= BUFFERING_HAVE_ENOUGH
;
576 const bool have_nothing_and_paused
= have_nothing
&& !sink_started_
;
577 if (was_background_rendering_
||
578 (use_new_video_renderering_path_
&& have_nothing_and_paused
&&
580 base::TimeTicks expiry_time
;
581 if (have_nothing_and_paused
) {
582 // Use the current media wall clock time plus the frame duration since
583 // RemoveExpiredFrames() is expecting the end point of an interval (it
584 // will subtract from the given value).
585 std::vector
<base::TimeTicks
> current_time
;
586 wall_clock_time_cb_
.Run(std::vector
<base::TimeDelta
>(), ¤t_time
);
587 expiry_time
= current_time
[0] + algorithm_
->average_frame_duration();
589 expiry_time
= tick_clock_
->NowTicks();
592 // Prior to rendering the first frame, |have_nothing_and_paused| will be
593 // true, correspondingly the |expiry_time| will be null; in this case
594 // there's no reason to try and remove any frames.
595 if (!expiry_time
.is_null()) {
596 const size_t removed_frames
=
597 algorithm_
->RemoveExpiredFrames(expiry_time
);
599 // Frames removed during underflow should be counted as dropped.
600 if (have_nothing_and_paused
&& removed_frames
)
601 frames_dropped_
+= removed_frames
;
605 // Signal buffering state if we've met our conditions for having enough
607 if (have_nothing
&& HaveEnoughData_Locked()) {
608 TransitionToHaveEnough_Locked();
609 if (use_new_video_renderering_path_
&& !sink_started_
&&
610 !rendered_end_of_stream_
) {
612 render_first_frame_and_stop_
= true;
613 posted_maybe_stop_after_first_paint_
= false;
617 // Always request more decoded video if we have capacity. This serves two
619 // 1) Prerolling while paused
620 // 2) Keeps decoding going if video rendering thread starts falling behind
621 AttemptRead_Locked();
624 // If time is progressing, the sink has already been started; this may be true
625 // if we have previously underflowed, yet weren't stopped because of audio.
626 if (use_new_video_renderering_path_
&& start_sink
) {
627 DCHECK(!sink_started_
);
632 bool VideoRendererImpl::HaveEnoughData_Locked() {
633 DCHECK_EQ(state_
, kPlaying
);
635 if (received_end_of_stream_
|| !video_frame_stream_
->CanReadWithoutStalling())
638 if (HaveReachedBufferingCap())
641 if (use_new_video_renderering_path_
&& was_background_rendering_
&&
649 return ready_frames_
.size() > 0 ||
650 (use_new_video_renderering_path_
&& algorithm_
->frames_queued() > 0);
653 void VideoRendererImpl::TransitionToHaveEnough_Locked() {
654 DCHECK(task_runner_
->BelongsToCurrentThread());
655 DCHECK_EQ(buffering_state_
, BUFFERING_HAVE_NOTHING
);
657 if (!ready_frames_
.empty()) {
658 DCHECK(!use_new_video_renderering_path_
);
659 // Because the clock might remain paused in for an undetermined amount
660 // of time (e.g., seeking while paused), paint the first frame.
661 PaintNextReadyFrame_Locked();
664 buffering_state_
= BUFFERING_HAVE_ENOUGH
;
665 buffering_state_cb_
.Run(BUFFERING_HAVE_ENOUGH
);
668 void VideoRendererImpl::TransitionToHaveNothing() {
669 DCHECK(task_runner_
->BelongsToCurrentThread());
671 base::AutoLock
auto_lock(lock_
);
672 if (buffering_state_
!= BUFFERING_HAVE_ENOUGH
|| HaveEnoughData_Locked())
675 buffering_state_
= BUFFERING_HAVE_NOTHING
;
676 buffering_state_cb_
.Run(BUFFERING_HAVE_NOTHING
);
679 void VideoRendererImpl::AddReadyFrame_Locked(
680 const scoped_refptr
<VideoFrame
>& frame
) {
681 DCHECK(task_runner_
->BelongsToCurrentThread());
682 lock_
.AssertAcquired();
683 DCHECK(!frame
->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM
));
687 if (use_new_video_renderering_path_
) {
688 algorithm_
->EnqueueFrame(frame
);
692 ready_frames_
.push_back(frame
);
693 DCHECK_LE(ready_frames_
.size(),
694 static_cast<size_t>(limits::kMaxVideoFrames
));
696 // Avoid needlessly waking up |thread_| unless playing.
697 if (state_
== kPlaying
)
698 frame_available_
.Signal();
701 void VideoRendererImpl::AttemptRead() {
702 base::AutoLock
auto_lock(lock_
);
703 AttemptRead_Locked();
706 void VideoRendererImpl::AttemptRead_Locked() {
707 DCHECK(task_runner_
->BelongsToCurrentThread());
708 lock_
.AssertAcquired();
710 if (pending_read_
|| received_end_of_stream_
)
713 if (HaveReachedBufferingCap())
718 pending_read_
= true;
719 if (gpu_memory_buffer_pool_
) {
720 video_frame_stream_
->Read(base::Bind(
721 &VideoRendererImpl::FrameReadyForCopyingToGpuMemoryBuffers
,
722 weak_factory_
.GetWeakPtr()));
724 video_frame_stream_
->Read(base::Bind(&VideoRendererImpl::FrameReady
,
725 weak_factory_
.GetWeakPtr(),
737 void VideoRendererImpl::OnVideoFrameStreamResetDone() {
738 base::AutoLock
auto_lock(lock_
);
739 DCHECK_EQ(kFlushing
, state_
);
740 DCHECK(ready_frames_
.empty());
741 DCHECK(!received_end_of_stream_
);
742 DCHECK(!rendered_end_of_stream_
);
743 DCHECK_EQ(buffering_state_
, BUFFERING_HAVE_NOTHING
);
745 // Pending read might be true if an async video frame copy is in flight.
746 pending_read_
= false;
749 latest_possible_paint_time_
= last_media_time_
= base::TimeTicks();
750 base::ResetAndReturn(&flush_cb_
).Run();
753 void VideoRendererImpl::UpdateStatsAndWait_Locked(
754 base::TimeDelta wait_duration
) {
755 lock_
.AssertAcquired();
756 DCHECK_GE(frames_decoded_
, 0);
757 DCHECK_GE(frames_dropped_
, 0);
759 if (frames_decoded_
|| frames_dropped_
) {
760 PipelineStatistics statistics
;
761 statistics
.video_frames_decoded
= frames_decoded_
;
762 statistics
.video_frames_dropped
= frames_dropped_
;
763 task_runner_
->PostTask(FROM_HERE
, base::Bind(statistics_cb_
, statistics
));
769 if (wait_duration
> base::TimeDelta())
770 frame_available_
.TimedWait(wait_duration
);
773 void VideoRendererImpl::MaybeStopSinkAfterFirstPaint() {
774 DCHECK(task_runner_
->BelongsToCurrentThread());
775 DCHECK(use_new_video_renderering_path_
);
777 if (!time_progressing_
&& sink_started_
)
780 base::AutoLock
auto_lock(lock_
);
781 render_first_frame_and_stop_
= false;
784 bool VideoRendererImpl::HaveReachedBufferingCap() {
785 DCHECK(task_runner_
->BelongsToCurrentThread());
786 const size_t kMaxVideoFrames
= limits::kMaxVideoFrames
;
788 if (use_new_video_renderering_path_
) {
789 // When the display rate is less than the frame rate, the effective frames
790 // queued may be much smaller than the actual number of frames queued. Here
791 // we ensure that frames_queued() doesn't get excessive.
792 return algorithm_
->EffectiveFramesQueued() >= kMaxVideoFrames
||
793 algorithm_
->frames_queued() >= 3 * kMaxVideoFrames
;
796 return ready_frames_
.size() >= kMaxVideoFrames
;
799 void VideoRendererImpl::StartSink() {
800 DCHECK(task_runner_
->BelongsToCurrentThread());
801 DCHECK_GT(algorithm_
->frames_queued(), 0u);
802 sink_started_
= true;
803 was_background_rendering_
= false;
807 void VideoRendererImpl::StopSink() {
808 DCHECK(task_runner_
->BelongsToCurrentThread());
810 algorithm_
->set_time_stopped();
811 sink_started_
= false;
812 was_background_rendering_
= false;
815 size_t VideoRendererImpl::MaybeFireEndedCallback_Locked(bool time_progressing
) {
816 lock_
.AssertAcquired();
818 // If there's only one frame in the video or Render() was never called, the
819 // algorithm will have one frame linger indefinitely. So in cases where the
820 // frame duration is unknown and we've received EOS, fire it once we get down
821 // to a single frame.
822 const size_t effective_frames
= algorithm_
->EffectiveFramesQueued();
824 // Don't fire ended if we haven't received EOS or have already done so.
825 if (!received_end_of_stream_
|| rendered_end_of_stream_
)
826 return effective_frames
;
828 // Don't fire ended if time isn't moving and we have frames.
829 if (!time_progressing
&& algorithm_
->frames_queued())
830 return effective_frames
;
832 // Fire ended if we have no more effective frames or only ever had one frame.
833 if (!effective_frames
||
834 (algorithm_
->frames_queued() == 1u &&
835 algorithm_
->average_frame_duration() == base::TimeDelta())) {
836 rendered_end_of_stream_
= true;
837 task_runner_
->PostTask(FROM_HERE
, ended_cb_
);
840 return effective_frames
;
843 base::TimeTicks
VideoRendererImpl::ConvertMediaTimestamp(
844 base::TimeDelta media_time
) {
845 std::vector
<base::TimeDelta
> media_times(1, media_time
);
846 std::vector
<base::TimeTicks
> wall_clock_times
;
847 if (!wall_clock_time_cb_
.Run(media_times
, &wall_clock_times
))
848 return base::TimeTicks();
849 return wall_clock_times
[0];