| 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> |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 62 // TODO(scherkus): change the callback format to pass a scoped_refptr<> or | 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. | 63 // better yet see if we can get away with not using reference counting. |
| 64 scoped_refptr<Buffer> buffer_ref = buffer; | 64 scoped_refptr<Buffer> buffer_ref = buffer; |
| 65 this->message_loop()->PostTask(FROM_HERE, | 65 this->message_loop()->PostTask(FROM_HERE, |
| 66 NewRunnableMethod(this, &DecoderBase::ReadCompleteTask, buffer_ref)); | 66 NewRunnableMethod(this, &DecoderBase::ReadCompleteTask, buffer_ref)); |
| 67 } | 67 } |
| 68 | 68 |
| 69 protected: | 69 protected: |
| 70 DecoderBase() | 70 DecoderBase() |
| 71 : pending_reads_(0), | 71 : pending_reads_(0), |
| 72 seeking_(false), | 72 expecting_discontinuous_(false), |
| 73 state_(UNINITIALIZED) { | 73 state_(kUninitialized) { |
| 74 } | 74 } |
| 75 | 75 |
| 76 virtual ~DecoderBase() { | 76 virtual ~DecoderBase() { |
| 77 DCHECK(state_ == UNINITIALIZED || state_ == STOPPED); | 77 DCHECK(state_ == kUninitialized || state_ == kStopped); |
| 78 DCHECK(result_queue_.empty()); | 78 DCHECK(result_queue_.empty()); |
| 79 DCHECK(read_queue_.empty()); | 79 DCHECK(read_queue_.empty()); |
| 80 } | 80 } |
| 81 | 81 |
| 82 // This method is called by the derived class from within the OnDecode method. | 82 // 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 | 83 // It places an output buffer in the result queue. It must be called from |
| 84 // within the OnDecode method. | 84 // within the OnDecode method. |
| 85 void EnqueueResult(Output* output) { | 85 void EnqueueResult(Output* output) { |
| 86 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 86 DCHECK_EQ(MessageLoop::current(), this->message_loop()); |
| 87 if (!IsStopped()) { | 87 if (!IsStopped()) { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 108 virtual void OnSeek(base::TimeDelta time) {} | 108 virtual void OnSeek(base::TimeDelta time) {} |
| 109 | 109 |
| 110 // Method that must be implemented by the derived class. If the decode | 110 // Method that must be implemented by the derived class. If the decode |
| 111 // operation produces one or more outputs, the derived class should call | 111 // operation produces one or more outputs, the derived class should call |
| 112 // the EnequeueResult() method from within this method. | 112 // the EnequeueResult() method from within this method. |
| 113 virtual void OnDecode(Buffer* input) = 0; | 113 virtual void OnDecode(Buffer* input) = 0; |
| 114 | 114 |
| 115 MediaFormat media_format_; | 115 MediaFormat media_format_; |
| 116 | 116 |
| 117 private: | 117 private: |
| 118 bool IsStopped() { return state_ == STOPPED; } | 118 bool IsStopped() { return state_ == kStopped; } |
| 119 | 119 |
| 120 void StopTask() { | 120 void StopTask() { |
| 121 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 121 DCHECK_EQ(MessageLoop::current(), this->message_loop()); |
| 122 | 122 |
| 123 // Delegate to the subclass first. | 123 // Delegate to the subclass first. |
| 124 OnStop(); | 124 OnStop(); |
| 125 | 125 |
| 126 // Throw away all buffers in all queues. | 126 // Throw away all buffers in all queues. |
| 127 result_queue_.clear(); | 127 result_queue_.clear(); |
| 128 STLDeleteElements(&read_queue_); | 128 STLDeleteElements(&read_queue_); |
| 129 state_ = STOPPED; | 129 state_ = kStopped; |
| 130 } | 130 } |
| 131 | 131 |
| 132 void SeekTask(base::TimeDelta time, FilterCallback* callback) { | 132 void SeekTask(base::TimeDelta time, FilterCallback* callback) { |
| 133 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 133 DCHECK_EQ(MessageLoop::current(), this->message_loop()); |
| 134 DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed"; |
| 135 DCHECK(read_queue_.empty()) << "Read requests should be empty"; |
| 134 scoped_ptr<FilterCallback> c(callback); | 136 scoped_ptr<FilterCallback> c(callback); |
| 135 | 137 |
| 136 // Delegate to the subclass first. | 138 // Delegate to the subclass first. |
| 139 // |
| 140 // 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 |
| 142 // either flush their buffers here or wait for IsDiscontinuous(). I'm |
| 143 // inclined to say that they should still wait for IsDiscontinuous() so they |
| 144 // don't have duplicated logic for Seek() and actual discontinuous frames. |
| 137 OnSeek(time); | 145 OnSeek(time); |
| 138 | 146 |
| 139 // Flush the result queue. | 147 // 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 |
| 149 // always be discontinuous. |
| 140 result_queue_.clear(); | 150 result_queue_.clear(); |
| 151 expecting_discontinuous_ = true; |
| 141 | 152 |
| 142 // Turn on the seeking flag so that we can discard buffers until a | 153 // Signal that we're done seeking. |
| 143 // discontinuous buffer is received. | |
| 144 seeking_ = true; | |
| 145 | |
| 146 // For now, signal that we're done seeking. | |
| 147 // TODO(scherkus): implement asynchronous seeking for decoder_base.h | |
| 148 callback->Run(); | 154 callback->Run(); |
| 149 } | 155 } |
| 150 | 156 |
| 151 void InitializeTask(DemuxerStream* demuxer_stream, FilterCallback* callback) { | 157 void InitializeTask(DemuxerStream* demuxer_stream, FilterCallback* callback) { |
| 152 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 158 DCHECK_EQ(MessageLoop::current(), this->message_loop()); |
| 153 DCHECK(state_ == UNINITIALIZED); | 159 CHECK(kUninitialized == state_); |
| 154 DCHECK(!demuxer_stream_); | 160 CHECK(!demuxer_stream_); |
| 155 scoped_ptr<FilterCallback> c(callback); | 161 scoped_ptr<FilterCallback> c(callback); |
| 156 demuxer_stream_ = demuxer_stream; | 162 demuxer_stream_ = demuxer_stream; |
| 157 | 163 |
| 158 // Delegate to subclass first. | 164 // Delegate to subclass first. |
| 159 if (!OnInitialize(demuxer_stream_)) { | 165 if (!OnInitialize(demuxer_stream_)) { |
| 160 this->host()->SetError(PIPELINE_ERROR_DECODE); | 166 this->host()->SetError(PIPELINE_ERROR_DECODE); |
| 161 callback->Run(); | 167 callback->Run(); |
| 162 return; | 168 return; |
| 163 } | 169 } |
| 164 | 170 |
| 165 // TODO(scherkus): subclass shouldn't mutate superclass media format. | 171 // TODO(scherkus): subclass shouldn't mutate superclass media format. |
| 166 DCHECK(!media_format_.empty()) << "Subclass did not set media_format_"; | 172 DCHECK(!media_format_.empty()) << "Subclass did not set media_format_"; |
| 167 state_ = INITIALIZED; | 173 state_ = kInitialized; |
| 168 callback->Run(); | 174 callback->Run(); |
| 169 } | 175 } |
| 170 | 176 |
| 171 void ReadTask(ReadCallback* read_callback) { | 177 void ReadTask(ReadCallback* read_callback) { |
| 172 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 178 DCHECK_EQ(MessageLoop::current(), this->message_loop()); |
| 173 | 179 |
| 174 // TODO(scherkus): should reply with a null operation (empty buffer). | 180 // TODO(scherkus): should reply with a null operation (empty buffer). |
| 175 if (IsStopped()) { | 181 if (IsStopped()) { |
| 176 delete read_callback; | 182 delete read_callback; |
| 177 return; | 183 return; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 189 } | 195 } |
| 190 | 196 |
| 191 void ReadCompleteTask(scoped_refptr<Buffer> buffer) { | 197 void ReadCompleteTask(scoped_refptr<Buffer> buffer) { |
| 192 DCHECK_EQ(MessageLoop::current(), this->message_loop()); | 198 DCHECK_EQ(MessageLoop::current(), this->message_loop()); |
| 193 DCHECK_GT(pending_reads_, 0u); | 199 DCHECK_GT(pending_reads_, 0u); |
| 194 --pending_reads_; | 200 --pending_reads_; |
| 195 if (IsStopped()) { | 201 if (IsStopped()) { |
| 196 return; | 202 return; |
| 197 } | 203 } |
| 198 | 204 |
| 199 // Once the |seeking_| flag is set we ignore every buffers here | 205 // TODO(scherkus): remove this when we're less paranoid about our seeking |
| 200 // until we receive a discontinuous buffer and we will turn off the | 206 // invariants. |
| 201 // |seeking_| flag. | |
| 202 if (buffer->IsDiscontinuous()) { | 207 if (buffer->IsDiscontinuous()) { |
| 203 // TODO(hclam): put a DCHECK here to assert |seeking_| being true. | 208 DCHECK(expecting_discontinuous_); |
| 204 // I cannot do this now because seek operation is not fully | 209 expecting_discontinuous_ = false; |
| 205 // asynchronous. There may be pending seek requests even before the | |
| 206 // previous was finished. | |
| 207 seeking_ = false; | |
| 208 } | |
| 209 if (seeking_) { | |
| 210 return; | |
| 211 } | 210 } |
| 212 | 211 |
| 213 // Decode the frame right away. | 212 // Decode the frame right away. |
| 214 OnDecode(buffer); | 213 OnDecode(buffer); |
| 215 | 214 |
| 216 // Attempt to fulfill a pending read callback and schedule additional reads | 215 // Attempt to fulfill a pending read callback and schedule additional reads |
| 217 // if necessary. | 216 // if necessary. |
| 218 FulfillPendingRead(); | 217 FulfillPendingRead(); |
| 219 | 218 |
| 220 // Issue reads as necessary. | 219 // Issue reads as necessary. |
| (...skipping 23 matching lines...) Expand all Loading... |
| 244 read_queue_.pop_front(); | 243 read_queue_.pop_front(); |
| 245 | 244 |
| 246 // Execute the callback! | 245 // Execute the callback! |
| 247 read_callback->Run(output); | 246 read_callback->Run(output); |
| 248 } | 247 } |
| 249 | 248 |
| 250 // Tracks the number of asynchronous reads issued to |demuxer_stream_|. | 249 // Tracks the number of asynchronous reads issued to |demuxer_stream_|. |
| 251 // Using size_t since it is always compared against deque::size(). | 250 // Using size_t since it is always compared against deque::size(). |
| 252 size_t pending_reads_; | 251 size_t pending_reads_; |
| 253 | 252 |
| 254 // An internal state of the decoder that indicates that are waiting for seek | 253 // A flag used for debugging that we expect our next read to be discontinuous. |
| 255 // to complete. We expect to receive a discontinuous frame/packet from the | 254 bool expecting_discontinuous_; |
| 256 // demuxer to signal that seeking is completed. | |
| 257 bool seeking_; | |
| 258 | 255 |
| 259 // Pointer to the demuxer stream that will feed us compressed buffers. | 256 // Pointer to the demuxer stream that will feed us compressed buffers. |
| 260 scoped_refptr<DemuxerStream> demuxer_stream_; | 257 scoped_refptr<DemuxerStream> demuxer_stream_; |
| 261 | 258 |
| 262 // Queue of decoded samples produced in the OnDecode() method of the decoder. | 259 // Queue of decoded samples produced in the OnDecode() method of the decoder. |
| 263 // Any samples placed in this queue will be assigned to the OutputQueue | 260 // Any samples placed in this queue will be assigned to the OutputQueue |
| 264 // buffers once the OnDecode() method returns. | 261 // buffers once the OnDecode() method returns. |
| 265 // | 262 // |
| 266 // TODO(ralphl): Eventually we want to have decoders get their destination | 263 // TODO(ralphl): Eventually we want to have decoders get their destination |
| 267 // buffer from the OutputQueue and write to it directly. Until we change | 264 // buffer from the OutputQueue and write to it directly. Until we change |
| 268 // from the Assignable buffer to callbacks and renderer-allocated buffers, | 265 // from the Assignable buffer to callbacks and renderer-allocated buffers, |
| 269 // we need this extra queue. | 266 // we need this extra queue. |
| 270 typedef std::deque<scoped_refptr<Output> > ResultQueue; | 267 typedef std::deque<scoped_refptr<Output> > ResultQueue; |
| 271 ResultQueue result_queue_; | 268 ResultQueue result_queue_; |
| 272 | 269 |
| 273 // Queue of callbacks supplied by the renderer through the Read() method. | 270 // Queue of callbacks supplied by the renderer through the Read() method. |
| 274 typedef std::deque<ReadCallback*> ReadQueue; | 271 typedef std::deque<ReadCallback*> ReadQueue; |
| 275 ReadQueue read_queue_; | 272 ReadQueue read_queue_; |
| 276 | 273 |
| 274 // Pause callback. |
| 275 scoped_ptr<FilterCallback> pause_callback_; |
| 276 |
| 277 // Simple state tracking variable. | 277 // Simple state tracking variable. |
| 278 enum State { | 278 enum State { |
| 279 UNINITIALIZED, | 279 kUninitialized, |
| 280 INITIALIZED, | 280 kInitialized, |
| 281 STOPPED, | 281 kStopped, |
| 282 }; | 282 }; |
| 283 State state_; | 283 State state_; |
| 284 | 284 |
| 285 DISALLOW_COPY_AND_ASSIGN(DecoderBase); | 285 DISALLOW_COPY_AND_ASSIGN(DecoderBase); |
| 286 }; | 286 }; |
| 287 | 287 |
| 288 } // namespace media | 288 } // namespace media |
| 289 | 289 |
| 290 #endif // MEDIA_FILTERS_DECODER_BASE_H_ | 290 #endif // MEDIA_FILTERS_DECODER_BASE_H_ |
| OLD | NEW |