blob: d136b8ede5ba8d4910002e43f3e23056e681823a [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/metrics/histogram_functions.h"
#include "base/strings/string_split.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_system_names.h"
#include "media/base/key_systems.h"
#include "media/base/media_switches.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
namespace content {
namespace {
void SendCdmAvailableUMA(const std::string& key_system, bool available) {
base::UmaHistogramBoolean("Media.EME." +
media::GetKeySystemNameForUMA(key_system) +
".LibraryCdmAvailable",
available);
}
template <typename T>
std::vector<T> SetToVector(const base::flat_set<T>& s) {
return std::vector<T>(s.begin(), s.end());
}
// Returns whether hardware secure codecs are enabled from command line. If
// true, |video_codecs| are filled with codecs specified on command line, which
// could be empty if no codecs are specified. If false, |video_codecs| will not
// be modified.
bool IsHardwareSecureCodecsOverriddenFromCommandLine(
std::vector<media::VideoCodec>* video_codecs,
std::vector<media::EncryptionMode>* encryption_schemes) {
DCHECK(video_codecs->empty());
DCHECK(encryption_schemes->empty());
auto* command_line = base::CommandLine::ForCurrentProcess();
if (!command_line || !command_line->HasSwitch(
switches::kOverrideHardwareSecureCodecsForTesting)) {
return false;
}
auto codecs_string = command_line->GetSwitchValueASCII(
switches::kOverrideHardwareSecureCodecsForTesting);
const auto supported_codecs = base::SplitStringPiece(
codecs_string, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
for (const auto& codec : supported_codecs) {
if (codec == "vp8")
video_codecs->push_back(media::VideoCodec::kCodecVP8);
else if (codec == "vp9")
video_codecs->push_back(media::VideoCodec::kCodecVP9);
else if (codec == "avc1")
video_codecs->push_back(media::VideoCodec::kCodecH264);
else
DVLOG(1) << "Unsupported codec specified on command line: " << codec;
}
// Codecs enabled from command line assumes CENC support.
if (!video_codecs->empty())
encryption_schemes->push_back(media::EncryptionMode::kCenc);
return true;
}
void GetHardwareSecureDecryptionCaps(
const std::string& key_system,
const base::flat_set<media::CdmProxy::Protocol>& cdm_proxy_protocols,
std::vector<media::VideoCodec>* video_codecs,
std::vector<media::EncryptionMode>* encryption_schemes) {
DCHECK(video_codecs->empty());
DCHECK(encryption_schemes->empty());
if (!base::FeatureList::IsEnabled(media::kHardwareSecureDecryption)) {
DVLOG(1) << "Hardware secure decryption disabled";
return;
}
// Secure codecs override takes precedence over other checks.
if (IsHardwareSecureCodecsOverriddenFromCommandLine(video_codecs,
encryption_schemes)) {
DVLOG(1) << "Hardware secure codecs overridden from command line";
return;
}
if (cdm_proxy_protocols.empty()) {
DVLOG(1) << "CDM does not support any CdmProxy protocols";
return;
}
// 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;
}
if (!base::FeatureList::IsEnabled(media::kMojoVideoDecoder)) {
DVLOG(1) << "Hardware secure codecs not supported because mojo video "
"decode disabled";
return;
}
base::flat_set<media::VideoCodec> video_codec_set;
base::flat_set<media::EncryptionMode> encryption_scheme_set;
GetContentClient()->browser()->GetHardwareSecureDecryptionCaps(
key_system, cdm_proxy_protocols, &video_codec_set,
&encryption_scheme_set);
*video_codecs = SetToVector(video_codec_set);
*encryption_schemes = SetToVector(encryption_scheme_set);
}
} // namespace
// static
void KeySystemSupportImpl::Create(
media::mojom::KeySystemSupportRequest request) {
DVLOG(3) << __func__;
// The created object is bound to (and owned by) |request|.
mojo::MakeStrongBinding(std::make_unique<KeySystemSupportImpl>(),
std::move(request));
}
// static
std::unique_ptr<CdmInfo> KeySystemSupportImpl::GetCdmInfoForKeySystem(
const std::string& key_system) {
DVLOG(2) << __func__ << ": key_system = " << key_system;
for (const auto& cdm : CdmRegistry::GetInstance()->GetAllRegisteredCdms()) {
if (cdm.supported_key_system == key_system ||
(cdm.supports_sub_key_systems &&
media::IsChildKeySystemOf(key_system, cdm.supported_key_system))) {
return std::make_unique<CdmInfo>(cdm);
}
}
return nullptr;
}
KeySystemSupportImpl::KeySystemSupportImpl() = default;
KeySystemSupportImpl::~KeySystemSupportImpl() = default;
void KeySystemSupportImpl::IsKeySystemSupported(
const std::string& key_system,
IsKeySystemSupportedCallback callback) {
DVLOG(3) << __func__ << ": key_system = " << key_system;
auto cdm_info = GetCdmInfoForKeySystem(key_system);
if (!cdm_info) {
SendCdmAvailableUMA(key_system, false);
std::move(callback).Run(false, nullptr);
return;
}
SendCdmAvailableUMA(key_system, true);
// Supported codecs and encryption schemes.
auto capability = media::mojom::KeySystemCapability::New();
capability->video_codecs = cdm_info->capability.video_codecs;
capability->supports_vp9_profile2 =
cdm_info->capability.supports_vp9_profile2;
capability->encryption_schemes =
SetToVector(cdm_info->capability.encryption_schemes);
GetHardwareSecureDecryptionCaps(key_system,
cdm_info->capability.cdm_proxy_protocols,
&capability->hw_secure_video_codecs,
&capability->hw_secure_encryption_schemes);
capability->session_types = SetToVector(cdm_info->capability.session_types);
std::move(callback).Run(true, std::move(capability));
}
} // namespace content