1 // Copyright (c) 2012 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_base.h"
8 #include "base/callback.h"
9 #include "base/callback_helpers.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/threading/platform_thread.h"
12 #include "media/base/buffers.h"
13 #include "media/base/limits.h"
14 #include "media/base/pipeline.h"
15 #include "media/base/video_frame.h"
19 base::TimeDelta
VideoRendererBase::kMaxLastFrameDuration() {
20 return base::TimeDelta::FromMilliseconds(250);
23 VideoRendererBase::VideoRendererBase(
24 const scoped_refptr
<base::MessageLoopProxy
>& message_loop
,
25 ScopedVector
<VideoDecoder
> decoders
,
26 const SetDecryptorReadyCB
& set_decryptor_ready_cb
,
27 const PaintCB
& paint_cb
,
28 const SetOpaqueCB
& set_opaque_cb
,
30 : message_loop_(message_loop
),
33 message_loop
, decoders
.Pass(), set_decryptor_ready_cb
),
34 received_end_of_stream_(false),
35 frame_available_(&lock_
),
36 state_(kUninitialized
),
39 drop_frames_(drop_frames
),
42 set_opaque_cb_(set_opaque_cb
),
43 last_timestamp_(kNoTimestamp()),
46 DCHECK(!paint_cb_
.is_null());
49 VideoRendererBase::~VideoRendererBase() {
50 base::AutoLock
auto_lock(lock_
);
51 CHECK(state_
== kStopped
|| state_
== kUninitialized
) << state_
;
52 CHECK(thread_
.is_null());
55 void VideoRendererBase::Play(const base::Closure
& callback
) {
56 DCHECK(message_loop_
->BelongsToCurrentThread());
57 base::AutoLock
auto_lock(lock_
);
58 DCHECK_EQ(kPrerolled
, state_
);
63 void VideoRendererBase::Pause(const base::Closure
& callback
) {
64 DCHECK(message_loop_
->BelongsToCurrentThread());
65 base::AutoLock
auto_lock(lock_
);
66 DCHECK(state_
!= kUninitialized
|| state_
== kError
);
71 void VideoRendererBase::Flush(const base::Closure
& callback
) {
72 DCHECK(message_loop_
->BelongsToCurrentThread());
73 base::AutoLock
auto_lock(lock_
);
74 DCHECK_EQ(state_
, kPaused
);
78 // This is necessary if the |video_frame_stream_| has already seen an end of
79 // stream and needs to drain it before flushing it.
80 ready_frames_
.clear();
81 received_end_of_stream_
= false;
82 video_frame_stream_
.Reset(base::Bind(
83 &VideoRendererBase::OnVideoFrameStreamResetDone
, weak_this_
));
86 void VideoRendererBase::Stop(const base::Closure
& callback
) {
87 DCHECK(message_loop_
->BelongsToCurrentThread());
88 base::AutoLock
auto_lock(lock_
);
89 if (state_
== kUninitialized
|| state_
== kStopped
) {
94 // TODO(scherkus): Consider invalidating |weak_factory_| and replacing
95 // task-running guards that check |state_| with DCHECK().
99 statistics_cb_
.Reset();
100 max_time_cb_
.Reset();
101 DoStopOrError_Locked();
103 // Clean up our thread if present.
104 base::PlatformThreadHandle thread_to_join
= base::PlatformThreadHandle();
105 if (!thread_
.is_null()) {
106 // Signal the thread since it's possible to get stopped with the video
107 // thread waiting for a read to complete.
108 frame_available_
.Signal();
109 std::swap(thread_
, thread_to_join
);
112 if (!thread_to_join
.is_null()) {
113 base::AutoUnlock
auto_unlock(lock_
);
114 base::PlatformThread::Join(thread_to_join
);
117 video_frame_stream_
.Stop(callback
);
120 void VideoRendererBase::SetPlaybackRate(float playback_rate
) {
121 DCHECK(message_loop_
->BelongsToCurrentThread());
122 base::AutoLock
auto_lock(lock_
);
123 playback_rate_
= playback_rate
;
126 void VideoRendererBase::Preroll(base::TimeDelta time
,
127 const PipelineStatusCB
& cb
) {
128 DCHECK(message_loop_
->BelongsToCurrentThread());
129 base::AutoLock
auto_lock(lock_
);
130 DCHECK_EQ(state_
, kFlushed
) << "Must flush prior to prerolling.";
131 DCHECK(!cb
.is_null());
132 DCHECK(preroll_cb_
.is_null());
134 state_
= kPrerolling
;
136 preroll_timestamp_
= time
;
137 AttemptRead_Locked();
140 void VideoRendererBase::Initialize(DemuxerStream
* stream
,
141 const PipelineStatusCB
& init_cb
,
142 const StatisticsCB
& statistics_cb
,
143 const TimeCB
& max_time_cb
,
144 const NaturalSizeChangedCB
& size_changed_cb
,
145 const base::Closure
& ended_cb
,
146 const PipelineStatusCB
& error_cb
,
147 const TimeDeltaCB
& get_time_cb
,
148 const TimeDeltaCB
& get_duration_cb
) {
149 DCHECK(message_loop_
->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(!max_time_cb
.is_null());
156 DCHECK(!size_changed_cb
.is_null());
157 DCHECK(!ended_cb
.is_null());
158 DCHECK(!get_time_cb
.is_null());
159 DCHECK(!get_duration_cb
.is_null());
160 DCHECK_EQ(kUninitialized
, state_
);
162 weak_this_
= weak_factory_
.GetWeakPtr();
164 statistics_cb_
= statistics_cb
;
165 max_time_cb_
= max_time_cb
;
166 size_changed_cb_
= size_changed_cb
;
167 ended_cb_
= ended_cb
;
168 error_cb_
= error_cb
;
169 get_time_cb_
= get_time_cb
;
170 get_duration_cb_
= get_duration_cb
;
171 state_
= kInitializing
;
173 video_frame_stream_
.Initialize(
176 base::Bind(&VideoRendererBase::OnVideoFrameStreamInitialized
,
180 void VideoRendererBase::OnVideoFrameStreamInitialized(bool success
,
182 DCHECK(message_loop_
->BelongsToCurrentThread());
183 base::AutoLock
auto_lock(lock_
);
185 if (state_
== kStopped
)
188 DCHECK_EQ(state_
, kInitializing
);
191 state_
= kUninitialized
;
192 base::ResetAndReturn(&init_cb_
).Run(DECODER_ERROR_NOT_SUPPORTED
);
196 // We're all good! Consider ourselves flushed. (ThreadMain() should never
197 // see us in the kUninitialized state).
198 // Since we had an initial Preroll(), we consider ourself flushed, because we
199 // have not populated any buffers yet.
202 set_opaque_cb_
.Run(!has_alpha
);
203 set_opaque_cb_
.Reset();
205 // Create our video thread.
206 if (!base::PlatformThread::Create(0, this, &thread_
)) {
207 NOTREACHED() << "Video thread creation failed";
209 base::ResetAndReturn(&init_cb_
).Run(PIPELINE_ERROR_INITIALIZATION_FAILED
);
214 // Bump up our priority so our sleeping is more accurate.
215 // TODO(scherkus): find out if this is necessary, but it seems to help.
216 ::SetThreadPriority(thread_
.platform_handle(), THREAD_PRIORITY_ABOVE_NORMAL
);
217 #endif // defined(OS_WIN)
218 base::ResetAndReturn(&init_cb_
).Run(PIPELINE_OK
);
221 // PlatformThread::Delegate implementation.
222 void VideoRendererBase::ThreadMain() {
223 base::PlatformThread::SetName("CrVideoRenderer");
225 // The number of milliseconds to idle when we do not have anything to do.
226 // Nothing special about the value, other than we're being more OS-friendly
227 // than sleeping for 1 millisecond.
229 // TODO(scherkus): switch to pure event-driven frame timing instead of this
230 // kIdleTimeDelta business http://crbug.com/106874
231 const base::TimeDelta kIdleTimeDelta
=
232 base::TimeDelta::FromMilliseconds(10);
235 base::AutoLock
auto_lock(lock_
);
237 // Thread exit condition.
238 if (state_
== kStopped
)
241 // Remain idle as long as we're not playing.
242 if (state_
!= kPlaying
|| playback_rate_
== 0) {
243 UpdateStatsAndWait_Locked(kIdleTimeDelta
);
247 // Remain idle until we have the next frame ready for rendering.
248 if (ready_frames_
.empty()) {
249 if (received_end_of_stream_
) {
253 // No need to sleep here as we idle when |state_ != kPlaying|.
257 UpdateStatsAndWait_Locked(kIdleTimeDelta
);
261 base::TimeDelta remaining_time
=
262 CalculateSleepDuration(ready_frames_
.front(), playback_rate_
);
264 // Sleep up to a maximum of our idle time until we're within the time to
265 // render the next frame.
266 if (remaining_time
.InMicroseconds() > 0) {
267 remaining_time
= std::min(remaining_time
, kIdleTimeDelta
);
268 UpdateStatsAndWait_Locked(remaining_time
);
272 // Deadline is defined as the midpoint between this frame and the next
273 // frame, using the delta between this frame and the previous frame as the
274 // assumption for frame duration.
276 // TODO(scherkus): An improvement over midpoint might be selecting the
277 // minimum and/or maximum between the midpoint and some constants. As a
278 // thought experiment, consider what would be better than the midpoint
279 // for both the 1fps case and 120fps case.
281 // TODO(scherkus): This can be vastly improved. Use a histogram to measure
282 // the accuracy of our frame timing code. http://crbug.com/149829
283 if (drop_frames_
&& last_timestamp_
!= kNoTimestamp()) {
284 base::TimeDelta now
= get_time_cb_
.Run();
285 base::TimeDelta deadline
= ready_frames_
.front()->GetTimestamp() +
286 (ready_frames_
.front()->GetTimestamp() - last_timestamp_
) / 2;
288 if (now
> deadline
) {
289 DropNextReadyFrame_Locked();
294 // Congratulations! You've made it past the video frame timing gauntlet.
296 // At this point enough time has passed that the next frame that ready for
298 PaintNextReadyFrame_Locked();
302 void VideoRendererBase::PaintNextReadyFrame_Locked() {
303 lock_
.AssertAcquired();
305 scoped_refptr
<VideoFrame
> next_frame
= ready_frames_
.front();
306 ready_frames_
.pop_front();
309 last_timestamp_
= next_frame
->GetTimestamp();
311 const gfx::Size
& natural_size
= next_frame
->natural_size();
312 if (natural_size
!= last_natural_size_
) {
313 last_natural_size_
= natural_size
;
314 size_changed_cb_
.Run(natural_size
);
317 paint_cb_
.Run(next_frame
);
319 message_loop_
->PostTask(FROM_HERE
, base::Bind(
320 &VideoRendererBase::AttemptRead
, weak_this_
));
323 void VideoRendererBase::DropNextReadyFrame_Locked() {
324 lock_
.AssertAcquired();
326 last_timestamp_
= ready_frames_
.front()->GetTimestamp();
327 ready_frames_
.pop_front();
331 message_loop_
->PostTask(FROM_HERE
, base::Bind(
332 &VideoRendererBase::AttemptRead
, weak_this_
));
335 void VideoRendererBase::FrameReady(VideoFrameStream::Status status
,
336 const scoped_refptr
<VideoFrame
>& frame
) {
337 base::AutoLock
auto_lock(lock_
);
338 DCHECK_NE(state_
, kUninitialized
);
339 DCHECK_NE(state_
, kFlushed
);
341 CHECK(pending_read_
);
342 pending_read_
= false;
344 if (status
== VideoFrameStream::DECODE_ERROR
||
345 status
== VideoFrameStream::DECRYPT_ERROR
) {
346 DCHECK(!frame
.get());
347 PipelineStatus error
= PIPELINE_ERROR_DECODE
;
348 if (status
== VideoFrameStream::DECRYPT_ERROR
)
349 error
= PIPELINE_ERROR_DECRYPT
;
351 if (!preroll_cb_
.is_null()) {
352 base::ResetAndReturn(&preroll_cb_
).Run(error
);
356 error_cb_
.Run(error
);
360 // Already-queued VideoFrameStream ReadCB's can fire after various state
361 // transitions have happened; in that case just drop those frames immediately.
362 if (state_
== kStopped
|| state_
== kError
|| state_
== kFlushing
)
366 // Abort preroll early for a NULL frame because we won't get more frames.
367 // A new preroll will be requested after this one completes so there is no
368 // point trying to collect more frames.
369 if (state_
== kPrerolling
)
370 TransitionToPrerolled_Locked();
375 if (frame
->IsEndOfStream()) {
376 DCHECK(!received_end_of_stream_
);
377 received_end_of_stream_
= true;
378 max_time_cb_
.Run(get_duration_cb_
.Run());
380 if (state_
== kPrerolling
)
381 TransitionToPrerolled_Locked();
386 // Maintain the latest frame decoded so the correct frame is displayed after
387 // prerolling has completed.
388 if (state_
== kPrerolling
&& frame
->GetTimestamp() <= preroll_timestamp_
) {
389 ready_frames_
.clear();
392 AddReadyFrame_Locked(frame
);
394 if (state_
== kPrerolling
) {
395 if (!video_frame_stream_
.CanReadWithoutStalling() ||
396 ready_frames_
.size() >= static_cast<size_t>(limits::kMaxVideoFrames
)) {
397 TransitionToPrerolled_Locked();
401 // Always request more decoded video if we have capacity. This serves two
403 // 1) Prerolling while paused
404 // 2) Keeps decoding going if video rendering thread starts falling behind
405 AttemptRead_Locked();
408 void VideoRendererBase::AddReadyFrame_Locked(
409 const scoped_refptr
<VideoFrame
>& frame
) {
410 lock_
.AssertAcquired();
411 DCHECK(!frame
->IsEndOfStream());
413 // Adjust the incoming frame if its rendering stop time is past the duration
414 // of the video itself. This is typically the last frame of the video and
415 // occurs if the container specifies a duration that isn't a multiple of the
416 // frame rate. Another way for this to happen is for the container to state
417 // a smaller duration than the largest packet timestamp.
418 base::TimeDelta duration
= get_duration_cb_
.Run();
419 if (frame
->GetTimestamp() > duration
) {
420 frame
->SetTimestamp(duration
);
423 ready_frames_
.push_back(frame
);
424 DCHECK_LE(ready_frames_
.size(),
425 static_cast<size_t>(limits::kMaxVideoFrames
));
427 max_time_cb_
.Run(frame
->GetTimestamp());
429 // Avoid needlessly waking up |thread_| unless playing.
430 if (state_
== kPlaying
)
431 frame_available_
.Signal();
434 void VideoRendererBase::AttemptRead() {
435 base::AutoLock
auto_lock(lock_
);
436 AttemptRead_Locked();
439 void VideoRendererBase::AttemptRead_Locked() {
440 DCHECK(message_loop_
->BelongsToCurrentThread());
441 lock_
.AssertAcquired();
443 if (pending_read_
|| received_end_of_stream_
||
444 ready_frames_
.size() == static_cast<size_t>(limits::kMaxVideoFrames
)) {
452 pending_read_
= true;
453 video_frame_stream_
.Read(base::Bind(&VideoRendererBase::FrameReady
,
469 void VideoRendererBase::OnVideoFrameStreamResetDone() {
470 base::AutoLock
auto_lock(lock_
);
471 if (state_
== kStopped
)
474 DCHECK_EQ(kFlushing
, state_
);
475 DCHECK(!pending_read_
);
476 DCHECK(ready_frames_
.empty());
477 DCHECK(!received_end_of_stream_
);
480 last_timestamp_
= kNoTimestamp();
481 base::ResetAndReturn(&flush_cb_
).Run();
484 base::TimeDelta
VideoRendererBase::CalculateSleepDuration(
485 const scoped_refptr
<VideoFrame
>& next_frame
,
486 float playback_rate
) {
487 // Determine the current and next presentation timestamps.
488 base::TimeDelta now
= get_time_cb_
.Run();
489 base::TimeDelta next_pts
= next_frame
->GetTimestamp();
491 // Scale our sleep based on the playback rate.
492 base::TimeDelta sleep
= next_pts
- now
;
493 return base::TimeDelta::FromMicroseconds(
494 static_cast<int64
>(sleep
.InMicroseconds() / playback_rate
));
497 void VideoRendererBase::DoStopOrError_Locked() {
498 lock_
.AssertAcquired();
499 last_timestamp_
= kNoTimestamp();
500 ready_frames_
.clear();
503 void VideoRendererBase::TransitionToPrerolled_Locked() {
504 lock_
.AssertAcquired();
505 DCHECK_EQ(state_
, kPrerolling
);
509 // Because we might remain in the prerolled state for an undetermined amount
510 // of time (e.g., we seeked while paused), we'll paint the first prerolled
512 if (!ready_frames_
.empty())
513 PaintNextReadyFrame_Locked();
515 base::ResetAndReturn(&preroll_cb_
).Run(PIPELINE_OK
);
518 void VideoRendererBase::UpdateStatsAndWait_Locked(
519 base::TimeDelta wait_duration
) {
520 lock_
.AssertAcquired();
521 DCHECK_GE(frames_decoded_
, 0);
522 DCHECK_LE(frames_dropped_
, frames_decoded_
);
524 if (frames_decoded_
) {
525 PipelineStatistics statistics
;
526 statistics
.video_frames_decoded
= frames_decoded_
;
527 statistics
.video_frames_dropped
= frames_dropped_
;
528 statistics_cb_
.Run(statistics
);
534 frame_available_
.TimedWait(wait_duration
);