123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- // 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 <algorithm>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/feature_list.h"
- #include "base/location.h"
- #include "base/logging.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/strings/stringprintf.h"
- #include "base/task/sequenced_task_runner.h"
- #include "base/trace_event/trace_event.h"
- #include "build/build_config.h"
- #include "build/chromeos_buildflags.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/media_switches.h"
- #include "media/base/video_decoder.h"
- #include "media/filters/decoder_stream_traits.h"
- #include "media/filters/decrypting_demuxer_stream.h"
- namespace media {
- namespace {
- const char kSelectDecoderTrace[] = "DecoderSelector::SelectDecoder";
- bool SkipDecoderForRTC(const AudioDecoderConfig& /*config*/,
- const AudioDecoder& /*decoder*/) {
- return false;
- }
- bool SkipDecoderForRTC(const VideoDecoderConfig& config,
- const VideoDecoder& decoder) {
- // Skip non-platform decoders for rtc based on the feature flag.
- return config.is_rtc() && !decoder.IsPlatformDecoder() &&
- !base::FeatureList::IsEnabled(kExposeSwDecodersToWebRTC);
- }
- template <typename ConfigT, typename DecoderT>
- DecoderPriority NormalDecoderPriority(const ConfigT& config,
- const DecoderT& decoder) {
- if (SkipDecoderForRTC(config, decoder))
- return DecoderPriority::kSkipped;
- return DecoderPriority::kNormal;
- }
- DecoderPriority ResolutionBasedDecoderPriority(const VideoDecoderConfig& config,
- const VideoDecoder& decoder) {
- #if BUILDFLAG(IS_ANDROID)
- constexpr auto kSoftwareDecoderHeightCutoff = 360;
- #elif BUILDFLAG(IS_CHROMEOS_ASH)
- constexpr auto kSoftwareDecoderHeightCutoff = 360;
- #else
- constexpr auto kSoftwareDecoderHeightCutoff = 720;
- #endif
- if (SkipDecoderForRTC(config, decoder))
- return DecoderPriority::kSkipped;
- // We only do a height check to err on the side of prioritizing platform
- // decoders.
- const auto at_or_above_software_cutoff =
- config.visible_rect().height() >= kSoftwareDecoderHeightCutoff;
- // Platform decoders are deprioritized below the cutoff, and non-platform
- // decoders are deprioritized above it.
- return at_or_above_software_cutoff == decoder.IsPlatformDecoder()
- ? DecoderPriority::kNormal
- : DecoderPriority::kDeprioritized;
- }
- DecoderPriority PreferNonPlatformDecoders(const VideoDecoderConfig& config,
- const VideoDecoder& decoder) {
- // Prefer software decoders over hardware decoders. This is useful to force
- // software fallback for WebRTC, but still use hardware if there's no software
- // implementation to choose.
- return decoder.IsPlatformDecoder() ? DecoderPriority::kDeprioritized
- : DecoderPriority::kNormal;
- }
- DecoderPriority UnifiedDecoderPriority(const VideoDecoderConfig& config,
- const VideoDecoder& decoder) {
- if (config.is_rtc() ||
- base::FeatureList::IsEnabled(kResolutionBasedDecoderPriority)) {
- return ResolutionBasedDecoderPriority(config, decoder);
- } else {
- return NormalDecoderPriority(config, decoder);
- }
- }
- template <typename ConfigT, typename DecoderT>
- DecoderPriority SkipNonPlatformDecoders(const ConfigT& config,
- const DecoderT& decoder) {
- if (SkipDecoderForRTC(config, decoder))
- return DecoderPriority::kSkipped;
- return decoder.IsPlatformDecoder() ? DecoderPriority::kNormal
- : DecoderPriority::kSkipped;
- }
- void SetDefaultDecoderPriorityCB(
- VideoDecoderSelector::DecoderPriorityCB* out,
- const DecoderStreamTraits<DemuxerStream::VIDEO>* traits) {
- if (base::FeatureList::IsEnabled(kForceHardwareVideoDecoders)) {
- *out = base::BindRepeating(
- SkipNonPlatformDecoders<VideoDecoderConfig, VideoDecoder>);
- } else if (traits->GetPreferNonPlatformDecoders()) {
- *out = base::BindRepeating(PreferNonPlatformDecoders);
- } else {
- *out = base::BindRepeating(UnifiedDecoderPriority);
- }
- }
- void SetDefaultDecoderPriorityCB(
- AudioDecoderSelector::DecoderPriorityCB* out,
- const DecoderStreamTraits<DemuxerStream::AUDIO>*) {
- if (base::FeatureList::IsEnabled(kForceHardwareAudioDecoders)) {
- *out = base::BindRepeating(
- SkipNonPlatformDecoders<AudioDecoderConfig, AudioDecoder>);
- } else {
- // Platform audio decoders are not currently prioritized or deprioritized
- *out = base::BindRepeating(
- NormalDecoderPriority<AudioDecoderConfig, AudioDecoder>);
- }
- }
- } // namespace
- template <DemuxerStream::Type StreamType>
- DecoderSelector<StreamType>::DecoderSelector(
- scoped_refptr<base::SequencedTaskRunner> 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) {
- DETACH_FROM_SEQUENCE(sequence_checker_);
- }
- template <DemuxerStream::Type StreamType>
- DecoderSelector<StreamType>::~DecoderSelector() {
- DVLOG(2) << __func__;
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (select_decoder_cb_)
- ReturnSelectionError(DecoderStatus::Codes::kFailed);
- }
- 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);
- // Only set this here if nobody has overridden it for tests.
- if (!decoder_priority_cb_)
- SetDefaultDecoderPriorityCB(&decoder_priority_cb_, traits_);
- }
- template <DemuxerStream::Type StreamType>
- void DecoderSelector<StreamType>::SelectDecoderInternal(
- SelectDecoderCB select_decoder_cb,
- typename Decoder::OutputCB output_cb,
- bool needs_new_decoders) {
- DVLOG(2) << __func__;
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- 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";
- ReturnSelectionError(DecoderStatus::Codes::kUnsupportedConfig);
- return;
- }
- if (needs_new_decoders) {
- decoder_selection_start_ = base::TimeTicks::Now();
- decode_failure_reinit_cause_ = absl::nullopt;
- CreateDecoders();
- }
- GetAndInitializeNextDecoder();
- }
- template <DemuxerStream::Type StreamType>
- void DecoderSelector<StreamType>::BeginDecoderSelection(
- SelectDecoderCB select_decoder_cb,
- typename Decoder::OutputCB output_cb) {
- SelectDecoderInternal(std::move(select_decoder_cb), std::move(output_cb),
- /*needs_new_decoders = */ true);
- }
- template <DemuxerStream::Type StreamType>
- void DecoderSelector<StreamType>::ResumeDecoderSelection(
- SelectDecoderCB select_decoder_cb,
- typename Decoder::OutputCB output_cb,
- DecoderStatus&& reinit_cause) {
- DVLOG(2) << __func__;
- if (!decode_failure_reinit_cause_.has_value())
- decode_failure_reinit_cause_ = std::move(reinit_cause);
- SelectDecoderInternal(std::move(select_decoder_cb), std::move(output_cb),
- /*needs_new_decoders = */ false);
- }
- template <DemuxerStream::Type StreamType>
- void DecoderSelector<StreamType>::FinalizeDecoderSelection() {
- DVLOG(2) << __func__;
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(!select_decoder_cb_);
- const std::string decoder_type = is_platform_decoder_ ? "HW" : "SW";
- const std::string stream_type =
- StreamType == DemuxerStream::AUDIO ? "Audio" : "Video";
- if (is_selecting_for_config_change_) {
- is_selecting_for_config_change_ = false;
- base::UmaHistogramTimes("Media.ConfigChangeDecoderSelectionTime." +
- stream_type + "." + decoder_type,
- base::TimeTicks::Now() - decoder_selection_start_);
- } else {
- // Initial selection
- base::UmaHistogramTimes(
- "Media.InitialDecoderSelectionTime." + stream_type + "." + decoder_type,
- base::TimeTicks::Now() - decoder_selection_start_);
- }
- if (is_codec_changing_) {
- is_codec_changing_ = false;
- 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_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- is_selecting_for_config_change_ = true;
- 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>::PrependDecoder(
- std::unique_ptr<Decoder> decoder) {
- DVLOG(2) << __func__;
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // Decoders inserted directly should be given priority over those returned by
- // |create_decoders_cb_|.
- decoders_.insert(decoders_.begin(), std::move(decoder));
- FilterAndSortAvailableDecoders();
- }
- template <DemuxerStream::Type StreamType>
- void DecoderSelector<StreamType>::OverrideDecoderPriorityCBForTesting(
- DecoderPriorityCB decoder_priority_cb) {
- decoder_priority_cb_ = std::move(decoder_priority_cb);
- }
- template <DemuxerStream::Type StreamType>
- void DecoderSelector<StreamType>::CreateDecoders() {
- // Post-insert decoders returned by `create_decoders_cb_`, so that
- // any decoders added via `PrependDecoder()` are not overwritten and retain
- // priority (even if they are ultimately de-ranked by
- // `FilterAndSortAvailableDecoders()`)
- auto new_decoders = create_decoders_cb_.Run();
- std::move(new_decoders.begin(), new_decoders.end(),
- std::inserter(decoders_, decoders_.end()));
- FilterAndSortAvailableDecoders();
- }
- template <DemuxerStream::Type StreamType>
- void DecoderSelector<StreamType>::GetAndInitializeNextDecoder() {
- DVLOG(2) << __func__;
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- 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;
- }
- if (decode_failure_reinit_cause_.has_value()) {
- ReturnSelectionError(std::move(*decode_failure_reinit_cause_));
- } else {
- ReturnSelectionError(DecoderStatus::Codes::kUnsupportedConfig);
- }
- 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,
- GetDecoderName(decoder_->GetDecoderType()));
- DVLOG(2) << __func__ << ": initializing " << decoder_->GetDecoderType();
- const bool is_live = stream_->liveness() == StreamLiveness::kLive;
- traits_->InitializeDecoder(
- decoder_.get(), config_, is_live, cdm_context_,
- base::BindOnce(&DecoderSelector<StreamType>::OnDecoderInitializeDone,
- weak_this_factory_.GetWeakPtr()),
- output_cb_, waiting_cb_);
- }
- template <DemuxerStream::Type StreamType>
- void DecoderSelector<StreamType>::OnDecoderInitializeDone(
- DecoderStatus status) {
- DCHECK(decoder_);
- DVLOG(2) << __func__ << ": " << decoder_->GetDecoderType()
- << " success=" << static_cast<int>(status.code());
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!status.is_ok()) {
- // Note: Don't track this decode status, as it is the result of
- // initialization failure.
- MEDIA_LOG(INFO, media_log_)
- << "Failed to initialize " << decoder_->GetDecoderType();
- // Try the next decoder on the list.
- decoder_ = nullptr;
- GetAndInitializeNextDecoder();
- return;
- }
- RunSelectDecoderCB(std::move(decoder_));
- }
- template <DemuxerStream::Type StreamType>
- void DecoderSelector<StreamType>::ReturnSelectionError(DecoderStatus error) {
- DVLOG(1) << __func__ << ": No decoder selected";
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(!error.is_ok());
- decrypting_demuxer_stream_.reset();
- decoders_.clear();
- RunSelectDecoderCB(std::move(error));
- }
- 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::BindOnce(
- &DecoderSelector<StreamType>::OnDecryptingDemuxerStreamInitializeDone,
- weak_this_factory_.GetWeakPtr()));
- }
- template <DemuxerStream::Type StreamType>
- void DecoderSelector<StreamType>::OnDecryptingDemuxerStreamInitializeDone(
- PipelineStatus status) {
- DVLOG(2) << __func__ << ": status=" << status;
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (status != PIPELINE_OK) {
- // Since we already tried every potential decoder without DDS, give up.
- ReturnSelectionError(
- {DecoderStatus::Codes::kUnsupportedEncryptionMode, std::move(status)});
- 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.
- CreateDecoders();
- GetAndInitializeNextDecoder();
- }
- template <DemuxerStream::Type StreamType>
- void DecoderSelector<StreamType>::RunSelectDecoderCB(
- DecoderOrError decoder_or_error) {
- DCHECK(select_decoder_cb_);
- TRACE_EVENT_ASYNC_END2(
- "media", kSelectDecoderTrace, this, "type",
- DemuxerStream::GetTypeName(StreamType), "decoder",
- base::StringPrintf(
- "%s (%s)",
- decoder_or_error.has_value()
- ? GetDecoderName(decoder_or_error->GetDecoderType()).c_str()
- : "null",
- decrypting_demuxer_stream_ ? "encrypted" : "unencrypted"));
- task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(select_decoder_cb_), std::move(decoder_or_error),
- std::move(decrypting_demuxer_stream_)));
- }
- template <DemuxerStream::Type StreamType>
- void DecoderSelector<StreamType>::FilterAndSortAvailableDecoders() {
- std::vector<std::unique_ptr<Decoder>> decoders = std::move(decoders_);
- std::vector<std::unique_ptr<Decoder>> deprioritized_decoders;
- DCHECK(decoder_priority_cb_);
- for (auto& decoder : decoders) {
- // Skip the decoder if this decoder doesn't support encryption for a
- // decrypting config
- if (config_.is_encrypted() && !decoder->SupportsDecryption())
- continue;
- // Run the predicate on this decoder.
- switch (decoder_priority_cb_.Run(config_, *decoder)) {
- case DecoderPriority::kSkipped:
- continue;
- case DecoderPriority::kNormal:
- decoders_.push_back(std::move(decoder));
- break;
- case DecoderPriority::kDeprioritized:
- deprioritized_decoders.push_back(std::move(decoder));
- break;
- }
- }
- // Post-insert deprioritized decoders
- std::move(deprioritized_decoders.begin(), deprioritized_decoders.end(),
- std::inserter(decoders_, decoders_.end()));
- }
- // 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
|