blob: 84bf832509f39ab6af384f7944e809d7b8cca61b [file] [log] [blame]
// Copyright 2017 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 "content/browser/media/key_system_support_impl.h"
#include <vector>
#include "base/command_line.h"
#include "base/containers/flat_set.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_split.h"
#include "build/build_config.h"
#include "content/browser/media/cdm_registry_impl.h"
#include "content/public/browser/cdm_registry.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/cdm_info.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "media/base/key_systems.h"
#include "media/base/media_switches.h"
#include "media/media_buildflags.h"
#include "media/mojo/buildflags.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#if defined(OS_WIN)
#include "content/browser/media/key_system_support_win.h"
#endif
#if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
#include "ash/constants/ash_features.h"
#endif // BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
namespace content {
namespace {
void SendCdmAvailableUMA(const std::string& key_system, bool available) {
base::UmaHistogramBoolean("Media.EME." +
media::GetKeySystemNameForUMA(key_system) +
".LibraryCdmAvailable",
available);
}
// Returns a CdmCapability with codecs specified on command line. Returns null
// if kOverrideHardwareSecureCodecsForTesting was not specified or not valid
// codecs specified.
absl::optional<media::CdmCapability>
GetHardwareSecureCapabilityOverriddenFromCommandLine() {
auto* command_line = base::CommandLine::ForCurrentProcess();
if (!command_line || !command_line->HasSwitch(
switches::kOverrideHardwareSecureCodecsForTesting)) {
return absl::nullopt;
}
auto overridden_codecs_string = command_line->GetSwitchValueASCII(
switches::kOverrideHardwareSecureCodecsForTesting);
const auto overridden_codecs =
base::SplitStringPiece(overridden_codecs_string, ",",
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
// As the command line switch does not include profiles, specify {} to
// indicate that all relevant profiles should be considered supported.
std::vector<media::AudioCodec> audio_codecs;
media::CdmCapability::VideoCodecMap video_codecs;
for (const auto& codec : overridden_codecs) {
if (codec == "vp8")
video_codecs[media::VideoCodec::kCodecVP8] = {};
else if (codec == "vp9")
video_codecs[media::VideoCodec::kCodecVP9] = {};
else if (codec == "avc1")
video_codecs[media::VideoCodec::kCodecH264] = {};
else if (codec == "hevc")
video_codecs[media::VideoCodec::kCodecHEVC] = {};
else if (codec == "mp4a")
audio_codecs.push_back(media::AudioCodec::kCodecAAC);
else if (codec == "vorbis")
audio_codecs.push_back(media::AudioCodec::kCodecVorbis);
else
DVLOG(1) << "Unsupported codec specified on command line: " << codec;
}
if (video_codecs.empty()) {
DVLOG(1) << "No codec codec specified on command line";
return absl::nullopt;
}
// Overridden codecs assume CENC and temporary session support.
// The EncryptedMediaSupportedTypesWidevineHwSecureTest tests depend
// on 'cbcs' not being supported.
return media::CdmCapability(std::move(audio_codecs), std::move(video_codecs),
{media::EncryptionScheme::kCenc},
{media::CdmSessionType::kTemporary});
}
// Software secure capability can be obtained synchronously in all supported
// cases. If needed, this can be easily converted to an asynchronous call.
absl::optional<media::CdmCapability> GetSoftwareSecureCapability(
const std::string& key_system) {
auto cdm_info = CdmRegistryImpl::GetInstance()->GetCdmInfo(
key_system, CdmInfo::Robustness::kSoftwareSecure);
if (!cdm_info) {
SendCdmAvailableUMA(key_system, false);
return absl::nullopt;
}
SendCdmAvailableUMA(key_system, true);
if (!cdm_info->capability) {
DVLOG(1) << "Lazy initialization of SoftwareSecure CdmCapability not "
"supported!";
return absl::nullopt;
}
return cdm_info->capability;
}
// Trying to get hardware secure capability synchronously. If lazy
// initialization is needed, set `lazy_initialize` to true.
absl::optional<media::CdmCapability> GetHardwareSecureCapability(
const std::string& key_system,
bool* lazy_initialize) {
*lazy_initialize = false;
#if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
if (!base::FeatureList::IsEnabled(chromeos::features::kCdmFactoryDaemon)) {
DVLOG(1) << "Hardware secure decryption disabled";
return absl::nullopt;
}
#else
if (!base::FeatureList::IsEnabled(media::kHardwareSecureDecryption)) {
DVLOG(1) << "Hardware secure decryption disabled";
return absl::nullopt;
}
#endif // BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
// Secure codecs override takes precedence over other checks.
auto overridden_capability =
GetHardwareSecureCapabilityOverriddenFromCommandLine();
if (overridden_capability) {
DVLOG(1) << "Hardware secure codecs overridden from command line";
return overridden_capability;
}
// Hardware secure video codecs need hardware video decoder support.
// TODO(xhwang): Make sure this check is as close as possible to the check
// in the render process. For example, also check check GPU features like
// GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE.
auto* command_line = base::CommandLine::ForCurrentProcess();
if (command_line &&
command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode)) {
DVLOG(1) << "Hardware secure codecs not supported because accelerated "
"video decode disabled";
return absl::nullopt;
}
auto cdm_info = CdmRegistryImpl::GetInstance()->GetCdmInfo(
key_system, CdmInfo::Robustness::kHardwareSecure);
if (!cdm_info) {
DVLOG(1) << "No Hardware secure decryption CDM registered";
return absl::nullopt;
}
if (cdm_info->capability) {
DVLOG(1) << "Hardware secure decryption CDM registered";
return cdm_info->capability;
}
DVLOG(1) << "Lazy initialization of CdmCapability";
*lazy_initialize = true;
return absl::nullopt;
}
} // namespace
// static
void KeySystemSupportImpl::Create(
mojo::PendingReceiver<media::mojom::KeySystemSupport> receiver) {
DVLOG(3) << __func__;
// The created object is bound to (and owned by) |request|.
mojo::MakeSelfOwnedReceiver(std::make_unique<KeySystemSupportImpl>(),
std::move(receiver));
}
KeySystemSupportImpl::KeySystemSupportImpl() = default;
KeySystemSupportImpl::~KeySystemSupportImpl() = default;
void KeySystemSupportImpl::IsKeySystemSupported(
const std::string& key_system,
IsKeySystemSupportedCallback callback) {
DVLOG(3) << __func__ << ": key_system=" << key_system;
bool lazy_initialize = false;
auto hw_secure_capability =
GetHardwareSecureCapability(key_system, &lazy_initialize);
if (lazy_initialize) {
LazyInitializeHardwareSecureCapability(
key_system,
base::BindOnce(&KeySystemSupportImpl::OnHardwareSecureCapability,
weak_ptr_factory_.GetWeakPtr(), key_system,
std::move(callback), /*lazy_initialize=*/true));
return;
}
OnHardwareSecureCapability(key_system, std::move(callback),
/*lazy_initialize=*/false, hw_secure_capability);
}
// It's possible this is called multiple times for the same key system when
// there are parallel `IsKeySystemSupported()` calls from different renderer
// processes. Since the query is typically fast, the chance for this to happen
// is low and it won't cause any collision. So we choose not to handle this
// case explicitly for simplicity.
// TODO(xhwang): Find a way to register this as callbacks so we don't have to
// hardcode platform-specific logic here.
// TODO(jrummell): Support Android query.
void KeySystemSupportImpl::LazyInitializeHardwareSecureCapability(
const std::string& key_system,
CdmCapabilityCB cdm_capability_cb) {
if (hw_secure_capability_cb_for_testing_) {
hw_secure_capability_cb_for_testing_.Run(key_system,
std::move(cdm_capability_cb));
return;
}
#if defined(OS_WIN)
auto cdm_info = CdmRegistryImpl::GetInstance()->GetCdmInfo(
key_system, CdmInfo::Robustness::kHardwareSecure);
DCHECK(cdm_info && !cdm_info->capability);
GetMediaFoundationServiceHardwareSecureCdmCapability(
key_system, cdm_info->path, std::move(cdm_capability_cb));
#else
std::move(cdm_capability_cb).Run(absl::nullopt);
#endif // defined(OS_WIN)
}
void KeySystemSupportImpl::SetHardwareSecureCapabilityCBForTesting(
HardwareSecureCapabilityCB cb) {
hw_secure_capability_cb_for_testing_ = std::move(cb);
}
void KeySystemSupportImpl::OnHardwareSecureCapability(
const std::string& key_system,
IsKeySystemSupportedCallback callback,
bool lazy_initialize,
absl::optional<media::CdmCapability> hw_secure_capability) {
// See comment above. This could be called multiple times when we have
// parallel `IsKeySystemSupported()` calls from different renderer processes.
// This is okay and won't cause collision or corruption of data.
if (lazy_initialize) {
ignore_result(CdmRegistryImpl::GetInstance()->FinalizeCdmCapability(
key_system, CdmInfo::Robustness::kHardwareSecure,
hw_secure_capability));
}
auto capability = media::mojom::KeySystemCapability::New();
capability->sw_secure_capability = GetSoftwareSecureCapability(key_system);
capability->hw_secure_capability = hw_secure_capability;
if (!capability->sw_secure_capability && !capability->hw_secure_capability) {
std::move(callback).Run(false, nullptr);
return;
}
std::move(callback).Run(true, std::move(capability));
}
} // namespace content