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

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

Issue 465044: Refactor FFmpegVideoDecoder to try and generalize code common to all video decoders. (Closed)
Patch Set: Fix SCOPED_TRACE since VS faults on %zd. Created 11 years 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/ffmpeg/ffmpeg_util.cc ('k') | media/filters/ffmpeg_audio_decoder.h » ('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>
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
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
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
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_
OLDNEW
« no previous file with comments | « media/ffmpeg/ffmpeg_util.cc ('k') | media/filters/ffmpeg_audio_decoder.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698