| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this |
| 2 // source code is governed by a BSD-style license that can be found in the | 2 // source code is governed by a BSD-style license that can be found in the |
| 3 // LICENSE file. | 3 // 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/lock.h" | 12 #include "base/lock.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/thread.h" | 15 #include "base/thread.h" |
| 16 #include "media/base/buffers.h" | 16 #include "media/base/buffers.h" |
| 17 #include "media/base/callback.h" |
| 17 #include "media/base/filters.h" | 18 #include "media/base/filters.h" |
| 18 #include "media/base/filter_host.h" | 19 #include "media/base/filter_host.h" |
| 19 | 20 |
| 20 namespace media { | 21 namespace media { |
| 21 | 22 |
| 22 // 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 |
| 23 // 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 |
| 24 // name resolution in templates. | 25 // name resolution in templates. |
| 25 template <class Decoder, class Output> | 26 template <class Decoder, class Output> |
| 26 class DecoderBase : public Decoder { | 27 class DecoderBase : public Decoder { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 48 } | 49 } |
| 49 | 50 |
| 50 virtual const MediaFormat& media_format() { return media_format_; } | 51 virtual const MediaFormat& media_format() { return media_format_; } |
| 51 | 52 |
| 52 // Audio or video decoder. | 53 // Audio or video decoder. |
| 53 virtual void Read(ReadCallback* read_callback) { | 54 virtual void Read(ReadCallback* read_callback) { |
| 54 this->message_loop()->PostTask(FROM_HERE, | 55 this->message_loop()->PostTask(FROM_HERE, |
| 55 NewRunnableMethod(this, &DecoderBase::ReadTask, read_callback)); | 56 NewRunnableMethod(this, &DecoderBase::ReadTask, read_callback)); |
| 56 } | 57 } |
| 57 | 58 |
| 58 void OnReadComplete(Buffer* buffer) { | |
| 59 // Little bit of magic here to get NewRunnableMethod() to generate a Task | |
| 60 // that holds onto a reference via scoped_refptr<>. | |
| 61 // | |
| 62 // TODO(scherkus): change the callback format to pass a scoped_refptr<> or | |
| 63 // better yet see if we can get away with not using reference counting. | |
| 64 scoped_refptr<Buffer> buffer_ref = buffer; | |
| 65 this->message_loop()->PostTask(FROM_HERE, | |
| 66 NewRunnableMethod(this, &DecoderBase::ReadCompleteTask, buffer_ref)); | |
| 67 } | |
| 68 | |
| 69 protected: | 59 protected: |
| 70 DecoderBase() | 60 DecoderBase() |
| 71 : pending_reads_(0), | 61 : pending_reads_(0), |
| 72 expecting_discontinuous_(false), | 62 expecting_discontinuous_(false), |
| 73 state_(kUninitialized) { | 63 state_(kUninitialized) { |
| 74 } | 64 } |
| 75 | 65 |
| 76 virtual ~DecoderBase() { | 66 virtual ~DecoderBase() { |
| 77 DCHECK(state_ == kUninitialized || state_ == kStopped); | 67 DCHECK(state_ == kUninitialized || state_ == kStopped); |
| 78 DCHECK(result_queue_.empty()); | 68 DCHECK(result_queue_.empty()); |
| 79 DCHECK(read_queue_.empty()); | 69 DCHECK(read_queue_.empty()); |
| 80 } | 70 } |
| 81 | 71 |
| 82 // This method is called by the derived class from within the OnDecode method. | 72 // This method is called by the derived class from within the OnDecode method. |
| 83 // It places an output buffer in the result queue. It must be called from | 73 // It places an output buffer in the result queue. It must be called from |
| 84 // within the OnDecode method. | 74 // within the OnDecode method. |
| 85 void EnqueueResult(Output* output) { | 75 void EnqueueResult(Output* output) { |
| 86 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 76 DCHECK_EQ(MessageLoop::current(), this->message_loop()); |
| 87 if (!IsStopped()) { | 77 if (!IsStopped()) { |
| 88 result_queue_.push_back(output); | 78 result_queue_.push_back(output); |
| 89 } | 79 } |
| 90 } | 80 } |
| 91 | 81 |
| 82 // TODO(ajwong): All these "Task*" used as completion callbacks should be |
| 83 // FilterCallbacks. However, since NewCallback() cannot prebind parameters, |
| 84 // we use NewRunnableMethod() instead which causes an unfortunate refcount. |
| 85 // We should move stoyan's Mutant code into base/task.h and turn these |
| 86 // back into FilterCallbacks. |
| 87 |
| 92 // Method that must be implemented by the derived class. Called from within | 88 // Method that must be implemented by the derived class. Called from within |
| 93 // the DecoderBase::Initialize() method before any reads are submitted to | 89 // the DecoderBase::Initialize() method before any reads are submitted to |
| 94 // the demuxer stream. Returns true if successful, otherwise false indicates | 90 // the demuxer stream. Returns true if successful, otherwise false indicates |
| 95 // a fatal error. The derived class should NOT call the filter host's | 91 // a fatal error. The derived class should NOT call the filter host's |
| 96 // InitializationComplete() method. If this method returns true, then the | 92 // InitializationComplete() method. If this method returns true, then the |
| 97 // base class will call the host to complete initialization. During this | 93 // base class will call the host to complete initialization. During this |
| 98 // call, the derived class must fill in the media_format_ member. | 94 // call, the derived class must fill in the media_format_ member. |
| 99 virtual bool OnInitialize(DemuxerStream* demuxer_stream) = 0; | 95 virtual void DoInitialize(DemuxerStream* demuxer_stream, bool* success, |
| 96 Task* done_cb) = 0; |
| 100 | 97 |
| 101 // Method that may be implemented by the derived class if desired. It will | 98 // Method that may be implemented by the derived class if desired. It will |
| 102 // be called from within the MediaFilter::Stop() method prior to stopping the | 99 // be called from within the MediaFilter::Stop() method prior to stopping the |
| 103 // base class. | 100 // base class. |
| 104 virtual void OnStop() {} | 101 // |
| 102 // TODO(ajwong): Make this asynchronous. |
| 103 virtual void DoStop() {} |
| 105 | 104 |
| 106 // Derived class can implement this method and perform seeking logic prior | 105 // Derived class can implement this method and perform seeking logic prior |
| 107 // to the base class. | 106 // to the base class. |
| 108 virtual void OnSeek(base::TimeDelta time) {} | 107 virtual void DoSeek(base::TimeDelta time, Task* done_cb) = 0; |
| 109 | 108 |
| 110 // Method that must be implemented by the derived class. If the decode | 109 // Method that must be implemented by the derived class. If the decode |
| 111 // operation produces one or more outputs, the derived class should call | 110 // operation produces one or more outputs, the derived class should call |
| 112 // the EnequeueResult() method from within this method. | 111 // the EnequeueResult() method from within this method. |
| 113 virtual void OnDecode(Buffer* input) = 0; | 112 virtual void DoDecode(Buffer* input, Task* done_cb) = 0; |
| 114 | 113 |
| 115 MediaFormat media_format_; | 114 MediaFormat media_format_; |
| 116 | 115 |
| 117 private: | 116 private: |
| 118 bool IsStopped() { return state_ == kStopped; } | 117 bool IsStopped() { return state_ == kStopped; } |
| 119 | 118 |
| 119 void OnReadComplete(Buffer* buffer) { |
| 120 // Little bit of magic here to get NewRunnableMethod() to generate a Task |
| 121 // that holds onto a reference via scoped_refptr<>. |
| 122 // |
| 123 // TODO(scherkus): change the callback format to pass a scoped_refptr<> or |
| 124 // better yet see if we can get away with not using reference counting. |
| 125 scoped_refptr<Buffer> buffer_ref = buffer; |
| 126 this->message_loop()->PostTask(FROM_HERE, |
| 127 NewRunnableMethod(this, &DecoderBase::ReadCompleteTask, buffer_ref)); |
| 128 } |
| 129 |
| 120 void StopTask() { | 130 void StopTask() { |
| 121 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 131 DCHECK_EQ(MessageLoop::current(), this->message_loop()); |
| 122 | 132 |
| 123 // Delegate to the subclass first. | 133 // Delegate to the subclass first. |
| 124 OnStop(); | 134 DoStop(); |
| 125 | 135 |
| 126 // Throw away all buffers in all queues. | 136 // Throw away all buffers in all queues. |
| 127 result_queue_.clear(); | 137 result_queue_.clear(); |
| 128 STLDeleteElements(&read_queue_); | 138 STLDeleteElements(&read_queue_); |
| 129 state_ = kStopped; | 139 state_ = kStopped; |
| 130 } | 140 } |
| 131 | 141 |
| 132 void SeekTask(base::TimeDelta time, FilterCallback* callback) { | 142 void SeekTask(base::TimeDelta time, FilterCallback* callback) { |
| 133 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 143 DCHECK_EQ(MessageLoop::current(), this->message_loop()); |
| 134 DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed"; | 144 DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed"; |
| 135 DCHECK(read_queue_.empty()) << "Read requests should be empty"; | 145 DCHECK(read_queue_.empty()) << "Read requests should be empty"; |
| 136 scoped_ptr<FilterCallback> c(callback); | |
| 137 | 146 |
| 138 // Delegate to the subclass first. | 147 // Delegate to the subclass first. |
| 139 // | 148 // |
| 140 // TODO(scherkus): if we have the strong assertion that there are no pending | 149 // TODO(scherkus): if we have the strong assertion that there are no pending |
| 141 // reads in the entire pipeline when we receive Seek(), subclasses could | 150 // reads in the entire pipeline when we receive Seek(), subclasses could |
| 142 // either flush their buffers here or wait for IsDiscontinuous(). I'm | 151 // either flush their buffers here or wait for IsDiscontinuous(). I'm |
| 143 // inclined to say that they should still wait for IsDiscontinuous() so they | 152 // inclined to say that they should still wait for IsDiscontinuous() so they |
| 144 // don't have duplicated logic for Seek() and actual discontinuous frames. | 153 // don't have duplicated logic for Seek() and actual discontinuous frames. |
| 145 OnSeek(time); | 154 DoSeek(time, |
| 155 NewRunnableMethod(this, &DecoderBase::OnSeekComplete, callback)); |
| 156 } |
| 146 | 157 |
| 158 void OnSeekComplete(FilterCallback* callback) { |
| 147 // Flush our decoded results. We'll set a boolean that we can DCHECK to | 159 // Flush our decoded results. We'll set a boolean that we can DCHECK to |
| 148 // verify our assertion that the first buffer received after a Seek() should | 160 // verify our assertion that the first buffer received after a Seek() should |
| 149 // always be discontinuous. | 161 // always be discontinuous. |
| 150 result_queue_.clear(); | 162 result_queue_.clear(); |
| 151 expecting_discontinuous_ = true; | 163 expecting_discontinuous_ = true; |
| 152 | 164 |
| 153 // Signal that we're done seeking. | 165 // Signal that we're done seeking. |
| 154 callback->Run(); | 166 callback->Run(); |
| 155 } | 167 } |
| 156 | 168 |
| 157 void InitializeTask(DemuxerStream* demuxer_stream, FilterCallback* callback) { | 169 void InitializeTask(DemuxerStream* demuxer_stream, FilterCallback* callback) { |
| 158 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 170 DCHECK_EQ(MessageLoop::current(), this->message_loop()); |
| 159 CHECK(kUninitialized == state_); | 171 CHECK(kUninitialized == state_); |
| 160 CHECK(!demuxer_stream_); | 172 CHECK(!demuxer_stream_); |
| 161 scoped_ptr<FilterCallback> c(callback); | |
| 162 demuxer_stream_ = demuxer_stream; | 173 demuxer_stream_ = demuxer_stream; |
| 163 | 174 |
| 175 bool* success = new bool; |
| 176 DoInitialize(demuxer_stream, |
| 177 success, |
| 178 NewRunnableMethod(this, &DecoderBase::OnInitializeComplete, |
| 179 success, callback)); |
| 180 } |
| 181 |
| 182 void OnInitializeComplete(bool* success, FilterCallback* done_cb) { |
| 183 // Note: The done_runner must be declared *last* to ensure proper |
| 184 // destruction order. |
| 185 scoped_ptr<bool> success_deleter(success); |
| 186 AutoCallbackRunner done_runner(done_cb); |
| 187 |
| 188 DCHECK_EQ(MessageLoop::current(), this->message_loop()); |
| 164 // Delegate to subclass first. | 189 // Delegate to subclass first. |
| 165 if (!OnInitialize(demuxer_stream_)) { | 190 if (!*success) { |
| 166 this->host()->SetError(PIPELINE_ERROR_DECODE); | 191 this->host()->SetError(PIPELINE_ERROR_DECODE); |
| 167 callback->Run(); | 192 } else { |
| 168 return; | 193 // TODO(scherkus): subclass shouldn't mutate superclass media format. |
| 194 DCHECK(!media_format_.empty()) << "Subclass did not set media_format_"; |
| 195 state_ = kInitialized; |
| 169 } | 196 } |
| 170 | |
| 171 // TODO(scherkus): subclass shouldn't mutate superclass media format. | |
| 172 DCHECK(!media_format_.empty()) << "Subclass did not set media_format_"; | |
| 173 state_ = kInitialized; | |
| 174 callback->Run(); | |
| 175 } | 197 } |
| 176 | 198 |
| 177 void ReadTask(ReadCallback* read_callback) { | 199 void ReadTask(ReadCallback* read_callback) { |
| 178 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 200 DCHECK_EQ(MessageLoop::current(), this->message_loop()); |
| 179 | 201 |
| 180 // TODO(scherkus): should reply with a null operation (empty buffer). | 202 // TODO(scherkus): should reply with a null operation (empty buffer). |
| 181 if (IsStopped()) { | 203 if (IsStopped()) { |
| 182 delete read_callback; | 204 delete read_callback; |
| 183 return; | 205 return; |
| 184 } | 206 } |
| (...skipping 18 matching lines...) Expand all Loading... |
| 203 } | 225 } |
| 204 | 226 |
| 205 // TODO(scherkus): remove this when we're less paranoid about our seeking | 227 // TODO(scherkus): remove this when we're less paranoid about our seeking |
| 206 // invariants. | 228 // invariants. |
| 207 if (buffer->IsDiscontinuous()) { | 229 if (buffer->IsDiscontinuous()) { |
| 208 DCHECK(expecting_discontinuous_); | 230 DCHECK(expecting_discontinuous_); |
| 209 expecting_discontinuous_ = false; | 231 expecting_discontinuous_ = false; |
| 210 } | 232 } |
| 211 | 233 |
| 212 // Decode the frame right away. | 234 // Decode the frame right away. |
| 213 OnDecode(buffer); | 235 DoDecode(buffer, NewRunnableMethod(this, &DecoderBase::OnDecodeComplete)); |
| 236 } |
| 214 | 237 |
| 238 void OnDecodeComplete() { |
| 215 // Attempt to fulfill a pending read callback and schedule additional reads | 239 // Attempt to fulfill a pending read callback and schedule additional reads |
| 216 // if necessary. | 240 // if necessary. |
| 217 FulfillPendingRead(); | 241 FulfillPendingRead(); |
| 218 | 242 |
| 219 // Issue reads as necessary. | 243 // Issue reads as necessary. |
| 220 // | 244 // |
| 221 // Note that it's possible for us to decode but not produce a frame, in | 245 // Note that it's possible for us to decode but not produce a frame, in |
| 222 // which case |pending_reads_| will remain less than |read_queue_| so we | 246 // which case |pending_reads_| will remain less than |read_queue_| so we |
| 223 // need to schedule an additional read. | 247 // need to schedule an additional read. |
| 224 DCHECK_LE(pending_reads_, read_queue_.size()); | 248 DCHECK_LE(pending_reads_, read_queue_.size()); |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 281 kStopped, | 305 kStopped, |
| 282 }; | 306 }; |
| 283 State state_; | 307 State state_; |
| 284 | 308 |
| 285 DISALLOW_COPY_AND_ASSIGN(DecoderBase); | 309 DISALLOW_COPY_AND_ASSIGN(DecoderBase); |
| 286 }; | 310 }; |
| 287 | 311 |
| 288 } // namespace media | 312 } // namespace media |
| 289 | 313 |
| 290 #endif // MEDIA_FILTERS_DECODER_BASE_H_ | 314 #endif // MEDIA_FILTERS_DECODER_BASE_H_ |
| OLD | NEW |