cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / media / filters / video_renderer_base.cc
blob806bb4a2fb8b8cbebae0c8ac164aba56a0b6f9ab
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"
7 #include "base/bind.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"
17 namespace media {
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,
29 bool drop_frames)
30 : message_loop_(message_loop),
31 weak_factory_(this),
32 video_frame_stream_(
33 message_loop, decoders.Pass(), set_decryptor_ready_cb),
34 received_end_of_stream_(false),
35 frame_available_(&lock_),
36 state_(kUninitialized),
37 thread_(),
38 pending_read_(false),
39 drop_frames_(drop_frames),
40 playback_rate_(0),
41 paint_cb_(paint_cb),
42 set_opaque_cb_(set_opaque_cb),
43 last_timestamp_(kNoTimestamp()) {
44 DCHECK(!paint_cb_.is_null());
47 VideoRendererBase::~VideoRendererBase() {
48 base::AutoLock auto_lock(lock_);
49 CHECK(state_ == kStopped || state_ == kUninitialized) << state_;
50 CHECK(thread_.is_null());
53 void VideoRendererBase::Play(const base::Closure& callback) {
54 DCHECK(message_loop_->BelongsToCurrentThread());
55 base::AutoLock auto_lock(lock_);
56 DCHECK_EQ(kPrerolled, state_);
57 state_ = kPlaying;
58 callback.Run();
61 void VideoRendererBase::Pause(const base::Closure& callback) {
62 DCHECK(message_loop_->BelongsToCurrentThread());
63 base::AutoLock auto_lock(lock_);
64 DCHECK(state_ != kUninitialized || state_ == kError);
65 state_ = kPaused;
66 callback.Run();
69 void VideoRendererBase::Flush(const base::Closure& callback) {
70 DCHECK(message_loop_->BelongsToCurrentThread());
71 base::AutoLock auto_lock(lock_);
72 DCHECK_EQ(state_, kPaused);
73 flush_cb_ = callback;
74 state_ = kFlushing;
76 // This is necessary if the |video_frame_stream_| has already seen an end of
77 // stream and needs to drain it before flushing it.
78 ready_frames_.clear();
79 received_end_of_stream_ = false;
80 video_frame_stream_.Reset(base::Bind(
81 &VideoRendererBase::OnVideoFrameStreamResetDone, weak_this_));
84 void VideoRendererBase::Stop(const base::Closure& callback) {
85 DCHECK(message_loop_->BelongsToCurrentThread());
86 base::AutoLock auto_lock(lock_);
87 if (state_ == kUninitialized || state_ == kStopped) {
88 callback.Run();
89 return;
92 // TODO(scherkus): Consider invalidating |weak_factory_| and replacing
93 // task-running guards that check |state_| with DCHECK().
95 state_ = kStopped;
97 statistics_cb_.Reset();
98 max_time_cb_.Reset();
99 DoStopOrError_Locked();
101 // Clean up our thread if present.
102 base::PlatformThreadHandle thread_to_join = base::PlatformThreadHandle();
103 if (!thread_.is_null()) {
104 // Signal the thread since it's possible to get stopped with the video
105 // thread waiting for a read to complete.
106 frame_available_.Signal();
107 std::swap(thread_, thread_to_join);
110 if (!thread_to_join.is_null()) {
111 base::AutoUnlock auto_unlock(lock_);
112 base::PlatformThread::Join(thread_to_join);
115 video_frame_stream_.Stop(callback);
118 void VideoRendererBase::SetPlaybackRate(float playback_rate) {
119 DCHECK(message_loop_->BelongsToCurrentThread());
120 base::AutoLock auto_lock(lock_);
121 playback_rate_ = playback_rate;
124 void VideoRendererBase::Preroll(base::TimeDelta time,
125 const PipelineStatusCB& cb) {
126 DCHECK(message_loop_->BelongsToCurrentThread());
127 base::AutoLock auto_lock(lock_);
128 DCHECK_EQ(state_, kFlushed) << "Must flush prior to prerolling.";
129 DCHECK(!cb.is_null());
130 DCHECK(preroll_cb_.is_null());
132 state_ = kPrerolling;
133 preroll_cb_ = cb;
134 preroll_timestamp_ = time;
135 AttemptRead_Locked();
138 void VideoRendererBase::Initialize(DemuxerStream* stream,
139 const PipelineStatusCB& init_cb,
140 const StatisticsCB& statistics_cb,
141 const TimeCB& max_time_cb,
142 const NaturalSizeChangedCB& size_changed_cb,
143 const base::Closure& ended_cb,
144 const PipelineStatusCB& error_cb,
145 const TimeDeltaCB& get_time_cb,
146 const TimeDeltaCB& get_duration_cb) {
147 DCHECK(message_loop_->BelongsToCurrentThread());
148 base::AutoLock auto_lock(lock_);
149 DCHECK(stream);
150 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO);
151 DCHECK(!init_cb.is_null());
152 DCHECK(!statistics_cb.is_null());
153 DCHECK(!max_time_cb.is_null());
154 DCHECK(!size_changed_cb.is_null());
155 DCHECK(!ended_cb.is_null());
156 DCHECK(!get_time_cb.is_null());
157 DCHECK(!get_duration_cb.is_null());
158 DCHECK_EQ(kUninitialized, state_);
160 weak_this_ = weak_factory_.GetWeakPtr();
161 init_cb_ = init_cb;
162 statistics_cb_ = statistics_cb;
163 max_time_cb_ = max_time_cb;
164 size_changed_cb_ = size_changed_cb;
165 ended_cb_ = ended_cb;
166 error_cb_ = error_cb;
167 get_time_cb_ = get_time_cb;
168 get_duration_cb_ = get_duration_cb;
169 state_ = kInitializing;
171 video_frame_stream_.Initialize(
172 stream,
173 statistics_cb,
174 base::Bind(&VideoRendererBase::OnVideoFrameStreamInitialized,
175 weak_this_));
178 void VideoRendererBase::OnVideoFrameStreamInitialized(bool success,
179 bool has_alpha) {
180 DCHECK(message_loop_->BelongsToCurrentThread());
181 base::AutoLock auto_lock(lock_);
183 if (state_ == kStopped)
184 return;
186 DCHECK_EQ(state_, kInitializing);
188 if (!success) {
189 state_ = kUninitialized;
190 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED);
191 return;
194 // We're all good! Consider ourselves flushed. (ThreadMain() should never
195 // see us in the kUninitialized state).
196 // Since we had an initial Preroll(), we consider ourself flushed, because we
197 // have not populated any buffers yet.
198 state_ = kFlushed;
200 set_opaque_cb_.Run(!has_alpha);
201 set_opaque_cb_.Reset();
203 // Create our video thread.
204 if (!base::PlatformThread::Create(0, this, &thread_)) {
205 NOTREACHED() << "Video thread creation failed";
206 state_ = kError;
207 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED);
208 return;
211 #if defined(OS_WIN)
212 // Bump up our priority so our sleeping is more accurate.
213 // TODO(scherkus): find out if this is necessary, but it seems to help.
214 ::SetThreadPriority(thread_.platform_handle(), THREAD_PRIORITY_ABOVE_NORMAL);
215 #endif // defined(OS_WIN)
216 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
219 // PlatformThread::Delegate implementation.
220 void VideoRendererBase::ThreadMain() {
221 base::PlatformThread::SetName("CrVideoRenderer");
223 // The number of milliseconds to idle when we do not have anything to do.
224 // Nothing special about the value, other than we're being more OS-friendly
225 // than sleeping for 1 millisecond.
227 // TODO(scherkus): switch to pure event-driven frame timing instead of this
228 // kIdleTimeDelta business http://crbug.com/106874
229 const base::TimeDelta kIdleTimeDelta =
230 base::TimeDelta::FromMilliseconds(10);
232 for (;;) {
233 base::AutoLock auto_lock(lock_);
235 // Thread exit condition.
236 if (state_ == kStopped)
237 return;
239 // Remain idle as long as we're not playing.
240 if (state_ != kPlaying || playback_rate_ == 0) {
241 frame_available_.TimedWait(kIdleTimeDelta);
242 continue;
245 // Remain idle until we have the next frame ready for rendering.
246 if (ready_frames_.empty()) {
247 if (received_end_of_stream_) {
248 state_ = kEnded;
249 ended_cb_.Run();
251 // No need to sleep here as we idle when |state_ != kPlaying|.
252 continue;
255 frame_available_.TimedWait(kIdleTimeDelta);
256 continue;
259 base::TimeDelta remaining_time =
260 CalculateSleepDuration(ready_frames_.front(), playback_rate_);
262 // Sleep up to a maximum of our idle time until we're within the time to
263 // render the next frame.
264 if (remaining_time.InMicroseconds() > 0) {
265 remaining_time = std::min(remaining_time, kIdleTimeDelta);
266 frame_available_.TimedWait(remaining_time);
267 continue;
270 // Deadline is defined as the midpoint between this frame and the next
271 // frame, using the delta between this frame and the previous frame as the
272 // assumption for frame duration.
274 // TODO(scherkus): An improvement over midpoint might be selecting the
275 // minimum and/or maximum between the midpoint and some constants. As a
276 // thought experiment, consider what would be better than the midpoint
277 // for both the 1fps case and 120fps case.
279 // TODO(scherkus): This can be vastly improved. Use a histogram to measure
280 // the accuracy of our frame timing code. http://crbug.com/149829
281 if (drop_frames_ && last_timestamp_ != kNoTimestamp()) {
282 base::TimeDelta now = get_time_cb_.Run();
283 base::TimeDelta deadline = ready_frames_.front()->GetTimestamp() +
284 (ready_frames_.front()->GetTimestamp() - last_timestamp_) / 2;
286 if (now > deadline) {
287 DropNextReadyFrame_Locked();
288 continue;
292 // Congratulations! You've made it past the video frame timing gauntlet.
294 // At this point enough time has passed that the next frame that ready for
295 // rendering.
296 PaintNextReadyFrame_Locked();
300 void VideoRendererBase::PaintNextReadyFrame_Locked() {
301 lock_.AssertAcquired();
303 scoped_refptr<VideoFrame> next_frame = ready_frames_.front();
304 ready_frames_.pop_front();
306 last_timestamp_ = next_frame->GetTimestamp();
308 const gfx::Size& natural_size = next_frame->natural_size();
309 if (natural_size != last_natural_size_) {
310 last_natural_size_ = natural_size;
311 size_changed_cb_.Run(natural_size);
314 paint_cb_.Run(next_frame);
316 message_loop_->PostTask(FROM_HERE, base::Bind(
317 &VideoRendererBase::AttemptRead, weak_this_));
320 void VideoRendererBase::DropNextReadyFrame_Locked() {
321 lock_.AssertAcquired();
323 last_timestamp_ = ready_frames_.front()->GetTimestamp();
324 ready_frames_.pop_front();
326 PipelineStatistics statistics;
327 statistics.video_frames_dropped = 1;
328 statistics_cb_.Run(statistics);
330 message_loop_->PostTask(FROM_HERE, base::Bind(
331 &VideoRendererBase::AttemptRead, weak_this_));
334 void VideoRendererBase::FrameReady(VideoFrameStream::Status status,
335 const scoped_refptr<VideoFrame>& frame) {
336 base::AutoLock auto_lock(lock_);
337 DCHECK_NE(state_, kUninitialized);
338 DCHECK_NE(state_, kFlushed);
340 CHECK(pending_read_);
341 pending_read_ = false;
343 if (status == VideoFrameStream::DECODE_ERROR ||
344 status == VideoFrameStream::DECRYPT_ERROR) {
345 DCHECK(!frame.get());
346 PipelineStatus error = PIPELINE_ERROR_DECODE;
347 if (status == VideoFrameStream::DECRYPT_ERROR)
348 error = PIPELINE_ERROR_DECRYPT;
350 if (!preroll_cb_.is_null()) {
351 base::ResetAndReturn(&preroll_cb_).Run(error);
352 return;
355 error_cb_.Run(error);
356 return;
359 // Already-queued VideoFrameStream ReadCB's can fire after various state
360 // transitions have happened; in that case just drop those frames immediately.
361 if (state_ == kStopped || state_ == kError || state_ == kFlushing)
362 return;
364 if (!frame.get()) {
365 // Abort preroll early for a NULL frame because we won't get more frames.
366 // A new preroll will be requested after this one completes so there is no
367 // point trying to collect more frames.
368 if (state_ == kPrerolling)
369 TransitionToPrerolled_Locked();
371 return;
374 if (frame->IsEndOfStream()) {
375 DCHECK(!received_end_of_stream_);
376 received_end_of_stream_ = true;
377 max_time_cb_.Run(get_duration_cb_.Run());
379 if (state_ == kPrerolling)
380 TransitionToPrerolled_Locked();
382 return;
385 // Maintain the latest frame decoded so the correct frame is displayed after
386 // prerolling has completed.
387 if (state_ == kPrerolling && frame->GetTimestamp() <= preroll_timestamp_) {
388 ready_frames_.clear();
391 AddReadyFrame_Locked(frame);
393 if (state_ == kPrerolling) {
394 if (!video_frame_stream_.CanReadWithoutStalling() ||
395 ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames)) {
396 TransitionToPrerolled_Locked();
398 } else {
399 // We only count frames decoded during normal playback.
400 PipelineStatistics statistics;
401 statistics.video_frames_decoded = 1;
402 statistics_cb_.Run(statistics);
405 // Always request more decoded video if we have capacity. This serves two
406 // purposes:
407 // 1) Prerolling while paused
408 // 2) Keeps decoding going if video rendering thread starts falling behind
409 AttemptRead_Locked();
412 void VideoRendererBase::AddReadyFrame_Locked(
413 const scoped_refptr<VideoFrame>& frame) {
414 lock_.AssertAcquired();
415 DCHECK(!frame->IsEndOfStream());
417 // Adjust the incoming frame if its rendering stop time is past the duration
418 // of the video itself. This is typically the last frame of the video and
419 // occurs if the container specifies a duration that isn't a multiple of the
420 // frame rate. Another way for this to happen is for the container to state
421 // a smaller duration than the largest packet timestamp.
422 base::TimeDelta duration = get_duration_cb_.Run();
423 if (frame->GetTimestamp() > duration) {
424 frame->SetTimestamp(duration);
427 ready_frames_.push_back(frame);
428 DCHECK_LE(ready_frames_.size(),
429 static_cast<size_t>(limits::kMaxVideoFrames));
431 max_time_cb_.Run(frame->GetTimestamp());
433 // Avoid needlessly waking up |thread_| unless playing.
434 if (state_ == kPlaying)
435 frame_available_.Signal();
438 void VideoRendererBase::AttemptRead() {
439 base::AutoLock auto_lock(lock_);
440 AttemptRead_Locked();
443 void VideoRendererBase::AttemptRead_Locked() {
444 DCHECK(message_loop_->BelongsToCurrentThread());
445 lock_.AssertAcquired();
447 if (pending_read_ || received_end_of_stream_ ||
448 ready_frames_.size() == static_cast<size_t>(limits::kMaxVideoFrames)) {
449 return;
452 switch (state_) {
453 case kPaused:
454 case kPrerolling:
455 case kPlaying:
456 pending_read_ = true;
457 video_frame_stream_.Read(base::Bind(&VideoRendererBase::FrameReady,
458 weak_this_));
459 return;
461 case kUninitialized:
462 case kInitializing:
463 case kPrerolled:
464 case kFlushing:
465 case kFlushed:
466 case kEnded:
467 case kStopped:
468 case kError:
469 return;
473 void VideoRendererBase::OnVideoFrameStreamResetDone() {
474 base::AutoLock auto_lock(lock_);
475 if (state_ == kStopped)
476 return;
478 DCHECK_EQ(kFlushing, state_);
479 DCHECK(!pending_read_);
480 DCHECK(ready_frames_.empty());
481 DCHECK(!received_end_of_stream_);
483 state_ = kFlushed;
484 last_timestamp_ = kNoTimestamp();
485 base::ResetAndReturn(&flush_cb_).Run();
488 base::TimeDelta VideoRendererBase::CalculateSleepDuration(
489 const scoped_refptr<VideoFrame>& next_frame,
490 float playback_rate) {
491 // Determine the current and next presentation timestamps.
492 base::TimeDelta now = get_time_cb_.Run();
493 base::TimeDelta next_pts = next_frame->GetTimestamp();
495 // Scale our sleep based on the playback rate.
496 base::TimeDelta sleep = next_pts - now;
497 return base::TimeDelta::FromMicroseconds(
498 static_cast<int64>(sleep.InMicroseconds() / playback_rate));
501 void VideoRendererBase::DoStopOrError_Locked() {
502 lock_.AssertAcquired();
503 last_timestamp_ = kNoTimestamp();
504 ready_frames_.clear();
507 void VideoRendererBase::TransitionToPrerolled_Locked() {
508 lock_.AssertAcquired();
509 DCHECK_EQ(state_, kPrerolling);
511 state_ = kPrerolled;
513 // Because we might remain in the prerolled state for an undetermined amount
514 // of time (e.g., we seeked while paused), we'll paint the first prerolled
515 // frame.
516 if (!ready_frames_.empty())
517 PaintNextReadyFrame_Locked();
519 base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK);
522 } // namespace media