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/filters/video_renderer_impl.h"
8 #include "base/callback.h"
9 #include "base/callback_helpers.h"
10 #include "base/debug/trace_event.h"
11 #include "base/location.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/threading/platform_thread.h"
14 #include "media/base/buffers.h"
15 #include "media/base/limits.h"
16 #include "media/base/pipeline.h"
17 #include "media/base/video_frame.h"
21 VideoRendererImpl::VideoRendererImpl(
22 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
,
23 ScopedVector
<VideoDecoder
> decoders
,
24 const SetDecryptorReadyCB
& set_decryptor_ready_cb
,
25 const PaintCB
& paint_cb
,
27 : task_runner_(task_runner
),
28 video_frame_stream_(task_runner
, decoders
.Pass(), set_decryptor_ready_cb
),
30 received_end_of_stream_(false),
31 rendered_end_of_stream_(false),
32 frame_available_(&lock_
),
33 state_(kUninitialized
),
36 drop_frames_(drop_frames
),
39 last_timestamp_(kNoTimestamp()),
43 DCHECK(!paint_cb_
.is_null());
46 VideoRendererImpl::~VideoRendererImpl() {
47 base::AutoLock
auto_lock(lock_
);
48 CHECK(state_
== kStopped
|| state_
== kUninitialized
) << state_
;
49 CHECK(thread_
.is_null());
52 void VideoRendererImpl::Play(const base::Closure
& callback
) {
53 DCHECK(task_runner_
->BelongsToCurrentThread());
54 base::AutoLock
auto_lock(lock_
);
55 DCHECK_EQ(kPrerolled
, state_
);
60 void VideoRendererImpl::Flush(const base::Closure
& callback
) {
61 DCHECK(task_runner_
->BelongsToCurrentThread());
62 base::AutoLock
auto_lock(lock_
);
63 DCHECK_NE(state_
, kUninitialized
);
67 // This is necessary if the |video_frame_stream_| has already seen an end of
68 // stream and needs to drain it before flushing it.
69 ready_frames_
.clear();
70 received_end_of_stream_
= false;
71 rendered_end_of_stream_
= false;
72 video_frame_stream_
.Reset(
73 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone
,
74 weak_factory_
.GetWeakPtr()));
77 void VideoRendererImpl::Stop(const base::Closure
& callback
) {
78 DCHECK(task_runner_
->BelongsToCurrentThread());
79 base::AutoLock
auto_lock(lock_
);
80 if (state_
== kUninitialized
|| state_
== kStopped
) {
85 // TODO(scherkus): Consider invalidating |weak_factory_| and replacing
86 // task-running guards that check |state_| with DCHECK().
90 statistics_cb_
.Reset();
92 DoStopOrError_Locked();
94 // Clean up our thread if present.
95 base::PlatformThreadHandle thread_to_join
= base::PlatformThreadHandle();
96 if (!thread_
.is_null()) {
97 // Signal the thread since it's possible to get stopped with the video
98 // thread waiting for a read to complete.
99 frame_available_
.Signal();
100 std::swap(thread_
, thread_to_join
);
103 if (!thread_to_join
.is_null()) {
104 base::AutoUnlock
auto_unlock(lock_
);
105 base::PlatformThread::Join(thread_to_join
);
108 video_frame_stream_
.Stop(callback
);
111 void VideoRendererImpl::SetPlaybackRate(float playback_rate
) {
112 DCHECK(task_runner_
->BelongsToCurrentThread());
113 base::AutoLock
auto_lock(lock_
);
114 playback_rate_
= playback_rate
;
117 void VideoRendererImpl::Preroll(base::TimeDelta time
,
118 const PipelineStatusCB
& cb
) {
119 DCHECK(task_runner_
->BelongsToCurrentThread());
120 base::AutoLock
auto_lock(lock_
);
121 DCHECK(!cb
.is_null());
122 DCHECK(preroll_cb_
.is_null());
123 DCHECK(state_
== kFlushed
|| state_
== kPlaying
) << "state_ " << state_
;
125 if (state_
== kFlushed
) {
126 DCHECK(time
!= kNoTimestamp());
127 DCHECK(!pending_read_
);
128 DCHECK(ready_frames_
.empty());
130 DCHECK(time
== kNoTimestamp());
133 state_
= kPrerolling
;
135 preroll_timestamp_
= time
;
137 if (ShouldTransitionToPrerolled_Locked()) {
138 TransitionToPrerolled_Locked();
142 AttemptRead_Locked();
145 void VideoRendererImpl::Initialize(DemuxerStream
* stream
,
147 const PipelineStatusCB
& init_cb
,
148 const StatisticsCB
& statistics_cb
,
149 const TimeCB
& max_time_cb
,
150 const base::Closure
& ended_cb
,
151 const PipelineStatusCB
& error_cb
,
152 const TimeDeltaCB
& get_time_cb
,
153 const TimeDeltaCB
& get_duration_cb
) {
154 DCHECK(task_runner_
->BelongsToCurrentThread());
155 base::AutoLock
auto_lock(lock_
);
157 DCHECK_EQ(stream
->type(), DemuxerStream::VIDEO
);
158 DCHECK(!init_cb
.is_null());
159 DCHECK(!statistics_cb
.is_null());
160 DCHECK(!max_time_cb
.is_null());
161 DCHECK(!ended_cb
.is_null());
162 DCHECK(!get_time_cb
.is_null());
163 DCHECK(!get_duration_cb
.is_null());
164 DCHECK_EQ(kUninitialized
, state_
);
166 low_delay_
= low_delay
;
169 statistics_cb_
= statistics_cb
;
170 max_time_cb_
= max_time_cb
;
171 ended_cb_
= ended_cb
;
172 error_cb_
= error_cb
;
173 get_time_cb_
= get_time_cb
;
174 get_duration_cb_
= get_duration_cb
;
175 state_
= kInitializing
;
177 video_frame_stream_
.Initialize(
181 base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized
,
182 weak_factory_
.GetWeakPtr()));
185 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success
) {
186 DCHECK(task_runner_
->BelongsToCurrentThread());
187 base::AutoLock
auto_lock(lock_
);
189 if (state_
== kStopped
)
192 DCHECK_EQ(state_
, kInitializing
);
195 state_
= kUninitialized
;
196 base::ResetAndReturn(&init_cb_
).Run(DECODER_ERROR_NOT_SUPPORTED
);
200 // We're all good! Consider ourselves flushed. (ThreadMain() should never
201 // see us in the kUninitialized state).
202 // Since we had an initial Preroll(), we consider ourself flushed, because we
203 // have not populated any buffers yet.
206 // Create our video thread.
207 CHECK(base::PlatformThread::Create(0, this, &thread_
));
210 // Bump up our priority so our sleeping is more accurate.
211 // TODO(scherkus): find out if this is necessary, but it seems to help.
212 ::SetThreadPriority(thread_
.platform_handle(), THREAD_PRIORITY_ABOVE_NORMAL
);
213 #endif // defined(OS_WIN)
214 base::ResetAndReturn(&init_cb_
).Run(PIPELINE_OK
);
217 // PlatformThread::Delegate implementation.
218 void VideoRendererImpl::ThreadMain() {
219 base::PlatformThread::SetName("CrVideoRenderer");
221 // The number of milliseconds to idle when we do not have anything to do.
222 // Nothing special about the value, other than we're being more OS-friendly
223 // than sleeping for 1 millisecond.
225 // TODO(scherkus): switch to pure event-driven frame timing instead of this
226 // kIdleTimeDelta business http://crbug.com/106874
227 const base::TimeDelta kIdleTimeDelta
=
228 base::TimeDelta::FromMilliseconds(10);
231 base::AutoLock
auto_lock(lock_
);
233 // Thread exit condition.
234 if (state_
== kStopped
)
237 // Remain idle as long as we're not playing.
238 if (state_
!= kPlaying
|| playback_rate_
== 0) {
239 UpdateStatsAndWait_Locked(kIdleTimeDelta
);
243 // Remain idle until we have the next frame ready for rendering.
244 if (ready_frames_
.empty()) {
245 if (received_end_of_stream_
&& !rendered_end_of_stream_
) {
246 rendered_end_of_stream_
= true;
250 UpdateStatsAndWait_Locked(kIdleTimeDelta
);
254 base::TimeDelta remaining_time
=
255 CalculateSleepDuration(ready_frames_
.front(), playback_rate_
);
257 // Sleep up to a maximum of our idle time until we're within the time to
258 // render the next frame.
259 if (remaining_time
.InMicroseconds() > 0) {
260 remaining_time
= std::min(remaining_time
, kIdleTimeDelta
);
261 UpdateStatsAndWait_Locked(remaining_time
);
265 // Deadline is defined as the midpoint between this frame and the next
266 // frame, using the delta between this frame and the previous frame as the
267 // assumption for frame duration.
269 // TODO(scherkus): An improvement over midpoint might be selecting the
270 // minimum and/or maximum between the midpoint and some constants. As a
271 // thought experiment, consider what would be better than the midpoint
272 // for both the 1fps case and 120fps case.
274 // TODO(scherkus): This can be vastly improved. Use a histogram to measure
275 // the accuracy of our frame timing code. http://crbug.com/149829
276 if (drop_frames_
&& last_timestamp_
!= kNoTimestamp()) {
277 base::TimeDelta now
= get_time_cb_
.Run();
278 base::TimeDelta deadline
= ready_frames_
.front()->timestamp() +
279 (ready_frames_
.front()->timestamp() - last_timestamp_
) / 2;
281 if (now
> deadline
) {
282 DropNextReadyFrame_Locked();
287 // Congratulations! You've made it past the video frame timing gauntlet.
289 // At this point enough time has passed that the next frame that ready for
291 PaintNextReadyFrame_Locked();
295 void VideoRendererImpl::PaintNextReadyFrame_Locked() {
296 lock_
.AssertAcquired();
298 scoped_refptr
<VideoFrame
> next_frame
= ready_frames_
.front();
299 ready_frames_
.pop_front();
302 last_timestamp_
= next_frame
->timestamp();
304 paint_cb_
.Run(next_frame
);
306 task_runner_
->PostTask(
308 base::Bind(&VideoRendererImpl::AttemptRead
, weak_factory_
.GetWeakPtr()));
311 void VideoRendererImpl::DropNextReadyFrame_Locked() {
312 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped");
314 lock_
.AssertAcquired();
316 last_timestamp_
= ready_frames_
.front()->timestamp();
317 ready_frames_
.pop_front();
321 task_runner_
->PostTask(
323 base::Bind(&VideoRendererImpl::AttemptRead
, weak_factory_
.GetWeakPtr()));
326 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status
,
327 const scoped_refptr
<VideoFrame
>& frame
) {
328 base::AutoLock
auto_lock(lock_
);
329 DCHECK_NE(state_
, kUninitialized
);
330 DCHECK_NE(state_
, kFlushed
);
332 CHECK(pending_read_
);
333 pending_read_
= false;
335 if (status
== VideoFrameStream::DECODE_ERROR
||
336 status
== VideoFrameStream::DECRYPT_ERROR
) {
337 DCHECK(!frame
.get());
338 PipelineStatus error
= PIPELINE_ERROR_DECODE
;
339 if (status
== VideoFrameStream::DECRYPT_ERROR
)
340 error
= PIPELINE_ERROR_DECRYPT
;
342 if (!preroll_cb_
.is_null()) {
343 base::ResetAndReturn(&preroll_cb_
).Run(error
);
347 error_cb_
.Run(error
);
351 // Already-queued VideoFrameStream ReadCB's can fire after various state
352 // transitions have happened; in that case just drop those frames immediately.
353 if (state_
== kStopped
|| state_
== kFlushing
)
357 // Abort preroll early for a NULL frame because we won't get more frames.
358 // A new preroll will be requested after this one completes so there is no
359 // point trying to collect more frames.
360 if (state_
== kPrerolling
)
361 TransitionToPrerolled_Locked();
366 if (frame
->end_of_stream()) {
367 DCHECK(!received_end_of_stream_
);
368 received_end_of_stream_
= true;
369 max_time_cb_
.Run(get_duration_cb_
.Run());
371 if (state_
== kPrerolling
)
372 TransitionToPrerolled_Locked();
377 // Maintain the latest frame decoded so the correct frame is displayed after
378 // prerolling has completed.
379 if (state_
== kPrerolling
&& preroll_timestamp_
!= kNoTimestamp() &&
380 frame
->timestamp() <= preroll_timestamp_
) {
381 ready_frames_
.clear();
384 AddReadyFrame_Locked(frame
);
386 if (ShouldTransitionToPrerolled_Locked())
387 TransitionToPrerolled_Locked();
389 // Always request more decoded video if we have capacity. This serves two
391 // 1) Prerolling while paused
392 // 2) Keeps decoding going if video rendering thread starts falling behind
393 AttemptRead_Locked();
396 bool VideoRendererImpl::ShouldTransitionToPrerolled_Locked() {
397 return state_
== kPrerolling
&&
398 (!video_frame_stream_
.CanReadWithoutStalling() ||
399 ready_frames_
.size() >= static_cast<size_t>(limits::kMaxVideoFrames
) ||
400 (low_delay_
&& ready_frames_
.size() > 0));
403 void VideoRendererImpl::AddReadyFrame_Locked(
404 const scoped_refptr
<VideoFrame
>& frame
) {
405 lock_
.AssertAcquired();
406 DCHECK(!frame
->end_of_stream());
408 // Adjust the incoming frame if its rendering stop time is past the duration
409 // of the video itself. This is typically the last frame of the video and
410 // occurs if the container specifies a duration that isn't a multiple of the
411 // frame rate. Another way for this to happen is for the container to state
412 // a smaller duration than the largest packet timestamp.
413 base::TimeDelta duration
= get_duration_cb_
.Run();
414 if (frame
->timestamp() > duration
) {
415 frame
->set_timestamp(duration
);
418 ready_frames_
.push_back(frame
);
419 DCHECK_LE(ready_frames_
.size(),
420 static_cast<size_t>(limits::kMaxVideoFrames
));
422 max_time_cb_
.Run(frame
->timestamp());
424 // Avoid needlessly waking up |thread_| unless playing.
425 if (state_
== kPlaying
)
426 frame_available_
.Signal();
429 void VideoRendererImpl::AttemptRead() {
430 base::AutoLock
auto_lock(lock_
);
431 AttemptRead_Locked();
434 void VideoRendererImpl::AttemptRead_Locked() {
435 DCHECK(task_runner_
->BelongsToCurrentThread());
436 lock_
.AssertAcquired();
438 if (pending_read_
|| received_end_of_stream_
||
439 ready_frames_
.size() == static_cast<size_t>(limits::kMaxVideoFrames
)) {
447 pending_read_
= true;
448 video_frame_stream_
.Read(base::Bind(&VideoRendererImpl::FrameReady
,
449 weak_factory_
.GetWeakPtr()));
461 void VideoRendererImpl::OnVideoFrameStreamResetDone() {
462 base::AutoLock
auto_lock(lock_
);
463 if (state_
== kStopped
)
466 DCHECK_EQ(kFlushing
, state_
);
467 DCHECK(!pending_read_
);
468 DCHECK(ready_frames_
.empty());
469 DCHECK(!received_end_of_stream_
);
470 DCHECK(!rendered_end_of_stream_
);
473 last_timestamp_
= kNoTimestamp();
474 base::ResetAndReturn(&flush_cb_
).Run();
477 base::TimeDelta
VideoRendererImpl::CalculateSleepDuration(
478 const scoped_refptr
<VideoFrame
>& next_frame
,
479 float playback_rate
) {
480 // Determine the current and next presentation timestamps.
481 base::TimeDelta now
= get_time_cb_
.Run();
482 base::TimeDelta next_pts
= next_frame
->timestamp();
484 // Scale our sleep based on the playback rate.
485 base::TimeDelta sleep
= next_pts
- now
;
486 return base::TimeDelta::FromMicroseconds(
487 static_cast<int64
>(sleep
.InMicroseconds() / playback_rate
));
490 void VideoRendererImpl::DoStopOrError_Locked() {
491 lock_
.AssertAcquired();
492 last_timestamp_
= kNoTimestamp();
493 ready_frames_
.clear();
496 void VideoRendererImpl::TransitionToPrerolled_Locked() {
497 lock_
.AssertAcquired();
498 DCHECK_EQ(state_
, kPrerolling
);
502 // Because we might remain in the prerolled state for an undetermined amount
503 // of time (e.g., we seeked while paused), we'll paint the first prerolled
505 if (!ready_frames_
.empty())
506 PaintNextReadyFrame_Locked();
508 base::ResetAndReturn(&preroll_cb_
).Run(PIPELINE_OK
);
511 void VideoRendererImpl::UpdateStatsAndWait_Locked(
512 base::TimeDelta wait_duration
) {
513 lock_
.AssertAcquired();
514 DCHECK_GE(frames_decoded_
, 0);
515 DCHECK_LE(frames_dropped_
, frames_decoded_
);
517 if (frames_decoded_
) {
518 PipelineStatistics statistics
;
519 statistics
.video_frames_decoded
= frames_decoded_
;
520 statistics
.video_frames_dropped
= frames_dropped_
;
521 statistics_cb_
.Run(statistics
);
527 frame_available_
.TimedWait(wait_duration
);