OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 // A base class that provides the plumbing for a decoder filters. | 5 // A base class that provides the plumbing for a decoder filters. |
6 | 6 |
7 #ifndef MEDIA_FILTERS_DECODER_BASE_H_ | 7 #ifndef MEDIA_FILTERS_DECODER_BASE_H_ |
8 #define MEDIA_FILTERS_DECODER_BASE_H_ | 8 #define MEDIA_FILTERS_DECODER_BASE_H_ |
9 | 9 |
10 #include <deque> | 10 #include <deque> |
11 | 11 |
12 #include "base/callback.h" | 12 #include "base/callback.h" |
13 #include "base/stl_util-inl.h" | 13 #include "base/stl_util-inl.h" |
14 #include "base/task.h" | 14 #include "base/task.h" |
15 #include "base/threading/thread.h" | 15 #include "base/threading/thread.h" |
16 #include "media/base/buffers.h" | 16 #include "media/base/buffers.h" |
17 #include "media/base/callback.h" | 17 #include "media/base/callback.h" |
18 #include "media/base/filters.h" | 18 #include "media/base/filters.h" |
19 #include "media/base/filter_host.h" | 19 #include "media/base/filter_host.h" |
20 | 20 |
21 namespace media { | 21 namespace media { |
22 | 22 |
23 // In this class, we over-specify method lookup via this-> to avoid unexpected | 23 // In this class, we over-specify method lookup via this-> to avoid unexpected |
24 // name resolution issues due to the two-phase lookup needed for dependent | 24 // name resolution issues due to the two-phase lookup needed for dependent |
25 // name resolution in templates. | 25 // name resolution in templates. |
26 template <class Decoder, class Output> | 26 template <class Decoder, class Output> |
27 class DecoderBase : public Decoder { | 27 class DecoderBase : public Decoder { |
28 public: | 28 public: |
29 | |
30 // Filter implementation. | 29 // Filter implementation. |
31 virtual void Stop(FilterCallback* callback) { | 30 virtual void Stop(FilterCallback* callback) { |
32 this->message_loop()->PostTask( | 31 message_loop_->PostTask( |
33 FROM_HERE, | 32 FROM_HERE, |
34 NewRunnableMethod(this, &DecoderBase::StopTask, callback)); | 33 NewRunnableMethod(this, &DecoderBase::StopTask, callback)); |
35 } | 34 } |
36 | 35 |
37 virtual void Seek(base::TimeDelta time, | 36 virtual void Seek(base::TimeDelta time, |
38 FilterCallback* callback) { | 37 FilterCallback* callback) { |
39 this->message_loop()->PostTask( | 38 message_loop_->PostTask( |
40 FROM_HERE, | 39 FROM_HERE, |
41 NewRunnableMethod(this, &DecoderBase::SeekTask, time, callback)); | 40 NewRunnableMethod(this, &DecoderBase::SeekTask, time, callback)); |
42 } | 41 } |
43 | 42 |
44 // Decoder implementation. | 43 // Decoder implementation. |
45 virtual void Initialize(DemuxerStream* demuxer_stream, | 44 virtual void Initialize(DemuxerStream* demuxer_stream, |
46 FilterCallback* callback) { | 45 FilterCallback* callback) { |
47 this->message_loop()->PostTask( | 46 message_loop_->PostTask( |
48 FROM_HERE, | 47 FROM_HERE, |
49 NewRunnableMethod(this, | 48 NewRunnableMethod(this, |
50 &DecoderBase::InitializeTask, | 49 &DecoderBase::InitializeTask, |
51 make_scoped_refptr(demuxer_stream), | 50 make_scoped_refptr(demuxer_stream), |
52 callback)); | 51 callback)); |
53 } | 52 } |
54 | 53 |
55 virtual const MediaFormat& media_format() { return media_format_; } | 54 virtual const MediaFormat& media_format() { return media_format_; } |
56 | 55 |
57 // Audio decoder. | 56 // Audio decoder. |
58 // Note that this class is only used by the audio decoder, this will | 57 // Note that this class is only used by the audio decoder, this will |
59 // eventually be merged into FFmpegAudioDecoder. | 58 // eventually be merged into FFmpegAudioDecoder. |
60 virtual void ProduceAudioSamples(scoped_refptr<Output> output) { | 59 virtual void ProduceAudioSamples(scoped_refptr<Output> output) { |
61 this->message_loop()->PostTask(FROM_HERE, | 60 message_loop_->PostTask(FROM_HERE, |
62 NewRunnableMethod(this, &DecoderBase::ReadTask, output)); | 61 NewRunnableMethod(this, &DecoderBase::ReadTask, output)); |
63 } | 62 } |
64 | 63 |
65 protected: | 64 protected: |
66 DecoderBase() | 65 explicit DecoderBase(MessageLoop* message_loop) |
67 : pending_reads_(0), | 66 : message_loop_(message_loop), |
| 67 pending_reads_(0), |
68 pending_requests_(0), | 68 pending_requests_(0), |
69 state_(kUninitialized) { | 69 state_(kUninitialized) { |
70 } | 70 } |
71 | 71 |
72 virtual ~DecoderBase() { | 72 virtual ~DecoderBase() { |
73 DCHECK(state_ == kUninitialized || state_ == kStopped); | 73 DCHECK(state_ == kUninitialized || state_ == kStopped); |
74 DCHECK(result_queue_.empty()); | 74 DCHECK(result_queue_.empty()); |
75 } | 75 } |
76 | 76 |
77 // This method is called by the derived class from within the OnDecode method. | 77 // This method is called by the derived class from within the OnDecode method. |
78 // It places an output buffer in the result queue. It must be called from | 78 // It places an output buffer in the result queue. It must be called from |
79 // within the OnDecode method. | 79 // within the OnDecode method. |
80 void EnqueueResult(Output* output) { | 80 void EnqueueResult(Output* output) { |
81 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 81 DCHECK_EQ(MessageLoop::current(), message_loop_); |
82 if (!IsStopped()) { | 82 if (!IsStopped()) { |
83 result_queue_.push_back(output); | 83 result_queue_.push_back(output); |
84 } | 84 } |
85 } | 85 } |
86 | 86 |
87 // TODO(ajwong): All these "Task*" used as completion callbacks should be | 87 // TODO(ajwong): All these "Task*" used as completion callbacks should be |
88 // FilterCallbacks. However, since NewCallback() cannot prebind parameters, | 88 // FilterCallbacks. However, since NewCallback() cannot prebind parameters, |
89 // we use NewRunnableMethod() instead which causes an unfortunate refcount. | 89 // we use NewRunnableMethod() instead which causes an unfortunate refcount. |
90 // We should move stoyan's Mutant code into base/task.h and turn these | 90 // We should move stoyan's Mutant code into base/task.h and turn these |
91 // back into FilterCallbacks. | 91 // back into FilterCallbacks. |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
129 // which case |pending_reads_| will remain less than |read_queue_| so we | 129 // which case |pending_reads_| will remain less than |read_queue_| so we |
130 // need to schedule an additional read. | 130 // need to schedule an additional read. |
131 DCHECK_LE(pending_reads_, pending_requests_); | 131 DCHECK_LE(pending_reads_, pending_requests_); |
132 if (!fulfilled) { | 132 if (!fulfilled) { |
133 DCHECK_LT(pending_reads_, pending_requests_); | 133 DCHECK_LT(pending_reads_, pending_requests_); |
134 demuxer_stream_->Read(NewCallback(this, &DecoderBase::OnReadComplete)); | 134 demuxer_stream_->Read(NewCallback(this, &DecoderBase::OnReadComplete)); |
135 ++pending_reads_; | 135 ++pending_reads_; |
136 } | 136 } |
137 } | 137 } |
138 | 138 |
| 139 // Provide access to subclasses. |
| 140 MessageLoop* message_loop() { return message_loop_; } |
| 141 |
139 private: | 142 private: |
140 bool IsStopped() { return state_ == kStopped; } | 143 bool IsStopped() { return state_ == kStopped; } |
141 | 144 |
142 void OnReadComplete(Buffer* buffer) { | 145 void OnReadComplete(Buffer* buffer) { |
143 // Little bit of magic here to get NewRunnableMethod() to generate a Task | 146 // Little bit of magic here to get NewRunnableMethod() to generate a Task |
144 // that holds onto a reference via scoped_refptr<>. | 147 // that holds onto a reference via scoped_refptr<>. |
145 // | 148 // |
146 // TODO(scherkus): change the callback format to pass a scoped_refptr<> or | 149 // TODO(scherkus): change the callback format to pass a scoped_refptr<> or |
147 // better yet see if we can get away with not using reference counting. | 150 // better yet see if we can get away with not using reference counting. |
148 scoped_refptr<Buffer> buffer_ref = buffer; | 151 scoped_refptr<Buffer> buffer_ref = buffer; |
149 this->message_loop()->PostTask(FROM_HERE, | 152 message_loop_->PostTask(FROM_HERE, |
150 NewRunnableMethod(this, &DecoderBase::ReadCompleteTask, buffer_ref)); | 153 NewRunnableMethod(this, &DecoderBase::ReadCompleteTask, buffer_ref)); |
151 } | 154 } |
152 | 155 |
153 void StopTask(FilterCallback* callback) { | 156 void StopTask(FilterCallback* callback) { |
154 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 157 DCHECK_EQ(MessageLoop::current(), message_loop_); |
155 | 158 |
156 // Delegate to the subclass first. | 159 // Delegate to the subclass first. |
157 DoStop(NewRunnableMethod(this, &DecoderBase::OnStopComplete, callback)); | 160 DoStop(NewRunnableMethod(this, &DecoderBase::OnStopComplete, callback)); |
158 } | 161 } |
159 | 162 |
160 void OnStopComplete(FilterCallback* callback) { | 163 void OnStopComplete(FilterCallback* callback) { |
161 // Throw away all buffers in all queues. | 164 // Throw away all buffers in all queues. |
162 result_queue_.clear(); | 165 result_queue_.clear(); |
163 state_ = kStopped; | 166 state_ = kStopped; |
164 | 167 |
165 if (callback) { | 168 if (callback) { |
166 callback->Run(); | 169 callback->Run(); |
167 delete callback; | 170 delete callback; |
168 } | 171 } |
169 } | 172 } |
170 | 173 |
171 void SeekTask(base::TimeDelta time, FilterCallback* callback) { | 174 void SeekTask(base::TimeDelta time, FilterCallback* callback) { |
172 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 175 DCHECK_EQ(MessageLoop::current(), message_loop_); |
173 DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed"; | 176 DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed"; |
174 DCHECK_EQ(0u, pending_requests_) << "Pending requests should be empty"; | 177 DCHECK_EQ(0u, pending_requests_) << "Pending requests should be empty"; |
175 | 178 |
176 // Delegate to the subclass first. | 179 // Delegate to the subclass first. |
177 DoSeek(time, | 180 DoSeek(time, |
178 NewRunnableMethod(this, &DecoderBase::OnSeekComplete, callback)); | 181 NewRunnableMethod(this, &DecoderBase::OnSeekComplete, callback)); |
179 } | 182 } |
180 | 183 |
181 void OnSeekComplete(FilterCallback* callback) { | 184 void OnSeekComplete(FilterCallback* callback) { |
182 // Flush our decoded results. | 185 // Flush our decoded results. |
183 result_queue_.clear(); | 186 result_queue_.clear(); |
184 | 187 |
185 // Signal that we're done seeking. | 188 // Signal that we're done seeking. |
186 if (callback) { | 189 if (callback) { |
187 callback->Run(); | 190 callback->Run(); |
188 delete callback; | 191 delete callback; |
189 } | 192 } |
190 } | 193 } |
191 | 194 |
192 void InitializeTask(DemuxerStream* demuxer_stream, FilterCallback* callback) { | 195 void InitializeTask(DemuxerStream* demuxer_stream, FilterCallback* callback) { |
193 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 196 DCHECK_EQ(MessageLoop::current(), message_loop_); |
194 CHECK(kUninitialized == state_); | 197 CHECK(kUninitialized == state_); |
195 CHECK(!demuxer_stream_); | 198 CHECK(!demuxer_stream_); |
196 demuxer_stream_ = demuxer_stream; | 199 demuxer_stream_ = demuxer_stream; |
197 | 200 |
198 bool* success = new bool; | 201 bool* success = new bool; |
199 DoInitialize(demuxer_stream, | 202 DoInitialize(demuxer_stream, |
200 success, | 203 success, |
201 NewRunnableMethod(this, &DecoderBase::OnInitializeComplete, | 204 NewRunnableMethod(this, &DecoderBase::OnInitializeComplete, |
202 success, callback)); | 205 success, callback)); |
203 } | 206 } |
204 | 207 |
205 void OnInitializeComplete(bool* success, FilterCallback* done_cb) { | 208 void OnInitializeComplete(bool* success, FilterCallback* done_cb) { |
206 // Note: The done_runner must be declared *last* to ensure proper | 209 // Note: The done_runner must be declared *last* to ensure proper |
207 // destruction order. | 210 // destruction order. |
208 scoped_ptr<bool> success_deleter(success); | 211 scoped_ptr<bool> success_deleter(success); |
209 AutoCallbackRunner done_runner(done_cb); | 212 AutoCallbackRunner done_runner(done_cb); |
210 | 213 |
211 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 214 DCHECK_EQ(MessageLoop::current(), message_loop_); |
212 // Delegate to subclass first. | 215 // Delegate to subclass first. |
213 if (!*success) { | 216 if (!*success) { |
214 this->host()->SetError(PIPELINE_ERROR_DECODE); | 217 this->host()->SetError(PIPELINE_ERROR_DECODE); |
215 } else { | 218 } else { |
216 // TODO(scherkus): subclass shouldn't mutate superclass media format. | 219 // TODO(scherkus): subclass shouldn't mutate superclass media format. |
217 DCHECK(!media_format_.empty()) << "Subclass did not set media_format_"; | 220 DCHECK(!media_format_.empty()) << "Subclass did not set media_format_"; |
218 state_ = kInitialized; | 221 state_ = kInitialized; |
219 } | 222 } |
220 } | 223 } |
221 | 224 |
222 void ReadTask(scoped_refptr<Output> output) { | 225 void ReadTask(scoped_refptr<Output> output) { |
223 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 226 DCHECK_EQ(MessageLoop::current(), message_loop_); |
224 | 227 |
225 // TODO(scherkus): should reply with a null operation (empty buffer). | 228 // TODO(scherkus): should reply with a null operation (empty buffer). |
226 if (IsStopped()) | 229 if (IsStopped()) |
227 return; | 230 return; |
228 | 231 |
229 ++pending_requests_; | 232 ++pending_requests_; |
230 | 233 |
231 // Try to fulfill it immediately. | 234 // Try to fulfill it immediately. |
232 if (FulfillPendingRead()) | 235 if (FulfillPendingRead()) |
233 return; | 236 return; |
234 | 237 |
235 // Since we can't fulfill a read request now then submit a read | 238 // Since we can't fulfill a read request now then submit a read |
236 // request to the demuxer stream. | 239 // request to the demuxer stream. |
237 demuxer_stream_->Read(NewCallback(this, &DecoderBase::OnReadComplete)); | 240 demuxer_stream_->Read(NewCallback(this, &DecoderBase::OnReadComplete)); |
238 ++pending_reads_; | 241 ++pending_reads_; |
239 } | 242 } |
240 | 243 |
241 void ReadCompleteTask(scoped_refptr<Buffer> buffer) { | 244 void ReadCompleteTask(scoped_refptr<Buffer> buffer) { |
242 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 245 DCHECK_EQ(MessageLoop::current(), message_loop_); |
243 DCHECK_GT(pending_reads_, 0u); | 246 DCHECK_GT(pending_reads_, 0u); |
244 --pending_reads_; | 247 --pending_reads_; |
245 if (IsStopped()) { | 248 if (IsStopped()) { |
246 return; | 249 return; |
247 } | 250 } |
248 | 251 |
249 // Decode the frame right away. | 252 // Decode the frame right away. |
250 DoDecode(buffer); | 253 DoDecode(buffer); |
251 } | 254 } |
252 | 255 |
253 // Attempts to fulfill a single pending read by dequeuing a buffer and read | 256 // Attempts to fulfill a single pending read by dequeuing a buffer and read |
254 // callback pair and executing the callback. | 257 // callback pair and executing the callback. |
255 // | 258 // |
256 // Return true if one read request is fulfilled. | 259 // Return true if one read request is fulfilled. |
257 bool FulfillPendingRead() { | 260 bool FulfillPendingRead() { |
258 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 261 DCHECK_EQ(MessageLoop::current(), message_loop_); |
259 if (!pending_requests_ || result_queue_.empty()) { | 262 if (!pending_requests_ || result_queue_.empty()) { |
260 return false; | 263 return false; |
261 } | 264 } |
262 | 265 |
263 // Dequeue a frame and read callback pair. | 266 // Dequeue a frame and read callback pair. |
264 scoped_refptr<Output> output = result_queue_.front(); | 267 scoped_refptr<Output> output = result_queue_.front(); |
265 result_queue_.pop_front(); | 268 result_queue_.pop_front(); |
266 | 269 |
267 // Execute the callback! | 270 // Execute the callback! |
268 --pending_requests_; | 271 --pending_requests_; |
269 | 272 |
270 // TODO(hclam): We only inherit this class from FFmpegAudioDecoder so we | 273 // TODO(hclam): We only inherit this class from FFmpegAudioDecoder so we |
271 // are making this call. We should correct this by merging this class into | 274 // are making this call. We should correct this by merging this class into |
272 // FFmpegAudioDecoder. | 275 // FFmpegAudioDecoder. |
273 Decoder::consume_audio_samples_callback()->Run(output); | 276 Decoder::consume_audio_samples_callback()->Run(output); |
274 return true; | 277 return true; |
275 } | 278 } |
276 | 279 |
| 280 MessageLoop* message_loop_; |
| 281 |
277 // Tracks the number of asynchronous reads issued to |demuxer_stream_|. | 282 // Tracks the number of asynchronous reads issued to |demuxer_stream_|. |
278 // Using size_t since it is always compared against deque::size(). | 283 // Using size_t since it is always compared against deque::size(). |
279 size_t pending_reads_; | 284 size_t pending_reads_; |
280 // Tracks the number of asynchronous reads issued from renderer. | 285 // Tracks the number of asynchronous reads issued from renderer. |
281 size_t pending_requests_; | 286 size_t pending_requests_; |
282 | 287 |
283 // Pointer to the demuxer stream that will feed us compressed buffers. | 288 // Pointer to the demuxer stream that will feed us compressed buffers. |
284 scoped_refptr<DemuxerStream> demuxer_stream_; | 289 scoped_refptr<DemuxerStream> demuxer_stream_; |
285 | 290 |
286 // Queue of decoded samples produced in the OnDecode() method of the decoder. | 291 // Queue of decoded samples produced in the OnDecode() method of the decoder. |
(...skipping 14 matching lines...) Expand all Loading... |
301 kStopped, | 306 kStopped, |
302 }; | 307 }; |
303 State state_; | 308 State state_; |
304 | 309 |
305 DISALLOW_COPY_AND_ASSIGN(DecoderBase); | 310 DISALLOW_COPY_AND_ASSIGN(DecoderBase); |
306 }; | 311 }; |
307 | 312 |
308 } // namespace media | 313 } // namespace media |
309 | 314 |
310 #endif // MEDIA_FILTERS_DECODER_BASE_H_ | 315 #endif // MEDIA_FILTERS_DECODER_BASE_H_ |
OLD | NEW |