diff options
author | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-19 15:28:33 +0000 |
---|---|---|
committer | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-19 15:28:33 +0000 |
commit | ddbc6ff92d9389a92033ced2bfc5b341a2218fc9 (patch) | |
tree | ff8b000cb6d7964fc248160b4f18f94cc77b5a86 | |
parent | f4f40a05cea2c55011677d70bf0cada2fa876017 (diff) | |
download | chromium_src-ddbc6ff92d9389a92033ced2bfc5b341a2218fc9.zip chromium_src-ddbc6ff92d9389a92033ced2bfc5b341a2218fc9.tar.gz chromium_src-ddbc6ff92d9389a92033ced2bfc5b341a2218fc9.tar.bz2 |
Reland: Remove reference counting from media::VideoDecoder and friends.
In addition:
* VideoRenderer is now passed a list of decoders via constructor instead of Initialize()
* WebMediaPlayerImpl's FilterCollection is now built in one shot instead of incrementally
BUG=173313
Review URL: https://chromiumcodereview.appspot.com/14348007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195212 0039d316-1c4b-4281-b951-d872f2087c98
33 files changed, 463 insertions, 474 deletions
diff --git a/media/base/filter_collection.cc b/media/base/filter_collection.cc index 148a192..911edd5 100644 --- a/media/base/filter_collection.cc +++ b/media/base/filter_collection.cc @@ -7,7 +7,6 @@ #include "base/logging.h" #include "media/base/audio_renderer.h" #include "media/base/demuxer.h" -#include "media/base/video_decoder.h" #include "media/base/video_renderer.h" namespace media { @@ -42,8 +41,4 @@ scoped_ptr<VideoRenderer> FilterCollection::GetVideoRenderer() { return video_renderer_.Pass(); } -FilterCollection::VideoDecoderList* FilterCollection::GetVideoDecoders() { - return &video_decoders_; -} - } // namespace media diff --git a/media/base/filter_collection.h b/media/base/filter_collection.h index e8b62f8..07266c6 100644 --- a/media/base/filter_collection.h +++ b/media/base/filter_collection.h @@ -5,8 +5,6 @@ #ifndef MEDIA_BASE_FILTER_COLLECTION_H_ #define MEDIA_BASE_FILTER_COLLECTION_H_ -#include <list> - #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "media/base/media_export.h" @@ -15,7 +13,6 @@ namespace media { class AudioRenderer; class Demuxer; -class VideoDecoder; class VideoRenderer; // Represents a set of uninitialized demuxer and audio/video decoders and @@ -25,8 +22,6 @@ class VideoRenderer; // http://crbug.com/110800 class MEDIA_EXPORT FilterCollection { public: - typedef std::list<scoped_refptr<VideoDecoder> > VideoDecoderList; - FilterCollection(); ~FilterCollection(); @@ -39,11 +34,8 @@ class MEDIA_EXPORT FilterCollection { void SetVideoRenderer(scoped_ptr<VideoRenderer> video_renderer); scoped_ptr<VideoRenderer> GetVideoRenderer(); - VideoDecoderList* GetVideoDecoders(); - private: scoped_refptr<Demuxer> demuxer_; - VideoDecoderList video_decoders_; scoped_ptr<AudioRenderer> audio_renderer_; scoped_ptr<VideoRenderer> video_renderer_; diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h index d0430a6..56b523a 100644 --- a/media/base/mock_filters.h +++ b/media/base/mock_filters.h @@ -109,16 +109,15 @@ class MockVideoRenderer : public VideoRenderer { virtual ~MockVideoRenderer(); // VideoRenderer implementation. - MOCK_METHOD10(Initialize, void(const scoped_refptr<DemuxerStream>& stream, - const VideoDecoderList& decoders, - const PipelineStatusCB& init_cb, - const StatisticsCB& statistics_cb, - const TimeCB& time_cb, - const NaturalSizeChangedCB& size_changed_cb, - const base::Closure& ended_cb, - const PipelineStatusCB& error_cb, - const TimeDeltaCB& get_time_cb, - const TimeDeltaCB& get_duration_cb)); + MOCK_METHOD9(Initialize, void(const scoped_refptr<DemuxerStream>& stream, + const PipelineStatusCB& init_cb, + const StatisticsCB& statistics_cb, + const TimeCB& time_cb, + const NaturalSizeChangedCB& size_changed_cb, + const base::Closure& ended_cb, + const PipelineStatusCB& error_cb, + const TimeDeltaCB& get_time_cb, + const TimeDeltaCB& get_duration_cb)); MOCK_METHOD1(Play, void(const base::Closure& callback)); MOCK_METHOD1(Pause, void(const base::Closure& callback)); MOCK_METHOD1(Flush, void(const base::Closure& callback)); diff --git a/media/base/pipeline.cc b/media/base/pipeline.cc index e656ef3..5ee835b 100644 --- a/media/base/pipeline.cc +++ b/media/base/pipeline.cc @@ -946,7 +946,6 @@ void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) { video_renderer_ = filter_collection_->GetVideoRenderer(); video_renderer_->Initialize( stream, - *filter_collection_->GetVideoDecoders(), done_cb, base::Bind(&Pipeline::OnUpdateStatistics, this), base::Bind(&Pipeline::OnVideoTimeUpdate, this), @@ -955,7 +954,6 @@ void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) { base::Bind(&Pipeline::SetError, this), base::Bind(&Pipeline::GetMediaTime, this), base::Bind(&Pipeline::GetMediaDuration, this)); - filter_collection_->GetVideoDecoders()->clear(); } void Pipeline::OnAudioUnderflow() { diff --git a/media/base/pipeline_unittest.cc b/media/base/pipeline_unittest.cc index 59d768b..988bb80 100644 --- a/media/base/pipeline_unittest.cc +++ b/media/base/pipeline_unittest.cc @@ -87,9 +87,6 @@ class PipelineTest : public ::testing::Test { demuxer_ = new MockDemuxer(); filter_collection_->SetDemuxer(demuxer_); - video_decoder_ = new MockVideoDecoder(); - filter_collection_->GetVideoDecoders()->push_back(video_decoder_); - video_renderer_ = new MockVideoRenderer(); scoped_ptr<VideoRenderer> video_renderer(video_renderer_); filter_collection_->SetVideoRenderer(video_renderer.Pass()); @@ -166,14 +163,12 @@ class PipelineTest : public ::testing::Test { // Sets up expectations to allow the video renderer to initialize. void InitializeVideoRenderer(const scoped_refptr<DemuxerStream>& stream) { - EXPECT_CALL(*video_renderer_, - Initialize(stream, _, _, _, _, _, _, _, _, _)) - .WillOnce(RunCallback<2>(PIPELINE_OK)); + EXPECT_CALL(*video_renderer_, Initialize(stream, _, _, _, _, _, _, _, _)) + .WillOnce(RunCallback<1>(PIPELINE_OK)); EXPECT_CALL(*video_renderer_, SetPlaybackRate(0.0f)); // Startup sequence. - EXPECT_CALL(*video_renderer_, - Preroll(demuxer_->GetStartTime(), _)) + EXPECT_CALL(*video_renderer_, Preroll(demuxer_->GetStartTime(), _)) .WillOnce(RunCallback<1>(PIPELINE_OK)); EXPECT_CALL(*video_renderer_, Play(_)) .WillOnce(RunClosure<0>()); @@ -301,7 +296,6 @@ class PipelineTest : public ::testing::Test { scoped_ptr<FilterCollection> filter_collection_; scoped_refptr<MockDemuxer> demuxer_; - scoped_refptr<MockVideoDecoder> video_decoder_; MockVideoRenderer* video_renderer_; MockAudioRenderer* audio_renderer_; scoped_refptr<StrictMock<MockDemuxerStream> > audio_stream_; @@ -1016,16 +1010,14 @@ class PipelineTeardownTest : public PipelineTest { if (state == kInitVideoRenderer) { if (stop_or_error == kStop) { - EXPECT_CALL(*video_renderer_, - Initialize(_, _, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _)) .WillOnce(DoAll(Stop(pipeline_, stop_cb), - RunCallback<2>(PIPELINE_OK))); + RunCallback<1>(PIPELINE_OK))); EXPECT_CALL(callbacks_, OnStop()); } else { status = PIPELINE_ERROR_INITIALIZATION_FAILED; - EXPECT_CALL(*video_renderer_, - Initialize(_, _, _, _, _, _, _, _, _, _)) - .WillOnce(RunCallback<2>(status)); + EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _)) + .WillOnce(RunCallback<1>(status)); } EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(RunClosure<0>()); @@ -1034,9 +1026,8 @@ class PipelineTeardownTest : public PipelineTest { return status; } - EXPECT_CALL(*video_renderer_, - Initialize(_, _, _, _, _, _, _, _, _, _)) - .WillOnce(RunCallback<2>(PIPELINE_OK)); + EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _)) + .WillOnce(RunCallback<1>(PIPELINE_OK)); EXPECT_CALL(callbacks_, OnBufferingState(Pipeline::kHaveMetadata)); diff --git a/media/base/video_decoder.h b/media/base/video_decoder.h index 97566da..4611645 100644 --- a/media/base/video_decoder.h +++ b/media/base/video_decoder.h @@ -16,8 +16,7 @@ namespace media { class DemuxerStream; class VideoFrame; -class MEDIA_EXPORT VideoDecoder - : public base::RefCountedThreadSafe<VideoDecoder> { +class MEDIA_EXPORT VideoDecoder { public: // Status codes for read operations on VideoDecoder. enum Status { @@ -26,6 +25,9 @@ class MEDIA_EXPORT VideoDecoder kDecryptError // Decrypting error happened. }; + VideoDecoder(); + virtual ~VideoDecoder(); + // Initializes a VideoDecoder with the given DemuxerStream, executing the // |status_cb| upon completion. // |statistics_cb| is used to update the global pipeline statistics. @@ -77,11 +79,7 @@ class MEDIA_EXPORT VideoDecoder // use a fixed set of VideoFrames for decoding. virtual bool HasOutputFrameAvailable() const; - protected: - friend class base::RefCountedThreadSafe<VideoDecoder>; - virtual ~VideoDecoder(); - VideoDecoder(); - + private: DISALLOW_COPY_AND_ASSIGN(VideoDecoder); }; diff --git a/media/base/video_renderer.h b/media/base/video_renderer.h index c609b68..9d42d14 100644 --- a/media/base/video_renderer.h +++ b/media/base/video_renderer.h @@ -5,8 +5,6 @@ #ifndef MEDIA_BASE_VIDEO_RENDERER_H_ #define MEDIA_BASE_VIDEO_RENDERER_H_ -#include <list> - #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/time.h" @@ -24,8 +22,6 @@ class VideoDecoder; class MEDIA_EXPORT VideoRenderer { public: - typedef std::list<scoped_refptr<VideoDecoder> > VideoDecoderList; - // Used to update the pipeline's clock time. The parameter is the time that // the clock should not exceed. typedef base::Callback<void(base::TimeDelta)> TimeCB; @@ -39,8 +35,8 @@ class MEDIA_EXPORT VideoRenderer { VideoRenderer(); virtual ~VideoRenderer(); - // Initialize a VideoRenderer with the given DemuxerStream and - // VideoDecoderList, executing |init_cb| callback upon completion. + // Initialize a VideoRenderer with |stream|, executing |init_cb| upon + // completion. // // |statistics_cb| is executed periodically with video rendering stats, such // as dropped frames. @@ -58,7 +54,6 @@ class MEDIA_EXPORT VideoRenderer { // // |get_duration_cb| is used to query the media duration. virtual void Initialize(const scoped_refptr<DemuxerStream>& stream, - const VideoDecoderList& decoders, const PipelineStatusCB& init_cb, const StatisticsCB& statistics_cb, const TimeCB& time_cb, diff --git a/media/filters/decrypting_video_decoder.cc b/media/filters/decrypting_video_decoder.cc index 6fe6e69..6179fd7 100644 --- a/media/filters/decrypting_video_decoder.cc +++ b/media/filters/decrypting_video_decoder.cc @@ -24,6 +24,7 @@ DecryptingVideoDecoder::DecryptingVideoDecoder( const scoped_refptr<base::MessageLoopProxy>& message_loop, const SetDecryptorReadyCB& set_decryptor_ready_cb) : message_loop_(message_loop), + weak_factory_(this), state_(kUninitialized), set_decryptor_ready_cb_(set_decryptor_ready_cb), decryptor_(NULL), @@ -40,6 +41,7 @@ void DecryptingVideoDecoder::Initialize( DCHECK_EQ(state_, kUninitialized) << state_; DCHECK(stream); init_cb_ = BindToCurrentLoop(status_cb); + weak_this_ = weak_factory_.GetWeakPtr(); const VideoDecoderConfig& config = stream->video_decoder_config(); if (!config.IsValidConfig()) { @@ -61,7 +63,7 @@ void DecryptingVideoDecoder::Initialize( state_ = kDecryptorRequested; set_decryptor_ready_cb_.Run(BindToCurrentLoop(base::Bind( - &DecryptingVideoDecoder::SetDecryptor, this))); + &DecryptingVideoDecoder::SetDecryptor, weak_this_))); } void DecryptingVideoDecoder::Read(const ReadCB& read_cb) { @@ -166,7 +168,7 @@ void DecryptingVideoDecoder::SetDecryptor(Decryptor* decryptor) { state_ = kPendingDecoderInit; decryptor_->InitializeVideoDecoder( demuxer_stream_->video_decoder_config(), BindToCurrentLoop(base::Bind( - &DecryptingVideoDecoder::FinishInitialization, this))); + &DecryptingVideoDecoder::FinishInitialization, weak_this_))); } void DecryptingVideoDecoder::FinishInitialization(bool success) { @@ -188,7 +190,7 @@ void DecryptingVideoDecoder::FinishInitialization(bool success) { } decryptor_->RegisterNewKeyCB(Decryptor::kVideo, BindToCurrentLoop( - base::Bind(&DecryptingVideoDecoder::OnKeyAdded, this))); + base::Bind(&DecryptingVideoDecoder::OnKeyAdded, weak_this_))); // Success! state_ = kIdle; @@ -230,7 +232,7 @@ void DecryptingVideoDecoder::ReadFromDemuxerStream() { DCHECK(!read_cb_.is_null()); demuxer_stream_->Read( - base::Bind(&DecryptingVideoDecoder::DecryptAndDecodeBuffer, this)); + base::Bind(&DecryptingVideoDecoder::DecryptAndDecodeBuffer, weak_this_)); } void DecryptingVideoDecoder::DecryptAndDecodeBuffer( @@ -253,7 +255,7 @@ void DecryptingVideoDecoder::DecryptAndDecodeBuffer( decryptor_->DeinitializeDecoder(Decryptor::kVideo); decryptor_->InitializeVideoDecoder( demuxer_stream_->video_decoder_config(), BindToCurrentLoop(base::Bind( - &DecryptingVideoDecoder::FinishConfigChange, this))); + &DecryptingVideoDecoder::FinishConfigChange, weak_this_))); return; } @@ -289,7 +291,7 @@ void DecryptingVideoDecoder::DecodePendingBuffer() { decryptor_->DecryptAndDecodeVideo( pending_buffer_to_decode_, BindToCurrentLoop(base::Bind( - &DecryptingVideoDecoder::DeliverFrame, this, buffer_size))); + &DecryptingVideoDecoder::DeliverFrame, weak_this_, buffer_size))); } void DecryptingVideoDecoder::DeliverFrame( diff --git a/media/filters/decrypting_video_decoder.h b/media/filters/decrypting_video_decoder.h index 9da57a1..eeb4210 100644 --- a/media/filters/decrypting_video_decoder.h +++ b/media/filters/decrypting_video_decoder.h @@ -6,7 +6,7 @@ #define MEDIA_FILTERS_DECRYPTING_VIDEO_DECODER_H_ #include "base/callback.h" -#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "media/base/decryptor.h" #include "media/base/demuxer_stream.h" #include "media/base/video_decoder.h" @@ -34,6 +34,7 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder { DecryptingVideoDecoder( const scoped_refptr<base::MessageLoopProxy>& message_loop, const SetDecryptorReadyCB& set_decryptor_ready_cb); + virtual ~DecryptingVideoDecoder(); // VideoDecoder implementation. virtual void Initialize(const scoped_refptr<DemuxerStream>& stream, @@ -43,9 +44,6 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder { virtual void Reset(const base::Closure& closure) OVERRIDE; virtual void Stop(const base::Closure& closure) OVERRIDE; - protected: - virtual ~DecryptingVideoDecoder(); - private: // For a detailed state diagram please see this link: http://goo.gl/8jAok // TODO(xhwang): Add a ASCII state diagram in this file after this class @@ -96,6 +94,8 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder { void DoStop(); scoped_refptr<base::MessageLoopProxy> message_loop_; + base::WeakPtrFactory<DecryptingVideoDecoder> weak_factory_; + base::WeakPtr<DecryptingVideoDecoder> weak_this_; State state_; diff --git a/media/filters/decrypting_video_decoder_unittest.cc b/media/filters/decrypting_video_decoder_unittest.cc index 9d8462e..140206f 100644 --- a/media/filters/decrypting_video_decoder_unittest.cc +++ b/media/filters/decrypting_video_decoder_unittest.cc @@ -235,7 +235,7 @@ class DecryptingVideoDecoderTest : public testing::Test { const scoped_refptr<VideoFrame>&)); MessageLoop message_loop_; - scoped_refptr<DecryptingVideoDecoder> decoder_; + scoped_ptr<DecryptingVideoDecoder> decoder_; scoped_ptr<StrictMock<MockDecryptor> > decryptor_; scoped_refptr<StrictMock<MockDemuxerStream> > demuxer_; MockStatisticsCB statistics_cb_; diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc index 9dd4058..105199f 100644 --- a/media/filters/ffmpeg_video_decoder.cc +++ b/media/filters/ffmpeg_video_decoder.cc @@ -58,6 +58,7 @@ static int GetThreadCount(CodecID codec_id) { FFmpegVideoDecoder::FFmpegVideoDecoder( const scoped_refptr<base::MessageLoopProxy>& message_loop) : message_loop_(message_loop), + weak_factory_(this), state_(kUninitialized), codec_context_(NULL), av_frame_(NULL) { @@ -134,6 +135,7 @@ void FFmpegVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, const StatisticsCB& statistics_cb) { DCHECK(message_loop_->BelongsToCurrentThread()); PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); + weak_this_ = weak_factory_.GetWeakPtr(); FFmpegGlue::InitializeFFmpeg(); DCHECK(!demuxer_stream_) << "Already initialized."; @@ -224,7 +226,8 @@ void FFmpegVideoDecoder::ReturnFrameOrReadFromDemuxerStream() { DCHECK_NE(state_, kUninitialized); DCHECK_NE(state_, kDecodeFinished); DCHECK(!read_cb_.is_null()); - demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::BufferReady, this)); + demuxer_stream_->Read(base::Bind( + &FFmpegVideoDecoder::BufferReady, weak_this_)); } void FFmpegVideoDecoder::BufferReady( diff --git a/media/filters/ffmpeg_video_decoder.h b/media/filters/ffmpeg_video_decoder.h index 4c6629e..de83814 100644 --- a/media/filters/ffmpeg_video_decoder.h +++ b/media/filters/ffmpeg_video_decoder.h @@ -8,7 +8,7 @@ #include <list> #include "base/callback.h" -#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "media/base/demuxer_stream.h" #include "media/base/video_decoder.h" @@ -27,6 +27,7 @@ class MEDIA_EXPORT FFmpegVideoDecoder : public VideoDecoder { public: explicit FFmpegVideoDecoder( const scoped_refptr<base::MessageLoopProxy>& message_loop); + virtual ~FFmpegVideoDecoder(); // VideoDecoder implementation. virtual void Initialize(const scoped_refptr<DemuxerStream>& stream, @@ -41,9 +42,6 @@ class MEDIA_EXPORT FFmpegVideoDecoder : public VideoDecoder { // documentation inside FFmpeg. int GetVideoBuffer(AVCodecContext *codec_context, AVFrame* frame); - protected: - virtual ~FFmpegVideoDecoder(); - private: enum DecoderState { kUninitialized, @@ -74,6 +72,8 @@ class MEDIA_EXPORT FFmpegVideoDecoder : public VideoDecoder { void DoReset(); scoped_refptr<base::MessageLoopProxy> message_loop_; + base::WeakPtrFactory<FFmpegVideoDecoder> weak_factory_; + base::WeakPtr<FFmpegVideoDecoder> weak_this_; DecoderState state_; diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc index 1aa1de33..863c317 100644 --- a/media/filters/ffmpeg_video_decoder_unittest.cc +++ b/media/filters/ffmpeg_video_decoder_unittest.cc @@ -50,14 +50,12 @@ ACTION_P(ReturnBuffer, buffer) { class FFmpegVideoDecoderTest : public testing::Test { public: FFmpegVideoDecoderTest() - : decoder_(NULL), + : decoder_(new FFmpegVideoDecoder(message_loop_.message_loop_proxy())), demuxer_(new StrictMock<MockDemuxerStream>()), read_cb_(base::Bind(&FFmpegVideoDecoderTest::FrameReady, base::Unretained(this))) { FFmpegGlue::InitializeFFmpeg(); - decoder_ = new FFmpegVideoDecoder(message_loop_.message_loop_proxy()); - // Initialize various test buffers. frame_buffer_.reset(new uint8[kCodedSize.GetArea()]); end_of_stream_buffer_ = DecoderBuffer::CreateEOSBuffer(); @@ -201,7 +199,7 @@ class FFmpegVideoDecoderTest : public testing::Test { const scoped_refptr<VideoFrame>&)); MessageLoop message_loop_; - scoped_refptr<FFmpegVideoDecoder> decoder_; + scoped_ptr<FFmpegVideoDecoder> decoder_; scoped_refptr<StrictMock<MockDemuxerStream> > demuxer_; MockStatisticsCB statistics_cb_; VideoDecoderConfig config_; diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc index a2fc662..555164e 100644 --- a/media/filters/gpu_video_decoder.cc +++ b/media/filters/gpu_video_decoder.cc @@ -9,6 +9,7 @@ #include "base/cpu.h" #include "base/message_loop.h" #include "base/stl_util.h" +#include "base/task_runner_util.h" #include "media/base/bind_to_loop.h" #include "media/base/decoder_buffer.h" #include "media/base/demuxer_stream.h" @@ -18,6 +19,112 @@ namespace media { +// Proxies calls to a VideoDecodeAccelerator::Client from the calling thread to +// the client's thread. +// +// TODO(scherkus): VDAClientProxy should hold onto GpuVideoDecoder::Factories +// and take care of some of the work that GpuVideoDecoder does to minimize +// thread hopping. See following for discussion: +// +// https://codereview.chromium.org/12989009/diff/27035/media/filters/gpu_video_decoder.cc#newcode23 +class VDAClientProxy + : public base::RefCountedThreadSafe<VDAClientProxy>, + public VideoDecodeAccelerator::Client { + public: + explicit VDAClientProxy(VideoDecodeAccelerator::Client* client); + + // Detaches the proxy. |weak_client_| will no longer be called and can be + // safely deleted. Any pending/future calls will be discarded. + // + // Must be called on |client_loop_|. + void Detach(); + + // VideoDecodeAccelerator::Client implementation. + virtual void NotifyInitializeDone() OVERRIDE; + virtual void ProvidePictureBuffers(uint32 count, + const gfx::Size& size, + uint32 texture_target) OVERRIDE; + virtual void DismissPictureBuffer(int32 id) OVERRIDE; + virtual void PictureReady(const media::Picture& picture) OVERRIDE; + virtual void NotifyEndOfBitstreamBuffer(int32 id) OVERRIDE; + virtual void NotifyFlushDone() OVERRIDE; + virtual void NotifyResetDone() OVERRIDE; + virtual void NotifyError(media::VideoDecodeAccelerator::Error error) OVERRIDE; + + private: + friend class base::RefCountedThreadSafe<VDAClientProxy>; + virtual ~VDAClientProxy(); + + scoped_refptr<base::MessageLoopProxy> client_loop_; + + // Weak pointers are used to invalidate tasks posted to |client_loop_| after + // Detach() has been called. + base::WeakPtrFactory<VideoDecodeAccelerator::Client> weak_client_factory_; + base::WeakPtr<VideoDecodeAccelerator::Client> weak_client_; + + DISALLOW_COPY_AND_ASSIGN(VDAClientProxy); +}; + +VDAClientProxy::VDAClientProxy(VideoDecodeAccelerator::Client* client) + : client_loop_(base::MessageLoopProxy::current()), + weak_client_factory_(client), + weak_client_(weak_client_factory_.GetWeakPtr()) { + DCHECK(weak_client_); +} + +VDAClientProxy::~VDAClientProxy() {} + +void VDAClientProxy::Detach() { + DCHECK(client_loop_->BelongsToCurrentThread()); + DCHECK(weak_client_) << "Detach() already called"; + weak_client_factory_.InvalidateWeakPtrs(); +} + +void VDAClientProxy::NotifyInitializeDone() { + client_loop_->PostTask(FROM_HERE, base::Bind( + &VideoDecodeAccelerator::Client::NotifyInitializeDone, weak_client_)); +} + +void VDAClientProxy::ProvidePictureBuffers(uint32 count, + const gfx::Size& size, + uint32 texture_target) { + client_loop_->PostTask(FROM_HERE, base::Bind( + &VideoDecodeAccelerator::Client::ProvidePictureBuffers, weak_client_, + count, size, texture_target)); +} + +void VDAClientProxy::DismissPictureBuffer(int32 id) { + client_loop_->PostTask(FROM_HERE, base::Bind( + &VideoDecodeAccelerator::Client::DismissPictureBuffer, weak_client_, id)); +} + +void VDAClientProxy::PictureReady(const media::Picture& picture) { + client_loop_->PostTask(FROM_HERE, base::Bind( + &VideoDecodeAccelerator::Client::PictureReady, weak_client_, picture)); +} + +void VDAClientProxy::NotifyEndOfBitstreamBuffer(int32 id) { + client_loop_->PostTask(FROM_HERE, base::Bind( + &VideoDecodeAccelerator::Client::NotifyEndOfBitstreamBuffer, weak_client_, + id)); +} + +void VDAClientProxy::NotifyFlushDone() { + client_loop_->PostTask(FROM_HERE, base::Bind( + &VideoDecodeAccelerator::Client::NotifyFlushDone, weak_client_)); +} + +void VDAClientProxy::NotifyResetDone() { + client_loop_->PostTask(FROM_HERE, base::Bind( + &VideoDecodeAccelerator::Client::NotifyResetDone, weak_client_)); +} + +void VDAClientProxy::NotifyError(media::VideoDecodeAccelerator::Error error) { + client_loop_->PostTask(FROM_HERE, base::Bind( + &VideoDecodeAccelerator::Client::NotifyError, weak_client_, error)); +} + + // Maximum number of concurrent VDA::Decode() operations GVD will maintain. // Higher values allow better pipelining in the GPU, but also require more // resources. @@ -54,6 +161,7 @@ GpuVideoDecoder::GpuVideoDecoder( const scoped_refptr<base::MessageLoopProxy>& message_loop, const scoped_refptr<Factories>& factories) : gvd_loop_proxy_(message_loop), + weak_factory_(this), vda_loop_proxy_(factories->GetMessageLoop()), factories_(factories), state_(kNormal), @@ -71,7 +179,7 @@ void GpuVideoDecoder::Reset(const base::Closure& closure) { if (state_ == kDrainingDecoder && !factories_->IsAborted()) { gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( - &GpuVideoDecoder::Reset, this, closure)); + &GpuVideoDecoder::Reset, weak_this_, closure)); // NOTE: if we're deferring Reset() until a Flush() completes, return // queued pictures to the VDA so they can be used to finish that Flush(). if (pending_read_cb_.is_null()) @@ -113,6 +221,8 @@ void GpuVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, const PipelineStatusCB& orig_status_cb, const StatisticsCB& statistics_cb) { DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); + weak_this_ = weak_factory_.GetWeakPtr(); + PipelineStatusCB status_cb = CreateUMAReportingPipelineCB( "Media.GpuVideoDecoderInitializeStatus", BindToCurrentLoop(orig_status_cb)); @@ -150,8 +260,9 @@ void GpuVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, } } + client_proxy_ = new VDAClientProxy(this); VideoDecodeAccelerator* vda = - factories_->CreateVideoDecodeAccelerator(config.profile(), this); + factories_->CreateVideoDecodeAccelerator(config.profile(), client_proxy_); if (!vda) { status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); return; @@ -164,17 +275,21 @@ void GpuVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, statistics_cb_ = statistics_cb; DVLOG(1) << "GpuVideoDecoder::Initialize() succeeded."; - vda_loop_proxy_->PostTaskAndReply( - FROM_HERE, - base::Bind(&GpuVideoDecoder::SetVDA, this, vda), - base::Bind(status_cb, PIPELINE_OK)); + PostTaskAndReplyWithResult( + vda_loop_proxy_, FROM_HERE, + base::Bind(&VideoDecodeAccelerator::AsWeakPtr, base::Unretained(vda)), + base::Bind(&GpuVideoDecoder::SetVDA, weak_this_, status_cb, vda)); } -void GpuVideoDecoder::SetVDA(VideoDecodeAccelerator* vda) { - DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); +void GpuVideoDecoder::SetVDA( + const PipelineStatusCB& status_cb, + VideoDecodeAccelerator* vda, + base::WeakPtr<VideoDecodeAccelerator> weak_vda) { + DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); DCHECK(!vda_.get()); vda_.reset(vda); - weak_vda_ = vda->AsWeakPtr(); + weak_vda_ = weak_vda; + status_cb.Run(PIPELINE_OK); } void GpuVideoDecoder::DestroyTextures() { @@ -186,17 +301,25 @@ void GpuVideoDecoder::DestroyTextures() { picture_buffers_in_decoder_.clear(); } +static void DestroyVDAWithClientProxy( + const scoped_refptr<VDAClientProxy>& client_proxy, + base::WeakPtr<VideoDecodeAccelerator> weak_vda) { + if (weak_vda) { + weak_vda->Destroy(); + DCHECK(!weak_vda); // Check VDA::Destroy() contract. + } +} + void GpuVideoDecoder::DestroyVDA() { DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); + + // |client_proxy| must stay alive until |weak_vda_| has been destroyed. + vda_loop_proxy_->PostTask(FROM_HERE, base::Bind( + &DestroyVDAWithClientProxy, client_proxy_, weak_vda_)); + VideoDecodeAccelerator* vda ALLOW_UNUSED = vda_.release(); - // Tricky: |this| needs to stay alive until after VDA::Destroy is actually - // called, not just posted, so we take an artificial ref to |this| and release - // it as |reply| after VDA::Destroy() returns. - AddRef(); - vda_loop_proxy_->PostTaskAndReply( - FROM_HERE, - base::Bind(&VideoDecodeAccelerator::Destroy, weak_vda_), - base::Bind(&GpuVideoDecoder::Release, this)); + client_proxy_->Detach(); + client_proxy_ = NULL; DestroyTextures(); } @@ -244,13 +367,9 @@ bool GpuVideoDecoder::CanMoreDecodeWorkBeDone() { void GpuVideoDecoder::RequestBufferDecode( DemuxerStream::Status status, const scoped_refptr<DecoderBuffer>& buffer) { + DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; - if (!gvd_loop_proxy_->BelongsToCurrentThread()) { - gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( - &GpuVideoDecoder::RequestBufferDecode, this, status, buffer)); - return; - } demuxer_read_in_progress_ = false; if (status != DemuxerStream::kOk) { @@ -305,7 +424,7 @@ void GpuVideoDecoder::RequestBufferDecode( if (CanMoreDecodeWorkBeDone()) { // Force post here to prevent reentrancy into DemuxerStream. gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( - &GpuVideoDecoder::EnsureDemuxOrDecode, this)); + &GpuVideoDecoder::EnsureDemuxOrDecode, weak_this_)); } } @@ -358,12 +477,7 @@ void GpuVideoDecoder::NotifyInitializeDone() { void GpuVideoDecoder::ProvidePictureBuffers(uint32 count, const gfx::Size& size, uint32 texture_target) { - if (!gvd_loop_proxy_->BelongsToCurrentThread()) { - gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( - &GpuVideoDecoder::ProvidePictureBuffers, this, count, size, - texture_target)); - return; - } + DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); std::vector<uint32> texture_ids; decoder_texture_target_ = texture_target; @@ -393,11 +507,8 @@ void GpuVideoDecoder::ProvidePictureBuffers(uint32 count, } void GpuVideoDecoder::DismissPictureBuffer(int32 id) { - if (!gvd_loop_proxy_->BelongsToCurrentThread()) { - gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( - &GpuVideoDecoder::DismissPictureBuffer, this, id)); - return; - } + DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); + std::map<int32, PictureBuffer>::iterator it = picture_buffers_in_decoder_.find(id); if (it == picture_buffers_in_decoder_.end()) { @@ -409,11 +520,8 @@ void GpuVideoDecoder::DismissPictureBuffer(int32 id) { } void GpuVideoDecoder::PictureReady(const media::Picture& picture) { - if (!gvd_loop_proxy_->BelongsToCurrentThread()) { - gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( - &GpuVideoDecoder::PictureReady, this, picture)); - return; - } + DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); + std::map<int32, PictureBuffer>::iterator it = picture_buffers_in_decoder_.find(picture.picture_buffer_id()); if (it == picture_buffers_in_decoder_.end()) { @@ -437,8 +545,9 @@ void GpuVideoDecoder::PictureReady(const media::Picture& picture) { base::Bind(&Factories::ReadPixels, factories_, pb.texture_id(), decoder_texture_target_, gfx::Size(visible_rect.width(), visible_rect.height())), - base::Bind(&GpuVideoDecoder::ReusePictureBuffer, this, - picture.picture_buffer_id()))); + BindToCurrentLoop(base::Bind( + &GpuVideoDecoder::ReusePictureBuffer, weak_this_, + picture.picture_buffer_id())))); CHECK_GT(available_pictures_, 0); available_pictures_--; @@ -467,11 +576,7 @@ void GpuVideoDecoder::EnqueueFrameAndTriggerFrameDelivery( } void GpuVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id) { - if (!gvd_loop_proxy_->BelongsToCurrentThread()) { - gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( - &GpuVideoDecoder::ReusePictureBuffer, this, picture_buffer_id)); - return; - } + DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); CHECK_GE(available_pictures_, 0); available_pictures_++; @@ -504,11 +609,7 @@ void GpuVideoDecoder::PutSHM(SHMBuffer* shm_buffer) { } void GpuVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) { - if (!gvd_loop_proxy_->BelongsToCurrentThread()) { - gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( - &GpuVideoDecoder::NotifyEndOfBitstreamBuffer, this, id)); - return; - } + DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); std::map<int32, BufferPair>::iterator it = bitstream_buffers_in_decoder_.find(id); @@ -563,27 +664,18 @@ void GpuVideoDecoder::EnsureDemuxOrDecode() { demuxer_read_in_progress_ = true; demuxer_stream_->Read(base::Bind( - &GpuVideoDecoder::RequestBufferDecode, this)); + &GpuVideoDecoder::RequestBufferDecode, weak_this_)); } void GpuVideoDecoder::NotifyFlushDone() { - if (!gvd_loop_proxy_->BelongsToCurrentThread()) { - gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( - &GpuVideoDecoder::NotifyFlushDone, this)); - return; - } + DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); DCHECK_EQ(state_, kDrainingDecoder); state_ = kDecoderDrained; EnqueueFrameAndTriggerFrameDelivery(VideoFrame::CreateEmptyFrame()); } void GpuVideoDecoder::NotifyResetDone() { - if (!gvd_loop_proxy_->BelongsToCurrentThread()) { - gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( - &GpuVideoDecoder::NotifyResetDone, this)); - return; - } - + DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); DCHECK(ready_video_frames_.empty()); // This needs to happen after the Reset() on vda_ is done to ensure pictures @@ -598,11 +690,7 @@ void GpuVideoDecoder::NotifyResetDone() { } void GpuVideoDecoder::NotifyError(media::VideoDecodeAccelerator::Error error) { - if (!gvd_loop_proxy_->BelongsToCurrentThread()) { - gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( - &GpuVideoDecoder::NotifyError, this, error)); - return; - } + DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); if (!vda_.get()) return; diff --git a/media/filters/gpu_video_decoder.h b/media/filters/gpu_video_decoder.h index 6acc2de..c2defb3 100644 --- a/media/filters/gpu_video_decoder.h +++ b/media/filters/gpu_video_decoder.h @@ -10,6 +10,7 @@ #include <utility> #include <vector> +#include "base/memory/weak_ptr.h" #include "media/base/pipeline_status.h" #include "media/base/demuxer_stream.h" #include "media/base/video_decoder.h" @@ -27,6 +28,7 @@ class SkBitmap; namespace media { class DecoderBuffer; +class VDAClientProxy; // GPU-accelerated video decoder implementation. Relies on // AcceleratedVideoDecoderMsg_Decode and friends. @@ -64,7 +66,7 @@ class MEDIA_EXPORT GpuVideoDecoder // attempts at factory operations virtual void Abort() = 0; - // Returns true if Abort has been called. + // Returns true if Abort() has been called. virtual bool IsAborted() = 0; protected: @@ -134,9 +136,10 @@ class MEDIA_EXPORT GpuVideoDecoder void GetBufferData(int32 id, base::TimeDelta* timetamp, gfx::Rect* visible_rect, gfx::Size* natural_size); - // Set |vda_| and |weak_vda_| on the VDA thread (in practice the render - // thread). - void SetVDA(VideoDecodeAccelerator* vda); + // Sets |vda_| and |weak_vda_| on the GVD thread and runs |status_cb|. + void SetVDA(const PipelineStatusCB& status_cb, + VideoDecodeAccelerator* vda, + base::WeakPtr<VideoDecodeAccelerator> weak_vda); // Call VDA::Destroy() on |vda_loop_proxy_| ensuring that |this| outlives the // Destroy() call. @@ -167,6 +170,8 @@ class MEDIA_EXPORT GpuVideoDecoder // MessageLoop on which to fire callbacks and trampoline calls to this class // if they arrive on other loops. scoped_refptr<base::MessageLoopProxy> gvd_loop_proxy_; + base::WeakPtrFactory<GpuVideoDecoder> weak_factory_; + base::WeakPtr<GpuVideoDecoder> weak_this_; // Message loop on which to makes all calls to vda_. (beware this loop may be // paused during the Pause/Flush/Stop dance PipelineImpl::Stop() goes @@ -175,8 +180,12 @@ class MEDIA_EXPORT GpuVideoDecoder scoped_refptr<Factories> factories_; + // Proxies calls from |vda_| to |gvd_loop_proxy_| and used to safely detach + // during shutdown. + scoped_refptr<VDAClientProxy> client_proxy_; + // Populated during Initialize() via SetVDA() (on success) and unchanged - // until an error occurs + // until an error occurs. scoped_ptr<VideoDecodeAccelerator> vda_; // Used to post tasks from the GVD thread to the VDA thread safely. base::WeakPtr<VideoDecodeAccelerator> weak_vda_; diff --git a/media/filters/pipeline_integration_test_base.cc b/media/filters/pipeline_integration_test_base.cc index 1b650d5..0be9861 100644 --- a/media/filters/pipeline_integration_test_base.cc +++ b/media/filters/pipeline_integration_test_base.cc @@ -223,16 +223,17 @@ PipelineIntegrationTestBase::CreateFilterCollection( Decryptor* decryptor) { scoped_ptr<FilterCollection> collection(new FilterCollection()); collection->SetDemuxer(demuxer); - scoped_refptr<VideoDecoder> video_decoder = new FFmpegVideoDecoder( - message_loop_.message_loop_proxy()); - scoped_refptr<VpxVideoDecoder> vpx_decoder = new VpxVideoDecoder( - message_loop_.message_loop_proxy()); - collection->GetVideoDecoders()->push_back(video_decoder); - collection->GetVideoDecoders()->push_back(vpx_decoder); + + ScopedVector<VideoDecoder> video_decoders; + video_decoders.push_back( + new FFmpegVideoDecoder(message_loop_.message_loop_proxy())); + video_decoders.push_back( + new VpxVideoDecoder(message_loop_.message_loop_proxy())); // Disable frame dropping if hashing is enabled. scoped_ptr<VideoRenderer> renderer(new VideoRendererBase( message_loop_.message_loop_proxy(), + video_decoders.Pass(), base::Bind(&PipelineIntegrationTestBase::SetDecryptor, base::Unretained(this), decryptor), base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint, diff --git a/media/filters/video_decoder_selector.cc b/media/filters/video_decoder_selector.cc index b1f186f..f1aebb26 100644 --- a/media/filters/video_decoder_selector.cc +++ b/media/filters/video_decoder_selector.cc @@ -19,10 +19,10 @@ namespace media { VideoDecoderSelector::VideoDecoderSelector( const scoped_refptr<base::MessageLoopProxy>& message_loop, - const VideoDecoderList& decoders, + ScopedVector<VideoDecoder> decoders, const SetDecryptorReadyCB& set_decryptor_ready_cb) : message_loop_(message_loop), - decoders_(decoders), + decoders_(decoders.Pass()), set_decryptor_ready_cb_(set_decryptor_ready_cb), ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { } @@ -43,7 +43,8 @@ void VideoDecoderSelector::SelectVideoDecoder( const VideoDecoderConfig& config = stream->video_decoder_config(); if (!config.IsValidConfig()) { DLOG(ERROR) << "Invalid video stream config."; - base::ResetAndReturn(&select_decoder_cb_).Run(NULL, NULL); + base::ResetAndReturn(&select_decoder_cb_).Run( + scoped_ptr<VideoDecoder>(), NULL); return; } @@ -51,24 +52,19 @@ void VideoDecoderSelector::SelectVideoDecoder( statistics_cb_ = statistics_cb; if (!config.is_encrypted()) { - if (decoders_.empty()) { - DLOG(ERROR) << "No video decoder can be used to decode the input stream."; - base::ResetAndReturn(&select_decoder_cb_).Run(NULL, NULL); - return; - } - - InitializeNextDecoder(); + InitializeDecoder(decoders_.begin()); return; } // This could happen if Encrypted Media Extension (EME) is not enabled. if (set_decryptor_ready_cb_.is_null()) { - base::ResetAndReturn(&select_decoder_cb_).Run(NULL, NULL); + base::ResetAndReturn(&select_decoder_cb_).Run( + scoped_ptr<VideoDecoder>(), NULL); return; } - video_decoder_ = new DecryptingVideoDecoder(message_loop_, - set_decryptor_ready_cb_); + video_decoder_.reset(new DecryptingVideoDecoder( + message_loop_, set_decryptor_ready_cb_)); video_decoder_->Initialize( input_stream_, @@ -83,16 +79,7 @@ void VideoDecoderSelector::DecryptingVideoDecoderInitDone( DCHECK(message_loop_->BelongsToCurrentThread()); if (status == PIPELINE_OK) { - decoders_.clear(); - base::ResetAndReturn(&select_decoder_cb_).Run(video_decoder_, NULL); - return; - } - - video_decoder_ = NULL; - - if (decoders_.empty()) { - DLOG(ERROR) << "No video decoder can be used to decode the input stream."; - base::ResetAndReturn(&select_decoder_cb_).Run(NULL, NULL); + base::ResetAndReturn(&select_decoder_cb_).Run(video_decoder_.Pass(), NULL); return; } @@ -112,42 +99,48 @@ void VideoDecoderSelector::DecryptingDemuxerStreamInitDone( if (status != PIPELINE_OK) { decrypted_stream_ = NULL; - base::ResetAndReturn(&select_decoder_cb_).Run(NULL, NULL); + base::ResetAndReturn(&select_decoder_cb_).Run( + scoped_ptr<VideoDecoder>(), NULL); return; } DCHECK(!decrypted_stream_->video_decoder_config().is_encrypted()); input_stream_ = decrypted_stream_; - InitializeNextDecoder(); + InitializeDecoder(decoders_.begin()); } -void VideoDecoderSelector::InitializeNextDecoder() { +void VideoDecoderSelector::InitializeDecoder( + ScopedVector<VideoDecoder>::iterator iter) { DCHECK(message_loop_->BelongsToCurrentThread()); - DCHECK(!decoders_.empty()); - - video_decoder_ = decoders_.front(); - decoders_.pop_front(); - DCHECK(video_decoder_); - video_decoder_->Initialize(input_stream_, - BindToCurrentLoop(base::Bind( - &VideoDecoderSelector::DecoderInitDone, - weak_ptr_factory_.GetWeakPtr())), - statistics_cb_); + + if (iter == decoders_.end()) { + base::ResetAndReturn(&select_decoder_cb_).Run( + scoped_ptr<VideoDecoder>(), NULL); + return; + } + + (*iter)->Initialize( + input_stream_, + BindToCurrentLoop(base::Bind( + &VideoDecoderSelector::DecoderInitDone, + weak_ptr_factory_.GetWeakPtr(), + iter)), + statistics_cb_); } -void VideoDecoderSelector::DecoderInitDone(PipelineStatus status) { +void VideoDecoderSelector::DecoderInitDone( + ScopedVector<VideoDecoder>::iterator iter, PipelineStatus status) { DCHECK(message_loop_->BelongsToCurrentThread()); if (status != PIPELINE_OK) { - if (!decoders_.empty()) - InitializeNextDecoder(); - else - base::ResetAndReturn(&select_decoder_cb_).Run(NULL, NULL); + InitializeDecoder(++iter); return; } - decoders_.clear(); - base::ResetAndReturn(&select_decoder_cb_).Run(video_decoder_, + scoped_ptr<VideoDecoder> video_decoder(*iter); + decoders_.weak_erase(iter); + + base::ResetAndReturn(&select_decoder_cb_).Run(video_decoder.Pass(), decrypted_stream_); } diff --git a/media/filters/video_decoder_selector.h b/media/filters/video_decoder_selector.h index 826bbf8..81a3110 100644 --- a/media/filters/video_decoder_selector.h +++ b/media/filters/video_decoder_selector.h @@ -5,10 +5,9 @@ #ifndef MEDIA_FILTERS_VIDEO_DECODER_SELECTOR_H_ #define MEDIA_FILTERS_VIDEO_DECODER_SELECTOR_H_ -#include <list> - #include "base/callback.h" #include "base/memory/ref_counted.h" +#include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" #include "media/base/decryptor.h" #include "media/base/demuxer_stream.h" @@ -29,8 +28,6 @@ class Decryptor; // encrypted, a DecryptingDemuxerStream may also be created. class MEDIA_EXPORT VideoDecoderSelector { public: - typedef std::list<scoped_refptr<VideoDecoder> > VideoDecoderList; - // Indicates completion of VideoDecoder selection. // - First parameter: The initialized VideoDecoder. If it's set to NULL, then // VideoDecoder initialization failed. @@ -41,14 +38,16 @@ class MEDIA_EXPORT VideoDecoderSelector { // The caller should call DecryptingDemuxerStream::Reset() before // calling VideoDecoder::Reset() to release any pending decryption or read. typedef base::Callback< - void(const scoped_refptr<VideoDecoder>&, + void(scoped_ptr<VideoDecoder>, const scoped_refptr<DecryptingDemuxerStream>&)> SelectDecoderCB; + // |decoders| contains the VideoDecoders to use when initializing. + // // |set_decryptor_ready_cb| is optional. If |set_decryptor_ready_cb| is null, // no decryptor will be available to perform decryption. VideoDecoderSelector( const scoped_refptr<base::MessageLoopProxy>& message_loop, - const VideoDecoderList& decoders, + ScopedVector<VideoDecoder> decoders, const SetDecryptorReadyCB& set_decryptor_ready_cb); ~VideoDecoderSelector(); @@ -62,18 +61,19 @@ class MEDIA_EXPORT VideoDecoderSelector { private: void DecryptingVideoDecoderInitDone(PipelineStatus status); void DecryptingDemuxerStreamInitDone(PipelineStatus status); - void InitializeNextDecoder(); - void DecoderInitDone(PipelineStatus status); + void InitializeDecoder(ScopedVector<VideoDecoder>::iterator iter); + void DecoderInitDone(ScopedVector<VideoDecoder>::iterator iter, + PipelineStatus status); scoped_refptr<base::MessageLoopProxy> message_loop_; - VideoDecoderList decoders_; + ScopedVector<VideoDecoder> decoders_; SetDecryptorReadyCB set_decryptor_ready_cb_; scoped_refptr<DemuxerStream> input_stream_; StatisticsCB statistics_cb_; SelectDecoderCB select_decoder_cb_; - scoped_refptr<VideoDecoder> video_decoder_; + scoped_ptr<VideoDecoder> video_decoder_; scoped_refptr<DecryptingDemuxerStream> decrypted_stream_; base::WeakPtrFactory<VideoDecoderSelector> weak_ptr_factory_; diff --git a/media/filters/video_decoder_selector_unittest.cc b/media/filters/video_decoder_selector_unittest.cc index 8086c62..764a0ba 100644 --- a/media/filters/video_decoder_selector_unittest.cc +++ b/media/filters/video_decoder_selector_unittest.cc @@ -18,7 +18,6 @@ using ::testing::NiceMock; using ::testing::NotNull; using ::testing::Return; using ::testing::ReturnRef; -using ::testing::SaveArg; using ::testing::StrictMock; namespace media { @@ -52,16 +51,17 @@ class VideoDecoderSelectorTest : public ::testing::Test { EXPECT_CALL(*demuxer_stream_, type()) .WillRepeatedly(Return(DemuxerStream::VIDEO)); - } - ~VideoDecoderSelectorTest() { EXPECT_CALL(*decoder_1_, Stop(_)) .WillRepeatedly(RunClosure<0>()); EXPECT_CALL(*decoder_2_, Stop(_)) .WillRepeatedly(RunClosure<0>()); + } - if (selected_decoder_) + ~VideoDecoderSelectorTest() { + if (selected_decoder_) { selected_decoder_->Stop(NewExpectedClosure()); + } message_loop_.RunUntilIdle(); } @@ -69,9 +69,16 @@ class VideoDecoderSelectorTest : public ::testing::Test { MOCK_METHOD1(OnStatistics, void(const PipelineStatistics&)); MOCK_METHOD1(SetDecryptorReadyCallback, void(const media::DecryptorReadyCB&)); MOCK_METHOD2(OnDecoderSelected, - void(const scoped_refptr<VideoDecoder>&, + void(VideoDecoder*, const scoped_refptr<DecryptingDemuxerStream>&)); + void MockOnDecoderSelected( + scoped_ptr<VideoDecoder> decoder, + const scoped_refptr<DecryptingDemuxerStream>& stream) { + OnDecoderSelected(decoder.get(), stream); + selected_decoder_ = decoder.Pass(); + } + void UseClearStream() { EXPECT_CALL(*demuxer_stream_, video_decoder_config()) .WillRepeatedly(ReturnRef(clear_video_config_)); @@ -104,12 +111,12 @@ class VideoDecoderSelectorTest : public ::testing::Test { } DCHECK_GE(all_decoders_.size(), static_cast<size_t>(num_decoders)); - VideoDecoderSelector::VideoDecoderList decoders( - all_decoders_.begin(), all_decoders_.begin() + num_decoders); + all_decoders_.erase( + all_decoders_.begin() + num_decoders, all_decoders_.end()); decoder_selector_.reset(new VideoDecoderSelector( message_loop_.message_loop_proxy(), - decoders, + all_decoders_.Pass(), set_decryptor_ready_cb)); } @@ -118,7 +125,7 @@ class VideoDecoderSelectorTest : public ::testing::Test { demuxer_stream_, base::Bind(&VideoDecoderSelectorTest::OnStatistics, base::Unretained(this)), - base::Bind(&VideoDecoderSelectorTest::OnDecoderSelected, + base::Bind(&VideoDecoderSelectorTest::MockOnDecoderSelected, base::Unretained(this))); message_loop_.RunUntilIdle(); } @@ -131,11 +138,11 @@ class VideoDecoderSelectorTest : public ::testing::Test { // Use NiceMock since we don't care about most of calls on the decryptor, e.g. // RegisterNewKeyCB(). scoped_ptr<NiceMock<MockDecryptor> > decryptor_; - scoped_refptr<StrictMock<MockVideoDecoder> > decoder_1_; - scoped_refptr<StrictMock<MockVideoDecoder> > decoder_2_; - std::vector<scoped_refptr<VideoDecoder> > all_decoders_; + StrictMock<MockVideoDecoder>* decoder_1_; + StrictMock<MockVideoDecoder>* decoder_2_; + ScopedVector<VideoDecoder> all_decoders_; - scoped_refptr<VideoDecoder> selected_decoder_; + scoped_ptr<VideoDecoder> selected_decoder_; MessageLoop message_loop_; @@ -162,9 +169,7 @@ TEST_F(VideoDecoderSelectorTest, ClearStream_NoDecryptor_OneClearDecoder) { EXPECT_CALL(*decoder_1_, Initialize(_, _, _)) .WillOnce(RunCallback<1>(PIPELINE_OK)); - EXPECT_CALL(*this, OnDecoderSelected(scoped_refptr<VideoDecoder>(decoder_1_), - IsNull())) - .WillOnce(SaveArg<0>(&selected_decoder_)); + EXPECT_CALL(*this, OnDecoderSelected(decoder_1_, IsNull())); SelectDecoder(); } @@ -179,9 +184,7 @@ TEST_F(VideoDecoderSelectorTest, ClearStream_NoDecryptor_MultipleClearDecoder) { .WillOnce(RunCallback<1>(DECODER_ERROR_NOT_SUPPORTED)); EXPECT_CALL(*decoder_2_, Initialize(_, _, _)) .WillOnce(RunCallback<1>(PIPELINE_OK)); - EXPECT_CALL(*this, OnDecoderSelected(scoped_refptr<VideoDecoder>(decoder_2_), - IsNull())) - .WillOnce(SaveArg<0>(&selected_decoder_)); + EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, IsNull())); SelectDecoder(); } @@ -194,9 +197,7 @@ TEST_F(VideoDecoderSelectorTest, ClearStream_HasDecryptor) { EXPECT_CALL(*decoder_1_, Initialize(_, _, _)) .WillOnce(RunCallback<1>(PIPELINE_OK)); - EXPECT_CALL(*this, OnDecoderSelected(scoped_refptr<VideoDecoder>(decoder_1_), - IsNull())) - .WillOnce(SaveArg<0>(&selected_decoder_)); + EXPECT_CALL(*this, OnDecoderSelected(decoder_1_, IsNull())); SelectDecoder(); } @@ -230,9 +231,7 @@ TEST_F(VideoDecoderSelectorTest, EncryptedStream_DecryptOnly_OneClearDecoder) { EXPECT_CALL(*decoder_1_, Initialize(_, _, _)) .WillOnce(RunCallback<1>(PIPELINE_OK)); - EXPECT_CALL(*this, OnDecoderSelected(scoped_refptr<VideoDecoder>(decoder_1_), - NotNull())) - .WillOnce(SaveArg<0>(&selected_decoder_)); + EXPECT_CALL(*this, OnDecoderSelected(decoder_1_, NotNull())); SelectDecoder(); } @@ -249,9 +248,7 @@ TEST_F(VideoDecoderSelectorTest, .WillOnce(RunCallback<1>(DECODER_ERROR_NOT_SUPPORTED)); EXPECT_CALL(*decoder_2_, Initialize(_, _, _)) .WillOnce(RunCallback<1>(PIPELINE_OK)); - EXPECT_CALL(*this, OnDecoderSelected(scoped_refptr<VideoDecoder>(decoder_2_), - NotNull())) - .WillOnce(SaveArg<0>(&selected_decoder_)); + EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, NotNull())); SelectDecoder(); } @@ -263,8 +260,7 @@ TEST_F(VideoDecoderSelectorTest, EncryptedStream_DecryptAndDecode) { UseEncryptedStream(); InitializeDecoderSelector(kDecryptAndDecode, 1); - EXPECT_CALL(*this, OnDecoderSelected(NotNull(), IsNull())) - .WillOnce(SaveArg<0>(&selected_decoder_)); + EXPECT_CALL(*this, OnDecoderSelected(NotNull(), IsNull())); SelectDecoder(); } diff --git a/media/filters/video_frame_stream.cc b/media/filters/video_frame_stream.cc index 72ee20d..93f2a0e 100644 --- a/media/filters/video_frame_stream.cc +++ b/media/filters/video_frame_stream.cc @@ -19,10 +19,12 @@ namespace media { VideoFrameStream::VideoFrameStream( const scoped_refptr<base::MessageLoopProxy>& message_loop, + ScopedVector<VideoDecoder> decoders, const SetDecryptorReadyCB& set_decryptor_ready_cb) : message_loop_(message_loop), weak_factory_(this), state_(UNINITIALIZED), + decoders_(decoders.Pass()), set_decryptor_ready_cb_(set_decryptor_ready_cb) { } @@ -31,7 +33,6 @@ VideoFrameStream::~VideoFrameStream() { } void VideoFrameStream::Initialize(const scoped_refptr<DemuxerStream>& stream, - const VideoDecoderList& decoders, const StatisticsCB& statistics_cb, const InitCB& init_cb) { DCHECK(message_loop_->BelongsToCurrentThread()); @@ -44,20 +45,18 @@ void VideoFrameStream::Initialize(const scoped_refptr<DemuxerStream>& stream, init_cb_ = init_cb; stream_ = stream; - scoped_ptr<VideoDecoderSelector> decoder_selector( - new VideoDecoderSelector(message_loop_, - decoders, - set_decryptor_ready_cb_)); + // TODO(scherkus): Make |decoder_selector| a member variable after + // DemuxerStream is no longer refcounted. Today we're forced to do this + // because it creates a refcount loop between |this| and |decoder_selector|. + scoped_ptr<VideoDecoderSelector> decoder_selector(new VideoDecoderSelector( + message_loop_, decoders_.Pass(), set_decryptor_ready_cb_)); + set_decryptor_ready_cb_.Reset(); - // To avoid calling |decoder_selector| methods and passing ownership of - // |decoder_selector| in the same line. VideoDecoderSelector* decoder_selector_ptr = decoder_selector.get(); - decoder_selector_ptr->SelectVideoDecoder( - this, - statistics_cb, - base::Bind(&VideoFrameStream::OnDecoderSelected, weak_this_, - base::Passed(&decoder_selector))); + decoder_selector_ptr->SelectVideoDecoder(this, statistics_cb, base::Bind( + &VideoFrameStream::OnDecoderSelected, weak_this_, + base::Passed(&decoder_selector))); } void VideoFrameStream::ReadFrame(const VideoDecoder::ReadCB& read_cb) { @@ -128,7 +127,7 @@ void VideoFrameStream::Stop(const base::Closure& closure) { // we don't need this here. See: http://crbug.com/173313 stream_ = NULL; decrypting_demuxer_stream_ = NULL; - decoder_ = NULL; + decoder_.reset(); message_loop_->PostTask(FROM_HERE, base::ResetAndReturn(&stop_cb_)); } @@ -165,7 +164,7 @@ void VideoFrameStream::EnableBitstreamConverter() { void VideoFrameStream::OnDecoderSelected( scoped_ptr<VideoDecoderSelector> decoder_selector, - const scoped_refptr<VideoDecoder>& selected_decoder, + scoped_ptr<VideoDecoder> selected_decoder, const scoped_refptr<DecryptingDemuxerStream>& decrypting_demuxer_stream) { DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK_EQ(state_, UNINITIALIZED); @@ -175,7 +174,7 @@ void VideoFrameStream::OnDecoderSelected( state_ = UNINITIALIZED; base::ResetAndReturn(&init_cb_).Run(false, false); } else { - decoder_ = selected_decoder; + decoder_ = selected_decoder.Pass(); decrypting_demuxer_stream_ = decrypting_demuxer_stream; state_ = NORMAL; base::ResetAndReturn(&init_cb_).Run(true, decoder_->HasAlpha()); @@ -239,7 +238,7 @@ void VideoFrameStream::OnDecoderStopped() { // we don't need this here. See: http://crbug.com/173313 stream_ = NULL; decrypting_demuxer_stream_ = NULL; - decoder_ = NULL; + decoder_.reset(); base::ResetAndReturn(&stop_cb_).Run(); } diff --git a/media/filters/video_frame_stream.h b/media/filters/video_frame_stream.h index f815419..10956c6 100644 --- a/media/filters/video_frame_stream.h +++ b/media/filters/video_frame_stream.h @@ -11,12 +11,14 @@ #include "base/callback.h" #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" +#include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" #include "media/base/decryptor.h" #include "media/base/demuxer_stream.h" #include "media/base/media_export.h" #include "media/base/pipeline_status.h" #include "media/base/video_decoder.h" +#include "media/filters/video_decoder_selector.h" namespace base { class MessageLoopProxy; @@ -31,18 +33,16 @@ class VideoDecoderSelector; // VideoFrames to its client (e.g. VideoRendererBase). class MEDIA_EXPORT VideoFrameStream : public DemuxerStream { public: - typedef std::list<scoped_refptr<VideoDecoder> > VideoDecoderList; - // Indicates completion of VideoFrameStream initialization. typedef base::Callback<void(bool success, bool has_alpha)> InitCB; VideoFrameStream(const scoped_refptr<base::MessageLoopProxy>& message_loop, + ScopedVector<VideoDecoder> decoders, const SetDecryptorReadyCB& set_decryptor_ready_cb); // Initializes the VideoFrameStream and returns the initialization result // through |init_cb|. Note that |init_cb| is always called asynchronously. void Initialize(const scoped_refptr<DemuxerStream>& stream, - const VideoDecoderList& decoders, const StatisticsCB& statistics_cb, const InitCB& init_cb); @@ -87,14 +87,12 @@ class MEDIA_EXPORT VideoFrameStream : public DemuxerStream { STOPPED }; - // Called when |decoder_selector_| selected the |selected_decoder|. + // Called when |decoder_selector| selected the |selected_decoder|. // |decrypting_demuxer_stream| was also populated if a DecryptingDemuxerStream // is created to help decrypt the encrypted stream. - // Note: |decoder_selector| is passed here to keep the VideoDecoderSelector - // alive until OnDecoderSelected() finishes. void OnDecoderSelected( scoped_ptr<VideoDecoderSelector> decoder_selector, - const scoped_refptr<VideoDecoder>& selected_decoder, + scoped_ptr<VideoDecoder> selected_decoder, const scoped_refptr<DecryptingDemuxerStream>& decrypting_demuxer_stream); // Callback for VideoDecoder::Read(). @@ -118,12 +116,15 @@ class MEDIA_EXPORT VideoFrameStream : public DemuxerStream { base::Closure reset_cb_; base::Closure stop_cb_; + // TODO(scherkus): Replace these with a VideoDecoderSelector instance after + // DemuxerStream is no longer refcounted. + ScopedVector<VideoDecoder> decoders_; SetDecryptorReadyCB set_decryptor_ready_cb_; scoped_refptr<DemuxerStream> stream_; // These two will be set by VideoDecoderSelector::SelectVideoDecoder(). - scoped_refptr<VideoDecoder> decoder_; + scoped_ptr<VideoDecoder> decoder_; scoped_refptr<DecryptingDemuxerStream> decrypting_demuxer_stream_; DISALLOW_COPY_AND_ASSIGN(VideoFrameStream); diff --git a/media/filters/video_frame_stream_unittest.cc b/media/filters/video_frame_stream_unittest.cc index e45c903..729bea7 100644 --- a/media/filters/video_frame_stream_unittest.cc +++ b/media/filters/video_frame_stream_unittest.cc @@ -31,18 +31,21 @@ static const gfx::Size kNaturalSize(320, 240); class VideoFrameStreamTest : public testing::TestWithParam<bool> { public: VideoFrameStreamTest() - : video_frame_stream_(new VideoFrameStream( - message_loop_.message_loop_proxy(), - base::Bind(&VideoFrameStreamTest::SetDecryptorReadyCallback, - base::Unretained(this)))), - video_config_(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, kVideoFormat, + : video_config_(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, kVideoFormat, kCodedSize, kVisibleRect, kNaturalSize, NULL, 0, GetParam()), demuxer_stream_(new StrictMock<MockDemuxerStream>()), decryptor_(new NiceMock<MockDecryptor>()), decoder_(new StrictMock<MockVideoDecoder>()), is_initialized_(false) { - decoders_.push_back(decoder_); + ScopedVector<VideoDecoder> decoders; + decoders.push_back(decoder_); + + video_frame_stream_ = new VideoFrameStream( + message_loop_.message_loop_proxy(), + decoders.Pass(), + base::Bind(&VideoFrameStreamTest::SetDecryptorReadyCallback, + base::Unretained(this))); EXPECT_CALL(*demuxer_stream_, type()) .WillRepeatedly(Return(DemuxerStream::VIDEO)); @@ -88,7 +91,6 @@ class VideoFrameStreamTest : public testing::TestWithParam<bool> { .WillOnce(SaveArg<1>(&decoder_init_cb_)); video_frame_stream_->Initialize( demuxer_stream_, - decoders_, base::Bind(&VideoFrameStreamTest::OnStatistics, base::Unretained(this)), base::Bind(&VideoFrameStreamTest::OnInitialized, base::Unretained(this))); @@ -101,6 +103,10 @@ class VideoFrameStreamTest : public testing::TestWithParam<bool> { base::ResetAndReturn(&decoder_init_cb_).Run( success ? PIPELINE_OK : DECODER_ERROR_NOT_SUPPORTED); message_loop_.RunUntilIdle(); + + // Failed initialization will delete unused decoders. + if (!success) + decoder_ = NULL; } void EnterPendingReadFrameState() { @@ -135,9 +141,11 @@ class VideoFrameStreamTest : public testing::TestWithParam<bool> { void EnterPendingStopState() { // If initialization failed, we won't call VideoDecoder::Stop() during // the stopping process. - EXPECT_CALL(*decoder_, Stop(_)) - .Times(AtMost(1)) - .WillRepeatedly(SaveArg<0>(&decoder_stop_cb_)); + if (decoder_) { + EXPECT_CALL(*decoder_, Stop(_)) + .WillRepeatedly(SaveArg<0>(&decoder_stop_cb_)); + } + EXPECT_CALL(*this, OnStopped()) .WillOnce(Assign(&is_initialized_, false)); video_frame_stream_->Stop(base::Bind(&VideoFrameStreamTest::OnStopped, @@ -188,8 +196,7 @@ class VideoFrameStreamTest : public testing::TestWithParam<bool> { // Use NiceMock since we don't care about most of calls on the decryptor, e.g. // RegisterNewKeyCB(). scoped_ptr<NiceMock<MockDecryptor> > decryptor_; - scoped_refptr<StrictMock<MockVideoDecoder> > decoder_; - VideoFrameStream::VideoDecoderList decoders_; + StrictMock<MockVideoDecoder>* decoder_; // Owned by |video_frame_stream_|. // Callbacks to simulate pending decoder operations. PipelineStatusCB decoder_init_cb_; diff --git a/media/filters/video_renderer_base.cc b/media/filters/video_renderer_base.cc index 98439c6..c9dee83 100644 --- a/media/filters/video_renderer_base.cc +++ b/media/filters/video_renderer_base.cc @@ -23,14 +23,15 @@ base::TimeDelta VideoRendererBase::kMaxLastFrameDuration() { VideoRendererBase::VideoRendererBase( const scoped_refptr<base::MessageLoopProxy>& message_loop, + ScopedVector<VideoDecoder> decoders, const SetDecryptorReadyCB& set_decryptor_ready_cb, const PaintCB& paint_cb, const SetOpaqueCB& set_opaque_cb, bool drop_frames) : message_loop_(message_loop), weak_factory_(this), - video_frame_stream_(new VideoFrameStream(message_loop, - set_decryptor_ready_cb)), + video_frame_stream_(new VideoFrameStream( + message_loop, decoders.Pass(), set_decryptor_ready_cb)), received_end_of_stream_(false), frame_available_(&lock_), state_(kUninitialized), @@ -136,7 +137,6 @@ void VideoRendererBase::Preroll(base::TimeDelta time, } void VideoRendererBase::Initialize(const scoped_refptr<DemuxerStream>& stream, - const VideoDecoderList& decoders, const PipelineStatusCB& init_cb, const StatisticsCB& statistics_cb, const TimeCB& max_time_cb, @@ -148,7 +148,6 @@ void VideoRendererBase::Initialize(const scoped_refptr<DemuxerStream>& stream, DCHECK(message_loop_->BelongsToCurrentThread()); base::AutoLock auto_lock(lock_); DCHECK(stream); - DCHECK(!decoders.empty()); DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); DCHECK(!init_cb.is_null()); DCHECK(!statistics_cb.is_null()); @@ -172,7 +171,6 @@ void VideoRendererBase::Initialize(const scoped_refptr<DemuxerStream>& stream, video_frame_stream_->Initialize( stream, - decoders, statistics_cb, base::Bind(&VideoRendererBase::OnVideoFrameStreamInitialized, weak_this_)); diff --git a/media/filters/video_renderer_base.h b/media/filters/video_renderer_base.h index 7b01653..94e0730 100644 --- a/media/filters/video_renderer_base.h +++ b/media/filters/video_renderer_base.h @@ -8,6 +8,7 @@ #include <deque> #include "base/memory/ref_counted.h" +#include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" @@ -43,6 +44,8 @@ class MEDIA_EXPORT VideoRendererBase // Maximum duration of the last frame. static base::TimeDelta kMaxLastFrameDuration(); + // |decoders| contains the VideoDecoders to use when initializing. + // // |paint_cb| is executed on the video frame timing thread whenever a new // frame is available for painting. // @@ -55,6 +58,7 @@ class MEDIA_EXPORT VideoRendererBase // // Setting |drop_frames_| to true causes the renderer to drop expired frames. VideoRendererBase(const scoped_refptr<base::MessageLoopProxy>& message_loop, + ScopedVector<VideoDecoder> decoders, const SetDecryptorReadyCB& set_decryptor_ready_cb, const PaintCB& paint_cb, const SetOpaqueCB& set_opaque_cb, @@ -63,7 +67,6 @@ class MEDIA_EXPORT VideoRendererBase // VideoRenderer implementation. virtual void Initialize(const scoped_refptr<DemuxerStream>& stream, - const VideoDecoderList& decoders, const PipelineStatusCB& init_cb, const StatisticsCB& statistics_cb, const TimeCB& max_time_cb, @@ -129,12 +132,17 @@ class MEDIA_EXPORT VideoRendererBase // A read is scheduled to replace the frame. void DropNextReadyFrame_Locked(); + void ResetDecoder(); + void StopDecoder(const base::Closure& callback); + void TransitionToPrerolled_Locked(); scoped_refptr<base::MessageLoopProxy> message_loop_; base::WeakPtrFactory<VideoRendererBase> weak_factory_; base::WeakPtr<VideoRendererBase> weak_this_; + scoped_ptr<VideoDecoderSelector> decoder_selector_; + // Used for accessing data members. base::Lock lock_; diff --git a/media/filters/video_renderer_base_unittest.cc b/media/filters/video_renderer_base_unittest.cc index c498c50..4ce9f08 100644 --- a/media/filters/video_renderer_base_unittest.cc +++ b/media/filters/video_renderer_base_unittest.cc @@ -45,8 +45,12 @@ class VideoRendererBaseTest : public ::testing::Test { demuxer_stream_(new MockDemuxerStream()), video_config_(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, kVideoFormat, kCodedSize, kVisibleRect, kNaturalSize, NULL, 0, false) { + ScopedVector<VideoDecoder> decoders; + decoders.push_back(decoder_); + renderer_.reset(new VideoRendererBase( message_loop_.message_loop_proxy(), + decoders.Pass(), media::SetDecryptorReadyCB(), base::Bind(&VideoRendererBaseTest::OnPaint, base::Unretained(this)), base::Bind(&VideoRendererBaseTest::OnSetOpaque, base::Unretained(this)), @@ -118,11 +122,8 @@ class VideoRendererBaseTest : public ::testing::Test { } void CallInitialize(const PipelineStatusCB& status_cb) { - VideoRendererBase::VideoDecoderList decoders; - decoders.push_back(decoder_); renderer_->Initialize( demuxer_stream_, - decoders, status_cb, base::Bind(&MockStatisticsCB::OnStatistics, base::Unretained(&statistics_cb_object_)), @@ -295,7 +296,7 @@ class VideoRendererBaseTest : public ::testing::Test { protected: // Fixture members. scoped_ptr<VideoRendererBase> renderer_; - scoped_refptr<MockVideoDecoder> decoder_; + MockVideoDecoder* decoder_; // Owned by |renderer_|. scoped_refptr<MockDemuxerStream> demuxer_stream_; MockStatisticsCB statistics_cb_object_; diff --git a/media/filters/vpx_video_decoder.cc b/media/filters/vpx_video_decoder.cc index 0b92a7d..baab017 100644 --- a/media/filters/vpx_video_decoder.cc +++ b/media/filters/vpx_video_decoder.cc @@ -58,6 +58,7 @@ static int GetThreadCount() { VpxVideoDecoder::VpxVideoDecoder( const scoped_refptr<base::MessageLoopProxy>& message_loop) : message_loop_(message_loop), + weak_factory_(this), state_(kUninitialized), vpx_codec_(NULL) { } @@ -73,6 +74,7 @@ void VpxVideoDecoder::Initialize( const StatisticsCB& statistics_cb) { DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK(!demuxer_stream_) << "Already initialized."; + weak_this_ = weak_factory_.GetWeakPtr(); if (!stream) { status_cb.Run(PIPELINE_ERROR_DECODE); @@ -182,7 +184,7 @@ void VpxVideoDecoder::ReadFromDemuxerStream() { DCHECK(!read_cb_.is_null()); demuxer_stream_->Read(base::Bind( - &VpxVideoDecoder::DoDecryptOrDecodeBuffer, this)); + &VpxVideoDecoder::DoDecryptOrDecodeBuffer, weak_this_)); } void VpxVideoDecoder::DoDecryptOrDecodeBuffer( diff --git a/media/filters/vpx_video_decoder.h b/media/filters/vpx_video_decoder.h index 77578fd..aea47bb 100644 --- a/media/filters/vpx_video_decoder.h +++ b/media/filters/vpx_video_decoder.h @@ -6,7 +6,7 @@ #define MEDIA_FILTERS_VPX_VIDEO_DECODER_H_ #include "base/callback.h" -#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "media/base/demuxer_stream.h" #include "media/base/video_decoder.h" @@ -23,6 +23,7 @@ class MEDIA_EXPORT VpxVideoDecoder : public VideoDecoder { public: explicit VpxVideoDecoder( const scoped_refptr<base::MessageLoopProxy>& message_loop); + virtual ~VpxVideoDecoder(); // VideoDecoder implementation. virtual void Initialize(const scoped_refptr<DemuxerStream>& stream, @@ -32,9 +33,6 @@ class MEDIA_EXPORT VpxVideoDecoder : public VideoDecoder { virtual void Reset(const base::Closure& closure) OVERRIDE; virtual void Stop(const base::Closure& closure) OVERRIDE; - protected: - virtual ~VpxVideoDecoder(); - private: enum DecoderState { kUninitialized, @@ -66,6 +64,8 @@ class MEDIA_EXPORT VpxVideoDecoder : public VideoDecoder { scoped_refptr<VideoFrame>* video_frame); scoped_refptr<base::MessageLoopProxy> message_loop_; + base::WeakPtrFactory<VpxVideoDecoder> weak_factory_; + base::WeakPtr<VpxVideoDecoder> weak_this_; DecoderState state_; diff --git a/media/tools/player_x11/player_x11.cc b/media/tools/player_x11/player_x11.cc index e9254e1..7b029fc 100644 --- a/media/tools/player_x11/player_x11.cc +++ b/media/tools/player_x11/player_x11.cc @@ -113,12 +113,13 @@ bool InitPipeline(const scoped_refptr<base::MessageLoopProxy>& message_loop, media::FFmpegNeedKeyCB need_key_cb = base::Bind(&NeedKey); collection->SetDemuxer(new media::FFmpegDemuxer(message_loop, data_source, need_key_cb)); - collection->GetVideoDecoders()->push_back(new media::FFmpegVideoDecoder( - message_loop)); - // Create our video renderer and save a reference to it for painting. + + ScopedVector<media::VideoDecoder> video_decoders; + video_decoders.push_back(new media::FFmpegVideoDecoder(message_loop)); scoped_ptr<media::VideoRenderer> video_renderer(new media::VideoRendererBase( message_loop, + video_decoders.Pass(), media::SetDecryptorReadyCB(), base::Bind(&Paint, paint_message_loop, paint_cb), base::Bind(&SetOpaque), diff --git a/webkit/media/filter_helpers.cc b/webkit/media/filter_helpers.cc deleted file mode 100644 index c803f69..0000000 --- a/webkit/media/filter_helpers.cc +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "webkit/media/filter_helpers.h" - -#include "base/bind.h" -#include "base/command_line.h" -#include "media/base/filter_collection.h" -#include "media/base/media_switches.h" -#include "media/filters/chunk_demuxer.h" -#include "media/filters/ffmpeg_audio_decoder.h" -#include "media/filters/ffmpeg_demuxer.h" -#include "media/filters/ffmpeg_video_decoder.h" -#include "media/filters/opus_audio_decoder.h" -#include "media/filters/vpx_video_decoder.h" -#include "third_party/WebKit/Source/Platform/chromium/public/WebURL.h" - -namespace webkit_media { - -void AddDefaultAudioDecoders( - const scoped_refptr<base::MessageLoopProxy>& message_loop, - ScopedVector<media::AudioDecoder>* audio_decoders) { - audio_decoders->push_back(new media::FFmpegAudioDecoder(message_loop)); - - const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); - if (cmd_line->HasSwitch(switches::kEnableOpusPlayback)) { - audio_decoders->push_back(new media::OpusAudioDecoder(message_loop)); - } -} - -// Constructs and adds the default video decoders to |filter_collection|. -// -// Note that decoders in the |filter_collection| are initialized in order. -static void AddDefaultDecodersToCollection( - const scoped_refptr<base::MessageLoopProxy>& message_loop, - media::FilterCollection* filter_collection) { - - scoped_refptr<media::FFmpegVideoDecoder> ffmpeg_video_decoder = - new media::FFmpegVideoDecoder(message_loop); - filter_collection->GetVideoDecoders()->push_back(ffmpeg_video_decoder); - - // TODO(phajdan.jr): Remove ifdefs when libvpx with vp9 support is released - // (http://crbug.com/174287) . -#if !defined(MEDIA_DISABLE_LIBVPX) - const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); - if (cmd_line->HasSwitch(switches::kEnableVp9Playback)) { - scoped_refptr<media::VpxVideoDecoder> vpx_video_decoder = - new media::VpxVideoDecoder(message_loop); - filter_collection->GetVideoDecoders()->push_back(vpx_video_decoder); - } -#endif // !defined(MEDIA_DISABLE_LIBVPX) -} - -void BuildMediaSourceCollection( - const scoped_refptr<media::ChunkDemuxer>& demuxer, - const scoped_refptr<base::MessageLoopProxy>& message_loop, - media::FilterCollection* filter_collection) { - DCHECK(demuxer); - filter_collection->SetDemuxer(demuxer); - - // Remove GPUVideoDecoder until it supports codec config changes. - // TODO(acolwell): Remove this once http://crbug.com/151045 is fixed. - DCHECK_LE(filter_collection->GetVideoDecoders()->size(), 1u); - filter_collection->GetVideoDecoders()->clear(); - - AddDefaultDecodersToCollection(message_loop, filter_collection); -} - -void BuildDefaultCollection( - const scoped_refptr<media::DataSource>& data_source, - const scoped_refptr<base::MessageLoopProxy>& message_loop, - media::FilterCollection* filter_collection, - const media::FFmpegNeedKeyCB& need_key_cb) { - filter_collection->SetDemuxer(new media::FFmpegDemuxer( - message_loop, data_source, need_key_cb)); - - AddDefaultDecodersToCollection(message_loop, filter_collection); -} - -} // webkit_media diff --git a/webkit/media/filter_helpers.h b/webkit/media/filter_helpers.h deleted file mode 100644 index 29a3e38..0000000 --- a/webkit/media/filter_helpers.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef WEBKIT_MEDIA_FILTER_HELPERS_H_ -#define WEBKIT_MEDIA_FILTER_HELPERS_H_ - -#include "base/basictypes.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_vector.h" -// TODO(fgalligan): Remove the dependency on FFmpeg. -#include "media/filters/ffmpeg_demuxer.h" - -namespace base { -class MessageLoopProxy; -} - -namespace media { -class AudioDecoder; -class ChunkDemuxer; -class DataSource; -class FilterCollection; -} - -namespace webkit_media { - -// Creates and adds the default set of audio decoders to |audio_decoders|. -void AddDefaultAudioDecoders( - const scoped_refptr<base::MessageLoopProxy>& message_loop, - ScopedVector<media::AudioDecoder>* audio_decoders); - -// Builds the required filters for handling media source URLs, adds them to -// |filter_collection|. -void BuildMediaSourceCollection( - const scoped_refptr<media::ChunkDemuxer>& demuxer, - const scoped_refptr<base::MessageLoopProxy>& message_loop, - media::FilterCollection* filter_collection); - -// Builds the required filters for handling regular URLs and adds them to -// |filter_collection| and fills |video_decoder| returning true if successful. -void BuildDefaultCollection( - const scoped_refptr<media::DataSource>& data_source, - const scoped_refptr<base::MessageLoopProxy>& message_loop, - media::FilterCollection* filter_collection, - const media::FFmpegNeedKeyCB& need_key_cb); - -} // webkit_media - -#endif // WEBKIT_MEDIA_FILTER_HELPERS_H_ diff --git a/webkit/media/webkit_media.gypi b/webkit/media/webkit_media.gypi index 19e6e23..2d57dfb 100644 --- a/webkit/media/webkit_media.gypi +++ b/webkit/media/webkit_media.gypi @@ -74,8 +74,6 @@ 'crypto/ppapi_decryptor.h', 'crypto/proxy_decryptor.cc', 'crypto/proxy_decryptor.h', - 'filter_helpers.cc', - 'filter_helpers.h', 'media_stream_audio_renderer.cc', 'media_stream_audio_renderer.h', 'media_stream_client.h', diff --git a/webkit/media/webmediaplayer_impl.cc b/webkit/media/webmediaplayer_impl.cc index 4c47acd..ce2cbc7 100644 --- a/webkit/media/webmediaplayer_impl.cc +++ b/webkit/media/webmediaplayer_impl.cc @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/callback.h" +#include "base/command_line.h" #include "base/debug/crash_logging.h" #include "base/message_loop_proxy.h" #include "base/metrics/histogram.h" @@ -23,11 +24,17 @@ #include "media/base/filter_collection.h" #include "media/base/limits.h" #include "media/base/media_log.h" +#include "media/base/media_switches.h" #include "media/base/pipeline.h" #include "media/base/video_frame.h" #include "media/filters/audio_renderer_impl.h" #include "media/filters/chunk_demuxer.h" +#include "media/filters/ffmpeg_audio_decoder.h" +#include "media/filters/ffmpeg_demuxer.h" +#include "media/filters/ffmpeg_video_decoder.h" +#include "media/filters/opus_audio_decoder.h" #include "media/filters/video_renderer_base.h" +#include "media/filters/vpx_video_decoder.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebRect.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebString.h" @@ -38,7 +45,6 @@ #include "v8/include/v8.h" #include "webkit/compositor_bindings/web_layer_impl.h" #include "webkit/media/buffered_data_source.h" -#include "webkit/media/filter_helpers.h" #include "webkit/media/webaudiosourceprovider_impl.h" #include "webkit/media/webmediaplayer_delegate.h" #include "webkit/media/webmediaplayer_params.h" @@ -132,7 +138,6 @@ WebMediaPlayerImpl::WebMediaPlayerImpl( network_state_(WebMediaPlayer::NetworkStateEmpty), ready_state_(WebMediaPlayer::ReadyStateHaveNothing), main_loop_(base::MessageLoopProxy::current()), - filter_collection_(new media::FilterCollection()), media_thread_("MediaPipeline"), paused_(true), seeking_(false), @@ -144,6 +149,7 @@ WebMediaPlayerImpl::WebMediaPlayerImpl( media_log_(params.media_log()), accelerated_compositing_reported_(false), incremented_externally_allocated_memory_(false), + gpu_factories_(params.gpu_factories()), is_local_source_(false), supports_save_(true), starting_(false), @@ -170,7 +176,6 @@ WebMediaPlayerImpl::WebMediaPlayerImpl( // Also we want to be notified of |main_loop_| destruction. MessageLoop::current()->AddDestructionObserver(this); - media::SetDecryptorReadyCB set_decryptor_ready_cb; if (WebKit::WebRuntimeFeatures::isEncryptedMediaEnabled()) { decryptor_.reset(new ProxyDecryptor( client, @@ -179,44 +184,12 @@ WebMediaPlayerImpl::WebMediaPlayerImpl( BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyError), BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyMessage), BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNeedKey))); - set_decryptor_ready_cb = base::Bind(&ProxyDecryptor::SetDecryptorReadyCB, - base::Unretained(decryptor_.get())); } - // Create the GPU video decoder if factories were provided. - if (params.gpu_factories()) { - filter_collection_->GetVideoDecoders()->push_back( - new media::GpuVideoDecoder( - media_thread_.message_loop_proxy(), - params.gpu_factories())); - gpu_factories_ = params.gpu_factories(); - } - - // Create default video renderer. - scoped_ptr<media::VideoRenderer> video_renderer( - new media::VideoRendererBase( - media_thread_.message_loop_proxy(), - set_decryptor_ready_cb, - base::Bind(&WebMediaPlayerImpl::FrameReady, base::Unretained(this)), - BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::SetOpaque), - true)); - filter_collection_->SetVideoRenderer(video_renderer.Pass()); - - // Create default audio renderer using the null sink if no sink was provided. + // Use the null sink if no sink was provided. audio_source_provider_ = new WebAudioSourceProviderImpl( params.audio_renderer_sink() ? params.audio_renderer_sink() : new media::NullAudioSink(media_thread_.message_loop_proxy())); - - ScopedVector<media::AudioDecoder> audio_decoders; - AddDefaultAudioDecoders(media_thread_.message_loop_proxy(), &audio_decoders); - - scoped_ptr<media::AudioRenderer> audio_renderer( - new media::AudioRendererImpl( - media_thread_.message_loop_proxy(), - audio_source_provider_, - audio_decoders.Pass(), - set_decryptor_ready_cb)); - filter_collection_->SetAudioRenderer(audio_renderer.Pass()); } WebMediaPlayerImpl::~WebMediaPlayerImpl() { @@ -288,12 +261,6 @@ void WebMediaPlayerImpl::load(const WebKit::WebURL& url, CORSMode cors_mode) { AsWeakPtr(), gurl)); is_local_source_ = !gurl.SchemeIs("http") && !gurl.SchemeIs("https"); - - BuildDefaultCollection( - data_source_, - media_thread_.message_loop_proxy(), - filter_collection_.get(), - BIND_TO_RENDER_LOOP_2(&WebMediaPlayerImpl::OnNeedKey, "", "")); } void WebMediaPlayerImpl::load(const WebKit::WebURL& url, @@ -309,9 +276,6 @@ void WebMediaPlayerImpl::load(const WebKit::WebURL& url, BIND_TO_RENDER_LOOP_2(&WebMediaPlayerImpl::OnNeedKey, "", ""), base::Bind(&LogMediaSourceError, media_log_)); - BuildMediaSourceCollection(chunk_demuxer_, - media_thread_.message_loop_proxy(), - filter_collection_.get()); supports_save_ = false; StartPipeline(); } @@ -1175,7 +1139,7 @@ void WebMediaPlayerImpl::NotifyDownloading(bool is_downloading) { void WebMediaPlayerImpl::StartPipeline() { starting_ = true; pipeline_->Start( - filter_collection_.Pass(), + BuildFilterCollection(), BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded), BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineError), BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek), @@ -1286,4 +1250,82 @@ void WebMediaPlayerImpl::FrameReady( &WebMediaPlayerImpl::Repaint, AsWeakPtr())); } +scoped_ptr<media::FilterCollection> +WebMediaPlayerImpl::BuildFilterCollection() { + const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + + scoped_ptr<media::FilterCollection> filter_collection( + new media::FilterCollection()); + + // Figure out which demuxer to use. + if (data_source_) { + DCHECK(!chunk_demuxer_); + filter_collection->SetDemuxer(new media::FFmpegDemuxer( + media_thread_.message_loop_proxy(), data_source_, + BIND_TO_RENDER_LOOP_2(&WebMediaPlayerImpl::OnNeedKey, "", ""))); + } else { + DCHECK(chunk_demuxer_); + filter_collection->SetDemuxer(chunk_demuxer_); + + // Disable GpuVideoDecoder creation until it supports codec config changes. + // TODO(acolwell): Remove this once http://crbug.com/151045 is fixed. + gpu_factories_ = NULL; + } + + // Figure out if EME is enabled. + media::SetDecryptorReadyCB set_decryptor_ready_cb; + if (decryptor_) { + set_decryptor_ready_cb = base::Bind(&ProxyDecryptor::SetDecryptorReadyCB, + base::Unretained(decryptor_.get())); + } + + // Create our audio decoders and renderer. + ScopedVector<media::AudioDecoder> audio_decoders; + audio_decoders.push_back(new media::FFmpegAudioDecoder( + media_thread_.message_loop_proxy())); + if (cmd_line->HasSwitch(switches::kEnableOpusPlayback)) { + audio_decoders.push_back(new media::OpusAudioDecoder( + media_thread_.message_loop_proxy())); + } + + scoped_ptr<media::AudioRenderer> audio_renderer( + new media::AudioRendererImpl(media_thread_.message_loop_proxy(), + audio_source_provider_, + audio_decoders.Pass(), + set_decryptor_ready_cb)); + filter_collection->SetAudioRenderer(audio_renderer.Pass()); + + // Create our video decoders and renderer. + ScopedVector<media::VideoDecoder> video_decoders; + + if (gpu_factories_) { + video_decoders.push_back(new media::GpuVideoDecoder( + media_thread_.message_loop_proxy(), gpu_factories_)); + } + + video_decoders.push_back(new media::FFmpegVideoDecoder( + media_thread_.message_loop_proxy())); + + // TODO(phajdan.jr): Remove ifdefs when libvpx with vp9 support is released + // (http://crbug.com/174287) . +#if !defined(MEDIA_DISABLE_LIBVPX) + if (cmd_line->HasSwitch(switches::kEnableVp9Playback)) { + video_decoders.push_back(new media::VpxVideoDecoder( + media_thread_.message_loop_proxy())); + } +#endif // !defined(MEDIA_DISABLE_LIBVPX) + + scoped_ptr<media::VideoRenderer> video_renderer( + new media::VideoRendererBase( + media_thread_.message_loop_proxy(), + video_decoders.Pass(), + set_decryptor_ready_cb, + base::Bind(&WebMediaPlayerImpl::FrameReady, base::Unretained(this)), + BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::SetOpaque), + true)); + filter_collection->SetVideoRenderer(video_renderer.Pass()); + + return filter_collection.Pass(); +} + } // namespace webkit_media diff --git a/webkit/media/webmediaplayer_impl.h b/webkit/media/webmediaplayer_impl.h index f733e6f..46f9985 100644 --- a/webkit/media/webmediaplayer_impl.h +++ b/webkit/media/webmediaplayer_impl.h @@ -272,6 +272,10 @@ class WebMediaPlayerImpl // painted. void FrameReady(const scoped_refptr<media::VideoFrame>& frame); + // Builds a FilterCollection based on the current configuration of + // WebMediaPlayerImpl. + scoped_ptr<media::FilterCollection> BuildFilterCollection(); + WebKit::WebFrame* frame_; // TODO(hclam): get rid of these members and read from the pipeline directly. @@ -285,7 +289,6 @@ class WebMediaPlayerImpl // for DCHECKs so methods calls won't execute in the wrong thread. const scoped_refptr<base::MessageLoopProxy> main_loop_; - scoped_ptr<media::FilterCollection> filter_collection_; scoped_refptr<media::Pipeline> pipeline_; base::Thread media_thread_; @@ -329,6 +332,9 @@ class WebMediaPlayerImpl bool incremented_externally_allocated_memory_; + // Factories for supporting GpuVideoDecoder. May be null. + scoped_refptr<media::GpuVideoDecoder::Factories> gpu_factories_; + // Routes audio playback to either AudioRendererSink or WebAudio. scoped_refptr<WebAudioSourceProviderImpl> audio_source_provider_; @@ -368,8 +374,6 @@ class WebMediaPlayerImpl // not NULL while the compositor is actively using this webmediaplayer. cc::VideoFrameProvider::Client* video_frame_provider_client_; - scoped_refptr<media::GpuVideoDecoder::Factories> gpu_factories_; - DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerImpl); }; |