| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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. | |
| 4 | |
| 5 // A base class that provides the plumbing for a decoder filters. | |
| 6 | |
| 7 #ifndef MEDIA_FILTERS_DECODER_BASE_H_ | |
| 8 #define MEDIA_FILTERS_DECODER_BASE_H_ | |
| 9 | |
| 10 #include <deque> | |
| 11 | |
| 12 #include "base/bind.h" | |
| 13 #include "base/callback.h" | |
| 14 #include "base/stl_util.h" | |
| 15 #include "base/task.h" | |
| 16 #include "base/threading/thread.h" | |
| 17 #include "media/base/buffers.h" | |
| 18 #include "media/base/callback.h" | |
| 19 #include "media/base/filters.h" | |
| 20 #include "media/base/filter_host.h" | |
| 21 | |
| 22 namespace media { | |
| 23 | |
| 24 // In this class, we over-specify method lookup via this-> to avoid unexpected | |
| 25 // name resolution issues due to the two-phase lookup needed for dependent | |
| 26 // name resolution in templates. | |
| 27 template <class Decoder, class Output> | |
| 28 class DecoderBase : public Decoder { | |
| 29 public: | |
| 30 // Filter implementation. | |
| 31 virtual void Stop(FilterCallback* callback) { | |
| 32 message_loop_->PostTask( | |
| 33 FROM_HERE, | |
| 34 NewRunnableMethod(this, &DecoderBase::StopTask, callback)); | |
| 35 } | |
| 36 | |
| 37 virtual void Seek(base::TimeDelta time, const FilterStatusCB& cb) { | |
| 38 message_loop_->PostTask( | |
| 39 FROM_HERE, | |
| 40 NewRunnableMethod(this, &DecoderBase::SeekTask, time, cb)); | |
| 41 } | |
| 42 | |
| 43 // Decoder implementation. | |
| 44 virtual void Initialize(DemuxerStream* demuxer_stream, | |
| 45 FilterCallback* callback, | |
| 46 StatisticsCallback* stats_callback) { | |
| 47 statistics_callback_.reset(stats_callback); | |
| 48 message_loop_->PostTask( | |
| 49 FROM_HERE, | |
| 50 NewRunnableMethod(this, | |
| 51 &DecoderBase::InitializeTask, | |
| 52 make_scoped_refptr(demuxer_stream), | |
| 53 callback)); | |
| 54 } | |
| 55 | |
| 56 // TODO(scherkus): Since FFmpegAudioDecoder is the only subclass this is a | |
| 57 // temporary hack until I finish removing DecoderBase. | |
| 58 void PostReadTaskHack(scoped_refptr<Output> output) { | |
| 59 message_loop_->PostTask(FROM_HERE, | |
| 60 NewRunnableMethod(this, &DecoderBase::ReadTask, output)); | |
| 61 } | |
| 62 | |
| 63 protected: | |
| 64 explicit DecoderBase(MessageLoop* message_loop) | |
| 65 : message_loop_(message_loop), | |
| 66 pending_reads_(0), | |
| 67 pending_requests_(0), | |
| 68 state_(kUninitialized) { | |
| 69 } | |
| 70 | |
| 71 virtual ~DecoderBase() { | |
| 72 DCHECK(state_ == kUninitialized || state_ == kStopped); | |
| 73 DCHECK(result_queue_.empty()); | |
| 74 } | |
| 75 | |
| 76 // This method is called by the derived class from within the OnDecode method. | |
| 77 // It places an output buffer in the result queue. It must be called from | |
| 78 // within the OnDecode method. | |
| 79 void EnqueueResult(Output* output) { | |
| 80 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 81 if (!IsStopped()) { | |
| 82 result_queue_.push_back(output); | |
| 83 } | |
| 84 } | |
| 85 | |
| 86 // TODO(ajwong): All these "Task*" used as completion callbacks should be | |
| 87 // FilterCallbacks. However, since NewCallback() cannot prebind parameters, | |
| 88 // we use NewRunnableMethod() instead which causes an unfortunate refcount. | |
| 89 // We should move stoyan's Mutant code into base/task.h and turn these | |
| 90 // back into FilterCallbacks. | |
| 91 | |
| 92 // Method that must be implemented by the derived class. Called from within | |
| 93 // the DecoderBase::Initialize() method before any reads are submitted to | |
| 94 // the demuxer stream. Returns true if successful, otherwise false indicates | |
| 95 // a fatal error. The derived class should NOT call the filter host's | |
| 96 // InitializationComplete() method. If this method returns true, then the | |
| 97 // base class will call the host to complete initialization. | |
| 98 virtual void DoInitialize(DemuxerStream* demuxer_stream, bool* success, | |
| 99 Task* done_cb) = 0; | |
| 100 | |
| 101 // Method that may be implemented by the derived class if desired. It will | |
| 102 // be called from within the Filter::Stop() method prior to stopping the | |
| 103 // base class. | |
| 104 virtual void DoStop(Task* done_cb) { | |
| 105 base::ScopedTaskRunner run_done_cb(done_cb); | |
| 106 } | |
| 107 | |
| 108 // Derived class can implement this method and perform seeking logic prior | |
| 109 // to the base class. | |
| 110 virtual void DoSeek(base::TimeDelta time, Task* done_cb) = 0; | |
| 111 | |
| 112 // Method that must be implemented by the derived class. If the decode | |
| 113 // operation produces one or more outputs, the derived class should call | |
| 114 // the EnequeueResult() method from within this method. | |
| 115 virtual void DoDecode(Buffer* input) = 0; | |
| 116 | |
| 117 void OnDecodeComplete(const PipelineStatistics& statistics) { | |
| 118 statistics_callback_->Run(statistics); | |
| 119 | |
| 120 // Attempt to fulfill a pending read callback and schedule additional reads | |
| 121 // if necessary. | |
| 122 bool fulfilled = FulfillPendingRead(); | |
| 123 | |
| 124 // Issue reads as necessary. | |
| 125 // | |
| 126 // Note that it's possible for us to decode but not produce a frame, in | |
| 127 // which case |pending_reads_| will remain less than |read_queue_| so we | |
| 128 // need to schedule an additional read. | |
| 129 DCHECK_LE(pending_reads_, pending_requests_); | |
| 130 if (!fulfilled) { | |
| 131 DCHECK_LT(pending_reads_, pending_requests_); | |
| 132 ++pending_reads_; | |
| 133 demuxer_stream_->Read(base::Bind(&DecoderBase::OnReadComplete, this)); | |
| 134 } | |
| 135 } | |
| 136 | |
| 137 // Provide access to subclasses. | |
| 138 MessageLoop* message_loop() { return message_loop_; } | |
| 139 | |
| 140 private: | |
| 141 bool IsStopped() { return state_ == kStopped; } | |
| 142 | |
| 143 void OnReadComplete(Buffer* buffer) { | |
| 144 // Little bit of magic here to get NewRunnableMethod() to generate a Task | |
| 145 // that holds onto a reference via scoped_refptr<>. | |
| 146 // | |
| 147 // TODO(scherkus): change the callback format to pass a scoped_refptr<> or | |
| 148 // better yet see if we can get away with not using reference counting. | |
| 149 scoped_refptr<Buffer> buffer_ref = buffer; | |
| 150 message_loop_->PostTask(FROM_HERE, | |
| 151 NewRunnableMethod(this, &DecoderBase::ReadCompleteTask, buffer_ref)); | |
| 152 } | |
| 153 | |
| 154 void StopTask(FilterCallback* callback) { | |
| 155 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 156 | |
| 157 // Delegate to the subclass first. | |
| 158 DoStop(NewRunnableMethod(this, &DecoderBase::OnStopComplete, callback)); | |
| 159 } | |
| 160 | |
| 161 void OnStopComplete(FilterCallback* callback) { | |
| 162 // Throw away all buffers in all queues. | |
| 163 result_queue_.clear(); | |
| 164 state_ = kStopped; | |
| 165 | |
| 166 if (callback) { | |
| 167 callback->Run(); | |
| 168 delete callback; | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 void SeekTask(base::TimeDelta time, const FilterStatusCB& cb) { | |
| 173 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 174 DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed"; | |
| 175 DCHECK_EQ(0u, pending_requests_) << "Pending requests should be empty"; | |
| 176 | |
| 177 // Delegate to the subclass first. | |
| 178 DoSeek(time, NewRunnableMethod(this, &DecoderBase::OnSeekComplete, cb)); | |
| 179 } | |
| 180 | |
| 181 void OnSeekComplete(const FilterStatusCB& cb) { | |
| 182 // Flush our decoded results. | |
| 183 result_queue_.clear(); | |
| 184 | |
| 185 // Signal that we're done seeking. | |
| 186 if (!cb.is_null()) | |
| 187 cb.Run(PIPELINE_OK); | |
| 188 } | |
| 189 | |
| 190 void InitializeTask(DemuxerStream* demuxer_stream, | |
| 191 FilterCallback* callback) { | |
| 192 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 193 CHECK(kUninitialized == state_); | |
| 194 CHECK(!demuxer_stream_); | |
| 195 demuxer_stream_ = demuxer_stream; | |
| 196 | |
| 197 bool* success = new bool; | |
| 198 DoInitialize(demuxer_stream, | |
| 199 success, | |
| 200 NewRunnableMethod(this, &DecoderBase::OnInitializeComplete, | |
| 201 success, callback)); | |
| 202 } | |
| 203 | |
| 204 void OnInitializeComplete(bool* success, FilterCallback* done_cb) { | |
| 205 // Note: The done_runner must be declared *last* to ensure proper | |
| 206 // destruction order. | |
| 207 scoped_ptr<bool> success_deleter(success); | |
| 208 AutoCallbackRunner done_runner(done_cb); | |
| 209 | |
| 210 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 211 // Delegate to subclass first. | |
| 212 if (!*success) { | |
| 213 this->host()->SetError(PIPELINE_ERROR_DECODE); | |
| 214 } else { | |
| 215 state_ = kInitialized; | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 void ReadTask(scoped_refptr<Output> output) { | |
| 220 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 221 | |
| 222 // TODO(scherkus): should reply with a null operation (empty buffer). | |
| 223 if (IsStopped()) | |
| 224 return; | |
| 225 | |
| 226 ++pending_requests_; | |
| 227 | |
| 228 // Try to fulfill it immediately. | |
| 229 if (FulfillPendingRead()) | |
| 230 return; | |
| 231 | |
| 232 // Since we can't fulfill a read request now then submit a read | |
| 233 // request to the demuxer stream. | |
| 234 ++pending_reads_; | |
| 235 demuxer_stream_->Read(base::Bind(&DecoderBase::OnReadComplete, this)); | |
| 236 } | |
| 237 | |
| 238 void ReadCompleteTask(scoped_refptr<Buffer> buffer) { | |
| 239 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 240 DCHECK_GT(pending_reads_, 0u); | |
| 241 --pending_reads_; | |
| 242 if (IsStopped()) { | |
| 243 return; | |
| 244 } | |
| 245 | |
| 246 // Decode the frame right away. | |
| 247 DoDecode(buffer); | |
| 248 } | |
| 249 | |
| 250 // Attempts to fulfill a single pending read by dequeuing a buffer and read | |
| 251 // callback pair and executing the callback. | |
| 252 // | |
| 253 // Return true if one read request is fulfilled. | |
| 254 bool FulfillPendingRead() { | |
| 255 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 256 if (!pending_requests_ || result_queue_.empty()) { | |
| 257 return false; | |
| 258 } | |
| 259 | |
| 260 // Dequeue a frame and read callback pair. | |
| 261 scoped_refptr<Output> output = result_queue_.front(); | |
| 262 result_queue_.pop_front(); | |
| 263 | |
| 264 // Execute the callback! | |
| 265 --pending_requests_; | |
| 266 | |
| 267 // TODO(scherkus): Since FFmpegAudioDecoder is the only subclass this is a | |
| 268 // temporary hack until I finish removing DecoderBase. | |
| 269 Decoder::ConsumeAudioSamples(output); | |
| 270 return true; | |
| 271 } | |
| 272 | |
| 273 MessageLoop* message_loop_; | |
| 274 | |
| 275 // Tracks the number of asynchronous reads issued to |demuxer_stream_|. | |
| 276 // Using size_t since it is always compared against deque::size(). | |
| 277 size_t pending_reads_; | |
| 278 // Tracks the number of asynchronous reads issued from renderer. | |
| 279 size_t pending_requests_; | |
| 280 | |
| 281 // Pointer to the demuxer stream that will feed us compressed buffers. | |
| 282 scoped_refptr<DemuxerStream> demuxer_stream_; | |
| 283 | |
| 284 // Queue of decoded samples produced in the OnDecode() method of the decoder. | |
| 285 // Any samples placed in this queue will be assigned to the OutputQueue | |
| 286 // buffers once the OnDecode() method returns. | |
| 287 // | |
| 288 // TODO(ralphl): Eventually we want to have decoders get their destination | |
| 289 // buffer from the OutputQueue and write to it directly. Until we change | |
| 290 // from the Assignable buffer to callbacks and renderer-allocated buffers, | |
| 291 // we need this extra queue. | |
| 292 typedef std::deque<scoped_refptr<Output> > ResultQueue; | |
| 293 ResultQueue result_queue_; | |
| 294 | |
| 295 // Simple state tracking variable. | |
| 296 enum State { | |
| 297 kUninitialized, | |
| 298 kInitialized, | |
| 299 kStopped, | |
| 300 }; | |
| 301 State state_; | |
| 302 | |
| 303 // Callback to update pipeline statistics. | |
| 304 scoped_ptr<StatisticsCallback> statistics_callback_; | |
| 305 | |
| 306 DISALLOW_COPY_AND_ASSIGN(DecoderBase); | |
| 307 }; | |
| 308 | |
| 309 } // namespace media | |
| 310 | |
| 311 #endif // MEDIA_FILTERS_DECODER_BASE_H_ | |
| OLD | NEW |