Commit 1492be9e authored by xhwang's avatar xhwang Committed by Commit bot

media: Support better decoder switching

Today on reinitialization error, or decode error of the first buffer,
DecoderStream will fall back to decoders left in the DecoderSelector.
Since we will have less and less decoders in the DecoderSelector, the
ability to switch decoders is limited.

This CL updates DecoderSelector so that it takes a callback to create
a list of decoders, instead of taking the list of decoders directly.
This allows the DecoderSelector to select a decoder that has been
tried or selected before, such that upon decoder reinitialization error
(e.g. switching from clear to encrypted config), or upon decode error of
the first buffer, we always have the full list of decoders to select
from.

Two mechanisms are added to avoid trying the same failing decoder again:

1. Blacklist

When SelectDecoder() is called due to reinitialization failure, or
decoding failure of the first buffer, the existing decoder is listed as
the "blacklisted decoder" such that DecoderSelector will not even try
it.

There is one exception though. When DecryptingDemuxerStream is selected,
since the input steam is changed (encrypted -> clear), the blacklisted
decoder is ignored. For example, FFmpegVideoDecoder is slected first for
a clear stream. Then on a config change, the stream becomes encrypted so
FFmpegVideoDecoder fails to reinitialize and we need to select a new
decoder. After DecryptingDemuxerStream is selected, we should still be
able to use FFmpegVideoDecoder to decode the decrypted stream.

2. Fall back at most once

If fallback has already happened and decode of the first buffer failed
again, we don't try to fallback again. This is to avoid the infinite
loop of "select decoder 1 -> decode error -> select decoder 2 -> decode
error -> select decoder 1".

BUG=695595
TEST=Updated/Added unittests.

