Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/filters/video_renderer_base.h" | 5 #include "media/filters/video_renderer_base.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/callback.h" | 8 #include "base/callback.h" |
| 9 #include "base/callback_helpers.h" | 9 #include "base/callback_helpers.h" |
| 10 #include "base/message_loop.h" | |
| 10 #include "base/threading/platform_thread.h" | 11 #include "base/threading/platform_thread.h" |
| 11 #include "media/base/buffers.h" | 12 #include "media/base/buffers.h" |
| 12 #include "media/base/limits.h" | 13 #include "media/base/limits.h" |
| 13 #include "media/base/pipeline.h" | 14 #include "media/base/pipeline.h" |
| 14 #include "media/base/video_frame.h" | 15 #include "media/base/video_frame.h" |
| 15 | 16 |
| 16 namespace media { | 17 namespace media { |
| 17 | 18 |
| 18 base::TimeDelta VideoRendererBase::kMaxLastFrameDuration() { | 19 base::TimeDelta VideoRendererBase::kMaxLastFrameDuration() { |
| 19 return base::TimeDelta::FromMilliseconds(250); | 20 return base::TimeDelta::FromMilliseconds(250); |
| 20 } | 21 } |
| 21 | 22 |
| 22 VideoRendererBase::VideoRendererBase(const base::Closure& paint_cb, | 23 VideoRendererBase::VideoRendererBase( |
| 23 const SetOpaqueCB& set_opaque_cb, | 24 const scoped_refptr<base::MessageLoopProxy>& message_loop, |
| 24 bool drop_frames) | 25 const base::Closure& paint_cb, |
| 25 : frame_available_(&lock_), | 26 const SetOpaqueCB& set_opaque_cb, |
| 27 bool drop_frames) | |
| 28 : message_loop_(message_loop), | |
| 29 frame_available_(&lock_), | |
| 26 state_(kUninitialized), | 30 state_(kUninitialized), |
| 27 thread_(base::kNullThreadHandle), | 31 thread_(base::kNullThreadHandle), |
| 28 pending_read_(false), | 32 pending_read_(false), |
| 29 pending_paint_(false), | 33 pending_paint_(false), |
| 30 pending_paint_with_last_available_(false), | 34 pending_paint_with_last_available_(false), |
| 31 drop_frames_(drop_frames), | 35 drop_frames_(drop_frames), |
| 32 playback_rate_(0), | 36 playback_rate_(0), |
| 33 paint_cb_(paint_cb), | 37 paint_cb_(paint_cb), |
| 34 set_opaque_cb_(set_opaque_cb) { | 38 set_opaque_cb_(set_opaque_cb) { |
| 35 DCHECK(!paint_cb_.is_null()); | 39 DCHECK(!paint_cb_.is_null()); |
| 36 } | 40 } |
| 37 | 41 |
| 38 void VideoRendererBase::Play(const base::Closure& callback) { | 42 void VideoRendererBase::Play(const base::Closure& callback) { |
| 43 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 39 base::AutoLock auto_lock(lock_); | 44 base::AutoLock auto_lock(lock_); |
| 40 DCHECK_EQ(kPrerolled, state_); | 45 DCHECK_EQ(kPrerolled, state_); |
| 41 state_ = kPlaying; | 46 state_ = kPlaying; |
| 42 callback.Run(); | 47 callback.Run(); |
| 43 } | 48 } |
| 44 | 49 |
| 45 void VideoRendererBase::Pause(const base::Closure& callback) { | 50 void VideoRendererBase::Pause(const base::Closure& callback) { |
| 51 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 46 base::AutoLock auto_lock(lock_); | 52 base::AutoLock auto_lock(lock_); |
| 47 DCHECK(state_ != kUninitialized || state_ == kError); | 53 DCHECK(state_ != kUninitialized || state_ == kError); |
| 48 state_ = kPaused; | 54 state_ = kPaused; |
| 49 callback.Run(); | 55 callback.Run(); |
| 50 } | 56 } |
| 51 | 57 |
| 52 void VideoRendererBase::Flush(const base::Closure& callback) { | 58 void VideoRendererBase::Flush(const base::Closure& callback) { |
| 59 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 53 base::AutoLock auto_lock(lock_); | 60 base::AutoLock auto_lock(lock_); |
| 54 DCHECK_EQ(state_, kPaused); | 61 DCHECK_EQ(state_, kPaused); |
| 55 flush_cb_ = callback; | 62 flush_cb_ = callback; |
| 56 state_ = kFlushingDecoder; | 63 state_ = kFlushingDecoder; |
| 57 | 64 |
| 58 // We must unlock here because the callback might run within the Flush() | 65 decoder_->Reset(base::Bind(&VideoRendererBase::OnDecoderResetDone, this)); |
| 59 // call. | |
| 60 // TODO: Remove this line when fixing http://crbug.com/125020 | |
| 61 base::AutoUnlock auto_unlock(lock_); | |
| 62 decoder_->Reset(base::Bind(&VideoRendererBase::OnDecoderFlushDone, this)); | |
| 63 } | 66 } |
| 64 | 67 |
| 65 void VideoRendererBase::Stop(const base::Closure& callback) { | 68 void VideoRendererBase::Stop(const base::Closure& callback) { |
| 69 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 66 if (state_ == kStopped) { | 70 if (state_ == kStopped) { |
| 67 callback.Run(); | 71 callback.Run(); |
| 68 return; | 72 return; |
| 69 } | 73 } |
| 70 | 74 |
| 71 base::PlatformThreadHandle thread_to_join = base::kNullThreadHandle; | 75 base::PlatformThreadHandle thread_to_join = base::kNullThreadHandle; |
| 72 { | 76 { |
| 73 base::AutoLock auto_lock(lock_); | 77 base::AutoLock auto_lock(lock_); |
| 74 state_ = kStopped; | 78 state_ = kStopped; |
| 75 | 79 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 87 thread_ = base::kNullThreadHandle; | 91 thread_ = base::kNullThreadHandle; |
| 88 } | 92 } |
| 89 } | 93 } |
| 90 if (thread_to_join != base::kNullThreadHandle) | 94 if (thread_to_join != base::kNullThreadHandle) |
| 91 base::PlatformThread::Join(thread_to_join); | 95 base::PlatformThread::Join(thread_to_join); |
| 92 | 96 |
| 93 decoder_->Stop(callback); | 97 decoder_->Stop(callback); |
| 94 } | 98 } |
| 95 | 99 |
| 96 void VideoRendererBase::SetPlaybackRate(float playback_rate) { | 100 void VideoRendererBase::SetPlaybackRate(float playback_rate) { |
| 101 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 97 base::AutoLock auto_lock(lock_); | 102 base::AutoLock auto_lock(lock_); |
| 98 playback_rate_ = playback_rate; | 103 playback_rate_ = playback_rate; |
| 99 } | 104 } |
| 100 | 105 |
| 101 void VideoRendererBase::Preroll(base::TimeDelta time, | 106 void VideoRendererBase::Preroll(base::TimeDelta time, |
| 102 const PipelineStatusCB& cb) { | 107 const PipelineStatusCB& cb) { |
| 108 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 103 base::AutoLock auto_lock(lock_); | 109 base::AutoLock auto_lock(lock_); |
| 104 DCHECK_EQ(state_, kFlushed) << "Must flush prior to prerolling."; | 110 DCHECK_EQ(state_, kFlushed) << "Must flush prior to prerolling."; |
| 105 DCHECK(!cb.is_null()); | 111 DCHECK(!cb.is_null()); |
| 106 DCHECK(preroll_cb_.is_null()); | 112 DCHECK(preroll_cb_.is_null()); |
| 107 | 113 |
| 108 state_ = kPrerolling; | 114 state_ = kPrerolling; |
| 109 preroll_cb_ = cb; | 115 preroll_cb_ = cb; |
| 110 preroll_timestamp_ = time; | 116 preroll_timestamp_ = time; |
| 111 prerolling_delayed_frame_ = NULL; | 117 prerolling_delayed_frame_ = NULL; |
| 112 AttemptRead_Locked(); | 118 AttemptRead_Locked(); |
| 113 } | 119 } |
| 114 | 120 |
| 115 void VideoRendererBase::Initialize(const scoped_refptr<DemuxerStream>& stream, | 121 void VideoRendererBase::Initialize(const scoped_refptr<DemuxerStream>& stream, |
| 116 const VideoDecoderList& decoders, | 122 const VideoDecoderList& decoders, |
| 117 const PipelineStatusCB& init_cb, | 123 const PipelineStatusCB& init_cb, |
| 118 const StatisticsCB& statistics_cb, | 124 const StatisticsCB& statistics_cb, |
| 119 const TimeCB& max_time_cb, | 125 const TimeCB& max_time_cb, |
| 120 const NaturalSizeChangedCB& size_changed_cb, | 126 const NaturalSizeChangedCB& size_changed_cb, |
| 121 const base::Closure& ended_cb, | 127 const base::Closure& ended_cb, |
| 122 const PipelineStatusCB& error_cb, | 128 const PipelineStatusCB& error_cb, |
| 123 const TimeDeltaCB& get_time_cb, | 129 const TimeDeltaCB& get_time_cb, |
| 124 const TimeDeltaCB& get_duration_cb) { | 130 const TimeDeltaCB& get_duration_cb) { |
| 131 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 125 base::AutoLock auto_lock(lock_); | 132 base::AutoLock auto_lock(lock_); |
| 126 DCHECK(stream); | 133 DCHECK(stream); |
| 127 DCHECK(!decoders.empty()); | 134 DCHECK(!decoders.empty()); |
| 128 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); | 135 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); |
| 129 DCHECK(!init_cb.is_null()); | 136 DCHECK(!init_cb.is_null()); |
| 130 DCHECK(!statistics_cb.is_null()); | 137 DCHECK(!statistics_cb.is_null()); |
| 131 DCHECK(!max_time_cb.is_null()); | 138 DCHECK(!max_time_cb.is_null()); |
| 132 DCHECK(!size_changed_cb.is_null()); | 139 DCHECK(!size_changed_cb.is_null()); |
| 133 DCHECK(!ended_cb.is_null()); | 140 DCHECK(!ended_cb.is_null()); |
| 134 DCHECK(!get_time_cb.is_null()); | 141 DCHECK(!get_time_cb.is_null()); |
| 135 DCHECK(!get_duration_cb.is_null()); | 142 DCHECK(!get_duration_cb.is_null()); |
| 136 DCHECK_EQ(kUninitialized, state_); | 143 DCHECK_EQ(kUninitialized, state_); |
| 137 | 144 |
| 138 init_cb_ = init_cb; | 145 init_cb_ = init_cb; |
| 139 statistics_cb_ = statistics_cb; | 146 statistics_cb_ = statistics_cb; |
| 140 max_time_cb_ = max_time_cb; | 147 max_time_cb_ = max_time_cb; |
| 141 size_changed_cb_ = size_changed_cb; | 148 size_changed_cb_ = size_changed_cb; |
| 142 ended_cb_ = ended_cb; | 149 ended_cb_ = ended_cb; |
| 143 error_cb_ = error_cb; | 150 error_cb_ = error_cb; |
| 144 get_time_cb_ = get_time_cb; | 151 get_time_cb_ = get_time_cb; |
| 145 get_duration_cb_ = get_duration_cb; | 152 get_duration_cb_ = get_duration_cb; |
| 146 | 153 |
| 147 scoped_ptr<VideoDecoderList> decoder_list(new VideoDecoderList(decoders)); | 154 scoped_ptr<VideoDecoderList> decoder_list(new VideoDecoderList(decoders)); |
| 148 InitializeNextDecoder(stream, decoder_list.Pass()); | 155 InitializeNextDecoder(stream, decoder_list.Pass()); |
| 149 } | 156 } |
| 150 | 157 |
| 151 void VideoRendererBase::InitializeNextDecoder( | 158 void VideoRendererBase::InitializeNextDecoder( |
| 152 const scoped_refptr<DemuxerStream>& demuxer_stream, | 159 const scoped_refptr<DemuxerStream>& demuxer_stream, |
| 153 scoped_ptr<VideoDecoderList> decoders) { | 160 scoped_ptr<VideoDecoderList> decoders) { |
| 161 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 154 lock_.AssertAcquired(); | 162 lock_.AssertAcquired(); |
| 155 DCHECK(!decoders->empty()); | 163 DCHECK(!decoders->empty()); |
| 156 | 164 |
| 157 scoped_refptr<VideoDecoder> decoder = decoders->front(); | 165 scoped_refptr<VideoDecoder> decoder = decoders->front(); |
| 158 decoders->pop_front(); | 166 decoders->pop_front(); |
| 159 | 167 |
| 160 DCHECK(decoder); | 168 DCHECK(decoder); |
| 161 decoder_ = decoder; | 169 decoder_ = decoder; |
| 162 | 170 |
| 163 base::AutoUnlock auto_unlock(lock_); | 171 base::AutoUnlock auto_unlock(lock_); |
| 164 decoder->Initialize( | 172 decoder->Initialize( |
| 165 demuxer_stream, | 173 demuxer_stream, |
| 166 base::Bind(&VideoRendererBase::OnDecoderInitDone, this, | 174 base::Bind(&VideoRendererBase::OnDecoderInitDone, this, |
| 167 demuxer_stream, | 175 demuxer_stream, |
| 168 base::Passed(&decoders)), | 176 base::Passed(&decoders)), |
| 169 statistics_cb_); | 177 statistics_cb_); |
| 170 } | 178 } |
| 171 | 179 |
| 172 void VideoRendererBase::OnDecoderInitDone( | 180 void VideoRendererBase::OnDecoderInitDone( |
| 173 const scoped_refptr<DemuxerStream>& demuxer_stream, | 181 const scoped_refptr<DemuxerStream>& demuxer_stream, |
| 174 scoped_ptr<VideoDecoderList> decoders, | 182 scoped_ptr<VideoDecoderList> decoders, |
| 175 PipelineStatus status) { | 183 PipelineStatus status) { |
| 184 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 176 base::AutoLock auto_lock(lock_); | 185 base::AutoLock auto_lock(lock_); |
| 177 | 186 |
| 178 if (state_ == kStopped) | 187 if (state_ == kStopped) |
| 179 return; | 188 return; |
| 180 | 189 |
| 181 if (!decoders->empty() && status == DECODER_ERROR_NOT_SUPPORTED) { | 190 if (!decoders->empty() && status == DECODER_ERROR_NOT_SUPPORTED) { |
| 182 InitializeNextDecoder(demuxer_stream, decoders.Pass()); | 191 InitializeNextDecoder(demuxer_stream, decoders.Pass()); |
| 183 return; | 192 return; |
| 184 } | 193 } |
| 185 | 194 |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 323 // Still a chance we can render the frame! | 332 // Still a chance we can render the frame! |
| 324 if (remaining_time.InMicroseconds() > 0) | 333 if (remaining_time.InMicroseconds() > 0) |
| 325 break; | 334 break; |
| 326 | 335 |
| 327 if (!drop_frames_) | 336 if (!drop_frames_) |
| 328 break; | 337 break; |
| 329 | 338 |
| 330 // Frame dropped: read again. | 339 // Frame dropped: read again. |
| 331 ++frames_dropped; | 340 ++frames_dropped; |
| 332 ready_frames_.pop_front(); | 341 ready_frames_.pop_front(); |
| 333 AttemptRead_Locked(); | 342 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 343 &VideoRendererBase::AttemptRead, this)); | |
| 334 } | 344 } |
| 335 // Continue waiting for the current paint to finish. | 345 // Continue waiting for the current paint to finish. |
| 336 frame_available_.TimedWait(kIdleTimeDelta); | 346 frame_available_.TimedWait(kIdleTimeDelta); |
| 337 continue; | 347 continue; |
| 338 } | 348 } |
| 339 | 349 |
| 340 | 350 |
| 341 // Congratulations! You've made it past the video frame timing gauntlet. | 351 // Congratulations! You've made it past the video frame timing gauntlet. |
| 342 // | 352 // |
| 343 // We can now safely update the current frame, request another frame, and | 353 // We can now safely update the current frame, request another frame, and |
| 344 // signal to the client that a new frame is available. | 354 // signal to the client that a new frame is available. |
| 345 DCHECK(!pending_paint_); | 355 DCHECK(!pending_paint_); |
| 346 DCHECK(!ready_frames_.empty()); | 356 DCHECK(!ready_frames_.empty()); |
| 347 SetCurrentFrameToNextReadyFrame(); | 357 SetCurrentFrameToNextReadyFrame(); |
| 348 AttemptRead_Locked(); | 358 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 359 &VideoRendererBase::AttemptRead, this)); | |
| 349 | 360 |
| 350 base::AutoUnlock auto_unlock(lock_); | 361 base::AutoUnlock auto_unlock(lock_); |
| 351 paint_cb_.Run(); | 362 paint_cb_.Run(); |
| 352 } | 363 } |
| 353 } | 364 } |
| 354 | 365 |
| 355 void VideoRendererBase::SetCurrentFrameToNextReadyFrame() { | 366 void VideoRendererBase::SetCurrentFrameToNextReadyFrame() { |
| 356 current_frame_ = ready_frames_.front(); | 367 current_frame_ = ready_frames_.front(); |
| 357 ready_frames_.pop_front(); | 368 ready_frames_.pop_front(); |
| 358 | 369 |
| (...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 551 DCHECK_LE(NumFrames_Locked(), limits::kMaxVideoFrames); | 562 DCHECK_LE(NumFrames_Locked(), limits::kMaxVideoFrames); |
| 552 | 563 |
| 553 base::TimeDelta max_clock_time = | 564 base::TimeDelta max_clock_time = |
| 554 frame->IsEndOfStream() ? duration : frame->GetTimestamp(); | 565 frame->IsEndOfStream() ? duration : frame->GetTimestamp(); |
| 555 DCHECK(max_clock_time != kNoTimestamp()); | 566 DCHECK(max_clock_time != kNoTimestamp()); |
| 556 max_time_cb_.Run(max_clock_time); | 567 max_time_cb_.Run(max_clock_time); |
| 557 | 568 |
| 558 frame_available_.Signal(); | 569 frame_available_.Signal(); |
| 559 } | 570 } |
| 560 | 571 |
| 572 void VideoRendererBase::AttemptRead() { | |
| 573 base::AutoLock auto_lock(lock_); | |
| 574 AttemptRead_Locked(); | |
| 575 } | |
| 576 | |
| 561 void VideoRendererBase::AttemptRead_Locked() { | 577 void VideoRendererBase::AttemptRead_Locked() { |
| 578 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 562 lock_.AssertAcquired(); | 579 lock_.AssertAcquired(); |
| 563 DCHECK_NE(kEnded, state_); | |
| 564 | 580 |
| 565 if (pending_read_ || | 581 if (pending_read_ || |
| 566 NumFrames_Locked() == limits::kMaxVideoFrames || | 582 NumFrames_Locked() == limits::kMaxVideoFrames || |
| 567 (!ready_frames_.empty() && ready_frames_.back()->IsEndOfStream()) || | 583 (!ready_frames_.empty() && ready_frames_.back()->IsEndOfStream()) || |
| 568 state_ == kFlushingDecoder || | 584 state_ == kFlushingDecoder || |
| 569 state_ == kFlushing) { | 585 state_ == kFlushing || |
| 586 state_ == kEnded) { | |
|
scherkus (not reviewing)
2012/12/06 21:22:14
due to task-posting it's possible to run AttemptRe
| |
| 570 return; | 587 return; |
| 571 } | 588 } |
| 572 | 589 |
| 573 pending_read_ = true; | 590 pending_read_ = true; |
| 574 decoder_->Read(base::Bind(&VideoRendererBase::FrameReady, this)); | 591 decoder_->Read(base::Bind(&VideoRendererBase::FrameReady, this)); |
| 575 } | 592 } |
| 576 | 593 |
| 577 void VideoRendererBase::OnDecoderFlushDone() { | 594 void VideoRendererBase::OnDecoderResetDone() { |
| 578 base::AutoLock auto_lock(lock_); | 595 base::AutoLock auto_lock(lock_); |
| 579 DCHECK_EQ(kFlushingDecoder, state_); | 596 DCHECK_EQ(kFlushingDecoder, state_); |
| 580 DCHECK(!pending_read_); | 597 DCHECK(!pending_read_); |
| 581 | 598 |
| 582 state_ = kFlushing; | 599 state_ = kFlushing; |
| 583 AttemptFlush_Locked(); | 600 AttemptFlush_Locked(); |
| 584 } | 601 } |
| 585 | 602 |
| 586 void VideoRendererBase::AttemptFlush_Locked() { | 603 void VideoRendererBase::AttemptFlush_Locked() { |
| 587 lock_.AssertAcquired(); | 604 lock_.AssertAcquired(); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 621 | 638 |
| 622 int VideoRendererBase::NumFrames_Locked() const { | 639 int VideoRendererBase::NumFrames_Locked() const { |
| 623 lock_.AssertAcquired(); | 640 lock_.AssertAcquired(); |
| 624 int outstanding_frames = | 641 int outstanding_frames = |
| 625 (current_frame_ ? 1 : 0) + (last_available_frame_ ? 1 : 0) + | 642 (current_frame_ ? 1 : 0) + (last_available_frame_ ? 1 : 0) + |
| 626 (current_frame_ && (current_frame_ == last_available_frame_) ? -1 : 0); | 643 (current_frame_ && (current_frame_ == last_available_frame_) ? -1 : 0); |
| 627 return ready_frames_.size() + outstanding_frames; | 644 return ready_frames_.size() + outstanding_frames; |
| 628 } | 645 } |
| 629 | 646 |
| 630 } // namespace media | 647 } // namespace media |
| OLD | NEW |