Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(589)

Side by Side Diff: media/filters/decoder_base.h

Issue 160005: Implemented proper pause-then-seek behaviour for the media pipeline. (Closed)
Patch Set: yay Created 11 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/filters/audio_renderer_base_unittest.cc ('k') | media/filters/ffmpeg_demuxer.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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_
OLDNEW
« no previous file with comments | « media/filters/audio_renderer_base_unittest.cc ('k') | media/filters/ffmpeg_demuxer.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698