summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-14 22:51:35 +0000
committerxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-14 22:51:35 +0000
commit893f2828f68362365ab77045e7b4a1c0f5806d6c (patch)
tree7e857a87f4f3c748ca5852f5c38a30dc88697b5c /media
parent042e77c20d44abe5efe17cedb0484e0cb5492f4a (diff)
downloadchromium_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.cc155
-rw-r--r--media/filters/audio_decoder_selector.h86
-rw-r--r--media/filters/audio_decoder_selector_unittest.cc242
-rw-r--r--media/filters/audio_renderer_impl.cc69
-rw-r--r--media/filters/audio_renderer_impl.h37
-rw-r--r--media/filters/audio_renderer_impl_unittest.cc15
-rw-r--r--media/filters/pipeline_integration_test_base.cc2
-rw-r--r--media/media.gyp3
-rw-r--r--media/tools/player_x11/player_x11.cc4
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());