blob: 3df475b591544cb10b734a4ae23f2c9b9ba20eba [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/location.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "media/base/audio_decoder.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"
namespace media {
const char kSelectDecoderTrace[] = "DecoderSelector::SelectDecoder";
template <DemuxerStream::Type StreamType>
DecoderSelector<StreamType>::DecoderSelector(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
CreateDecodersCB create_decoders_cb,
MediaLog* media_log)
: task_runner_(std::move(task_runner)),
create_decoders_cb_(std::move(create_decoders_cb)),
media_log_(media_log) {}
template <DemuxerStream::Type StreamType>
DecoderSelector<StreamType>::~DecoderSelector() {
DVLOG(2) << __func__;
DCHECK(task_runner_->BelongsToCurrentThread());
if (select_decoder_cb_)
ReturnNullDecoder();
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::Initialize(StreamTraits* traits,
DemuxerStream* stream,
CdmContext* cdm_context,
WaitingCB waiting_cb) {
DVLOG(2) << __func__;
DCHECK(traits);
DCHECK(stream);
traits_ = traits;
stream_ = stream;
cdm_context_ = cdm_context;
waiting_cb_ = std::move(waiting_cb);
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::SelectDecoder(
SelectDecoderCB select_decoder_cb,
typename Decoder::OutputCB output_cb) {
DVLOG(2) << __func__;
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(select_decoder_cb);
DCHECK(!select_decoder_cb_);
select_decoder_cb_ = std::move(select_decoder_cb);
output_cb_ = std::move(output_cb);
config_ = traits_->GetDecoderConfig(stream_);
TRACE_EVENT_ASYNC_BEGIN2("media", kSelectDecoderTrace, this, "type",
DemuxerStream::GetTypeName(StreamType), "config",
config_.AsHumanReadableString());
if (!config_.IsValidConfig()) {
DLOG(ERROR) << "Invalid stream config";
ReturnNullDecoder();
return;
}
// If this is the first selection (ever or since FinalizeDecoderSelection()),
// start selection with the full list of potential decoders.
if (!is_selecting_decoders_) {
is_selecting_decoders_ = true;
decoders_ = create_decoders_cb_.Run();
}
InitializeDecoder();
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::FinalizeDecoderSelection() {
DVLOG(2) << __func__;
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!select_decoder_cb_);
is_selecting_decoders_ = false;
if (is_codec_changing_) {
is_codec_changing_ = false;
const std::string decoder_type = is_platform_decoder_ ? "HW" : "SW";
const std::string stream_type =
StreamType == DemuxerStream::AUDIO ? "Audio" : "Video";
base::UmaHistogramTimes(
"Media.MSE.CodecChangeTime." + stream_type + "." + decoder_type,
base::TimeTicks::Now() - codec_change_start_);
}
// Discard any remaining decoder instances, they won't be used.
decoders_.clear();
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::NotifyConfigChanged() {
DVLOG(2) << __func__;
DCHECK(task_runner_->BelongsToCurrentThread());
DecoderConfig config = traits_->GetDecoderConfig(stream_);
if (config.codec() != config_.codec()) {
is_codec_changing_ = true;
codec_change_start_ = base::TimeTicks::Now();
}
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::InitializeDecoder() {
DVLOG(2) << __func__;
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!decoder_);
if (decoders_.empty()) {
// Decoder selection failed. If the stream is encrypted, try again using
// DecryptingDemuxerStream.
if (config_.is_encrypted() && cdm_context_) {
InitializeDecryptingDemuxerStream();
return;
}
ReturnNullDecoder();
return;
}
// Initialize the first decoder on the list.
decoder_ = std::move(decoders_.front());
decoders_.erase(decoders_.begin());
is_platform_decoder_ = decoder_->IsPlatformDecoder();
TRACE_EVENT_ASYNC_STEP_INTO0("media", kSelectDecoderTrace, this,
decoder_->GetDisplayName());
DVLOG(2) << __func__ << ": initializing " << decoder_->GetDisplayName();
const bool is_live = stream_->liveness() == DemuxerStream::LIVENESS_LIVE;
traits_->InitializeDecoder(
decoder_.get(), config_, is_live, cdm_context_,
base::BindRepeating(&DecoderSelector<StreamType>::OnDecoderInitializeDone,
weak_this_factory_.GetWeakPtr()),
output_cb_, waiting_cb_);
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::OnDecoderInitializeDone(bool success) {
DVLOG(2) << __func__ << ": " << decoder_->GetDisplayName()
<< " success=" << success;
DCHECK(task_runner_->BelongsToCurrentThread());
if (!success) {
// Try the next decoder on the list.
decoder_.reset();
InitializeDecoder();
return;
}
RunSelectDecoderCB();
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::ReturnNullDecoder() {
DVLOG(1) << __func__ << ": No decoder selected";
DCHECK(task_runner_->BelongsToCurrentThread());
decrypting_demuxer_stream_.reset();
decoder_.reset();
decoders_.clear();
RunSelectDecoderCB();
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::InitializeDecryptingDemuxerStream() {
DCHECK(decoders_.empty());
DCHECK(config_.is_encrypted());
DCHECK(cdm_context_);
TRACE_EVENT_ASYNC_STEP_INTO0("media", kSelectDecoderTrace, this,
"DecryptingDemuxerStream");
decrypting_demuxer_stream_ = std::make_unique<DecryptingDemuxerStream>(
task_runner_, media_log_, waiting_cb_);
decrypting_demuxer_stream_->Initialize(
stream_, cdm_context_,
base::BindRepeating(
&DecoderSelector<StreamType>::OnDecryptingDemuxerStreamInitializeDone,
weak_this_factory_.GetWeakPtr()));
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::OnDecryptingDemuxerStreamInitializeDone(
PipelineStatus status) {
DVLOG(2) << __func__
<< ": status=" << MediaLog::PipelineStatusToString(status);
DCHECK(task_runner_->BelongsToCurrentThread());
if (status != PIPELINE_OK) {
// Since we already tried every potential decoder without DDS, give up.
ReturnNullDecoder();
return;
}
// Once DDS is enabled, there is no going back.
// TODO(sandersd): Support transitions from encrypted to unencrypted.
stream_ = decrypting_demuxer_stream_.get();
cdm_context_ = nullptr;
// We'll use the decrypted config from now on.
config_ = traits_->GetDecoderConfig(stream_);
DCHECK(!config_.is_encrypted());
// Try decoder selection again now that DDS is being used.
decoders_ = create_decoders_cb_.Run();
InitializeDecoder();
}
template <DemuxerStream::Type StreamType>
void DecoderSelector<StreamType>::RunSelectDecoderCB() {
DCHECK(select_decoder_cb_);
TRACE_EVENT_ASYNC_END2(
"media", kSelectDecoderTrace, this, "type",
DemuxerStream::GetTypeName(StreamType), "decoder",
base::StringPrintf(
"%s (%s)", decoder_ ? decoder_->GetDisplayName().c_str() : "null",
decrypting_demuxer_stream_ ? "encrypted" : "unencrypted"));
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(select_decoder_cb_), std::move(decoder_),
std::move(decrypting_demuxer_stream_)));
}
// 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