| 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 | 
|---|