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/message_loop.h" | 10 #include "base/location.h" |
| 11 #include "base/single_thread_task_runner.h" |
11 #include "base/threading/platform_thread.h" | 12 #include "base/threading/platform_thread.h" |
12 #include "media/base/buffers.h" | 13 #include "media/base/buffers.h" |
13 #include "media/base/limits.h" | 14 #include "media/base/limits.h" |
14 #include "media/base/pipeline.h" | 15 #include "media/base/pipeline.h" |
15 #include "media/base/video_frame.h" | 16 #include "media/base/video_frame.h" |
16 | 17 |
17 namespace media { | 18 namespace media { |
18 | 19 |
19 base::TimeDelta VideoRendererBase::kMaxLastFrameDuration() { | 20 base::TimeDelta VideoRendererBase::kMaxLastFrameDuration() { |
20 return base::TimeDelta::FromMilliseconds(250); | 21 return base::TimeDelta::FromMilliseconds(250); |
21 } | 22 } |
22 | 23 |
23 VideoRendererBase::VideoRendererBase( | 24 VideoRendererBase::VideoRendererBase( |
24 const scoped_refptr<base::MessageLoopProxy>& message_loop, | 25 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
25 ScopedVector<VideoDecoder> decoders, | 26 ScopedVector<VideoDecoder> decoders, |
26 const SetDecryptorReadyCB& set_decryptor_ready_cb, | 27 const SetDecryptorReadyCB& set_decryptor_ready_cb, |
27 const PaintCB& paint_cb, | 28 const PaintCB& paint_cb, |
28 const SetOpaqueCB& set_opaque_cb, | 29 const SetOpaqueCB& set_opaque_cb, |
29 bool drop_frames) | 30 bool drop_frames) |
30 : message_loop_(message_loop), | 31 : task_runner_(task_runner), |
31 weak_factory_(this), | 32 weak_factory_(this), |
32 video_frame_stream_( | 33 video_frame_stream_( |
33 message_loop, decoders.Pass(), set_decryptor_ready_cb), | 34 task_runner, decoders.Pass(), set_decryptor_ready_cb), |
34 received_end_of_stream_(false), | 35 received_end_of_stream_(false), |
35 frame_available_(&lock_), | 36 frame_available_(&lock_), |
36 state_(kUninitialized), | 37 state_(kUninitialized), |
37 thread_(), | 38 thread_(), |
38 pending_read_(false), | 39 pending_read_(false), |
39 drop_frames_(drop_frames), | 40 drop_frames_(drop_frames), |
40 playback_rate_(0), | 41 playback_rate_(0), |
41 paint_cb_(paint_cb), | 42 paint_cb_(paint_cb), |
42 set_opaque_cb_(set_opaque_cb), | 43 set_opaque_cb_(set_opaque_cb), |
43 last_timestamp_(kNoTimestamp()), | 44 last_timestamp_(kNoTimestamp()), |
44 frames_decoded_(0), | 45 frames_decoded_(0), |
45 frames_dropped_(0) { | 46 frames_dropped_(0) { |
46 DCHECK(!paint_cb_.is_null()); | 47 DCHECK(!paint_cb_.is_null()); |
47 } | 48 } |
48 | 49 |
49 VideoRendererBase::~VideoRendererBase() { | 50 VideoRendererBase::~VideoRendererBase() { |
50 base::AutoLock auto_lock(lock_); | 51 base::AutoLock auto_lock(lock_); |
51 CHECK(state_ == kStopped || state_ == kUninitialized) << state_; | 52 CHECK(state_ == kStopped || state_ == kUninitialized) << state_; |
52 CHECK(thread_.is_null()); | 53 CHECK(thread_.is_null()); |
53 } | 54 } |
54 | 55 |
55 void VideoRendererBase::Play(const base::Closure& callback) { | 56 void VideoRendererBase::Play(const base::Closure& callback) { |
56 DCHECK(message_loop_->BelongsToCurrentThread()); | 57 DCHECK(task_runner_->BelongsToCurrentThread()); |
57 base::AutoLock auto_lock(lock_); | 58 base::AutoLock auto_lock(lock_); |
58 DCHECK_EQ(kPrerolled, state_); | 59 DCHECK_EQ(kPrerolled, state_); |
59 state_ = kPlaying; | 60 state_ = kPlaying; |
60 callback.Run(); | 61 callback.Run(); |
61 } | 62 } |
62 | 63 |
63 void VideoRendererBase::Pause(const base::Closure& callback) { | 64 void VideoRendererBase::Pause(const base::Closure& callback) { |
64 DCHECK(message_loop_->BelongsToCurrentThread()); | 65 DCHECK(task_runner_->BelongsToCurrentThread()); |
65 base::AutoLock auto_lock(lock_); | 66 base::AutoLock auto_lock(lock_); |
66 DCHECK(state_ != kUninitialized || state_ == kError); | 67 DCHECK(state_ != kUninitialized || state_ == kError); |
67 state_ = kPaused; | 68 state_ = kPaused; |
68 callback.Run(); | 69 callback.Run(); |
69 } | 70 } |
70 | 71 |
71 void VideoRendererBase::Flush(const base::Closure& callback) { | 72 void VideoRendererBase::Flush(const base::Closure& callback) { |
72 DCHECK(message_loop_->BelongsToCurrentThread()); | 73 DCHECK(task_runner_->BelongsToCurrentThread()); |
73 base::AutoLock auto_lock(lock_); | 74 base::AutoLock auto_lock(lock_); |
74 DCHECK_EQ(state_, kPaused); | 75 DCHECK_EQ(state_, kPaused); |
75 flush_cb_ = callback; | 76 flush_cb_ = callback; |
76 state_ = kFlushing; | 77 state_ = kFlushing; |
77 | 78 |
78 // This is necessary if the |video_frame_stream_| has already seen an end of | 79 // 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 // stream and needs to drain it before flushing it. |
80 ready_frames_.clear(); | 81 ready_frames_.clear(); |
81 received_end_of_stream_ = false; | 82 received_end_of_stream_ = false; |
82 video_frame_stream_.Reset(base::Bind( | 83 video_frame_stream_.Reset(base::Bind( |
83 &VideoRendererBase::OnVideoFrameStreamResetDone, weak_this_)); | 84 &VideoRendererBase::OnVideoFrameStreamResetDone, weak_this_)); |
84 } | 85 } |
85 | 86 |
86 void VideoRendererBase::Stop(const base::Closure& callback) { | 87 void VideoRendererBase::Stop(const base::Closure& callback) { |
87 DCHECK(message_loop_->BelongsToCurrentThread()); | 88 DCHECK(task_runner_->BelongsToCurrentThread()); |
88 base::AutoLock auto_lock(lock_); | 89 base::AutoLock auto_lock(lock_); |
89 if (state_ == kUninitialized || state_ == kStopped) { | 90 if (state_ == kUninitialized || state_ == kStopped) { |
90 callback.Run(); | 91 callback.Run(); |
91 return; | 92 return; |
92 } | 93 } |
93 | 94 |
94 // TODO(scherkus): Consider invalidating |weak_factory_| and replacing | 95 // TODO(scherkus): Consider invalidating |weak_factory_| and replacing |
95 // task-running guards that check |state_| with DCHECK(). | 96 // task-running guards that check |state_| with DCHECK(). |
96 | 97 |
97 state_ = kStopped; | 98 state_ = kStopped; |
(...skipping 13 matching lines...) Expand all Loading... |
111 | 112 |
112 if (!thread_to_join.is_null()) { | 113 if (!thread_to_join.is_null()) { |
113 base::AutoUnlock auto_unlock(lock_); | 114 base::AutoUnlock auto_unlock(lock_); |
114 base::PlatformThread::Join(thread_to_join); | 115 base::PlatformThread::Join(thread_to_join); |
115 } | 116 } |
116 | 117 |
117 video_frame_stream_.Stop(callback); | 118 video_frame_stream_.Stop(callback); |
118 } | 119 } |
119 | 120 |
120 void VideoRendererBase::SetPlaybackRate(float playback_rate) { | 121 void VideoRendererBase::SetPlaybackRate(float playback_rate) { |
121 DCHECK(message_loop_->BelongsToCurrentThread()); | 122 DCHECK(task_runner_->BelongsToCurrentThread()); |
122 base::AutoLock auto_lock(lock_); | 123 base::AutoLock auto_lock(lock_); |
123 playback_rate_ = playback_rate; | 124 playback_rate_ = playback_rate; |
124 } | 125 } |
125 | 126 |
126 void VideoRendererBase::Preroll(base::TimeDelta time, | 127 void VideoRendererBase::Preroll(base::TimeDelta time, |
127 const PipelineStatusCB& cb) { | 128 const PipelineStatusCB& cb) { |
128 DCHECK(message_loop_->BelongsToCurrentThread()); | 129 DCHECK(task_runner_->BelongsToCurrentThread()); |
129 base::AutoLock auto_lock(lock_); | 130 base::AutoLock auto_lock(lock_); |
130 DCHECK_EQ(state_, kFlushed) << "Must flush prior to prerolling."; | 131 DCHECK_EQ(state_, kFlushed) << "Must flush prior to prerolling."; |
131 DCHECK(!cb.is_null()); | 132 DCHECK(!cb.is_null()); |
132 DCHECK(preroll_cb_.is_null()); | 133 DCHECK(preroll_cb_.is_null()); |
133 | 134 |
134 state_ = kPrerolling; | 135 state_ = kPrerolling; |
135 preroll_cb_ = cb; | 136 preroll_cb_ = cb; |
136 preroll_timestamp_ = time; | 137 preroll_timestamp_ = time; |
137 AttemptRead_Locked(); | 138 AttemptRead_Locked(); |
138 } | 139 } |
139 | 140 |
140 void VideoRendererBase::Initialize(DemuxerStream* stream, | 141 void VideoRendererBase::Initialize(DemuxerStream* stream, |
141 const PipelineStatusCB& init_cb, | 142 const PipelineStatusCB& init_cb, |
142 const StatisticsCB& statistics_cb, | 143 const StatisticsCB& statistics_cb, |
143 const TimeCB& max_time_cb, | 144 const TimeCB& max_time_cb, |
144 const NaturalSizeChangedCB& size_changed_cb, | 145 const NaturalSizeChangedCB& size_changed_cb, |
145 const base::Closure& ended_cb, | 146 const base::Closure& ended_cb, |
146 const PipelineStatusCB& error_cb, | 147 const PipelineStatusCB& error_cb, |
147 const TimeDeltaCB& get_time_cb, | 148 const TimeDeltaCB& get_time_cb, |
148 const TimeDeltaCB& get_duration_cb) { | 149 const TimeDeltaCB& get_duration_cb) { |
149 DCHECK(message_loop_->BelongsToCurrentThread()); | 150 DCHECK(task_runner_->BelongsToCurrentThread()); |
150 base::AutoLock auto_lock(lock_); | 151 base::AutoLock auto_lock(lock_); |
151 DCHECK(stream); | 152 DCHECK(stream); |
152 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); | 153 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); |
153 DCHECK(!init_cb.is_null()); | 154 DCHECK(!init_cb.is_null()); |
154 DCHECK(!statistics_cb.is_null()); | 155 DCHECK(!statistics_cb.is_null()); |
155 DCHECK(!max_time_cb.is_null()); | 156 DCHECK(!max_time_cb.is_null()); |
156 DCHECK(!size_changed_cb.is_null()); | 157 DCHECK(!size_changed_cb.is_null()); |
157 DCHECK(!ended_cb.is_null()); | 158 DCHECK(!ended_cb.is_null()); |
158 DCHECK(!get_time_cb.is_null()); | 159 DCHECK(!get_time_cb.is_null()); |
159 DCHECK(!get_duration_cb.is_null()); | 160 DCHECK(!get_duration_cb.is_null()); |
(...skipping 12 matching lines...) Expand all Loading... |
172 | 173 |
173 video_frame_stream_.Initialize( | 174 video_frame_stream_.Initialize( |
174 stream, | 175 stream, |
175 statistics_cb, | 176 statistics_cb, |
176 base::Bind(&VideoRendererBase::OnVideoFrameStreamInitialized, | 177 base::Bind(&VideoRendererBase::OnVideoFrameStreamInitialized, |
177 weak_this_)); | 178 weak_this_)); |
178 } | 179 } |
179 | 180 |
180 void VideoRendererBase::OnVideoFrameStreamInitialized(bool success, | 181 void VideoRendererBase::OnVideoFrameStreamInitialized(bool success, |
181 bool has_alpha) { | 182 bool has_alpha) { |
182 DCHECK(message_loop_->BelongsToCurrentThread()); | 183 DCHECK(task_runner_->BelongsToCurrentThread()); |
183 base::AutoLock auto_lock(lock_); | 184 base::AutoLock auto_lock(lock_); |
184 | 185 |
185 if (state_ == kStopped) | 186 if (state_ == kStopped) |
186 return; | 187 return; |
187 | 188 |
188 DCHECK_EQ(state_, kInitializing); | 189 DCHECK_EQ(state_, kInitializing); |
189 | 190 |
190 if (!success) { | 191 if (!success) { |
191 state_ = kUninitialized; | 192 state_ = kUninitialized; |
192 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); | 193 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
309 last_timestamp_ = next_frame->GetTimestamp(); | 310 last_timestamp_ = next_frame->GetTimestamp(); |
310 | 311 |
311 const gfx::Size& natural_size = next_frame->natural_size(); | 312 const gfx::Size& natural_size = next_frame->natural_size(); |
312 if (natural_size != last_natural_size_) { | 313 if (natural_size != last_natural_size_) { |
313 last_natural_size_ = natural_size; | 314 last_natural_size_ = natural_size; |
314 size_changed_cb_.Run(natural_size); | 315 size_changed_cb_.Run(natural_size); |
315 } | 316 } |
316 | 317 |
317 paint_cb_.Run(next_frame); | 318 paint_cb_.Run(next_frame); |
318 | 319 |
319 message_loop_->PostTask(FROM_HERE, base::Bind( | 320 task_runner_->PostTask(FROM_HERE, base::Bind( |
320 &VideoRendererBase::AttemptRead, weak_this_)); | 321 &VideoRendererBase::AttemptRead, weak_this_)); |
321 } | 322 } |
322 | 323 |
323 void VideoRendererBase::DropNextReadyFrame_Locked() { | 324 void VideoRendererBase::DropNextReadyFrame_Locked() { |
324 lock_.AssertAcquired(); | 325 lock_.AssertAcquired(); |
325 | 326 |
326 last_timestamp_ = ready_frames_.front()->GetTimestamp(); | 327 last_timestamp_ = ready_frames_.front()->GetTimestamp(); |
327 ready_frames_.pop_front(); | 328 ready_frames_.pop_front(); |
328 frames_decoded_++; | 329 frames_decoded_++; |
329 frames_dropped_++; | 330 frames_dropped_++; |
330 | 331 |
331 message_loop_->PostTask(FROM_HERE, base::Bind( | 332 task_runner_->PostTask(FROM_HERE, base::Bind( |
332 &VideoRendererBase::AttemptRead, weak_this_)); | 333 &VideoRendererBase::AttemptRead, weak_this_)); |
333 } | 334 } |
334 | 335 |
335 void VideoRendererBase::FrameReady(VideoFrameStream::Status status, | 336 void VideoRendererBase::FrameReady(VideoFrameStream::Status status, |
336 const scoped_refptr<VideoFrame>& frame) { | 337 const scoped_refptr<VideoFrame>& frame) { |
337 base::AutoLock auto_lock(lock_); | 338 base::AutoLock auto_lock(lock_); |
338 DCHECK_NE(state_, kUninitialized); | 339 DCHECK_NE(state_, kUninitialized); |
339 DCHECK_NE(state_, kFlushed); | 340 DCHECK_NE(state_, kFlushed); |
340 | 341 |
341 CHECK(pending_read_); | 342 CHECK(pending_read_); |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
430 if (state_ == kPlaying) | 431 if (state_ == kPlaying) |
431 frame_available_.Signal(); | 432 frame_available_.Signal(); |
432 } | 433 } |
433 | 434 |
434 void VideoRendererBase::AttemptRead() { | 435 void VideoRendererBase::AttemptRead() { |
435 base::AutoLock auto_lock(lock_); | 436 base::AutoLock auto_lock(lock_); |
436 AttemptRead_Locked(); | 437 AttemptRead_Locked(); |
437 } | 438 } |
438 | 439 |
439 void VideoRendererBase::AttemptRead_Locked() { | 440 void VideoRendererBase::AttemptRead_Locked() { |
440 DCHECK(message_loop_->BelongsToCurrentThread()); | 441 DCHECK(task_runner_->BelongsToCurrentThread()); |
441 lock_.AssertAcquired(); | 442 lock_.AssertAcquired(); |
442 | 443 |
443 if (pending_read_ || received_end_of_stream_ || | 444 if (pending_read_ || received_end_of_stream_ || |
444 ready_frames_.size() == static_cast<size_t>(limits::kMaxVideoFrames)) { | 445 ready_frames_.size() == static_cast<size_t>(limits::kMaxVideoFrames)) { |
445 return; | 446 return; |
446 } | 447 } |
447 | 448 |
448 switch (state_) { | 449 switch (state_) { |
449 case kPaused: | 450 case kPaused: |
450 case kPrerolling: | 451 case kPrerolling: |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
528 statistics_cb_.Run(statistics); | 529 statistics_cb_.Run(statistics); |
529 | 530 |
530 frames_decoded_ = 0; | 531 frames_decoded_ = 0; |
531 frames_dropped_ = 0; | 532 frames_dropped_ = 0; |
532 } | 533 } |
533 | 534 |
534 frame_available_.TimedWait(wait_duration); | 535 frame_available_.TimedWait(wait_duration); |
535 } | 536 } |
536 | 537 |
537 } // namespace media | 538 } // namespace media |
OLD | NEW |