blob: 23e10e5250f64015e0495ceb058e7241e6126374 [file] [log] [blame]
// Copyright 2014 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/decoder_selector.h"
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "build/build_config.h"
#include "media/base/audio_decoder.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/cdm_context.h"
#include "media/base/demuxer_stream.h"
#include "media/base/media_log.h"
#include "media/base/video_decoder.h"
#include "media/filters/decoder_stream_traits.h"
#include "media/filters/decrypting_demuxer_stream.h"
#if !defined(OS_ANDROID)
#include "media/filters/decrypting_audio_decoder.h"
#include "media/filters/decrypting_video_decoder.h"
#endif
namespace media {
static bool HasValidStreamConfig(DemuxerStream* stream) {
switch (stream->type()) {
case DemuxerStream::AUDIO:
return stream->audio_decoder_config().IsValidConfig();
case DemuxerStream::VIDEO:
return stream->video_decoder_config().IsValidConfig();
case DemuxerStream::TEXT:
case DemuxerStream::UNKNOWN:
NOTREACHED();
}
return false;
}
template <DemuxerStream::Type StreamType>
DecoderSelector<StreamType>::DecoderSelector(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
CreateDecodersCB create_decoders_cb,
MediaLog* media_log)
: task_runner_(task_runner),
create_decoders_cb_(std::move(create_decoders_cb)),
media_log_(media_log),
input_stream_(nullptr),
weak_ptr_factory_(this) {}
template <DemuxerStream::Type StreamType>
DecoderSelector<StreamType>::~DecoderSelector() {
DVLOG(2) << __func__;
DCHECK(task_runner_->BelongsToCurrentThread());
if (!select_decoder_cb_.is_null())
ReturnNullDecoder();
decoder_.reset();
decrypted_stream_.reset();
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::SelectDecoder(
StreamTraits* traits,
DemuxerStream* stream,
CdmContext* cdm_context,
const std::string& blacklisted_decoder,
const SelectDecoderCB& select_decoder_cb,
const typename Decoder::OutputCB& output_cb,
const base::Closure& waiting_for_decryption_key_cb) {
DVLOG(2) << __func__ << ": cdm_context=" << cdm_context
<< ", blacklisted_decoder=" << blacklisted_decoder;
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(traits);
DCHECK(stream);
DCHECK(select_decoder_cb_.is_null());
// Make sure |select_decoder_cb| runs on a different execution stack.
select_decoder_cb_ = BindToCurrentLoop(select_decoder_cb);
if (!HasValidStreamConfig(stream)) {
DLOG(ERROR) << "Invalid stream config.";
ReturnNullDecoder();
return;
}
traits_ = traits;
input_stream_ = stream;
cdm_context_ = cdm_context;
blacklisted_decoder_ = blacklisted_decoder;
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
// demuxer-stream first.
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)
InitializeDecryptingDecoder();
#else
InitializeDecryptingDemuxerStream();
#endif
return;
}
InitializeDecoder();
}
#if !defined(OS_ANDROID)
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::InitializeDecryptingDecoder() {
DVLOG(2) << __func__;
decoder_.reset(new typename StreamTraits::DecryptingDecoderType(
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(
decoder_.get(), StreamTraits::GetDecoderConfig(input_stream_),
input_stream_->liveness() == DemuxerStream::LIVENESS_LIVE, cdm_context_,
base::Bind(&DecoderSelector<StreamType>::DecryptingDecoderInitDone,
weak_ptr_factory_.GetWeakPtr()),
output_cb_);
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::DecryptingDecoderInitDone(bool success) {
DVLOG(2) << __func__ << ": success=" << success;
DCHECK(task_runner_->BelongsToCurrentThread());
if (success) {
DVLOG(1) << __func__ << ": " << decoder_->GetDisplayName() << " selected.";
decoders_.clear();
base::ResetAndReturn(&select_decoder_cb_)
.Run(std::move(decoder_), std::unique_ptr<DecryptingDemuxerStream>());
return;
}
decoder_.reset();
// When we get here decrypt-and-decode is not supported. Try to use
// DecryptingDemuxerStream to do decrypt-only.
InitializeDecryptingDemuxerStream();
}
#endif // !defined(OS_ANDROID)
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::InitializeDecryptingDemuxerStream() {
decrypted_stream_.reset(new DecryptingDemuxerStream(
task_runner_, media_log_, waiting_for_decryption_key_cb_));
decrypted_stream_->Initialize(
input_stream_, cdm_context_,
base::Bind(&DecoderSelector<StreamType>::DecryptingDemuxerStreamInitDone,
weak_ptr_factory_.GetWeakPtr()));
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::DecryptingDemuxerStreamInitDone(
PipelineStatus status) {
DVLOG(2) << __func__
<< ": status=" << MediaLog::PipelineStatusToString(status);
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(cdm_context_);
// If DecryptingDemuxerStream initialization succeeded, we'll use it to do
// decryption and use a decoder to decode the clear stream. Otherwise, we'll
// try to see whether any decoder can decrypt-and-decode the encrypted stream
// directly. So in both cases, we'll initialize the decoders.
if (status == PIPELINE_OK) {
input_stream_ = decrypted_stream_.get();
config_ = StreamTraits::GetDecoderConfig(input_stream_);
DCHECK(!config_.is_encrypted());
} else {
decrypted_stream_.reset();
DCHECK(config_.is_encrypted());
}
InitializeDecoder();
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::InitializeDecoder() {
DVLOG(2) << __func__;
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!decoder_);
// Select the next non-blacklisted decoder.
while (!decoders_.empty()) {
std::unique_ptr<Decoder> decoder(std::move(decoders_.front()));
decoders_.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();
return;
}
traits_->InitializeDecoder(
decoder_.get(), config_,
input_stream_->liveness() == DemuxerStream::LIVENESS_LIVE, cdm_context_,
base::Bind(&DecoderSelector<StreamType>::DecoderInitDone,
weak_ptr_factory_.GetWeakPtr()),
output_cb_);
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::DecoderInitDone(bool success) {
DVLOG(2) << __func__ << ": success=" << success;
DCHECK(task_runner_->BelongsToCurrentThread());
if (!success) {
decoder_.reset();
InitializeDecoder();
return;
}
DVLOG(1) << __func__ << ": " << decoder_->GetDisplayName()
<< " selected. DecryptingDemuxerStream "
<< (decrypted_stream_ ? "also" : "not") << " selected.";
decoders_.clear();
base::ResetAndReturn(&select_decoder_cb_)
.Run(std::move(decoder_), std::move(decrypted_stream_));
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::ReturnNullDecoder() {
DVLOG(1) << __func__ << ": No decoder selected.";
DCHECK(task_runner_->BelongsToCurrentThread());
decoders_.clear();
base::ResetAndReturn(&select_decoder_cb_)
.Run(std::unique_ptr<Decoder>(),
std::unique_ptr<DecryptingDemuxerStream>());
}
// These forward declarations tell the compiler that we will use
// DecoderSelector with these arguments, allowing us to keep these definitions
// in our .cc without causing linker errors. This also means if anyone tries to
// instantiate a DecoderSelector with anything but these two specializations
// they'll most likely get linker errors.
template class DecoderSelector<DemuxerStream::AUDIO>;
template class DecoderSelector<DemuxerStream::VIDEO>;
} // namespace media