Review-Url: https://codereview.chromium.org/2837613004
Cr-Commit-Position: refs/heads/master@{#469579}
parent 6815aefe
...@@ -43,7 +43,11 @@ class MEDIA_EXPORT AudioDecoder { ...@@ -43,7 +43,11 @@ class MEDIA_EXPORT AudioDecoder {
// depends on |this|. // depends on |this|.
virtual ~AudioDecoder(); virtual ~AudioDecoder();
// Returns the name of the decoder for logging purpose. // Returns the name of the decoder for logging and decoder selection purposes.
// This name should be available immediately after construction (e.g. before
// Initialize() is called). It should also be stable in the sense that the
// name does not change across multiple constructions.
// TODO(xhwang): Rename this method since the name is not only for display.
virtual std::string GetDisplayName() const = 0; virtual std::string GetDisplayName() const = 0;
// Initializes an AudioDecoder with |config|, executing the |init_cb| upon // Initializes an AudioDecoder with |config|, executing the |init_cb| upon
......
...@@ -88,9 +88,7 @@ class MEDIA_EXPORT AudioDecoderConfig { ...@@ -88,9 +88,7 @@ class MEDIA_EXPORT AudioDecoderConfig {
} }
// Sets the config to be encrypted or not encrypted manually. This can be // Sets the config to be encrypted or not encrypted manually. This can be
// useful for decryptors that decrypts an encrypted stream to a clear stream, // useful for decryptors that decrypts an encrypted stream to a clear stream.
// or for decoder selectors that wants to select decrypting decoders instead
// of clear decoders.
void SetIsEncrypted(bool is_encrypted); void SetIsEncrypted(bool is_encrypted);
private: private:
......
...@@ -88,24 +88,26 @@ VideoRotation MockDemuxerStream::video_rotation() { ...@@ -88,24 +88,26 @@ VideoRotation MockDemuxerStream::video_rotation() {
return VIDEO_ROTATION_0; return VIDEO_ROTATION_0;
} }
std::string MockVideoDecoder::GetDisplayName() const { MockVideoDecoder::MockVideoDecoder(const std::string& decoder_name)
return "MockVideoDecoder"; : decoder_name_(decoder_name) {
}
MockVideoDecoder::MockVideoDecoder() {
ON_CALL(*this, CanReadWithoutStalling()).WillByDefault(Return(true)); ON_CALL(*this, CanReadWithoutStalling()).WillByDefault(Return(true));
} }
std::string MockAudioDecoder::GetDisplayName() const {
return "MockAudioDecoder";
}
MockVideoDecoder::~MockVideoDecoder() {} MockVideoDecoder::~MockVideoDecoder() {}
MockAudioDecoder::MockAudioDecoder() {} std::string MockVideoDecoder::GetDisplayName() const {
return decoder_name_;
}
MockAudioDecoder::MockAudioDecoder(const std::string& decoder_name)
: decoder_name_(decoder_name) {}
MockAudioDecoder::~MockAudioDecoder() {} MockAudioDecoder::~MockAudioDecoder() {}
std::string MockAudioDecoder::GetDisplayName() const {
return decoder_name_;
}
MockRendererClient::MockRendererClient() {} MockRendererClient::MockRendererClient() {}
MockRendererClient::~MockRendererClient() {} MockRendererClient::~MockRendererClient() {}
......
...@@ -179,7 +179,8 @@ class MockDemuxerStream : public DemuxerStream { ...@@ -179,7 +179,8 @@ class MockDemuxerStream : public DemuxerStream {
class MockVideoDecoder : public VideoDecoder { class MockVideoDecoder : public VideoDecoder {
public: public:
MockVideoDecoder(); explicit MockVideoDecoder(
const std::string& decoder_name = "MockVideoDecoder");
virtual ~MockVideoDecoder(); virtual ~MockVideoDecoder();
// VideoDecoder implementation. // VideoDecoder implementation.
...@@ -197,12 +198,14 @@ class MockVideoDecoder : public VideoDecoder { ...@@ -197,12 +198,14 @@ class MockVideoDecoder : public VideoDecoder {
MOCK_CONST_METHOD0(CanReadWithoutStalling, bool()); MOCK_CONST_METHOD0(CanReadWithoutStalling, bool());
private: private:
std::string decoder_name_;
DISALLOW_COPY_AND_ASSIGN(MockVideoDecoder); DISALLOW_COPY_AND_ASSIGN(MockVideoDecoder);
}; };
class MockAudioDecoder : public AudioDecoder { class MockAudioDecoder : public AudioDecoder {
public: public:
MockAudioDecoder(); explicit MockAudioDecoder(
const std::string& decoder_name = "MockAudioDecoder");
virtual ~MockAudioDecoder(); virtual ~MockAudioDecoder();
// AudioDecoder implementation. // AudioDecoder implementation.
...@@ -218,6 +221,7 @@ class MockAudioDecoder : public AudioDecoder { ...@@ -218,6 +221,7 @@ class MockAudioDecoder : public AudioDecoder {
MOCK_METHOD1(Reset, void(const base::Closure&)); MOCK_METHOD1(Reset, void(const base::Closure&));
private: private:
std::string decoder_name_;
DISALLOW_COPY_AND_ASSIGN(MockAudioDecoder); DISALLOW_COPY_AND_ASSIGN(MockAudioDecoder);
}; };
......
...@@ -44,7 +44,10 @@ class MEDIA_EXPORT VideoDecoder { ...@@ -44,7 +44,10 @@ class MEDIA_EXPORT VideoDecoder {
// depends on |this|. // depends on |this|.
virtual ~VideoDecoder(); virtual ~VideoDecoder();
// Returns the name of the decoder for logging purpose. // Returns the name of the decoder for logging and decoder selection purposes.
// This name should be available immediately after construction (e.g. before
// Initialize() is called). It should also be stable in the sense that the
// name does not change across multiple constructions.
virtual std::string GetDisplayName() const = 0; virtual std::string GetDisplayName() const = 0;
// Initializes a VideoDecoder with the given |config|, executing the // Initializes a VideoDecoder with the given |config|, executing the
......
...@@ -120,9 +120,7 @@ class MEDIA_EXPORT VideoDecoderConfig { ...@@ -120,9 +120,7 @@ class MEDIA_EXPORT VideoDecoderConfig {
base::Optional<HDRMetadata> hdr_metadata() const; base::Optional<HDRMetadata> hdr_metadata() const;
// Sets the config to be encrypted or not encrypted manually. This can be // Sets the config to be encrypted or not encrypted manually. This can be
// useful for decryptors that decrypts an encrypted stream to a clear stream, // useful for decryptors that decrypts an encrypted stream to a clear stream.
// or for decoder selectors that wants to select decrypting decoders instead
// of clear decoders.
void SetIsEncrypted(bool is_encrypted); void SetIsEncrypted(bool is_encrypted);
private: private:
......
...@@ -42,6 +42,9 @@ MATCHER(ClearConfig, "") { ...@@ -42,6 +42,9 @@ MATCHER(ClearConfig, "") {
namespace media { namespace media {
const char kDecoder1[] = "Decoder1";
const char kDecoder2[] = "Decoder2";
class AudioDecoderSelectorTest : public ::testing::Test { class AudioDecoderSelectorTest : public ::testing::Test {
public: public:
enum DecryptorCapability { enum DecryptorCapability {
...@@ -55,8 +58,8 @@ class AudioDecoderSelectorTest : public ::testing::Test { ...@@ -55,8 +58,8 @@ class AudioDecoderSelectorTest : public ::testing::Test {
: traits_(&media_log_), : traits_(&media_log_),
demuxer_stream_( demuxer_stream_(
new StrictMock<MockDemuxerStream>(DemuxerStream::AUDIO)), new StrictMock<MockDemuxerStream>(DemuxerStream::AUDIO)),
decoder_1_(new StrictMock<MockAudioDecoder>()), decoder_1_(new StrictMock<MockAudioDecoder>(kDecoder1)),
decoder_2_(new StrictMock<MockAudioDecoder>()) { decoder_2_(new StrictMock<MockAudioDecoder>(kDecoder2)) {
all_decoders_.push_back(decoder_1_); all_decoders_.push_back(decoder_1_);
all_decoders_.push_back(decoder_2_); all_decoders_.push_back(decoder_2_);
// |cdm_context_| and |decryptor_| are conditionally created in // |cdm_context_| and |decryptor_| are conditionally created in
...@@ -88,6 +91,10 @@ class AudioDecoderSelectorTest : public ::testing::Test { ...@@ -88,6 +91,10 @@ class AudioDecoderSelectorTest : public ::testing::Test {
demuxer_stream_->set_audio_decoder_config(encrypted_audio_config); demuxer_stream_->set_audio_decoder_config(encrypted_audio_config);
} }
ScopedVector<AudioDecoder> CreateVideoDecodersForTest() {
return std::move(all_decoders_);
}
void InitializeDecoderSelector(DecryptorCapability decryptor_capability, void InitializeDecoderSelector(DecryptorCapability decryptor_capability,
int num_decoders) { int num_decoders) {
if (decryptor_capability != kNoCdm) { if (decryptor_capability != kNoCdm) {
...@@ -111,12 +118,16 @@ class AudioDecoderSelectorTest : public ::testing::Test { ...@@ -111,12 +118,16 @@ class AudioDecoderSelectorTest : public ::testing::Test {
all_decoders_.begin() + num_decoders, all_decoders_.end()); all_decoders_.begin() + num_decoders, all_decoders_.end());
decoder_selector_.reset(new AudioDecoderSelector( decoder_selector_.reset(new AudioDecoderSelector(
message_loop_.task_runner(), std::move(all_decoders_), &media_log_)); message_loop_.task_runner(),
base::Bind(&AudioDecoderSelectorTest::CreateVideoDecodersForTest,
base::Unretained(this)),
&media_log_));
} }
void SelectDecoder() { void SelectDecoderWithBlacklist(const std::string& blacklisted_decoder) {
decoder_selector_->SelectDecoder( decoder_selector_->SelectDecoder(
&traits_, demuxer_stream_.get(), cdm_context_.get(), &traits_, demuxer_stream_.get(), cdm_context_.get(),
blacklisted_decoder,
base::Bind(&AudioDecoderSelectorTest::MockOnDecoderSelected, base::Bind(&AudioDecoderSelectorTest::MockOnDecoderSelected,
base::Unretained(this)), base::Unretained(this)),
base::Bind(&AudioDecoderSelectorTest::OnDecoderOutput), base::Bind(&AudioDecoderSelectorTest::OnDecoderOutput),
...@@ -124,6 +135,8 @@ class AudioDecoderSelectorTest : public ::testing::Test { ...@@ -124,6 +135,8 @@ class AudioDecoderSelectorTest : public ::testing::Test {
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
void SelectDecoder() { SelectDecoderWithBlacklist(""); }
void SelectDecoderAndDestroy() { void SelectDecoderAndDestroy() {
SelectDecoder(); SelectDecoder();
...@@ -168,18 +181,22 @@ class AudioDecoderSelectorTest : public ::testing::Test { ...@@ -168,18 +181,22 @@ class AudioDecoderSelectorTest : public ::testing::Test {
DISALLOW_COPY_AND_ASSIGN(AudioDecoderSelectorTest); DISALLOW_COPY_AND_ASSIGN(AudioDecoderSelectorTest);
}; };
// Tests for clear streams. // Tests for clear streams. The CDM will not be used for clear streams so
// DecryptorCapability doesn't really matter.
TEST_F(AudioDecoderSelectorTest, ClearStream_NoDecryptor_NoClearDecoder) { TEST_F(AudioDecoderSelectorTest, ClearStream_NoClearDecoder) {
UseClearStream(); UseClearStream();
InitializeDecoderSelector(kNoDecryptor, 0);
// DecoderSelector will not try decrypting decoders for clear stream, even
// if the CDM is capable of doing decrypt and decode.
InitializeDecoderSelector(kDecryptAndDecode, 0);
EXPECT_CALL(*this, OnDecoderSelected(IsNull(), IsNull())); EXPECT_CALL(*this, OnDecoderSelected(IsNull(), IsNull()));
SelectDecoder(); SelectDecoder();
} }
TEST_F(AudioDecoderSelectorTest, ClearStream_NoCdm_OneClearDecoder) { TEST_F(AudioDecoderSelectorTest, ClearStream_OneClearDecoder) {
UseClearStream(); UseClearStream();
InitializeDecoderSelector(kNoCdm, 1); InitializeDecoderSelector(kNoCdm, 1);
...@@ -190,7 +207,7 @@ TEST_F(AudioDecoderSelectorTest, ClearStream_NoCdm_OneClearDecoder) { ...@@ -190,7 +207,7 @@ TEST_F(AudioDecoderSelectorTest, ClearStream_NoCdm_OneClearDecoder) {
SelectDecoder(); SelectDecoder();
} }
TEST_F(AudioDecoderSelectorTest, Destroy_ClearStream_NoCdm_OneClearDecoder) { TEST_F(AudioDecoderSelectorTest, Destroy_ClearStream_OneClearDecoder) {
UseClearStream(); UseClearStream();
InitializeDecoderSelector(kNoCdm, 1); InitializeDecoderSelector(kNoCdm, 1);
...@@ -199,7 +216,7 @@ TEST_F(AudioDecoderSelectorTest, Destroy_ClearStream_NoCdm_OneClearDecoder) { ...@@ -199,7 +216,7 @@ TEST_F(AudioDecoderSelectorTest, Destroy_ClearStream_NoCdm_OneClearDecoder) {
SelectDecoderAndDestroy(); SelectDecoderAndDestroy();
} }
TEST_F(AudioDecoderSelectorTest, ClearStream_NoCdm_MultipleClearDecoder) { TEST_F(AudioDecoderSelectorTest, ClearStream_MultipleClearDecoder) {
UseClearStream(); UseClearStream();
InitializeDecoderSelector(kNoCdm, 2); InitializeDecoderSelector(kNoCdm, 2);
...@@ -212,8 +229,7 @@ TEST_F(AudioDecoderSelectorTest, ClearStream_NoCdm_MultipleClearDecoder) { ...@@ -212,8 +229,7 @@ TEST_F(AudioDecoderSelectorTest, ClearStream_NoCdm_MultipleClearDecoder) {
SelectDecoder(); SelectDecoder();
} }
TEST_F(AudioDecoderSelectorTest, TEST_F(AudioDecoderSelectorTest, Destroy_ClearStream_MultipleClearDecoder) {
Destroy_ClearStream_NoCdm_MultipleClearDecoder) {
UseClearStream(); UseClearStream();
InitializeDecoderSelector(kNoCdm, 2); InitializeDecoderSelector(kNoCdm, 2);
...@@ -224,90 +240,16 @@ TEST_F(AudioDecoderSelectorTest, ...@@ -224,90 +240,16 @@ TEST_F(AudioDecoderSelectorTest,
SelectDecoderAndDestroy(); SelectDecoderAndDestroy();
} }
TEST_F(AudioDecoderSelectorTest, ClearStream_NoDecryptor_OneClearDecoder) { TEST_F(AudioDecoderSelectorTest, ClearStream_BlackListedDecoder) {
UseClearStream(); UseClearStream();
InitializeDecoderSelector(kNoDecryptor, 1); InitializeDecoderSelector(kNoCdm, 2);
EXPECT_CALL(*decoder_1_, Initialize(EncryptedConfig(), _, _, _))
.WillOnce(RunCallback<2>(false));
EXPECT_CALL(*this, OnDecoderSelected(IsNull(), IsNull()));
SelectDecoder();
}
TEST_F(AudioDecoderSelectorTest,
Destroy_ClearStream_NoDecryptor_OneClearDecoder) {
UseClearStream();
InitializeDecoderSelector(kNoDecryptor, 1);
EXPECT_CALL(*decoder_1_, Initialize(EncryptedConfig(), _, _, _));
SelectDecoderAndDestroy();
}
TEST_F(AudioDecoderSelectorTest, ClearStream_NoDecryptor_MultipleClearDecoder) {
UseClearStream();
InitializeDecoderSelector(kNoDecryptor, 2);
EXPECT_CALL(*decoder_1_, Initialize(EncryptedConfig(), _, _, _)) // Decoder 1 is blacklisted and will not even be tried.
.WillOnce(RunCallback<2>(false)); EXPECT_CALL(*decoder_2_, Initialize(ClearConfig(), _, _, _))
EXPECT_CALL(*decoder_2_, Initialize(EncryptedConfig(), _, _, _))
.WillOnce(RunCallback<2>(true)); .WillOnce(RunCallback<2>(true));
EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, IsNull())); EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, IsNull()));
SelectDecoder(); SelectDecoderWithBlacklist(kDecoder1);
}
TEST_F(AudioDecoderSelectorTest,
Destroy_ClearStream_NoDecryptor_MultipleClearDecoder) {
UseClearStream();
InitializeDecoderSelector(kNoDecryptor, 2);
EXPECT_CALL(*decoder_1_, Initialize(EncryptedConfig(), _, _, _))
.WillOnce(RunCallback<2>(false));
EXPECT_CALL(*decoder_2_, Initialize(EncryptedConfig(), _, _, _));
SelectDecoderAndDestroy();
}
TEST_F(AudioDecoderSelectorTest, ClearStream_DecryptOnly) {
UseClearStream();
InitializeDecoderSelector(kDecryptOnly, 1);
EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _))
.WillOnce(RunCallback<2>(true));
EXPECT_CALL(*this, OnDecoderSelected(decoder_1_, NotNull()));
SelectDecoder();
}
TEST_F(AudioDecoderSelectorTest, Destroy_ClearStream_DecryptOnly) {
UseClearStream();
InitializeDecoderSelector(kDecryptOnly, 1);
EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _));
SelectDecoderAndDestroy();
}
TEST_F(AudioDecoderSelectorTest, ClearStream_DecryptAndDecode) {
UseClearStream();
InitializeDecoderSelector(kDecryptAndDecode, 1);
#if !defined(OS_ANDROID)
// A DecryptingVideoDecoder will be created and selected. The clear decoder
// should not be touched at all. No DecryptingDemuxerStream should be
// created.
EXPECT_CALL(*this, OnDecoderSelected(NotNull(), IsNull()));
#else
// A DecryptingDemuxerStream will be created. The clear decoder will be
// initialized and returned.
EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _))
.WillOnce(RunCallback<2>(true));
EXPECT_CALL(*this, OnDecoderSelected(NotNull(), NotNull()));
#endif
SelectDecoder();
} }
// Tests for encrypted streams. // Tests for encrypted streams.
...@@ -434,4 +376,48 @@ TEST_F(AudioDecoderSelectorTest, EncryptedStream_DecryptAndDecode) { ...@@ -434,4 +376,48 @@ TEST_F(AudioDecoderSelectorTest, EncryptedStream_DecryptAndDecode) {
SelectDecoder(); SelectDecoder();
} }
TEST_F(AudioDecoderSelectorTest,
EncryptedStream_NoDecryptor_BlackListedDecoder) {
UseEncryptedStream();
InitializeDecoderSelector(kNoDecryptor, 2);
EXPECT_CALL(*decoder_2_, Initialize(EncryptedConfig(), _, _, _))
.WillOnce(RunCallback<2>(true));
EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, IsNull()));
SelectDecoderWithBlacklist(kDecoder1);
}
TEST_F(AudioDecoderSelectorTest,
EncryptedStream_DecryptOnly_BlackListedDecoder) {
UseEncryptedStream();
InitializeDecoderSelector(kDecryptOnly, 2);
// When DecryptingDemuxerStream is chosen, the blacklist is ignored.
EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _))
.WillOnce(RunCallback<2>(false));
EXPECT_CALL(*decoder_2_, Initialize(ClearConfig(), _, _, _))
.WillOnce(RunCallback<2>(true));
EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, NotNull()));
SelectDecoderWithBlacklist(kDecoder2);
}
TEST_F(AudioDecoderSelectorTest,
EncryptedStream_DecryptAndDecode_BlackListedDecoder) {
UseEncryptedStream();
InitializeDecoderSelector(kDecryptAndDecode, 2);
// DecryptingAudioDecoder is blacklisted so we'll fall back to use
// DecryptingDemuxerStream to do decrypt-only.
EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _))
.WillOnce(RunCallback<2>(false));
EXPECT_CALL(*decoder_2_, Initialize(ClearConfig(), _, _, _))
.WillOnce(RunCallback<2>(true));
EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, NotNull()));
// TODO(xhwang): Avoid the hardcoded string here.
SelectDecoderWithBlacklist("DecryptingAudioDecoder");
}
} // namespace media } // namespace media
...@@ -43,10 +43,10 @@ static bool HasValidStreamConfig(DemuxerStream* stream) { ...@@ -43,10 +43,10 @@ static bool HasValidStreamConfig(DemuxerStream* stream) {
template <DemuxerStream::Type StreamType> template <DemuxerStream::Type StreamType>
DecoderSelector<StreamType>::DecoderSelector( DecoderSelector<StreamType>::DecoderSelector(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
ScopedVector<Decoder> decoders, CreateDecodersCB create_decoders_cb,
MediaLog* media_log) MediaLog* media_log)
: task_runner_(task_runner), : task_runner_(task_runner),
decoders_(std::move(decoders)), create_decoders_cb_(std::move(create_decoders_cb)),
media_log_(media_log), media_log_(media_log),
input_stream_(nullptr), input_stream_(nullptr),
weak_ptr_factory_(this) {} weak_ptr_factory_(this) {}
...@@ -68,18 +68,17 @@ void DecoderSelector<StreamType>::SelectDecoder( ...@@ -68,18 +68,17 @@ void DecoderSelector<StreamType>::SelectDecoder(
StreamTraits* traits, StreamTraits* traits,
DemuxerStream* stream, DemuxerStream* stream,
CdmContext* cdm_context, CdmContext* cdm_context,
const std::string& blacklisted_decoder,
const SelectDecoderCB& select_decoder_cb, const SelectDecoderCB& select_decoder_cb,
const typename Decoder::OutputCB& output_cb, const typename Decoder::OutputCB& output_cb,
const base::Closure& waiting_for_decryption_key_cb) { const base::Closure& waiting_for_decryption_key_cb) {
DVLOG(2) << __func__; DVLOG(2) << __func__ << ": cdm_context=" << cdm_context
<< ", blacklisted_decoder=" << blacklisted_decoder;
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(traits); DCHECK(traits);
DCHECK(stream); DCHECK(stream);
DCHECK(select_decoder_cb_.is_null()); DCHECK(select_decoder_cb_.is_null());
cdm_context_ = cdm_context;
waiting_for_decryption_key_cb_ = waiting_for_decryption_key_cb;
// Make sure |select_decoder_cb| runs on a different execution stack. // Make sure |select_decoder_cb| runs on a different execution stack.
select_decoder_cb_ = BindToCurrentLoop(select_decoder_cb); select_decoder_cb_ = BindToCurrentLoop(select_decoder_cb);
...@@ -91,11 +90,20 @@ void DecoderSelector<StreamType>::SelectDecoder( ...@@ -91,11 +90,20 @@ void DecoderSelector<StreamType>::SelectDecoder(
traits_ = traits; traits_ = traits;
input_stream_ = stream; input_stream_ = stream;
cdm_context_ = cdm_context;
blacklisted_decoder_ = blacklisted_decoder;
output_cb_ = output_cb; output_cb_ = output_cb;
waiting_for_decryption_key_cb_ = waiting_for_decryption_key_cb;
decoders_ = create_decoders_cb_.Run();
config_ = StreamTraits::GetDecoderConfig(input_stream_);
// When there is a CDM attached, always try the decrypting decoder or // When there is a CDM attached, always try the decrypting decoder or
// demuxer-stream first. // demuxer-stream first.
if (cdm_context_) { if (config_.is_encrypted()) {
DCHECK(cdm_context_);
// TODO(xhwang): This if-defined doesn't make a lot of sense. It should be
// replaced by some better checks.
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
InitializeDecryptingDecoder(); InitializeDecryptingDecoder();
#else #else
...@@ -104,11 +112,6 @@ void DecoderSelector<StreamType>::SelectDecoder( ...@@ -104,11 +112,6 @@ void DecoderSelector<StreamType>::SelectDecoder(
return; return;
} }
config_ = StreamTraits::GetDecoderConfig(input_stream_);
// If the input stream is encrypted, CdmContext must be non-null.
DCHECK(!config_.is_encrypted());
InitializeDecoder(); InitializeDecoder();
} }
...@@ -116,9 +119,16 @@ void DecoderSelector<StreamType>::SelectDecoder( ...@@ -116,9 +119,16 @@ void DecoderSelector<StreamType>::SelectDecoder(
template <DemuxerStream::Type StreamType> template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::InitializeDecryptingDecoder() { void DecoderSelector<StreamType>::InitializeDecryptingDecoder() {
DVLOG(2) << __func__; DVLOG(2) << __func__;
decoder_.reset(new typename StreamTraits::DecryptingDecoderType( decoder_.reset(new typename StreamTraits::DecryptingDecoderType(
task_runner_, media_log_, waiting_for_decryption_key_cb_)); task_runner_, media_log_, waiting_for_decryption_key_cb_));
if (decoder_->GetDisplayName() == blacklisted_decoder_) {
DVLOG(1) << __func__ << ": Decrypting decoder is blacklisted.";
DecryptingDecoderInitDone(false);
return;
}
traits_->InitializeDecoder( traits_->InitializeDecoder(
decoder_.get(), StreamTraits::GetDecoderConfig(input_stream_), decoder_.get(), StreamTraits::GetDecoderConfig(input_stream_),
input_stream_->liveness() == DemuxerStream::LIVENESS_LIVE, cdm_context_, input_stream_->liveness() == DemuxerStream::LIVENESS_LIVE, cdm_context_,
...@@ -134,6 +144,7 @@ void DecoderSelector<StreamType>::DecryptingDecoderInitDone(bool success) { ...@@ -134,6 +144,7 @@ void DecoderSelector<StreamType>::DecryptingDecoderInitDone(bool success) {
if (success) { if (success) {
DVLOG(1) << __func__ << ": " << decoder_->GetDisplayName() << " selected."; DVLOG(1) << __func__ << ": " << decoder_->GetDisplayName() << " selected.";
decoders_.clear();
base::ResetAndReturn(&select_decoder_cb_) base::ResetAndReturn(&select_decoder_cb_)
.Run(std::move(decoder_), std::unique_ptr<DecryptingDemuxerStream>()); .Run(std::move(decoder_), std::unique_ptr<DecryptingDemuxerStream>());
return; return;
...@@ -177,11 +188,7 @@ void DecoderSelector<StreamType>::DecryptingDemuxerStreamInitDone( ...@@ -177,11 +188,7 @@ void DecoderSelector<StreamType>::DecryptingDemuxerStreamInitDone(
DCHECK(!config_.is_encrypted()); DCHECK(!config_.is_encrypted());
} else { } else {
decrypted_stream_.reset(); decrypted_stream_.reset();
config_ = StreamTraits::GetDecoderConfig(input_stream_); DCHECK(config_.is_encrypted());
// Prefer decrypting decoder by using an encrypted config.
if (!config_.is_encrypted())
config_.SetIsEncrypted(true);
} }
InitializeDecoder(); InitializeDecoder();
...@@ -193,14 +200,24 @@ void DecoderSelector<StreamType>::InitializeDecoder() { ...@@ -193,14 +200,24 @@ void DecoderSelector<StreamType>::InitializeDecoder() {
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!decoder_); DCHECK(!decoder_);
if (decoders_.empty()) { // Select the next non-blacklisted decoder.
while (!decoders_.empty()) {
std::unique_ptr<Decoder> decoder(decoders_.front());
decoders_.weak_erase(decoders_.begin());
// When |decrypted_stream_| is selected, the |config_| has changed so ignore
// the blacklist.
if (decrypted_stream_ ||
decoder->GetDisplayName() != blacklisted_decoder_) {
decoder_ = std::move(decoder);
break;
}
}
if (!decoder_) {
ReturnNullDecoder(); ReturnNullDecoder();
return; return;
} }
decoder_.reset(decoders_.front());
decoders_.weak_erase(decoders_.begin());
traits_->InitializeDecoder( traits_->InitializeDecoder(
decoder_.get(), config_, decoder_.get(), config_,
input_stream_->liveness() == DemuxerStream::LIVENESS_LIVE, cdm_context_, input_stream_->liveness() == DemuxerStream::LIVENESS_LIVE, cdm_context_,
...@@ -224,6 +241,7 @@ void DecoderSelector<StreamType>::DecoderInitDone(bool success) { ...@@ -224,6 +241,7 @@ void DecoderSelector<StreamType>::DecoderInitDone(bool success) {
<< " selected. DecryptingDemuxerStream " << " selected. DecryptingDemuxerStream "
<< (decrypted_stream_ ? "also" : "not") << " selected."; << (decrypted_stream_ ? "also" : "not") << " selected.";
decoders_.clear();
base::ResetAndReturn(&select_decoder_cb_) base::ResetAndReturn(&select_decoder_cb_)
.Run(std::move(decoder_), std::move(decrypted_stream_)); .Run(std::move(decoder_), std::move(decrypted_stream_));
} }
...@@ -232,6 +250,7 @@ template <DemuxerStream::Type StreamType> ...@@ -232,6 +250,7 @@ template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::ReturnNullDecoder() { void DecoderSelector<StreamType>::ReturnNullDecoder() {
DVLOG(1) << __func__ << ": No decoder selected."; DVLOG(1) << __func__ << ": No decoder selected.";
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
decoders_.clear();
base::ResetAndReturn(&select_decoder_cb_) base::ResetAndReturn(&select_decoder_cb_)
.Run(std::unique_ptr<Decoder>(), .Run(std::unique_ptr<Decoder>(),
std::unique_ptr<DecryptingDemuxerStream>()); std::unique_ptr<DecryptingDemuxerStream>());
......
...@@ -39,6 +39,11 @@ class MEDIA_EXPORT DecoderSelector { ...@@ -39,6 +39,11 @@ class MEDIA_EXPORT DecoderSelector {
typedef typename StreamTraits::DecoderType Decoder; typedef typename StreamTraits::DecoderType Decoder;
typedef typename StreamTraits::DecoderConfigType DecoderConfig; typedef typename StreamTraits::DecoderConfigType DecoderConfig;
// Callback to create a list of decoders to select from.
// TODO(xhwang): Use a DecoderFactory to create decoders one by one as needed,
// instead of creating a list of decoders all at once.
using CreateDecodersCB = base::RepeatingCallback<ScopedVector<Decoder>()>;
// Indicates completion of Decoder selection. // Indicates completion of Decoder selection.
// - First parameter: The initialized Decoder. If it's set to NULL, then // - First parameter: The initialized Decoder. If it's set to NULL, then
// Decoder initialization failed. // Decoder initialization failed.
...@@ -48,18 +53,17 @@ class MEDIA_EXPORT DecoderSelector { ...@@ -48,18 +53,17 @@ class MEDIA_EXPORT DecoderSelector {
// Note: The caller owns selected Decoder and DecryptingDemuxerStream. // Note: The caller owns selected Decoder and DecryptingDemuxerStream.
// The caller should call DecryptingDemuxerStream::Reset() before // The caller should call DecryptingDemuxerStream::Reset() before
// calling Decoder::Reset() to release any pending decryption or read. // calling Decoder::Reset() to release any pending decryption or read.
typedef base::Callback<void(std::unique_ptr<Decoder>, using SelectDecoderCB =
std::unique_ptr<DecryptingDemuxerStream>)> base::Callback<void(std::unique_ptr<Decoder>,
SelectDecoderCB; std::unique_ptr<DecryptingDemuxerStream>)>;
// |decoders| contains the Decoders to use when initializing.
DecoderSelector( DecoderSelector(
const scoped_refptr<base::SingleThreadTaskRunner>& message_loop, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
ScopedVector<Decoder> decoders, CreateDecodersCB create_decoders_cb,
MediaLog* media_log); MediaLog* media_log);
// Aborts pending Decoder selection and fires |select_decoder_cb| with // Aborts pending Decoder selection and fires |select_decoder_cb| with
// NULL and NULL immediately if it's pending. // null and null immediately if it's pending.
~DecoderSelector(); ~DecoderSelector();
// Initializes and selects the first Decoder that can decode the |stream|. // Initializes and selects the first Decoder that can decode the |stream|.
...@@ -67,13 +71,20 @@ class MEDIA_EXPORT DecoderSelector { ...@@ -67,13 +71,20 @@ class MEDIA_EXPORT DecoderSelector {
// the |select_decoder_cb|. // the |select_decoder_cb|.
// Notes: // Notes:
// 1. This must not be called again before |select_decoder_cb| is run. // 1. This must not be called again before |select_decoder_cb| is run.
// 2. Decoders that fail to initialize will be deleted. Future calls will // 2. |create_decoders_cb| will be called to create a list of candidate
// select from the decoders following the decoder that was last returned. // decoders to select from.
// 3. |cdm_context| is optional. If |cdm_context| is // 3. The |blacklisted_decoder| will be skipped in the decoder selection
// null, no CDM will be available to perform decryption. // process, unless DecryptingDemuxerStream is chosen. This is because
// DecryptingDemuxerStream updates the |config_|, and the blacklist should
// only be applied to the original |stream| config.
// 4. All decoders that are not selected will be deleted upon returning
// |select_decoder_cb|.
// 5. |cdm_context| is optional. If |cdm_context| is null, no CDM will be
// available to perform decryption.
void SelectDecoder(StreamTraits* traits, void SelectDecoder(StreamTraits* traits,
DemuxerStream* stream, DemuxerStream* stream,
CdmContext* cdm_context, CdmContext* cdm_context,
const std::string& blacklisted_decoder,
const SelectDecoderCB& select_decoder_cb, const SelectDecoderCB& select_decoder_cb,
const typename Decoder::OutputCB& output_cb, const typename Decoder::OutputCB& output_cb,
const base::Closure& waiting_for_decryption_key_cb); const base::Closure& waiting_for_decryption_key_cb);
...@@ -90,7 +101,7 @@ class MEDIA_EXPORT DecoderSelector { ...@@ -90,7 +101,7 @@ class MEDIA_EXPORT DecoderSelector {
void ReturnNullDecoder(); void ReturnNullDecoder();
scoped_refptr<base::SingleThreadTaskRunner> task_runner_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
ScopedVector<Decoder> decoders_; CreateDecodersCB create_decoders_cb_;
MediaLog* media_log_; MediaLog* media_log_;
StreamTraits* traits_; StreamTraits* traits_;
...@@ -100,10 +111,13 @@ class MEDIA_EXPORT DecoderSelector { ...@@ -100,10 +111,13 @@ class MEDIA_EXPORT DecoderSelector {
DemuxerStream* input_stream_; DemuxerStream* input_stream_;
CdmContext* cdm_context_; CdmContext* cdm_context_;
std::string blacklisted_decoder_;
SelectDecoderCB select_decoder_cb_; SelectDecoderCB select_decoder_cb_;
typename Decoder::OutputCB output_cb_; typename Decoder::OutputCB output_cb_;
base::Closure waiting_for_decryption_key_cb_; base::Closure waiting_for_decryption_key_cb_;
ScopedVector<Decoder> decoders_;
std::unique_ptr<Decoder> decoder_; std::unique_ptr<Decoder> decoder_;
std::unique_ptr<DecryptingDemuxerStream> decrypted_stream_; std::unique_ptr<DecryptingDemuxerStream> decrypted_stream_;
......
...@@ -45,18 +45,17 @@ const char* GetTraceString<DemuxerStream::AUDIO>() { ...@@ -45,18 +45,17 @@ const char* GetTraceString<DemuxerStream::AUDIO>() {
template <DemuxerStream::Type StreamType> template <DemuxerStream::Type StreamType>
DecoderStream<StreamType>::DecoderStream( DecoderStream<StreamType>::DecoderStream(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
ScopedVector<Decoder> decoders, CreateDecodersCB create_decoders_cb,
MediaLog* media_log) MediaLog* media_log)
: traits_(media_log), : traits_(media_log),
task_runner_(task_runner), task_runner_(task_runner),
create_decoders_cb_(std::move(create_decoders_cb)),
media_log_(media_log), media_log_(media_log),
state_(STATE_UNINITIALIZED), state_(STATE_UNINITIALIZED),
stream_(NULL), stream_(NULL),
cdm_context_(nullptr), cdm_context_(nullptr),
decoder_selector_(new DecoderSelector<StreamType>(task_runner,
std::move(decoders),
media_log)),
decoder_produced_a_frame_(false), decoder_produced_a_frame_(false),
has_fallen_back_once_on_decode_error_(false),
decoding_eos_(false), decoding_eos_(false),
pending_decode_requests_(0), pending_decode_requests_(0),
duration_tracker_(8), duration_tracker_(8),
...@@ -256,9 +255,13 @@ void DecoderStream<StreamType>::SelectDecoder() { ...@@ -256,9 +255,13 @@ void DecoderStream<StreamType>::SelectDecoder() {
// the |cdm_context_|. This will also help prevent creating a new DDS on top // the |cdm_context_|. This will also help prevent creating a new DDS on top
// of the current DDS. // of the current DDS.
CdmContext* cdm_context = decrypting_demuxer_stream_ ? nullptr : cdm_context_; CdmContext* cdm_context = decrypting_demuxer_stream_ ? nullptr : cdm_context_;
std::string blacklisted_decoder = decoder_ ? decoder_->GetDisplayName() : "";
decoder_selector_.reset(new DecoderSelector<StreamType>(
task_runner_, create_decoders_cb_, media_log_));
decoder_selector_->SelectDecoder( decoder_selector_->SelectDecoder(
&traits_, stream_, cdm_context, &traits_, stream_, cdm_context, blacklisted_decoder,
base::Bind(&DecoderStream<StreamType>::OnDecoderSelected, base::Bind(&DecoderStream<StreamType>::OnDecoderSelected,
weak_factory_.GetWeakPtr()), weak_factory_.GetWeakPtr()),
base::Bind(&DecoderStream<StreamType>::OnDecodeOutputReady, base::Bind(&DecoderStream<StreamType>::OnDecodeOutputReady,
...@@ -276,6 +279,9 @@ void DecoderStream<StreamType>::OnDecoderSelected( ...@@ -276,6 +279,9 @@ void DecoderStream<StreamType>::OnDecoderSelected(
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(state_ == STATE_INITIALIZING || state_ == STATE_REINITIALIZING_DECODER) DCHECK(state_ == STATE_INITIALIZING || state_ == STATE_REINITIALIZING_DECODER)
<< state_; << state_;
decoder_selector_.reset();
if (state_ == STATE_INITIALIZING) { if (state_ == STATE_INITIALIZING) {
DCHECK(!init_cb_.is_null()); DCHECK(!init_cb_.is_null());
DCHECK(read_cb_.is_null()); DCHECK(read_cb_.is_null());
...@@ -431,7 +437,11 @@ void DecoderStream<StreamType>::OnDecodeDone(int buffer_size, ...@@ -431,7 +437,11 @@ void DecoderStream<StreamType>::OnDecodeDone(int buffer_size,
switch (status) { switch (status) {
case DecodeStatus::DECODE_ERROR: case DecodeStatus::DECODE_ERROR:
if (!decoder_produced_a_frame_) { // Only fall back to a new decoder after failing to decode the first
// buffer, and if we have not fallen back before.
if (!decoder_produced_a_frame_ &&
!has_fallen_back_once_on_decode_error_) {
has_fallen_back_once_on_decode_error_ = true;
pending_decode_requests_ = 0; pending_decode_requests_ = 0;
// Prevent all pending decode requests and outputs from those requests // Prevent all pending decode requests and outputs from those requests
...@@ -444,6 +454,7 @@ void DecoderStream<StreamType>::OnDecodeDone(int buffer_size, ...@@ -444,6 +454,7 @@ void DecoderStream<StreamType>::OnDecodeDone(int buffer_size,
SelectDecoder(); SelectDecoder();
return; return;
} }
FUNCTION_DVLOG(1) << ": Decode error!"; FUNCTION_DVLOG(1) << ": Decode error!";
state_ = STATE_ERROR; state_ = STATE_ERROR;
MEDIA_LOG(ERROR, media_log_) << GetStreamTypeString() << " decode error"; MEDIA_LOG(ERROR, media_log_) << GetStreamTypeString() << " decode error";
...@@ -503,7 +514,7 @@ void DecoderStream<StreamType>::OnDecodeOutputReady( ...@@ -503,7 +514,7 @@ void DecoderStream<StreamType>::OnDecodeOutputReady(
decoder_produced_a_frame_ = true; decoder_produced_a_frame_ = true;
traits_.OnDecodeDone(output); traits_.OnDecodeDone(output);
// |decoder_| sucessfully decoded a frame. No need to keep buffers for a // |decoder_| successfully decoded a frame. No need to keep buffers for a
// fallback decoder. // fallback decoder.
// Note: |fallback_buffers_| might still have buffers, and we will keep // Note: |fallback_buffers_| might still have buffers, and we will keep
// reading from there before requesting new buffers from |stream_|. // reading from there before requesting new buffers from |stream_|.
......
...@@ -50,14 +50,17 @@ class MEDIA_EXPORT DecoderStream { ...@@ -50,14 +50,17 @@ class MEDIA_EXPORT DecoderStream {
DECODE_ERROR, // Decoder returned decode error. DECODE_ERROR, // Decoder returned decode error.
}; };
// Callback to create a list of decoders.
using CreateDecodersCB = base::RepeatingCallback<ScopedVector<Decoder>()>;
// Indicates completion of a DecoderStream initialization. // Indicates completion of a DecoderStream initialization.
typedef base::Callback<void(bool success)> InitCB; using InitCB = base::Callback<void(bool success)>;
// Indicates completion of a DecoderStream read. // Indicates completion of a DecoderStream read.
typedef base::Callback<void(Status, const scoped_refptr<Output>&)> ReadCB; using ReadCB = base::Callback<void(Status, const scoped_refptr<Output>&)>;
DecoderStream(const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, DecoderStream(const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
ScopedVector<Decoder> decoders, CreateDecodersCB create_decoders_cb,
MediaLog* media_log); MediaLog* media_log);
virtual ~DecoderStream(); virtual ~DecoderStream();
...@@ -188,7 +191,7 @@ class MEDIA_EXPORT DecoderStream { ...@@ -188,7 +191,7 @@ class MEDIA_EXPORT DecoderStream {
DecoderStreamTraits<StreamType> traits_; DecoderStreamTraits<StreamType> traits_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
CreateDecodersCB create_decoders_cb_;
MediaLog* media_log_; MediaLog* media_log_;
State state_; State state_;
...@@ -211,6 +214,16 @@ class MEDIA_EXPORT DecoderStream { ...@@ -211,6 +214,16 @@ class MEDIA_EXPORT DecoderStream {
// Whether |decoder_| has produced a frame yet. Reset on fallback. // Whether |decoder_| has produced a frame yet. Reset on fallback.
bool decoder_produced_a_frame_; bool decoder_produced_a_frame_;
// Whether we have already fallen back once on decode error, used to prevent
// issues like infinite fallback like:
// 1. select decoder 1
// 2. decode error on decoder 1
// 3. black list decoder 1 and select decoder 2
// 4. decode error again on decoder 2
// 5. black list decoder 2 and select decoder 1
// 6. go to (2)
bool has_fallen_back_once_on_decode_error_;
std::unique_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream_; std::unique_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream_;
ConfigChangeObserverCB config_change_observer_cb_; ConfigChangeObserverCB config_change_observer_cb_;
......
...@@ -23,12 +23,12 @@ FakeVideoDecoder::FakeVideoDecoder(const std::string& decoder_name, ...@@ -23,12 +23,12 @@ FakeVideoDecoder::FakeVideoDecoder(const std::string& decoder_name,
total_bytes_decoded_(0), total_bytes_decoded_(0),
fail_to_initialize_(false), fail_to_initialize_(false),
weak_factory_(this) { weak_factory_(this) {
DVLOG(1) << __func__; DVLOG(1) << decoder_name_ << ": " << __func__;
DCHECK_GE(decoding_delay, 0); DCHECK_GE(decoding_delay, 0);
} }
FakeVideoDecoder::~FakeVideoDecoder() { FakeVideoDecoder::~FakeVideoDecoder() {
DVLOG(1) << __func__; DVLOG(1) << decoder_name_ << ": " << __func__;
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
if (state_ == STATE_UNINITIALIZED) if (state_ == STATE_UNINITIALIZED)
...@@ -57,7 +57,7 @@ void FakeVideoDecoder::Initialize(const VideoDecoderConfig& config, ...@@ -57,7 +57,7 @@ void FakeVideoDecoder::Initialize(const VideoDecoderConfig& config,
CdmContext* cdm_context, CdmContext* cdm_context,
const InitCB& init_cb, const InitCB& init_cb,
const OutputCB& output_cb) { const OutputCB& output_cb) {
DVLOG(1) << __func__; DVLOG(1) << decoder_name_ << ": " << __func__;
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(config.IsValidConfig()); DCHECK(config.IsValidConfig());
DCHECK(held_decode_callbacks_.empty()) DCHECK(held_decode_callbacks_.empty())
...@@ -82,9 +82,11 @@ void FakeVideoDecoder::Initialize(const VideoDecoderConfig& config, ...@@ -82,9 +82,11 @@ void FakeVideoDecoder::Initialize(const VideoDecoderConfig& config,
} }
if (fail_to_initialize_) { if (fail_to_initialize_) {
DVLOG(1) << decoder_name_ << ": Initialization failed.";
state_ = STATE_ERROR; state_ = STATE_ERROR;
init_cb_.RunOrHold(false); init_cb_.RunOrHold(false);
} else { } else {
DVLOG(1) << decoder_name_ << ": Initialization succeeded.";
state_ = STATE_NORMAL; state_ = STATE_NORMAL;
init_cb_.RunOrHold(true); init_cb_.RunOrHold(true);
} }
......
...@@ -40,6 +40,9 @@ MATCHER(ClearConfig, "") { ...@@ -40,6 +40,9 @@ MATCHER(ClearConfig, "") {
namespace media { namespace media {
const char kDecoder1[] = "Decoder1";
const char kDecoder2[] = "Decoder2";
class VideoDecoderSelectorTest : public ::testing::Test { class VideoDecoderSelectorTest : public ::testing::Test {
public: public:
enum DecryptorCapability { enum DecryptorCapability {
...@@ -53,8 +56,8 @@ class VideoDecoderSelectorTest : public ::testing::Test { ...@@ -53,8 +56,8 @@ class VideoDecoderSelectorTest : public ::testing::Test {
: traits_(&media_log_), : traits_(&media_log_),
demuxer_stream_( demuxer_stream_(
new StrictMock<MockDemuxerStream>(DemuxerStream::VIDEO)), new StrictMock<MockDemuxerStream>(DemuxerStream::VIDEO)),
decoder_1_(new StrictMock<MockVideoDecoder>()), decoder_1_(new StrictMock<MockVideoDecoder>(kDecoder1)),
decoder_2_(new StrictMock<MockVideoDecoder>()) { decoder_2_(new StrictMock<MockVideoDecoder>(kDecoder2)) {
all_decoders_.push_back(decoder_1_); all_decoders_.push_back(decoder_1_);
all_decoders_.push_back(decoder_2_); all_decoders_.push_back(decoder_2_);
// |cdm_context_| and |decryptor_| are conditionally created in // |cdm_context_| and |decryptor_| are conditionally created in
...@@ -81,6 +84,10 @@ class VideoDecoderSelectorTest : public ::testing::Test { ...@@ -81,6 +84,10 @@ class VideoDecoderSelectorTest : public ::testing::Test {
TestVideoConfig::NormalEncrypted()); TestVideoConfig::NormalEncrypted());
} }
ScopedVector<VideoDecoder> CreateVideoDecodersForTest() {
return std::move(all_decoders_);
}
void InitializeDecoderSelector(DecryptorCapability decryptor_capability, void InitializeDecoderSelector(DecryptorCapability decryptor_capability,
int num_decoders) { int num_decoders) {
if (decryptor_capability != kNoCdm) { if (decryptor_capability != kNoCdm) {
...@@ -104,12 +111,18 @@ class VideoDecoderSelectorTest : public ::testing::Test { ...@@ -104,12 +111,18 @@ class VideoDecoderSelectorTest : public ::testing::Test {
all_decoders_.begin() + num_decoders, all_decoders_.end()); all_decoders_.begin() + num_decoders, all_decoders_.end());
decoder_selector_.reset(new VideoDecoderSelector( decoder_selector_.reset(new VideoDecoderSelector(
message_loop_.task_runner(), std::move(all_decoders_), &media_log_)); message_loop_.task_runner(),
base::Bind(&VideoDecoderSelectorTest::CreateVideoDecodersForTest,
base::Unretained(this)),
&media_log_));
} }
void SelectDecoder() { void SelectDecoder() { SelectDecoderWithBlacklist(""); }
void SelectDecoderWithBlacklist(const std::string& blacklisted_decoder) {
decoder_selector_->SelectDecoder( decoder_selector_->SelectDecoder(
&traits_, demuxer_stream_.get(), cdm_context_.get(), &traits_, demuxer_stream_.get(), cdm_context_.get(),
blacklisted_decoder,
base::Bind(&VideoDecoderSelectorTest::MockOnDecoderSelected, base::Bind(&VideoDecoderSelectorTest::MockOnDecoderSelected,
base::Unretained(this)), base::Unretained(this)),
base::Bind(&VideoDecoderSelectorTest::FrameReady, base::Bind(&VideoDecoderSelectorTest::FrameReady,
...@@ -163,18 +176,22 @@ class VideoDecoderSelectorTest : public ::testing::Test { ...@@ -163,18 +176,22 @@ class VideoDecoderSelectorTest : public ::testing::Test {
DISALLOW_COPY_AND_ASSIGN(VideoDecoderSelectorTest); DISALLOW_COPY_AND_ASSIGN(VideoDecoderSelectorTest);
}; };
// Tests for clear streams. // Tests for clear streams. CDM will not be used for clear streams so
// DecryptorCapability doesn't really matter.
TEST_F(VideoDecoderSelectorTest, ClearStream_NoDecryptor_NoClearDecoder) { TEST_F(VideoDecoderSelectorTest, ClearStream_NoClearDecoder) {
UseClearStream(); UseClearStream();
InitializeDecoderSelector(kNoDecryptor, 0);
// DecoderSelector will not try decrypting decoders for clear stream, even
// if the CDM is capable of doing decrypt and decode.
InitializeDecoderSelector(kDecryptAndDecode, 0);
EXPECT_CALL(*this, OnDecoderSelected(IsNull(), IsNull())); EXPECT_CALL(*this, OnDecoderSelected(IsNull(), IsNull()));
SelectDecoder(); SelectDecoder();
} }
TEST_F(VideoDecoderSelectorTest, ClearStream_NoCdm_OneClearDecoder) { TEST_F(VideoDecoderSelectorTest, ClearStream_OneClearDecoder) {
UseClearStream(); UseClearStream();
InitializeDecoderSelector(kNoCdm, 1); InitializeDecoderSelector(kNoCdm, 1);
...@@ -185,7 +202,7 @@ TEST_F(VideoDecoderSelectorTest, ClearStream_NoCdm_OneClearDecoder) { ...@@ -185,7 +202,7 @@ TEST_F(VideoDecoderSelectorTest, ClearStream_NoCdm_OneClearDecoder) {
SelectDecoder(); SelectDecoder();
} }
TEST_F(VideoDecoderSelectorTest, Destroy_ClearStream_NoCdm_OneClearDecoder) { TEST_F(VideoDecoderSelectorTest, Destroy_ClearStream_OneClearDecoder) {
UseClearStream(); UseClearStream();
InitializeDecoderSelector(kNoCdm, 1); InitializeDecoderSelector(kNoCdm, 1);
...@@ -194,7 +211,7 @@ TEST_F(VideoDecoderSelectorTest, Destroy_ClearStream_NoCdm_OneClearDecoder) { ...@@ -194,7 +211,7 @@ TEST_F(VideoDecoderSelectorTest, Destroy_ClearStream_NoCdm_OneClearDecoder) {
SelectDecoderAndDestroy(); SelectDecoderAndDestroy();
} }
TEST_F(VideoDecoderSelectorTest, ClearStream_NoCdm_MultipleClearDecoder) { TEST_F(VideoDecoderSelectorTest, ClearStream_MultipleClearDecoder) {
UseClearStream(); UseClearStream();
InitializeDecoderSelector(kNoCdm, 2); InitializeDecoderSelector(kNoCdm, 2);
...@@ -207,8 +224,7 @@ TEST_F(VideoDecoderSelectorTest, ClearStream_NoCdm_MultipleClearDecoder) { ...@@ -207,8 +224,7 @@ TEST_F(VideoDecoderSelectorTest, ClearStream_NoCdm_MultipleClearDecoder) {
SelectDecoder(); SelectDecoder();
} }
TEST_F(VideoDecoderSelectorTest, TEST_F(VideoDecoderSelectorTest, Destroy_ClearStream_MultipleClearDecoder) {
Destroy_ClearStream_NoCdm_MultipleClearDecoder) {
UseClearStream(); UseClearStream();
InitializeDecoderSelector(kNoCdm, 2); InitializeDecoderSelector(kNoCdm, 2);
...@@ -219,90 +235,16 @@ TEST_F(VideoDecoderSelectorTest, ...@@ -219,90 +235,16 @@ TEST_F(VideoDecoderSelectorTest,
SelectDecoderAndDestroy(); SelectDecoderAndDestroy();
} }
TEST_F(VideoDecoderSelectorTest, ClearStream_NoDecryptor_OneClearDecoder) { TEST_F(VideoDecoderSelectorTest, ClearStream_BlackListedDecoder) {
UseClearStream(); UseClearStream();
InitializeDecoderSelector(kNoDecryptor, 1); InitializeDecoderSelector(kNoCdm, 2);
EXPECT_CALL(*decoder_1_, Initialize(EncryptedConfig(), _, _, _, _))
.WillOnce(RunCallback<3>(false));
EXPECT_CALL(*this, OnDecoderSelected(IsNull(), IsNull()));
SelectDecoder();
}
TEST_F(VideoDecoderSelectorTest,
Destroy_ClearStream_NoDecryptor_OneClearDecoder) {
UseClearStream();
InitializeDecoderSelector(kNoDecryptor, 1);
EXPECT_CALL(*decoder_1_, Initialize(EncryptedConfig(), _, _, _, _));
SelectDecoderAndDestroy();
}
TEST_F(VideoDecoderSelectorTest, ClearStream_NoDecryptor_MultipleClearDecoder) {
UseClearStream();
InitializeDecoderSelector(kNoDecryptor, 2);
EXPECT_CALL(*decoder_1_, Initialize(EncryptedConfig(), _, _, _, _)) // Decoder 1 is blacklisted and will not even be tried.
.WillOnce(RunCallback<3>(false)); EXPECT_CALL(*decoder_2_, Initialize(ClearConfig(), _, _, _, _))
EXPECT_CALL(*decoder_2_, Initialize(EncryptedConfig(), _, _, _, _))
.WillOnce(RunCallback<3>(true)); .WillOnce(RunCallback<3>(true));
EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, IsNull())); EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, IsNull()));
SelectDecoder(); SelectDecoderWithBlacklist(kDecoder1);
}
TEST_F(VideoDecoderSelectorTest,
Destroy_ClearStream_NoDecryptor_MultipleClearDecoder) {
UseClearStream();
InitializeDecoderSelector(kNoDecryptor, 2);
EXPECT_CALL(*decoder_1_, Initialize(EncryptedConfig(), _, _, _, _))
.WillOnce(RunCallback<3>(false));
EXPECT_CALL(*decoder_2_, Initialize(EncryptedConfig(), _, _, _, _));
SelectDecoderAndDestroy();
}
TEST_F(VideoDecoderSelectorTest, ClearStream_DecryptOnly) {
UseClearStream();
InitializeDecoderSelector(kDecryptOnly, 1);
EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _, _))
.WillOnce(RunCallback<3>(true));
EXPECT_CALL(*this, OnDecoderSelected(decoder_1_, NotNull()));
SelectDecoder();
}
TEST_F(VideoDecoderSelectorTest, Destroy_ClearStream_DecryptOnly) {
UseClearStream();
InitializeDecoderSelector(kDecryptOnly, 1);
EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _, _));
SelectDecoderAndDestroy();
}
TEST_F(VideoDecoderSelectorTest, ClearStream_DecryptAndDecode) {
UseClearStream();
InitializeDecoderSelector(kDecryptAndDecode, 1);
#if !defined(OS_ANDROID)
// A DecryptingVideoDecoder will be created and selected. The clear decoder
// should not be touched at all. No DecryptingDemuxerStream should be
// created.
EXPECT_CALL(*this, OnDecoderSelected(NotNull(), IsNull()));
#else
// A DecryptingDemuxerStream will be created. The clear decoder will be
// initialized and returned.
EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _, _))
.WillOnce(RunCallback<3>(true));
EXPECT_CALL(*this, OnDecoderSelected(NotNull(), NotNull()));
#endif
SelectDecoder();
} }
// Tests for encrypted streams. // Tests for encrypted streams.
...@@ -431,4 +373,48 @@ TEST_F(VideoDecoderSelectorTest, EncryptedStream_DecryptAndDecode) { ...@@ -431,4 +373,48 @@ TEST_F(VideoDecoderSelectorTest, EncryptedStream_DecryptAndDecode) {
SelectDecoder(); SelectDecoder();
} }
TEST_F(VideoDecoderSelectorTest,
EncryptedStream_NoDecryptor_BlackListedDecoder) {
UseEncryptedStream();
InitializeDecoderSelector(kNoDecryptor, 2);
EXPECT_CALL(*decoder_2_, Initialize(EncryptedConfig(), _, _, _, _))
.WillOnce(RunCallback<3>(true));
EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, IsNull()));
SelectDecoderWithBlacklist(kDecoder1);
}
TEST_F(VideoDecoderSelectorTest,
EncryptedStream_DecryptOnly_BlackListedDecoder) {
UseEncryptedStream();
InitializeDecoderSelector(kDecryptOnly, 2);
// When DecryptingDemuxerStream is chosen, the blacklist is ignored.
EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _, _))
.WillOnce(RunCallback<3>(false));
EXPECT_CALL(*decoder_2_, Initialize(ClearConfig(), _, _, _, _))
.WillOnce(RunCallback<3>(true));
EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, NotNull()));
SelectDecoderWithBlacklist(kDecoder2);
}
TEST_F(VideoDecoderSelectorTest,
EncryptedStream_DecryptAndDecode_BlackListedDecoder) {
UseEncryptedStream();
InitializeDecoderSelector(kDecryptAndDecode, 2);
// DecryptingAudioDecoder is blacklisted so we'll fallback to use
// DecryptingDemuxerStream to do decrypt-only.
EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _, _))
.WillOnce(RunCallback<3>(false));
EXPECT_CALL(*decoder_2_, Initialize(ClearConfig(), _, _, _, _))
.WillOnce(RunCallback<3>(true));
EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, NotNull()));
// TODO(xhwang): Avoid the hardcoded string here.
SelectDecoderWithBlacklist("DecryptingVideoDecoder");
}
} // namespace media } // namespace media
...@@ -356,7 +356,7 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream, ...@@ -356,7 +356,7 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream,
client_ = client; client_ = client;
audio_buffer_stream_ = base::MakeUnique<AudioBufferStream>( audio_buffer_stream_ = base::MakeUnique<AudioBufferStream>(
task_runner_, create_audio_decoders_cb_.Run(), media_log_); task_runner_, create_audio_decoders_cb_, media_log_);
audio_buffer_stream_->set_config_change_observer(base::Bind( audio_buffer_stream_->set_config_change_observer(base::Bind(
&AudioRendererImpl::OnConfigChange, weak_factory_.GetWeakPtr())); &AudioRendererImpl::OnConfigChange, weak_factory_.GetWeakPtr()));
......
...@@ -150,9 +150,8 @@ void VideoRendererImpl::Initialize( ...@@ -150,9 +150,8 @@ void VideoRendererImpl::Initialize(
DCHECK(!was_background_rendering_); DCHECK(!was_background_rendering_);
DCHECK(!time_progressing_); DCHECK(!time_progressing_);
ScopedVector<VideoDecoder> decoders = create_video_decoders_cb_.Run(); video_frame_stream_.reset(new VideoFrameStream(
video_frame_stream_.reset( task_runner_, create_video_decoders_cb_, media_log_));
new VideoFrameStream(task_runner_, std::move(decoders), media_log_));
// Always re-initialize or reset the |gpu_memory_buffer_pool_| in case we are // Always re-initialize or reset the |gpu_memory_buffer_pool_| in case we are
// switching between video tracks with incompatible video formats (e.g. 8-bit // switching between video tracks with incompatible video formats (e.g. 8-bit
......
...@@ -57,9 +57,9 @@ class VideoRendererImplTest : public testing::Test { ...@@ -57,9 +57,9 @@ class VideoRendererImplTest : public testing::Test {
decoder_ = new NiceMock<MockVideoDecoder>(); decoder_ = new NiceMock<MockVideoDecoder>();
ScopedVector<VideoDecoder> decoders; ScopedVector<VideoDecoder> decoders;
decoders.push_back(decoder_); decoders.push_back(decoder_);
EXPECT_CALL(*decoder_, Initialize(_, _, _, _, _)) ON_CALL(*decoder_, Initialize(_, _, _, _, _))
.WillOnce(DoAll(SaveArg<4>(&output_cb_), .WillByDefault(DoAll(SaveArg<4>(&output_cb_),
RunCallback<3>(expect_init_success_))); RunCallback<3>(expect_init_success_)));
// Monitor decodes from the decoder. // Monitor decodes from the decoder.
ON_CALL(*decoder_, Decode(_, _)) ON_CALL(*decoder_, Decode(_, _))
.WillByDefault(Invoke(this, &VideoRendererImplTest::DecodeRequested)); .WillByDefault(Invoke(this, &VideoRendererImplTest::DecodeRequested));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment