| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 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_impl.h" | 5 #include "media/filters/video_renderer_impl.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/debug/trace_event.h" | 10 #include "base/debug/trace_event.h" |
| 11 #include "base/message_loop/message_loop.h" | 11 #include "base/location.h" |
| 12 #include "base/single_thread_task_runner.h" |
| 12 #include "base/threading/platform_thread.h" | 13 #include "base/threading/platform_thread.h" |
| 13 #include "media/base/buffers.h" | 14 #include "media/base/buffers.h" |
| 14 #include "media/base/limits.h" | 15 #include "media/base/limits.h" |
| 15 #include "media/base/pipeline.h" | 16 #include "media/base/pipeline.h" |
| 16 #include "media/base/video_frame.h" | 17 #include "media/base/video_frame.h" |
| 17 | 18 |
| 18 namespace media { | 19 namespace media { |
| 19 | 20 |
| 20 base::TimeDelta VideoRendererImpl::kMaxLastFrameDuration() { | 21 base::TimeDelta VideoRendererImpl::kMaxLastFrameDuration() { |
| 21 return base::TimeDelta::FromMilliseconds(250); | 22 return base::TimeDelta::FromMilliseconds(250); |
| 22 } | 23 } |
| 23 | 24 |
| 24 VideoRendererImpl::VideoRendererImpl( | 25 VideoRendererImpl::VideoRendererImpl( |
| 25 const scoped_refptr<base::MessageLoopProxy>& message_loop, | 26 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| 26 ScopedVector<VideoDecoder> decoders, | 27 ScopedVector<VideoDecoder> decoders, |
| 27 const SetDecryptorReadyCB& set_decryptor_ready_cb, | 28 const SetDecryptorReadyCB& set_decryptor_ready_cb, |
| 28 const PaintCB& paint_cb, | 29 const PaintCB& paint_cb, |
| 29 const SetOpaqueCB& set_opaque_cb, | 30 const SetOpaqueCB& set_opaque_cb, |
| 30 bool drop_frames) | 31 bool drop_frames) |
| 31 : message_loop_(message_loop), | 32 : task_runner_(task_runner), |
| 32 weak_factory_(this), | 33 weak_factory_(this), |
| 33 video_frame_stream_( | 34 video_frame_stream_( |
| 34 message_loop, decoders.Pass(), set_decryptor_ready_cb), | 35 task_runner, decoders.Pass(), set_decryptor_ready_cb), |
| 35 received_end_of_stream_(false), | 36 received_end_of_stream_(false), |
| 36 frame_available_(&lock_), | 37 frame_available_(&lock_), |
| 37 state_(kUninitialized), | 38 state_(kUninitialized), |
| 38 thread_(), | 39 thread_(), |
| 39 pending_read_(false), | 40 pending_read_(false), |
| 40 drop_frames_(drop_frames), | 41 drop_frames_(drop_frames), |
| 41 playback_rate_(0), | 42 playback_rate_(0), |
| 42 paint_cb_(paint_cb), | 43 paint_cb_(paint_cb), |
| 43 set_opaque_cb_(set_opaque_cb), | 44 set_opaque_cb_(set_opaque_cb), |
| 44 last_timestamp_(kNoTimestamp()), | 45 last_timestamp_(kNoTimestamp()), |
| 45 frames_decoded_(0), | 46 frames_decoded_(0), |
| 46 frames_dropped_(0) { | 47 frames_dropped_(0) { |
| 47 DCHECK(!paint_cb_.is_null()); | 48 DCHECK(!paint_cb_.is_null()); |
| 48 } | 49 } |
| 49 | 50 |
| 50 VideoRendererImpl::~VideoRendererImpl() { | 51 VideoRendererImpl::~VideoRendererImpl() { |
| 51 base::AutoLock auto_lock(lock_); | 52 base::AutoLock auto_lock(lock_); |
| 52 CHECK(state_ == kStopped || state_ == kUninitialized) << state_; | 53 CHECK(state_ == kStopped || state_ == kUninitialized) << state_; |
| 53 CHECK(thread_.is_null()); | 54 CHECK(thread_.is_null()); |
| 54 } | 55 } |
| 55 | 56 |
| 56 void VideoRendererImpl::Play(const base::Closure& callback) { | 57 void VideoRendererImpl::Play(const base::Closure& callback) { |
| 57 DCHECK(message_loop_->BelongsToCurrentThread()); | 58 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 58 base::AutoLock auto_lock(lock_); | 59 base::AutoLock auto_lock(lock_); |
| 59 DCHECK_EQ(kPrerolled, state_); | 60 DCHECK_EQ(kPrerolled, state_); |
| 60 state_ = kPlaying; | 61 state_ = kPlaying; |
| 61 callback.Run(); | 62 callback.Run(); |
| 62 } | 63 } |
| 63 | 64 |
| 64 void VideoRendererImpl::Pause(const base::Closure& callback) { | 65 void VideoRendererImpl::Pause(const base::Closure& callback) { |
| 65 DCHECK(message_loop_->BelongsToCurrentThread()); | 66 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 66 base::AutoLock auto_lock(lock_); | 67 base::AutoLock auto_lock(lock_); |
| 67 DCHECK(state_ != kUninitialized || state_ == kError); | 68 DCHECK(state_ != kUninitialized || state_ == kError); |
| 68 state_ = kPaused; | 69 state_ = kPaused; |
| 69 callback.Run(); | 70 callback.Run(); |
| 70 } | 71 } |
| 71 | 72 |
| 72 void VideoRendererImpl::Flush(const base::Closure& callback) { | 73 void VideoRendererImpl::Flush(const base::Closure& callback) { |
| 73 DCHECK(message_loop_->BelongsToCurrentThread()); | 74 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 74 base::AutoLock auto_lock(lock_); | 75 base::AutoLock auto_lock(lock_); |
| 75 DCHECK_EQ(state_, kPaused); | 76 DCHECK_EQ(state_, kPaused); |
| 76 flush_cb_ = callback; | 77 flush_cb_ = callback; |
| 77 state_ = kFlushing; | 78 state_ = kFlushing; |
| 78 | 79 |
| 79 // This is necessary if the |video_frame_stream_| has already seen an end of | 80 // This is necessary if the |video_frame_stream_| has already seen an end of |
| 80 // stream and needs to drain it before flushing it. | 81 // stream and needs to drain it before flushing it. |
| 81 ready_frames_.clear(); | 82 ready_frames_.clear(); |
| 82 received_end_of_stream_ = false; | 83 received_end_of_stream_ = false; |
| 83 video_frame_stream_.Reset(base::Bind( | 84 video_frame_stream_.Reset(base::Bind( |
| 84 &VideoRendererImpl::OnVideoFrameStreamResetDone, weak_this_)); | 85 &VideoRendererImpl::OnVideoFrameStreamResetDone, weak_this_)); |
| 85 } | 86 } |
| 86 | 87 |
| 87 void VideoRendererImpl::Stop(const base::Closure& callback) { | 88 void VideoRendererImpl::Stop(const base::Closure& callback) { |
| 88 DCHECK(message_loop_->BelongsToCurrentThread()); | 89 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 89 base::AutoLock auto_lock(lock_); | 90 base::AutoLock auto_lock(lock_); |
| 90 if (state_ == kUninitialized || state_ == kStopped) { | 91 if (state_ == kUninitialized || state_ == kStopped) { |
| 91 callback.Run(); | 92 callback.Run(); |
| 92 return; | 93 return; |
| 93 } | 94 } |
| 94 | 95 |
| 95 // TODO(scherkus): Consider invalidating |weak_factory_| and replacing | 96 // TODO(scherkus): Consider invalidating |weak_factory_| and replacing |
| 96 // task-running guards that check |state_| with DCHECK(). | 97 // task-running guards that check |state_| with DCHECK(). |
| 97 | 98 |
| 98 state_ = kStopped; | 99 state_ = kStopped; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 112 | 113 |
| 113 if (!thread_to_join.is_null()) { | 114 if (!thread_to_join.is_null()) { |
| 114 base::AutoUnlock auto_unlock(lock_); | 115 base::AutoUnlock auto_unlock(lock_); |
| 115 base::PlatformThread::Join(thread_to_join); | 116 base::PlatformThread::Join(thread_to_join); |
| 116 } | 117 } |
| 117 | 118 |
| 118 video_frame_stream_.Stop(callback); | 119 video_frame_stream_.Stop(callback); |
| 119 } | 120 } |
| 120 | 121 |
| 121 void VideoRendererImpl::SetPlaybackRate(float playback_rate) { | 122 void VideoRendererImpl::SetPlaybackRate(float playback_rate) { |
| 122 DCHECK(message_loop_->BelongsToCurrentThread()); | 123 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 123 base::AutoLock auto_lock(lock_); | 124 base::AutoLock auto_lock(lock_); |
| 124 playback_rate_ = playback_rate; | 125 playback_rate_ = playback_rate; |
| 125 } | 126 } |
| 126 | 127 |
| 127 void VideoRendererImpl::Preroll(base::TimeDelta time, | 128 void VideoRendererImpl::Preroll(base::TimeDelta time, |
| 128 const PipelineStatusCB& cb) { | 129 const PipelineStatusCB& cb) { |
| 129 DCHECK(message_loop_->BelongsToCurrentThread()); | 130 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 130 base::AutoLock auto_lock(lock_); | 131 base::AutoLock auto_lock(lock_); |
| 131 DCHECK(!cb.is_null()); | 132 DCHECK(!cb.is_null()); |
| 132 DCHECK(preroll_cb_.is_null()); | 133 DCHECK(preroll_cb_.is_null()); |
| 133 DCHECK(state_ == kFlushed || state_== kPaused) << "state_ " << state_; | 134 DCHECK(state_ == kFlushed || state_== kPaused) << "state_ " << state_; |
| 134 | 135 |
| 135 if (state_ == kFlushed) { | 136 if (state_ == kFlushed) { |
| 136 DCHECK(time != kNoTimestamp()); | 137 DCHECK(time != kNoTimestamp()); |
| 137 DCHECK(!pending_read_); | 138 DCHECK(!pending_read_); |
| 138 DCHECK(ready_frames_.empty()); | 139 DCHECK(ready_frames_.empty()); |
| 139 } else { | 140 } else { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 154 | 155 |
| 155 void VideoRendererImpl::Initialize(DemuxerStream* stream, | 156 void VideoRendererImpl::Initialize(DemuxerStream* stream, |
| 156 const PipelineStatusCB& init_cb, | 157 const PipelineStatusCB& init_cb, |
| 157 const StatisticsCB& statistics_cb, | 158 const StatisticsCB& statistics_cb, |
| 158 const TimeCB& max_time_cb, | 159 const TimeCB& max_time_cb, |
| 159 const NaturalSizeChangedCB& size_changed_cb, | 160 const NaturalSizeChangedCB& size_changed_cb, |
| 160 const base::Closure& ended_cb, | 161 const base::Closure& ended_cb, |
| 161 const PipelineStatusCB& error_cb, | 162 const PipelineStatusCB& error_cb, |
| 162 const TimeDeltaCB& get_time_cb, | 163 const TimeDeltaCB& get_time_cb, |
| 163 const TimeDeltaCB& get_duration_cb) { | 164 const TimeDeltaCB& get_duration_cb) { |
| 164 DCHECK(message_loop_->BelongsToCurrentThread()); | 165 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 165 base::AutoLock auto_lock(lock_); | 166 base::AutoLock auto_lock(lock_); |
| 166 DCHECK(stream); | 167 DCHECK(stream); |
| 167 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); | 168 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); |
| 168 DCHECK(!init_cb.is_null()); | 169 DCHECK(!init_cb.is_null()); |
| 169 DCHECK(!statistics_cb.is_null()); | 170 DCHECK(!statistics_cb.is_null()); |
| 170 DCHECK(!max_time_cb.is_null()); | 171 DCHECK(!max_time_cb.is_null()); |
| 171 DCHECK(!size_changed_cb.is_null()); | 172 DCHECK(!size_changed_cb.is_null()); |
| 172 DCHECK(!ended_cb.is_null()); | 173 DCHECK(!ended_cb.is_null()); |
| 173 DCHECK(!get_time_cb.is_null()); | 174 DCHECK(!get_time_cb.is_null()); |
| 174 DCHECK(!get_duration_cb.is_null()); | 175 DCHECK(!get_duration_cb.is_null()); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 187 | 188 |
| 188 video_frame_stream_.Initialize( | 189 video_frame_stream_.Initialize( |
| 189 stream, | 190 stream, |
| 190 statistics_cb, | 191 statistics_cb, |
| 191 base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized, | 192 base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized, |
| 192 weak_this_)); | 193 weak_this_)); |
| 193 } | 194 } |
| 194 | 195 |
| 195 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success, | 196 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success, |
| 196 bool has_alpha) { | 197 bool has_alpha) { |
| 197 DCHECK(message_loop_->BelongsToCurrentThread()); | 198 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 198 base::AutoLock auto_lock(lock_); | 199 base::AutoLock auto_lock(lock_); |
| 199 | 200 |
| 200 if (state_ == kStopped) | 201 if (state_ == kStopped) |
| 201 return; | 202 return; |
| 202 | 203 |
| 203 DCHECK_EQ(state_, kInitializing); | 204 DCHECK_EQ(state_, kInitializing); |
| 204 | 205 |
| 205 if (!success) { | 206 if (!success) { |
| 206 state_ = kUninitialized; | 207 state_ = kUninitialized; |
| 207 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); | 208 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 324 last_timestamp_ = next_frame->GetTimestamp(); | 325 last_timestamp_ = next_frame->GetTimestamp(); |
| 325 | 326 |
| 326 const gfx::Size& natural_size = next_frame->natural_size(); | 327 const gfx::Size& natural_size = next_frame->natural_size(); |
| 327 if (natural_size != last_natural_size_) { | 328 if (natural_size != last_natural_size_) { |
| 328 last_natural_size_ = natural_size; | 329 last_natural_size_ = natural_size; |
| 329 size_changed_cb_.Run(natural_size); | 330 size_changed_cb_.Run(natural_size); |
| 330 } | 331 } |
| 331 | 332 |
| 332 paint_cb_.Run(next_frame); | 333 paint_cb_.Run(next_frame); |
| 333 | 334 |
| 334 message_loop_->PostTask(FROM_HERE, base::Bind( | 335 task_runner_->PostTask(FROM_HERE, base::Bind( |
| 335 &VideoRendererImpl::AttemptRead, weak_this_)); | 336 &VideoRendererImpl::AttemptRead, weak_this_)); |
| 336 } | 337 } |
| 337 | 338 |
| 338 void VideoRendererImpl::DropNextReadyFrame_Locked() { | 339 void VideoRendererImpl::DropNextReadyFrame_Locked() { |
| 339 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped"); | 340 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped"); |
| 340 | 341 |
| 341 lock_.AssertAcquired(); | 342 lock_.AssertAcquired(); |
| 342 | 343 |
| 343 last_timestamp_ = ready_frames_.front()->GetTimestamp(); | 344 last_timestamp_ = ready_frames_.front()->GetTimestamp(); |
| 344 ready_frames_.pop_front(); | 345 ready_frames_.pop_front(); |
| 345 frames_decoded_++; | 346 frames_decoded_++; |
| 346 frames_dropped_++; | 347 frames_dropped_++; |
| 347 | 348 |
| 348 message_loop_->PostTask(FROM_HERE, base::Bind( | 349 task_runner_->PostTask(FROM_HERE, base::Bind( |
| 349 &VideoRendererImpl::AttemptRead, weak_this_)); | 350 &VideoRendererImpl::AttemptRead, weak_this_)); |
| 350 } | 351 } |
| 351 | 352 |
| 352 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, | 353 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, |
| 353 const scoped_refptr<VideoFrame>& frame) { | 354 const scoped_refptr<VideoFrame>& frame) { |
| 354 base::AutoLock auto_lock(lock_); | 355 base::AutoLock auto_lock(lock_); |
| 355 DCHECK_NE(state_, kUninitialized); | 356 DCHECK_NE(state_, kUninitialized); |
| 356 DCHECK_NE(state_, kFlushed); | 357 DCHECK_NE(state_, kFlushed); |
| 357 | 358 |
| 358 CHECK(pending_read_); | 359 CHECK(pending_read_); |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 450 if (state_ == kPlaying) | 451 if (state_ == kPlaying) |
| 451 frame_available_.Signal(); | 452 frame_available_.Signal(); |
| 452 } | 453 } |
| 453 | 454 |
| 454 void VideoRendererImpl::AttemptRead() { | 455 void VideoRendererImpl::AttemptRead() { |
| 455 base::AutoLock auto_lock(lock_); | 456 base::AutoLock auto_lock(lock_); |
| 456 AttemptRead_Locked(); | 457 AttemptRead_Locked(); |
| 457 } | 458 } |
| 458 | 459 |
| 459 void VideoRendererImpl::AttemptRead_Locked() { | 460 void VideoRendererImpl::AttemptRead_Locked() { |
| 460 DCHECK(message_loop_->BelongsToCurrentThread()); | 461 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 461 lock_.AssertAcquired(); | 462 lock_.AssertAcquired(); |
| 462 | 463 |
| 463 if (pending_read_ || received_end_of_stream_ || | 464 if (pending_read_ || received_end_of_stream_ || |
| 464 ready_frames_.size() == static_cast<size_t>(limits::kMaxVideoFrames)) { | 465 ready_frames_.size() == static_cast<size_t>(limits::kMaxVideoFrames)) { |
| 465 return; | 466 return; |
| 466 } | 467 } |
| 467 | 468 |
| 468 switch (state_) { | 469 switch (state_) { |
| 469 case kPaused: | 470 case kPaused: |
| 470 case kPrerolling: | 471 case kPrerolling: |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 548 statistics_cb_.Run(statistics); | 549 statistics_cb_.Run(statistics); |
| 549 | 550 |
| 550 frames_decoded_ = 0; | 551 frames_decoded_ = 0; |
| 551 frames_dropped_ = 0; | 552 frames_dropped_ = 0; |
| 552 } | 553 } |
| 553 | 554 |
| 554 frame_available_.TimedWait(wait_duration); | 555 frame_available_.TimedWait(wait_duration); |
| 555 } | 556 } |
| 556 | 557 |
| 557 } // namespace media | 558 } // namespace media |
| OLD | NEW |