| 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/callback.h" | |
| 13 #include "base/stl_util-inl.h" | |
| 14 #include "base/task.h" | |
| 15 #include "base/threading/thread.h" | |
| 16 #include "media/base/buffers.h" | |
| 17 #include "media/base/callback.h" | |
| 18 #include "media/base/filters.h" | |
| 19 #include "media/base/filter_host.h" | |
| 20 | |
| 21 namespace media { | |
| 22 | |
| 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 | |
| 25 // name resolution in templates. | |
| 26 template <class Decoder, class Output> | |
| 27 class DecoderBase : public Decoder { | |
| 28 public: | |
| 29 // Filter implementation. | |
| 30 virtual void Stop(FilterCallback* callback) { | |
| 31 message_loop_->PostTask( | |
| 32 FROM_HERE, | |
| 33 NewRunnableMethod(this, &DecoderBase::StopTask, callback)); | |
| 34 } | |
| 35 | |
| 36 virtual void Seek(base::TimeDelta time, | |
| 37 FilterCallback* callback) { | |
| 38 message_loop_->PostTask( | |
| 39 FROM_HERE, | |
| 40 NewRunnableMethod(this, &DecoderBase::SeekTask, time, callback)); | |
| 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 AutoTaskRunner done_runner(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 demuxer_stream_->Read(NewCallback(this, &DecoderBase::OnReadComplete)); | |
| 133 ++pending_reads_; | |
| 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, FilterCallback* callback) { | |
| 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, | |
| 179 NewRunnableMethod(this, &DecoderBase::OnSeekComplete, callback)); | |
| 180 } | |
| 181 | |
| 182 void OnSeekComplete(FilterCallback* callback) { | |
| 183 // Flush our decoded results. | |
| 184 result_queue_.clear(); | |
| 185 | |
| 186 // Signal that we're done seeking. | |
| 187 if (callback) { | |
| 188 callback->Run(); | |
| 189 delete callback; | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 void InitializeTask(DemuxerStream* demuxer_stream, | |
| 194 FilterCallback* callback) { | |
| 195 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 196 CHECK(kUninitialized == state_); | |
| 197 CHECK(!demuxer_stream_); | |
| 198 demuxer_stream_ = demuxer_stream; | |
| 199 | |
| 200 bool* success = new bool; | |
| 201 DoInitialize(demuxer_stream, | |
| 202 success, | |
| 203 NewRunnableMethod(this, &DecoderBase::OnInitializeComplete, | |
| 204 success, callback)); | |
| 205 } | |
| 206 | |
| 207 void OnInitializeComplete(bool* success, FilterCallback* done_cb) { | |
| 208 // Note: The done_runner must be declared *last* to ensure proper | |
| 209 // destruction order. | |
| 210 scoped_ptr<bool> success_deleter(success); | |
| 211 AutoCallbackRunner done_runner(done_cb); | |
| 212 | |
| 213 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 214 // Delegate to subclass first. | |
| 215 if (!*success) { | |
| 216 this->host()->SetError(PIPELINE_ERROR_DECODE); | |
| 217 } else { | |
| 218 state_ = kInitialized; | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 void ReadTask(scoped_refptr<Output> output) { | |
| 223 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 224 | |
| 225 // TODO(scherkus): should reply with a null operation (empty buffer). | |
| 226 if (IsStopped()) | |
| 227 return; | |
| 228 | |
| 229 ++pending_requests_; | |
| 230 | |
| 231 // Try to fulfill it immediately. | |
| 232 if (FulfillPendingRead()) | |
| 233 return; | |
| 234 | |
| 235 // Since we can't fulfill a read request now then submit a read | |
| 236 // request to the demuxer stream. | |
| 237 demuxer_stream_->Read(NewCallback(this, &DecoderBase::OnReadComplete)); | |
| 238 ++pending_reads_; | |
| 239 } | |
| 240 | |
| 241 void ReadCompleteTask(scoped_refptr<Buffer> buffer) { | |
| 242 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 243 DCHECK_GT(pending_reads_, 0u); | |
| 244 --pending_reads_; | |
| 245 if (IsStopped()) { | |
| 246 return; | |
| 247 } | |
| 248 | |
| 249 // Decode the frame right away. | |
| 250 DoDecode(buffer); | |
| 251 } | |
| 252 | |
| 253 // Attempts to fulfill a single pending read by dequeuing a buffer and read | |
| 254 // callback pair and executing the callback. | |
| 255 // | |
| 256 // Return true if one read request is fulfilled. | |
| 257 bool FulfillPendingRead() { | |
| 258 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 259 if (!pending_requests_ || result_queue_.empty()) { | |
| 260 return false; | |
| 261 } | |
| 262 | |
| 263 // Dequeue a frame and read callback pair. | |
| 264 scoped_refptr<Output> output = result_queue_.front(); | |
| 265 result_queue_.pop_front(); | |
| 266 | |
| 267 // Execute the callback! | |
| 268 --pending_requests_; | |
| 269 | |
| 270 // TODO(scherkus): Since FFmpegAudioDecoder is the only subclass this is a | |
| 271 // temporary hack until I finish removing DecoderBase. | |
| 272 Decoder::ConsumeAudioSamples(output); | |
| 273 return true; | |
| 274 } | |
| 275 | |
| 276 MessageLoop* message_loop_; | |
| 277 | |
| 278 // Tracks the number of asynchronous reads issued to |demuxer_stream_|. | |
| 279 // Using size_t since it is always compared against deque::size(). | |
| 280 size_t pending_reads_; | |
| 281 // Tracks the number of asynchronous reads issued from renderer. | |
| 282 size_t pending_requests_; | |
| 283 | |
| 284 // Pointer to the demuxer stream that will feed us compressed buffers. | |
| 285 scoped_refptr<DemuxerStream> demuxer_stream_; | |
| 286 | |
| 287 // Queue of decoded samples produced in the OnDecode() method of the decoder. | |
| 288 // Any samples placed in this queue will be assigned to the OutputQueue | |
| 289 // buffers once the OnDecode() method returns. | |
| 290 // | |
| 291 // TODO(ralphl): Eventually we want to have decoders get their destination | |
| 292 // buffer from the OutputQueue and write to it directly. Until we change | |
| 293 // from the Assignable buffer to callbacks and renderer-allocated buffers, | |
| 294 // we need this extra queue. | |
| 295 typedef std::deque<scoped_refptr<Output> > ResultQueue; | |
| 296 ResultQueue result_queue_; | |
| 297 | |
| 298 // Simple state tracking variable. | |
| 299 enum State { | |
| 300 kUninitialized, | |
| 301 kInitialized, | |
| 302 kStopped, | |
| 303 }; | |
| 304 State state_; | |
| 305 | |
| 306 // Callback to update pipeline statistics. | |
| 307 scoped_ptr<StatisticsCallback> statistics_callback_; | |
| 308 | |
| 309 DISALLOW_COPY_AND_ASSIGN(DecoderBase); | |
| 310 }; | |
| 311 | |
| 312 } // namespace media | |
| 313 | |
| 314 #endif // MEDIA_FILTERS_DECODER_BASE_H_ | |
| OLD | NEW |