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.
6 #include "base/callback.h"
7 #include "base/callback_helpers.h"
8 #include "base/threading/platform_thread.h"
9 #include "media/base/buffers.h"
10 #include "media/base/filter_host.h"
11 #include "media/base/limits.h"
12 #include "media/base/pipeline.h"
13 #include "media/base/video_frame.h"
14 #include "media/filters/video_renderer_base.h"
18 VideoRendererBase::VideoRendererBase(const base::Closure
& paint_cb
,
19 const SetOpaqueCB
& set_opaque_cb
,
21 : frame_available_(&lock_
),
22 state_(kUninitialized
),
23 thread_(base::kNullThreadHandle
),
25 pending_paint_(false),
26 pending_paint_with_last_available_(false),
27 drop_frames_(drop_frames
),
30 set_opaque_cb_(set_opaque_cb
) {
31 DCHECK(!paint_cb_
.is_null());
34 void VideoRendererBase::Play(const base::Closure
& callback
) {
35 base::AutoLock
auto_lock(lock_
);
36 DCHECK_EQ(kPrerolled
, state_
);
41 void VideoRendererBase::Pause(const base::Closure
& callback
) {
42 base::AutoLock
auto_lock(lock_
);
43 DCHECK(state_
!= kUninitialized
|| state_
== kError
);
48 void VideoRendererBase::Flush(const base::Closure
& callback
) {
49 base::AutoLock
auto_lock(lock_
);
50 DCHECK_EQ(state_
, kPaused
);
52 state_
= kFlushingDecoder
;
54 // We must unlock here because the callback might run within the Flush()
56 // TODO: Remove this line when fixing http://crbug.com/125020
57 base::AutoUnlock
auto_unlock(lock_
);
58 decoder_
->Reset(base::Bind(&VideoRendererBase::OnDecoderFlushDone
, this));
61 void VideoRendererBase::Stop(const base::Closure
& callback
) {
62 if (state_
== kStopped
) {
67 base::PlatformThreadHandle thread_to_join
= base::kNullThreadHandle
;
69 base::AutoLock
auto_lock(lock_
);
72 statistics_cb_
.Reset();
74 if (!pending_paint_
&& !pending_paint_with_last_available_
)
75 DoStopOrError_Locked();
77 // Clean up our thread if present.
78 if (thread_
!= base::kNullThreadHandle
) {
79 // Signal the thread since it's possible to get stopped with the video
80 // thread waiting for a read to complete.
81 frame_available_
.Signal();
82 thread_to_join
= thread_
;
83 thread_
= base::kNullThreadHandle
;
86 if (thread_to_join
!= base::kNullThreadHandle
)
87 base::PlatformThread::Join(thread_to_join
);
89 decoder_
->Stop(callback
);
92 void VideoRendererBase::SetPlaybackRate(float playback_rate
) {
93 base::AutoLock
auto_lock(lock_
);
94 playback_rate_
= playback_rate
;
97 void VideoRendererBase::Seek(base::TimeDelta time
, const PipelineStatusCB
& cb
) {
98 base::AutoLock
auto_lock(lock_
);
99 DCHECK_EQ(state_
, kFlushed
) << "Must flush prior to seeking.";
100 DCHECK(!cb
.is_null());
101 DCHECK(seek_cb_
.is_null());
105 seek_timestamp_
= time
;
106 AttemptRead_Locked();
109 void VideoRendererBase::Initialize(const scoped_refptr
<VideoDecoder
>& decoder
,
110 const PipelineStatusCB
& status_cb
,
111 const StatisticsCB
& statistics_cb
,
112 const TimeCB
& time_cb
) {
113 base::AutoLock
auto_lock(lock_
);
115 DCHECK(!status_cb
.is_null());
116 DCHECK(!statistics_cb
.is_null());
117 DCHECK(!time_cb
.is_null());
118 DCHECK_EQ(kUninitialized
, state_
);
121 statistics_cb_
= statistics_cb
;
124 // Notify the pipeline of the video dimensions.
125 host()->SetNaturalVideoSize(decoder_
->natural_size());
127 // We're all good! Consider ourselves flushed. (ThreadMain() should never
128 // see us in the kUninitialized state).
129 // Since we had an initial Seek, we consider ourself flushed, because we
130 // have not populated any buffers yet.
133 set_opaque_cb_
.Run(!decoder
->HasAlpha());
134 set_opaque_cb_
.Reset();
136 // Create our video thread.
137 if (!base::PlatformThread::Create(0, this, &thread_
)) {
138 NOTREACHED() << "Video thread creation failed";
140 status_cb
.Run(PIPELINE_ERROR_INITIALIZATION_FAILED
);
145 // Bump up our priority so our sleeping is more accurate.
146 // TODO(scherkus): find out if this is necessary, but it seems to help.
147 ::SetThreadPriority(thread_
, THREAD_PRIORITY_ABOVE_NORMAL
);
148 #endif // defined(OS_WIN)
149 status_cb
.Run(PIPELINE_OK
);
152 bool VideoRendererBase::HasEnded() {
153 base::AutoLock
auto_lock(lock_
);
154 return state_
== kEnded
;
157 // PlatformThread::Delegate implementation.
158 void VideoRendererBase::ThreadMain() {
159 base::PlatformThread::SetName("CrVideoRenderer");
161 // The number of milliseconds to idle when we do not have anything to do.
162 // Nothing special about the value, other than we're being more OS-friendly
163 // than sleeping for 1 millisecond.
165 // TOOD(scherkus): switch to pure event-driven frame timing instead of this
166 // kIdleTimeDelta business http://crbug.com/106874
167 const base::TimeDelta kIdleTimeDelta
=
168 base::TimeDelta::FromMilliseconds(10);
170 uint32 frames_dropped
= 0;
173 if (frames_dropped
> 0) {
174 PipelineStatistics statistics
;
175 statistics
.video_frames_dropped
= frames_dropped
;
176 statistics_cb_
.Run(statistics
);
181 base::AutoLock
auto_lock(lock_
);
183 // Thread exit condition.
184 if (state_
== kStopped
)
187 // Remain idle as long as we're not playing.
188 if (state_
!= kPlaying
|| playback_rate_
== 0) {
189 frame_available_
.TimedWait(kIdleTimeDelta
);
193 // Remain idle until we have the next frame ready for rendering.
194 if (ready_frames_
.empty()) {
195 frame_available_
.TimedWait(kIdleTimeDelta
);
199 // Remain idle until we've initialized |current_frame_| via prerolling.
200 if (!current_frame_
) {
201 // This can happen if our preroll only contains end of stream frames.
202 if (ready_frames_
.front()->IsEndOfStream()) {
204 host()->NotifyEnded();
205 ready_frames_
.clear();
207 // No need to sleep here as we idle when |state_ != kPlaying|.
211 frame_available_
.TimedWait(kIdleTimeDelta
);
215 // Calculate how long until we should advance the frame, which is
216 // typically negative but for playback rates < 1.0f may be long enough
217 // that it makes more sense to idle and check again.
218 base::TimeDelta remaining_time
=
219 CalculateSleepDuration(ready_frames_
.front(), playback_rate_
);
221 // Sleep up to a maximum of our idle time until we're within the time to
222 // render the next frame.
223 if (remaining_time
.InMicroseconds() > 0) {
224 remaining_time
= std::min(remaining_time
, kIdleTimeDelta
);
225 frame_available_
.TimedWait(remaining_time
);
230 // We're almost there!
232 // At this point we've rendered |current_frame_| for the proper amount
233 // of time and also have the next frame that ready for rendering.
236 // If the next frame is end of stream then we are truly at the end of the
239 // TODO(scherkus): deduplicate this end of stream check after we get rid of
241 if (ready_frames_
.front()->IsEndOfStream()) {
243 host()->NotifyEnded();
244 ready_frames_
.clear();
246 // No need to sleep here as we idle when |state_ != kPlaying|.
250 // We cannot update |current_frame_| until we've completed the pending
251 // paint. Furthermore, the pending paint might be really slow: check to
252 // see if we have any ready frames that we can drop if they've already
254 if (pending_paint_
) {
255 while (!ready_frames_
.empty()) {
256 // Can't drop anything if we're at the end.
257 if (ready_frames_
.front()->IsEndOfStream())
260 base::TimeDelta remaining_time
=
261 ready_frames_
.front()->GetTimestamp() - host()->GetTime();
263 // Still a chance we can render the frame!
264 if (remaining_time
.InMicroseconds() > 0)
270 // Frame dropped: read again.
272 ready_frames_
.pop_front();
273 AttemptRead_Locked();
275 // Continue waiting for the current paint to finish.
276 frame_available_
.TimedWait(kIdleTimeDelta
);
281 // Congratulations! You've made it past the video frame timing gauntlet.
283 // We can now safely update the current frame, request another frame, and
284 // signal to the client that a new frame is available.
285 DCHECK(!pending_paint_
);
286 DCHECK(!ready_frames_
.empty());
287 current_frame_
= ready_frames_
.front();
288 ready_frames_
.pop_front();
289 AttemptRead_Locked();
291 base::AutoUnlock
auto_unlock(lock_
);
296 void VideoRendererBase::GetCurrentFrame(scoped_refptr
<VideoFrame
>* frame_out
) {
297 base::AutoLock
auto_lock(lock_
);
298 DCHECK(!pending_paint_
&& !pending_paint_with_last_available_
);
300 if ((!current_frame_
|| current_frame_
->IsEndOfStream()) &&
301 (!last_available_frame_
|| last_available_frame_
->IsEndOfStream())) {
306 // We should have initialized and have the current frame.
307 DCHECK_NE(state_
, kUninitialized
);
308 DCHECK_NE(state_
, kStopped
);
309 DCHECK_NE(state_
, kError
);
311 if (current_frame_
) {
312 *frame_out
= current_frame_
;
313 last_available_frame_
= current_frame_
;
314 pending_paint_
= true;
316 DCHECK(last_available_frame_
);
317 *frame_out
= last_available_frame_
;
318 pending_paint_with_last_available_
= true;
322 void VideoRendererBase::PutCurrentFrame(scoped_refptr
<VideoFrame
> frame
) {
323 base::AutoLock
auto_lock(lock_
);
325 // Note that we do not claim |pending_paint_| when we return NULL frame, in
326 // that case, |current_frame_| could be changed before PutCurrentFrame.
327 if (pending_paint_
) {
328 DCHECK_EQ(current_frame_
, frame
);
329 DCHECK(!pending_paint_with_last_available_
);
330 pending_paint_
= false;
331 } else if (pending_paint_with_last_available_
) {
332 DCHECK_EQ(last_available_frame_
, frame
);
333 DCHECK(!pending_paint_
);
334 pending_paint_with_last_available_
= false;
339 // We had cleared the |pending_paint_| flag, there are chances that current
340 // frame is timed-out. We will wake up our main thread to advance the current
341 // frame when this is true.
342 frame_available_
.Signal();
343 if (state_
== kFlushingDecoder
)
346 if (state_
== kFlushing
) {
347 AttemptFlush_Locked();
351 if (state_
== kError
|| state_
== kStopped
) {
352 DoStopOrError_Locked();
356 VideoRendererBase::~VideoRendererBase() {
357 base::AutoLock
auto_lock(lock_
);
358 DCHECK(state_
== kUninitialized
|| state_
== kStopped
) << state_
;
361 void VideoRendererBase::FrameReady(VideoDecoder::DecoderStatus status
,
362 scoped_refptr
<VideoFrame
> frame
) {
363 base::AutoLock
auto_lock(lock_
);
364 DCHECK_NE(state_
, kUninitialized
);
366 CHECK(pending_read_
);
367 pending_read_
= false;
369 if (status
!= VideoDecoder::kOk
) {
371 PipelineStatus error
= PIPELINE_ERROR_DECODE
;
372 if (status
== VideoDecoder::kDecryptError
)
373 error
= PIPELINE_ERROR_DECRYPT
;
375 if (!seek_cb_
.is_null()) {
376 base::ResetAndReturn(&seek_cb_
).Run(error
);
380 host()->SetError(error
);
384 // Already-queued Decoder ReadCB's can fire after various state transitions
385 // have happened; in that case just drop those frames immediately.
386 if (state_
== kStopped
|| state_
== kError
|| state_
== kFlushed
||
387 state_
== kFlushingDecoder
)
390 if (state_
== kFlushing
) {
391 AttemptFlush_Locked();
396 if (state_
!= kSeeking
)
399 // Abort seek early for a NULL frame because we won't get more frames.
400 // A new seek will be requested after this one completes so there is no
401 // point trying to collect more frames.
403 base::ResetAndReturn(&seek_cb_
).Run(PIPELINE_OK
);
407 // Discard frames until we reach our desired seek timestamp.
408 if (state_
== kSeeking
&& !frame
->IsEndOfStream() &&
409 (frame
->GetTimestamp() + frame
->GetDuration()) <= seek_timestamp_
) {
410 AttemptRead_Locked();
414 // Adjust the incoming frame if its rendering stop time is past the duration
415 // of the video itself. This is typically the last frame of the video and
416 // occurs if the container specifies a duration that isn't a multiple of the
417 // frame rate. Another way for this to happen is for the container to state a
418 // smaller duration than the largest packet timestamp.
419 if (!frame
->IsEndOfStream()) {
420 if (frame
->GetTimestamp() > host()->GetDuration())
421 frame
->SetTimestamp(host()->GetDuration());
422 if ((frame
->GetTimestamp() + frame
->GetDuration()) > host()->GetDuration())
423 frame
->SetDuration(host()->GetDuration() - frame
->GetTimestamp());
426 // This one's a keeper! Place it in the ready queue.
427 ready_frames_
.push_back(frame
);
428 DCHECK_LE(NumFrames_Locked(), limits::kMaxVideoFrames
);
429 if (!frame
->IsEndOfStream())
430 time_cb_
.Run(frame
->GetTimestamp() + frame
->GetDuration());
431 frame_available_
.Signal();
433 PipelineStatistics statistics
;
434 statistics
.video_frames_decoded
= 1;
435 statistics_cb_
.Run(statistics
);
437 // Always request more decoded video if we have capacity. This serves two
439 // 1) Prerolling while paused
440 // 2) Keeps decoding going if video rendering thread starts falling behind
441 if (NumFrames_Locked() < limits::kMaxVideoFrames
&& !frame
->IsEndOfStream()) {
442 AttemptRead_Locked();
446 // If we're at capacity or end of stream while seeking we need to transition
448 if (state_
== kSeeking
) {
449 DCHECK(!current_frame_
);
452 // Because we might remain in the prerolled state for an undetermined amount
453 // of time (i.e., we were not playing before we received a seek), we'll
454 // manually update the current frame and notify the subclass below.
455 if (!ready_frames_
.front()->IsEndOfStream()) {
456 current_frame_
= ready_frames_
.front();
457 ready_frames_
.pop_front();
460 // ...and we're done seeking!
461 DCHECK(!seek_cb_
.is_null());
462 base::ResetAndReturn(&seek_cb_
).Run(PIPELINE_OK
);
464 base::AutoUnlock
ul(lock_
);
469 void VideoRendererBase::AttemptRead_Locked() {
470 lock_
.AssertAcquired();
471 DCHECK_NE(kEnded
, state_
);
474 NumFrames_Locked() == limits::kMaxVideoFrames
||
475 (!ready_frames_
.empty() && ready_frames_
.back()->IsEndOfStream()) ||
476 state_
== kFlushingDecoder
||
477 state_
== kFlushing
) {
481 pending_read_
= true;
482 decoder_
->Read(base::Bind(&VideoRendererBase::FrameReady
, this));
485 void VideoRendererBase::OnDecoderFlushDone() {
486 base::AutoLock
auto_lock(lock_
);
487 DCHECK_EQ(kFlushingDecoder
, state_
);
488 DCHECK(!pending_read_
);
491 AttemptFlush_Locked();
494 void VideoRendererBase::AttemptFlush_Locked() {
495 lock_
.AssertAcquired();
496 DCHECK_EQ(kFlushing
, state_
);
498 // Get rid of any ready frames.
499 ready_frames_
.clear();
501 if (!pending_paint_
&& !pending_read_
) {
503 current_frame_
= NULL
;
504 base::ResetAndReturn(&flush_cb_
).Run();
508 base::TimeDelta
VideoRendererBase::CalculateSleepDuration(
509 const scoped_refptr
<VideoFrame
>& next_frame
,
510 float playback_rate
) {
511 // Determine the current and next presentation timestamps.
512 base::TimeDelta now
= host()->GetTime();
513 base::TimeDelta this_pts
= current_frame_
->GetTimestamp();
514 base::TimeDelta next_pts
;
515 if (!next_frame
->IsEndOfStream()) {
516 next_pts
= next_frame
->GetTimestamp();
518 next_pts
= this_pts
+ current_frame_
->GetDuration();
521 // Scale our sleep based on the playback rate.
522 base::TimeDelta sleep
= next_pts
- now
;
523 return base::TimeDelta::FromMicroseconds(
524 static_cast<int64
>(sleep
.InMicroseconds() / playback_rate
));
527 void VideoRendererBase::DoStopOrError_Locked() {
528 DCHECK(!pending_paint_
);
529 DCHECK(!pending_paint_with_last_available_
);
530 lock_
.AssertAcquired();
531 current_frame_
= NULL
;
532 last_available_frame_
= NULL
;
533 ready_frames_
.clear();
536 int VideoRendererBase::NumFrames_Locked() const {
537 lock_
.AssertAcquired();
538 int outstanding_frames
=
539 (current_frame_
? 1 : 0) + (last_available_frame_
? 1 : 0) +
540 (current_frame_
&& (current_frame_
== last_available_frame_
) ? -1 : 0);
541 return ready_frames_
.size() + outstanding_frames
;