diff options
author | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-14 22:51:35 +0000 |
---|---|---|
committer | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-14 22:51:35 +0000 |
commit | 893f2828f68362365ab77045e7b4a1c0f5806d6c (patch) | |
tree | 7e857a87f4f3c748ca5852f5c38a30dc88697b5c /media | |
parent | 042e77c20d44abe5efe17cedb0484e0cb5492f4a (diff) | |
download | chromium_src-893f2828f68362365ab77045e7b4a1c0f5806d6c.zip chromium_src-893f2828f68362365ab77045e7b4a1c0f5806d6c.tar.gz chromium_src-893f2828f68362365ab77045e7b4a1c0f5806d6c.tar.bz2 |
Encrypted Media: Support Audio Decrypt-Only.
- Add AudioDecoderSelector to facilitate AudioDecoder selection.
- Add AudioDecoderSelectorTest.
- Update key_system.cc to support audio in clearkey key system.
- Update content_browsertests to test audio decrypt-only.
BUG=123421
TEST=both decrypt-only and decrypt-and-decode works for audio!
Review URL: https://codereview.chromium.org/11492003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@173235 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/filters/audio_decoder_selector.cc | 155 | ||||
-rw-r--r-- | media/filters/audio_decoder_selector.h | 86 | ||||
-rw-r--r-- | media/filters/audio_decoder_selector_unittest.cc | 242 | ||||
-rw-r--r-- | media/filters/audio_renderer_impl.cc | 69 | ||||
-rw-r--r-- | media/filters/audio_renderer_impl.h | 37 | ||||
-rw-r--r-- | media/filters/audio_renderer_impl_unittest.cc | 15 | ||||
-rw-r--r-- | media/filters/pipeline_integration_test_base.cc | 2 | ||||
-rw-r--r-- | media/media.gyp | 3 | ||||
-rw-r--r-- | media/tools/player_x11/player_x11.cc | 4 |
9 files changed, 562 insertions, 51 deletions
diff --git a/media/filters/audio_decoder_selector.cc b/media/filters/audio_decoder_selector.cc new file mode 100644 index 0000000..fb6e2b6 --- /dev/null +++ b/media/filters/audio_decoder_selector.cc @@ -0,0 +1,155 @@ +// 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 "media/filters/audio_decoder_selector.h" + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/logging.h" +#include "base/message_loop_proxy.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/bind_to_loop.h" +#include "media/base/demuxer_stream.h" +#include "media/base/pipeline.h" +#include "media/filters/decrypting_audio_decoder.h" +#include "media/filters/decrypting_demuxer_stream.h" + +namespace media { + +AudioDecoderSelector::AudioDecoderSelector( + const scoped_refptr<base::MessageLoopProxy>& message_loop, + const AudioDecoderList& decoders, + const SetDecryptorReadyCB& set_decryptor_ready_cb) + : message_loop_(message_loop), + decoders_(decoders), + set_decryptor_ready_cb_(set_decryptor_ready_cb), + ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { +} + +AudioDecoderSelector::~AudioDecoderSelector() {} + +void AudioDecoderSelector::SelectAudioDecoder( + const scoped_refptr<DemuxerStream>& stream, + const StatisticsCB& statistics_cb, + const SelectDecoderCB& select_decoder_cb) { + DVLOG(2) << "SelectAudioDecoder()"; + DCHECK(message_loop_->BelongsToCurrentThread()); + DCHECK(stream); + + // Make sure |select_decoder_cb| runs on a different execution stack. + select_decoder_cb_ = BindToCurrentLoop(select_decoder_cb); + + const AudioDecoderConfig& config = stream->audio_decoder_config(); + if (!config.IsValidConfig()) { + DLOG(ERROR) << "Invalid audio stream config."; + base::ResetAndReturn(&select_decoder_cb_).Run(NULL, NULL); + return; + } + + input_stream_ = stream; + statistics_cb_ = statistics_cb; + + if (!config.is_encrypted()) { + if (decoders_.empty()) { + DLOG(ERROR) << "No audio decoder can be used to decode the input stream."; + base::ResetAndReturn(&select_decoder_cb_).Run(NULL, NULL); + return; + } + + InitializeNextDecoder(); + 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); + return; + } + + audio_decoder_ = new DecryptingAudioDecoder(message_loop_, + set_decryptor_ready_cb_); + + audio_decoder_->Initialize( + input_stream_, + BindToCurrentLoop(base::Bind( + &AudioDecoderSelector::DecryptingAudioDecoderInitDone, + weak_ptr_factory_.GetWeakPtr())), + statistics_cb_); +} + +void AudioDecoderSelector::DecryptingAudioDecoderInitDone( + PipelineStatus status) { + DCHECK(message_loop_->BelongsToCurrentThread()); + + if (status == PIPELINE_OK) { + decoders_.clear(); + base::ResetAndReturn(&select_decoder_cb_).Run(audio_decoder_, NULL); + return; + } + + audio_decoder_ = NULL; + + if (decoders_.empty()) { + DLOG(ERROR) << "No audio decoder can be used to decode the input stream."; + base::ResetAndReturn(&select_decoder_cb_).Run(NULL, NULL); + return; + } + + decrypted_stream_ = new DecryptingDemuxerStream( + message_loop_, set_decryptor_ready_cb_); + + decrypted_stream_->Initialize( + input_stream_, + BindToCurrentLoop(base::Bind( + &AudioDecoderSelector::DecryptingDemuxerStreamInitDone, + weak_ptr_factory_.GetWeakPtr()))); +} + +void AudioDecoderSelector::DecryptingDemuxerStreamInitDone( + PipelineStatus status) { + DCHECK(message_loop_->BelongsToCurrentThread()); + + if (status != PIPELINE_OK) { + decrypted_stream_ = NULL; + base::ResetAndReturn(&select_decoder_cb_).Run(NULL, NULL); + return; + } + + DCHECK(!decrypted_stream_->audio_decoder_config().is_encrypted()); + input_stream_ = decrypted_stream_; + InitializeNextDecoder(); +} + +void AudioDecoderSelector::InitializeNextDecoder() { + DCHECK(message_loop_->BelongsToCurrentThread()); + DCHECK(!decoders_.empty()); + + audio_decoder_ = decoders_.front(); + decoders_.pop_front(); + DCHECK(audio_decoder_); + audio_decoder_->Initialize( + input_stream_, + BindToCurrentLoop(base::Bind( + &AudioDecoderSelector::DecoderInitDone, + weak_ptr_factory_.GetWeakPtr())), + statistics_cb_); +} + +void AudioDecoderSelector::DecoderInitDone(PipelineStatus status) { + DCHECK(message_loop_->BelongsToCurrentThread()); + + if (status != PIPELINE_OK) { + if (!decoders_.empty()) + InitializeNextDecoder(); + else + base::ResetAndReturn(&select_decoder_cb_).Run(NULL, NULL); + return; + } + + decoders_.clear(); + base::ResetAndReturn(&select_decoder_cb_).Run(audio_decoder_, + decrypted_stream_); +} + +} // namespace media diff --git a/media/filters/audio_decoder_selector.h b/media/filters/audio_decoder_selector.h new file mode 100644 index 0000000..84a483b --- /dev/null +++ b/media/filters/audio_decoder_selector.h @@ -0,0 +1,86 @@ +// 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 MEDIA_FILTERS_AUDIO_DECODER_SELECTOR_H_ +#define MEDIA_FILTERS_AUDIO_DECODER_SELECTOR_H_ + +#include <list> + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "media/base/audio_decoder.h" +#include "media/base/decryptor.h" +#include "media/base/demuxer_stream.h" + +namespace base { +class MessageLoopProxy; +} + +namespace media { + +class DecoderBuffer; +class DecryptingDemuxerStream; +class Decryptor; + +// AudioDecoderSelector (creates if necessary and) initializes the proper +// AudioDecoder for a given DemuxerStream. If the given DemuxerStream is +// encrypted, a DecryptingDemuxerStream may also be created. +class MEDIA_EXPORT AudioDecoderSelector { + public: + typedef std::list<scoped_refptr<AudioDecoder> > AudioDecoderList; + + // Indicates completion of AudioDecoder selection. + // - First parameter: The initialized AudioDecoder. If it's set to NULL, then + // AudioDecoder initialization failed. + // - Second parameter: The initialized DecryptingDemuxerStream. If it's not + // NULL, then a DecryptingDemuxerStream is created and initialized to do + // decryption for the initialized AudioDecoder. + // Note: The caller owns selected AudioDecoder and DecryptingDemuxerStream. + // The caller should call DecryptingDemuxerStream::Reset() before + // calling AudioDecoder::Reset() to release any pending decryption or read. + typedef base::Callback< + void(const scoped_refptr<AudioDecoder>&, + const scoped_refptr<DecryptingDemuxerStream>&)> SelectDecoderCB; + + // |set_decryptor_ready_cb| is optional. If |set_decryptor_ready_cb| is null, + // no decryptor will be available to perform decryption. + AudioDecoderSelector( + const scoped_refptr<base::MessageLoopProxy>& message_loop, + const AudioDecoderList& decoders, + const SetDecryptorReadyCB& set_decryptor_ready_cb); + ~AudioDecoderSelector(); + + // Initializes and selects an AudioDecoder that can decode the |stream|. + // Selected AudioDecoder (and DecryptingDemuxerStream) is returned via + // the |select_decoder_cb|. + void SelectAudioDecoder(const scoped_refptr<DemuxerStream>& stream, + const StatisticsCB& statistics_cb, + const SelectDecoderCB& select_decoder_cb); + + private: + void DecryptingAudioDecoderInitDone(PipelineStatus status); + void DecryptingDemuxerStreamInitDone(PipelineStatus status); + void InitializeNextDecoder(); + void DecoderInitDone(PipelineStatus status); + + scoped_refptr<base::MessageLoopProxy> message_loop_; + AudioDecoderList decoders_; + SetDecryptorReadyCB set_decryptor_ready_cb_; + + scoped_refptr<DemuxerStream> input_stream_; + StatisticsCB statistics_cb_; + SelectDecoderCB select_decoder_cb_; + + scoped_refptr<AudioDecoder> audio_decoder_; + scoped_refptr<DecryptingDemuxerStream> decrypted_stream_; + + base::WeakPtrFactory<AudioDecoderSelector> weak_ptr_factory_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(AudioDecoderSelector); +}; + +} // namespace media + +#endif // MEDIA_FILTERS_AUDIO_DECODER_SELECTOR_H_ diff --git a/media/filters/audio_decoder_selector_unittest.cc b/media/filters/audio_decoder_selector_unittest.cc new file mode 100644 index 0000000..7b0631a --- /dev/null +++ b/media/filters/audio_decoder_selector_unittest.cc @@ -0,0 +1,242 @@ +// 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 <vector> + +#include "base/bind.h" +#include "base/message_loop.h" +#include "media/base/gmock_callback_support.h" +#include "media/base/mock_filters.h" +#include "media/filters/audio_decoder_selector.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::IsNull; +using ::testing::NiceMock; +using ::testing::NotNull; +using ::testing::Return; +using ::testing::ReturnRef; +using ::testing::StrictMock; + +namespace media { + +class AudioDecoderSelectorTest : public ::testing::Test { + public: + enum DecryptorCapability { + kNoDecryptor, + kDecryptOnly, + kDecryptAndDecode + }; + + AudioDecoderSelectorTest() + : clear_audio_config_( + kCodecVorbis, 16, CHANNEL_LAYOUT_STEREO, 44100, NULL, 0, false), + encrypted_audio_config_( + kCodecVorbis, 16, CHANNEL_LAYOUT_STEREO, 44100, NULL, 0, true), + demuxer_stream_(new StrictMock<MockDemuxerStream>()), + decryptor_(new NiceMock<MockDecryptor>()), + decoder_1_(new StrictMock<MockAudioDecoder>()), + decoder_2_(new StrictMock<MockAudioDecoder>()) { + all_decoders_.push_back(decoder_1_); + all_decoders_.push_back(decoder_2_); + + EXPECT_CALL(*demuxer_stream_, type()) + .WillRepeatedly(Return(DemuxerStream::AUDIO)); + } + + MOCK_METHOD1(OnStatistics, void(const PipelineStatistics&)); + MOCK_METHOD1(SetDecryptorReadyCallback, void(const media::DecryptorReadyCB&)); + MOCK_METHOD2(OnDecoderSelected, + void(const scoped_refptr<AudioDecoder>&, + const scoped_refptr<DecryptingDemuxerStream>&)); + + void UseClearStream() { + EXPECT_CALL(*demuxer_stream_, audio_decoder_config()) + .WillRepeatedly(ReturnRef(clear_audio_config_)); + } + + void UseEncryptedStream() { + EXPECT_CALL(*demuxer_stream_, audio_decoder_config()) + .WillRepeatedly(ReturnRef(encrypted_audio_config_)); + } + + void InitializeDecoderSelector(DecryptorCapability decryptor_capability, + int num_decoders) { + SetDecryptorReadyCB set_decryptor_ready_cb; + if (decryptor_capability == kDecryptOnly || + decryptor_capability == kDecryptAndDecode) { + set_decryptor_ready_cb = base::Bind( + &AudioDecoderSelectorTest::SetDecryptorReadyCallback, + base::Unretained(this)); + + EXPECT_CALL(*this, SetDecryptorReadyCallback(_)) + .WillRepeatedly(RunCallback<0>(decryptor_.get())); + + if (decryptor_capability == kDecryptOnly) { + EXPECT_CALL(*decryptor_, InitializeAudioDecoderMock(_, _)) + .WillRepeatedly(RunCallback<1>(false)); + } else { + EXPECT_CALL(*decryptor_, InitializeAudioDecoderMock(_, _)) + .WillRepeatedly(RunCallback<1>(true)); + } + } + + DCHECK_GE(all_decoders_.size(), static_cast<size_t>(num_decoders)); + AudioDecoderSelector::AudioDecoderList decoders( + all_decoders_.begin(), all_decoders_.begin() + num_decoders); + + decoder_selector_.reset(new AudioDecoderSelector( + message_loop_.message_loop_proxy(), + decoders, + set_decryptor_ready_cb)); + } + + void SelectDecoder() { + decoder_selector_->SelectAudioDecoder( + demuxer_stream_, + base::Bind(&AudioDecoderSelectorTest::OnStatistics, + base::Unretained(this)), + base::Bind(&AudioDecoderSelectorTest::OnDecoderSelected, + base::Unretained(this))); + message_loop_.RunUntilIdle(); + } + + // Fixture members. + scoped_ptr<AudioDecoderSelector> decoder_selector_; + AudioDecoderConfig clear_audio_config_; + AudioDecoderConfig encrypted_audio_config_; + scoped_refptr<StrictMock<MockDemuxerStream> > demuxer_stream_; + // Use NiceMock since we don't care about most of calls on the decryptor, e.g. + // RegisterKeyAddedCB(). + scoped_ptr<NiceMock<MockDecryptor> > decryptor_; + scoped_refptr<StrictMock<MockAudioDecoder> > decoder_1_; + scoped_refptr<StrictMock<MockAudioDecoder> > decoder_2_; + std::vector<scoped_refptr<AudioDecoder> > all_decoders_; + MessageLoop message_loop_; + + private: + DISALLOW_COPY_AND_ASSIGN(AudioDecoderSelectorTest); +}; + +// The stream is not encrypted but we have no clear decoder. No decoder can be +// selected. +TEST_F(AudioDecoderSelectorTest, ClearStream_NoDecryptor_NoClearDecoder) { + UseClearStream(); + InitializeDecoderSelector(kNoDecryptor, 0); + + EXPECT_CALL(*this, OnDecoderSelected(IsNull(), IsNull())); + + SelectDecoder(); +} + +// The stream is not encrypted and we have one clear decoder. The decoder +// will be selected. +TEST_F(AudioDecoderSelectorTest, ClearStream_NoDecryptor_OneClearDecoder) { + UseClearStream(); + InitializeDecoderSelector(kNoDecryptor, 1); + + EXPECT_CALL(*decoder_1_, Initialize(_, _, _)) + .WillOnce(RunCallback<1>(PIPELINE_OK)); + EXPECT_CALL(*this, OnDecoderSelected(scoped_refptr<AudioDecoder>(decoder_1_), + IsNull())); + + SelectDecoder(); +} + +// The stream is not encrypted and we have multiple clear decoders. The first +// decoder that can decode the input stream will be selected. +TEST_F(AudioDecoderSelectorTest, ClearStream_NoDecryptor_MultipleClearDecoder) { + UseClearStream(); + InitializeDecoderSelector(kNoDecryptor, 2); + + EXPECT_CALL(*decoder_1_, Initialize(_, _, _)) + .WillOnce(RunCallback<1>(DECODER_ERROR_NOT_SUPPORTED)); + EXPECT_CALL(*decoder_2_, Initialize(_, _, _)) + .WillOnce(RunCallback<1>(PIPELINE_OK)); + EXPECT_CALL(*this, OnDecoderSelected(scoped_refptr<AudioDecoder>(decoder_2_), + IsNull())); + + SelectDecoder(); +} + +// There is a decryptor but the stream is not encrypted. The decoder will be +// selected. +TEST_F(AudioDecoderSelectorTest, ClearStream_HasDecryptor) { + UseClearStream(); + InitializeDecoderSelector(kDecryptOnly, 1); + + EXPECT_CALL(*decoder_1_, Initialize(_, _, _)) + .WillOnce(RunCallback<1>(PIPELINE_OK)); + EXPECT_CALL(*this, OnDecoderSelected(scoped_refptr<AudioDecoder>(decoder_1_), + IsNull())); + + SelectDecoder(); +} + +// The stream is encrypted and there's no decryptor. No decoder can be selected. +TEST_F(AudioDecoderSelectorTest, EncryptedStream_NoDecryptor) { + UseEncryptedStream(); + InitializeDecoderSelector(kNoDecryptor, 1); + + EXPECT_CALL(*this, OnDecoderSelected(IsNull(), IsNull())); + + SelectDecoder(); +} + +// Decryptor can only do decryption and there's no decoder available. No decoder +// can be selected. +TEST_F(AudioDecoderSelectorTest, EncryptedStream_DecryptOnly_NoClearDecoder) { + UseEncryptedStream(); + InitializeDecoderSelector(kDecryptOnly, 0); + + EXPECT_CALL(*this, OnDecoderSelected(IsNull(), IsNull())); + + SelectDecoder(); +} + +// Decryptor can do decryption-only and there's a decoder available. The decoder +// will be selected and a DecryptingDemuxerStream will be created. +TEST_F(AudioDecoderSelectorTest, EncryptedStream_DecryptOnly_OneClearDecoder) { + UseEncryptedStream(); + InitializeDecoderSelector(kDecryptOnly, 1); + + EXPECT_CALL(*decoder_1_, Initialize(_, _, _)) + .WillOnce(RunCallback<1>(PIPELINE_OK)); + EXPECT_CALL(*this, OnDecoderSelected(scoped_refptr<AudioDecoder>(decoder_1_), + NotNull())); + + SelectDecoder(); +} + +// Decryptor can only do decryption and there are multiple decoders available. +// The first decoder that can decode the input stream will be selected and +// a DecryptingDemuxerStream will be created. +TEST_F(AudioDecoderSelectorTest, + EncryptedStream_DecryptOnly_MultipleClearDecoder) { + UseEncryptedStream(); + InitializeDecoderSelector(kDecryptOnly, 2); + + EXPECT_CALL(*decoder_1_, Initialize(_, _, _)) + .WillOnce(RunCallback<1>(DECODER_ERROR_NOT_SUPPORTED)); + EXPECT_CALL(*decoder_2_, Initialize(_, _, _)) + .WillOnce(RunCallback<1>(PIPELINE_OK)); + EXPECT_CALL(*this, OnDecoderSelected(scoped_refptr<AudioDecoder>(decoder_2_), + NotNull())); + + SelectDecoder(); +} + +// Decryptor can do decryption and decoding. A DecryptingAudioDecoder will be +// created and selected. The clear decoders should not be touched at all. +// No DecryptingDemuxerStream should to be created. +TEST_F(AudioDecoderSelectorTest, EncryptedStream_DecryptAndDecode) { + UseEncryptedStream(); + InitializeDecoderSelector(kDecryptAndDecode, 1); + + EXPECT_CALL(*this, OnDecoderSelected(NotNull(), IsNull())); + + SelectDecoder(); +} + +} // namespace media diff --git a/media/filters/audio_renderer_impl.cc b/media/filters/audio_renderer_impl.cc index c070481..20a6a2b 100644 --- a/media/filters/audio_renderer_impl.cc +++ b/media/filters/audio_renderer_impl.cc @@ -19,11 +19,16 @@ #include "media/base/bind_to_loop.h" #include "media/base/demuxer_stream.h" #include "media/base/media_switches.h" +#include "media/filters/audio_decoder_selector.h" +#include "media/filters/decrypting_demuxer_stream.h" namespace media { -AudioRendererImpl::AudioRendererImpl(media::AudioRendererSink* sink) +AudioRendererImpl::AudioRendererImpl( + media::AudioRendererSink* sink, + const SetDecryptorReadyCB& set_decryptor_ready_cb) : sink_(sink), + set_decryptor_ready_cb_(set_decryptor_ready_cb), state_(kUninitialized), pending_read_(false), received_end_of_stream_(false), @@ -92,6 +97,18 @@ void AudioRendererImpl::DoPause() { void AudioRendererImpl::Flush(const base::Closure& callback) { DCHECK(pipeline_thread_checker_.CalledOnValidThread()); + + if (decrypting_demuxer_stream_) { + decrypting_demuxer_stream_->Reset(base::Bind( + &AudioRendererImpl::ResetDecoder, this, callback)); + return; + } + + decoder_->Reset(callback); +} + +void AudioRendererImpl::ResetDecoder(const base::Closure& callback) { + DCHECK(pipeline_thread_checker_.CalledOnValidThread()); decoder_->Reset(callback); } @@ -180,32 +197,26 @@ void AudioRendererImpl::Initialize(const scoped_refptr<DemuxerStream>& stream, disabled_cb_ = disabled_cb; error_cb_ = error_cb; - scoped_ptr<AudioDecoderList> decoder_list(new AudioDecoderList(decoders)); - InitializeNextDecoder(stream, decoder_list.Pass()); -} + scoped_ptr<AudioDecoderSelector> decoder_selector( + new AudioDecoderSelector(base::MessageLoopProxy::current(), + decoders, + set_decryptor_ready_cb_)); -void AudioRendererImpl::InitializeNextDecoder( - const scoped_refptr<DemuxerStream>& demuxer_stream, - scoped_ptr<AudioDecoderList> decoders) { - DCHECK(pipeline_thread_checker_.CalledOnValidThread()); - DCHECK(!decoders->empty()); - - scoped_refptr<AudioDecoder> decoder = decoders->front(); - decoders->pop_front(); - - DCHECK(decoder); - decoder_ = decoder; - decoder->Initialize( - demuxer_stream, BindToLoop(base::MessageLoopProxy::current(), base::Bind( - &AudioRendererImpl::OnDecoderInitDone, this, demuxer_stream, - base::Passed(&decoders))), - statistics_cb_); + // To avoid calling |decoder_selector| methods and passing ownership of + // |decoder_selector| in the same line. + AudioDecoderSelector* decoder_selector_ptr = decoder_selector.get(); + + decoder_selector_ptr->SelectAudioDecoder( + stream, + statistics_cb, + base::Bind(&AudioRendererImpl::OnDecoderSelected, this, + base::Passed(&decoder_selector))); } -void AudioRendererImpl::OnDecoderInitDone( - const scoped_refptr<DemuxerStream>& demuxer_stream, - scoped_ptr<AudioDecoderList> decoders, - PipelineStatus status) { +void AudioRendererImpl::OnDecoderSelected( + scoped_ptr<AudioDecoderSelector> decoder_selector, + const scoped_refptr<AudioDecoder>& selected_decoder, + const scoped_refptr<DecryptingDemuxerStream>& decrypting_demuxer_stream) { DCHECK(pipeline_thread_checker_.CalledOnValidThread()); if (state_ == kStopped) { @@ -213,15 +224,13 @@ void AudioRendererImpl::OnDecoderInitDone( return; } - if (!decoders->empty() && status == DECODER_ERROR_NOT_SUPPORTED) { - InitializeNextDecoder(demuxer_stream, decoders.Pass()); + if (!selected_decoder) { + base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); return; } - if (status != PIPELINE_OK) { - base::ResetAndReturn(&init_cb_).Run(status); - return; - } + decoder_ = selected_decoder; + decrypting_demuxer_stream_ = decrypting_demuxer_stream; int sample_rate = decoder_->samples_per_second(); int buffer_size = GetHighLatencyOutputBufferSize(sample_rate); diff --git a/media/filters/audio_renderer_impl.h b/media/filters/audio_renderer_impl.h index 1d716be..9158aa1 100644 --- a/media/filters/audio_renderer_impl.h +++ b/media/filters/audio_renderer_impl.h @@ -29,19 +29,23 @@ #include "media/base/audio_renderer.h" #include "media/base/audio_renderer_sink.h" #include "media/base/buffers.h" +#include "media/base/decryptor.h" #include "media/filters/audio_renderer_algorithm.h" namespace media { +class AudioDecoderSelector; class AudioSplicer; +class DecryptingDemuxerStream; class MEDIA_EXPORT AudioRendererImpl : public AudioRenderer, - NON_EXPORTED_BASE(public media::AudioRendererSink::RenderCallback) { + NON_EXPORTED_BASE(public AudioRendererSink::RenderCallback) { public: // Methods called on Render thread ------------------------------------------ // An AudioRendererSink is used as the destination for the rendered audio. - explicit AudioRendererImpl(media::AudioRendererSink* sink); + AudioRendererImpl(AudioRendererSink* sink, + const SetDecryptorReadyCB& set_decryptor_ready_cb); // Methods called on pipeline thread ---------------------------------------- // AudioRenderer implementation. @@ -138,20 +142,17 @@ class MEDIA_EXPORT AudioRendererImpl // in the kPrerolling state. bool IsBeforePrerollTime(const scoped_refptr<Buffer>& buffer); - // Pops the front of |decoders|, assigns it to |decoder_| and then - // calls initialize on the new decoder. - void InitializeNextDecoder(const scoped_refptr<DemuxerStream>& demuxer_stream, - scoped_ptr<AudioDecoderList> decoders); + // Called when |decoder_selector_| selected the |selected_decoder|. + // |decrypting_demuxer_stream| was also populated if a DecryptingDemuxerStream + // created to help decrypt the encrypted stream. + // Note: |decoder_selector| is passed here to keep the AudioDecoderSelector + // alive until OnDecoderSelected() finishes. + void OnDecoderSelected( + scoped_ptr<AudioDecoderSelector> decoder_selector, + const scoped_refptr<AudioDecoder>& selected_decoder, + const scoped_refptr<DecryptingDemuxerStream>& decrypting_demuxer_stream); - // Called when |decoder_| initialization completes. - // |demuxer_stream| & |decoders| are used if initialization failed and - // InitializeNextDecoder() needs to be called again. - void OnDecoderInitDone(const scoped_refptr<DemuxerStream>& demuxer_stream, - scoped_ptr<AudioDecoderList> decoders, - PipelineStatus status); - - // Audio decoder. - scoped_refptr<AudioDecoder> decoder_; + void ResetDecoder(const base::Closure& callback); scoped_ptr<AudioSplicer> splicer_; @@ -161,6 +162,12 @@ class MEDIA_EXPORT AudioRendererImpl // audio, pipeline, and decoder threads may deadlock. scoped_refptr<media::AudioRendererSink> sink_; + SetDecryptorReadyCB set_decryptor_ready_cb_; + + // These two will be set by AudioDecoderSelector::SelectAudioDecoder(). + scoped_refptr<AudioDecoder> decoder_; + scoped_refptr<DecryptingDemuxerStream> decrypting_demuxer_stream_; + // Ensures certain methods are always called on the pipeline thread. base::ThreadChecker pipeline_thread_checker_; diff --git a/media/filters/audio_renderer_impl_unittest.cc b/media/filters/audio_renderer_impl_unittest.cc index 23f2078..a17c6b0 100644 --- a/media/filters/audio_renderer_impl_unittest.cc +++ b/media/filters/audio_renderer_impl_unittest.cc @@ -20,6 +20,7 @@ using ::testing::_; using ::testing::AnyNumber; using ::testing::Invoke; using ::testing::Return; +using ::testing::ReturnRef; using ::testing::NiceMock; using ::testing::StrictMock; @@ -34,11 +35,16 @@ class AudioRendererImplTest : public ::testing::Test { public: // Give the decoder some non-garbage media properties. AudioRendererImplTest() - : renderer_(new AudioRendererImpl(new NiceMock<MockAudioRendererSink>())), + : renderer_(new AudioRendererImpl(new NiceMock<MockAudioRendererSink>(), + SetDecryptorReadyCB())), demuxer_stream_(new MockDemuxerStream()), - decoder_(new MockAudioDecoder()) { + decoder_(new MockAudioDecoder()), + audio_config_(kCodecVorbis, 16, CHANNEL_LAYOUT_STEREO, + 44100, NULL, 0, false) { EXPECT_CALL(*demuxer_stream_, type()) .WillRepeatedly(Return(DemuxerStream::AUDIO)); + EXPECT_CALL(*demuxer_stream_, audio_decoder_config()) + .WillRepeatedly(ReturnRef(audio_config_)); // Queue all reads from the decoder by default. ON_CALL(*decoder_, Read(_)) @@ -236,6 +242,7 @@ class AudioRendererImplTest : public ::testing::Test { AudioRendererImpl::AudioDecoderList decoders_; AudioDecoder::ReadCB read_cb_; scoped_ptr<AudioTimestampHelper> next_timestamp_; + AudioDecoderConfig audio_config_; MessageLoop message_loop_; private: @@ -267,8 +274,8 @@ TEST_F(AudioRendererImplTest, Initialize_Successful) { TEST_F(AudioRendererImplTest, Initialize_DecoderInitFailure) { EXPECT_CALL(*decoder_, Initialize(_, _, _)) - .WillOnce(RunCallback<1>(PIPELINE_ERROR_DECODE)); - InitializeWithStatus(PIPELINE_ERROR_DECODE); + .WillOnce(RunCallback<1>(DECODER_ERROR_NOT_SUPPORTED)); + InitializeWithStatus(DECODER_ERROR_NOT_SUPPORTED); // We should have no reads. EXPECT_TRUE(read_cb_.is_null()); diff --git a/media/filters/pipeline_integration_test_base.cc b/media/filters/pipeline_integration_test_base.cc index 1acee61..22bb922 100644 --- a/media/filters/pipeline_integration_test_base.cc +++ b/media/filters/pipeline_integration_test_base.cc @@ -217,7 +217,7 @@ PipelineIntegrationTestBase::CreateFilterCollection( if (hashing_enabled_) audio_sink_->StartAudioHashForTesting(); scoped_refptr<AudioRendererImpl> audio_renderer(new AudioRendererImpl( - audio_sink_)); + audio_sink_, SetDecryptorReadyCB())); // Disable underflow if hashing is enabled. if (hashing_enabled_) audio_renderer->DisableUnderflowForTesting(); diff --git a/media/media.gyp b/media/media.gyp index 0bb6e00..f242707 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -261,6 +261,8 @@ 'crypto/aes_decryptor.h', 'ffmpeg/ffmpeg_common.cc', 'ffmpeg/ffmpeg_common.h', + 'filters/audio_decoder_selector.cc', + 'filters/audio_decoder_selector.h', 'filters/audio_file_reader.cc', 'filters/audio_file_reader.h', 'filters/audio_renderer_algorithm.cc', @@ -705,6 +707,7 @@ 'base/yuv_convert_unittest.cc', 'crypto/aes_decryptor_unittest.cc', 'ffmpeg/ffmpeg_common_unittest.cc', + 'filters/audio_decoder_selector_unittest.cc', 'filters/audio_file_reader_unittest.cc', 'filters/audio_renderer_algorithm_unittest.cc', 'filters/audio_renderer_impl_unittest.cc', diff --git a/media/tools/player_x11/player_x11.cc b/media/tools/player_x11/player_x11.cc index 42b891f..02b9fe9 100644 --- a/media/tools/player_x11/player_x11.cc +++ b/media/tools/player_x11/player_x11.cc @@ -15,6 +15,7 @@ #include "base/threading/thread.h" #include "media/audio/audio_manager.h" #include "media/audio/null_audio_sink.h" +#include "media/base/decryptor.h" #include "media/base/filter_collection.h" #include "media/base/media.h" #include "media/base/media_log.h" @@ -125,7 +126,8 @@ bool InitPipeline(const scoped_refptr<base::MessageLoopProxy>& message_loop, collection->AddVideoRenderer(g_video_renderer); collection->AddAudioRenderer( - new media::AudioRendererImpl(new media::NullAudioSink())); + new media::AudioRendererImpl(new media::NullAudioSink(), + media::SetDecryptorReadyCB())); // Create the pipeline and start it. *pipeline = new media::Pipeline(message_loop, new media::MediaLog()); |