| Index: media/filters/decoder_base.h
|
| diff --git a/media/filters/decoder_base.h b/media/filters/decoder_base.h
|
| index a2289581508887dee0bbf30b18ebd3a0cf22a976..cc403bcd11eb91b36fccb9159396e4ad83a076ad 100644
|
| --- a/media/filters/decoder_base.h
|
| +++ b/media/filters/decoder_base.h
|
| @@ -14,6 +14,7 @@
|
| #include "base/task.h"
|
| #include "base/thread.h"
|
| #include "media/base/buffers.h"
|
| +#include "media/base/callback.h"
|
| #include "media/base/filters.h"
|
| #include "media/base/filter_host.h"
|
|
|
| @@ -55,17 +56,6 @@ class DecoderBase : public Decoder {
|
| NewRunnableMethod(this, &DecoderBase::ReadTask, read_callback));
|
| }
|
|
|
| - void OnReadComplete(Buffer* buffer) {
|
| - // Little bit of magic here to get NewRunnableMethod() to generate a Task
|
| - // that holds onto a reference via scoped_refptr<>.
|
| - //
|
| - // TODO(scherkus): change the callback format to pass a scoped_refptr<> or
|
| - // better yet see if we can get away with not using reference counting.
|
| - scoped_refptr<Buffer> buffer_ref = buffer;
|
| - this->message_loop()->PostTask(FROM_HERE,
|
| - NewRunnableMethod(this, &DecoderBase::ReadCompleteTask, buffer_ref));
|
| - }
|
| -
|
| protected:
|
| DecoderBase()
|
| : pending_reads_(0),
|
| @@ -89,6 +79,12 @@ class DecoderBase : public Decoder {
|
| }
|
| }
|
|
|
| + // TODO(ajwong): All these "Task*" used as completion callbacks should be
|
| + // FilterCallbacks. However, since NewCallback() cannot prebind parameters,
|
| + // we use NewRunnableMethod() instead which causes an unfortunate refcount.
|
| + // We should move stoyan's Mutant code into base/task.h and turn these
|
| + // back into FilterCallbacks.
|
| +
|
| // Method that must be implemented by the derived class. Called from within
|
| // the DecoderBase::Initialize() method before any reads are submitted to
|
| // the demuxer stream. Returns true if successful, otherwise false indicates
|
| @@ -96,32 +92,46 @@ class DecoderBase : public Decoder {
|
| // InitializationComplete() method. If this method returns true, then the
|
| // base class will call the host to complete initialization. During this
|
| // call, the derived class must fill in the media_format_ member.
|
| - virtual bool OnInitialize(DemuxerStream* demuxer_stream) = 0;
|
| + virtual void DoInitialize(DemuxerStream* demuxer_stream, bool* success,
|
| + Task* done_cb) = 0;
|
|
|
| // Method that may be implemented by the derived class if desired. It will
|
| // be called from within the MediaFilter::Stop() method prior to stopping the
|
| // base class.
|
| - virtual void OnStop() {}
|
| + //
|
| + // TODO(ajwong): Make this asynchronous.
|
| + virtual void DoStop() {}
|
|
|
| // Derived class can implement this method and perform seeking logic prior
|
| // to the base class.
|
| - virtual void OnSeek(base::TimeDelta time) {}
|
| + virtual void DoSeek(base::TimeDelta time, Task* done_cb) = 0;
|
|
|
| // Method that must be implemented by the derived class. If the decode
|
| // operation produces one or more outputs, the derived class should call
|
| // the EnequeueResult() method from within this method.
|
| - virtual void OnDecode(Buffer* input) = 0;
|
| + virtual void DoDecode(Buffer* input, Task* done_cb) = 0;
|
|
|
| MediaFormat media_format_;
|
|
|
| private:
|
| bool IsStopped() { return state_ == kStopped; }
|
|
|
| + void OnReadComplete(Buffer* buffer) {
|
| + // Little bit of magic here to get NewRunnableMethod() to generate a Task
|
| + // that holds onto a reference via scoped_refptr<>.
|
| + //
|
| + // TODO(scherkus): change the callback format to pass a scoped_refptr<> or
|
| + // better yet see if we can get away with not using reference counting.
|
| + scoped_refptr<Buffer> buffer_ref = buffer;
|
| + this->message_loop()->PostTask(FROM_HERE,
|
| + NewRunnableMethod(this, &DecoderBase::ReadCompleteTask, buffer_ref));
|
| + }
|
| +
|
| void StopTask() {
|
| DCHECK_EQ(MessageLoop::current(), this->message_loop());
|
|
|
| // Delegate to the subclass first.
|
| - OnStop();
|
| + DoStop();
|
|
|
| // Throw away all buffers in all queues.
|
| result_queue_.clear();
|
| @@ -133,7 +143,6 @@ class DecoderBase : public Decoder {
|
| DCHECK_EQ(MessageLoop::current(), this->message_loop());
|
| DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed";
|
| DCHECK(read_queue_.empty()) << "Read requests should be empty";
|
| - scoped_ptr<FilterCallback> c(callback);
|
|
|
| // Delegate to the subclass first.
|
| //
|
| @@ -142,8 +151,11 @@ class DecoderBase : public Decoder {
|
| // either flush their buffers here or wait for IsDiscontinuous(). I'm
|
| // inclined to say that they should still wait for IsDiscontinuous() so they
|
| // don't have duplicated logic for Seek() and actual discontinuous frames.
|
| - OnSeek(time);
|
| + DoSeek(time,
|
| + NewRunnableMethod(this, &DecoderBase::OnSeekComplete, callback));
|
| + }
|
|
|
| + void OnSeekComplete(FilterCallback* callback) {
|
| // Flush our decoded results. We'll set a boolean that we can DCHECK to
|
| // verify our assertion that the first buffer received after a Seek() should
|
| // always be discontinuous.
|
| @@ -158,20 +170,30 @@ class DecoderBase : public Decoder {
|
| DCHECK_EQ(MessageLoop::current(), this->message_loop());
|
| CHECK(kUninitialized == state_);
|
| CHECK(!demuxer_stream_);
|
| - scoped_ptr<FilterCallback> c(callback);
|
| demuxer_stream_ = demuxer_stream;
|
|
|
| + bool* success = new bool;
|
| + DoInitialize(demuxer_stream,
|
| + success,
|
| + NewRunnableMethod(this, &DecoderBase::OnInitializeComplete,
|
| + success, callback));
|
| + }
|
| +
|
| + void OnInitializeComplete(bool* success, FilterCallback* done_cb) {
|
| + // Note: The done_runner must be declared *last* to ensure proper
|
| + // destruction order.
|
| + scoped_ptr<bool> success_deleter(success);
|
| + AutoCallbackRunner done_runner(done_cb);
|
| +
|
| + DCHECK_EQ(MessageLoop::current(), this->message_loop());
|
| // Delegate to subclass first.
|
| - if (!OnInitialize(demuxer_stream_)) {
|
| + if (!*success) {
|
| this->host()->SetError(PIPELINE_ERROR_DECODE);
|
| - callback->Run();
|
| - return;
|
| + } else {
|
| + // TODO(scherkus): subclass shouldn't mutate superclass media format.
|
| + DCHECK(!media_format_.empty()) << "Subclass did not set media_format_";
|
| + state_ = kInitialized;
|
| }
|
| -
|
| - // TODO(scherkus): subclass shouldn't mutate superclass media format.
|
| - DCHECK(!media_format_.empty()) << "Subclass did not set media_format_";
|
| - state_ = kInitialized;
|
| - callback->Run();
|
| }
|
|
|
| void ReadTask(ReadCallback* read_callback) {
|
| @@ -210,8 +232,10 @@ class DecoderBase : public Decoder {
|
| }
|
|
|
| // Decode the frame right away.
|
| - OnDecode(buffer);
|
| + DoDecode(buffer, NewRunnableMethod(this, &DecoderBase::OnDecodeComplete));
|
| + }
|
|
|
| + void OnDecodeComplete() {
|
| // Attempt to fulfill a pending read callback and schedule additional reads
|
| // if necessary.
|
| FulfillPendingRead();
|
|
|