blob: 85e33ac608a178cfecba311d1da9d8af29f82379 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/media/media_internals_cdm_helper.h"
#include <memory>
#include "base/values.h"
#include "content/browser/media/media_internals.h"
#include "content/public/browser/web_ui.h"
#include "media/base/audio_codecs.h"
#include "media/base/content_decryption_module.h"
#include "media/base/encryption_scheme.h"
#include "media/base/video_codecs.h"
#include "media/cdm/cdm_capability.h"
namespace content {
namespace {
std::string GetCdmInfoCapabilityStatusName(CdmInfo::Status status) {
switch (status) {
case CdmInfo::Status::kUninitialized:
return "Uninitialized";
case CdmInfo::Status::kEnabled:
return "Enabled";
case CdmInfo::Status::kCommandLineOverridden:
return "Overridden from command line";
case CdmInfo::Status::kHardwareSecureDecryptionDisabled:
return "Disabled because Hardware Secure Decryption is disabled";
case CdmInfo::Status::kAcceleratedVideoDecodeDisabled:
return "Disabled because Accelerated Video Decode is disabled";
case CdmInfo::Status::kGpuFeatureDisabled:
return "Disabled via GPU feature (e.g. bad GPU or driver)";
case CdmInfo::Status::kGpuCompositionDisabled:
return "Disabled because GPU (direct) composition is disabled";
case CdmInfo::Status::kDisabledByPref:
return "Disabled due to previous errors (stored in Local State)";
case CdmInfo::Status::kDisabledOnError:
return "Disabled after errors or crashes";
}
}
std::string GetCdmSessionTypeName(media::CdmSessionType session_type) {
switch (session_type) {
case media::CdmSessionType::kTemporary:
return "temporary";
case media::CdmSessionType::kPersistentLicense:
return "persistent-license";
}
}
base::Value::List VideoCodecInfoToList(
const media::VideoCodecInfo& video_codec_info) {
auto& profiles = video_codec_info.supported_profiles;
base::Value::List list;
for (const auto& profile : profiles)
list.Append(media::GetProfileName(profile));
return list;
}
base::Value::Dict CdmCapabilityToDict(
const media::CdmCapability& cdm_capability) {
base::Value::Dict dict;
base::Value::List audio_codec_list;
for (const auto& audio_codec : cdm_capability.audio_codecs)
audio_codec_list.Append(media::GetCodecName(audio_codec));
dict.Set("Audio Codecs", std::move(audio_codec_list));
auto* video_codec_dict = dict.EnsureDict("Video Codecs");
for (const auto& [video_codec, video_codec_info] :
cdm_capability.video_codecs) {
auto codec_name = media::GetCodecName(video_codec);
// Codecs marked with "*" signals clear lead not supported.
if (!video_codec_info.supports_clear_lead)
codec_name += "*";
video_codec_dict->Set(codec_name, VideoCodecInfoToList(video_codec_info));
}
base::Value::List encryption_scheme_list;
for (const auto& encryption_scheme : cdm_capability.encryption_schemes) {
encryption_scheme_list.Append(
media::GetEncryptionSchemeName(encryption_scheme));
}
dict.Set("Encryption Schemes", std::move(encryption_scheme_list));
base::Value::List session_type_list;
for (const auto& session_type : cdm_capability.session_types)
session_type_list.Append(GetCdmSessionTypeName(session_type));
dict.Set("Session Types", std::move(session_type_list));
return dict;
}
base::Value::Dict CdmInfoToDict(const CdmInfo& cdm_info) {
base::Value::Dict dict;
dict.Set("key_system", cdm_info.key_system);
dict.Set("robustness", GetCdmInfoRobustnessName(cdm_info.robustness));
dict.Set("name", cdm_info.name);
dict.Set("version",
cdm_info.version.IsValid() ? cdm_info.version.GetString() : "N/A");
dict.Set("path",
cdm_info.path.empty() ? "N/A" : cdm_info.path.AsUTF8Unsafe());
dict.Set("status", GetCdmInfoCapabilityStatusName(cdm_info.status));
if (cdm_info.capability) {
dict.Set("capability", CdmCapabilityToDict(cdm_info.capability.value()));
} else {
// This could happen if hardware secure capabilities are overridden or
// hardware video decode is disabled from command line.
dict.Set("capability", "No Capability");
}
return dict;
}
std::u16string SerializeUpdate(base::StringPiece function,
const base::Value::List& value) {
base::ValueView args[] = {value};
return content::WebUI::GetJavascriptCall(function, args);
}
} // namespace
MediaInternalsCdmHelper::MediaInternalsCdmHelper() = default;
MediaInternalsCdmHelper::~MediaInternalsCdmHelper() = default;
void MediaInternalsCdmHelper::GetRegisteredCdms() {
CdmRegistryImpl::GetInstance()->ObserveKeySystemCapabilities(
base::BindRepeating(
&MediaInternalsCdmHelper::OnKeySystemCapabilitiesUpdated,
weak_factory_.GetWeakPtr()));
}
// Ignore results since we'll get them from CdmRegistryImpl directly.
void MediaInternalsCdmHelper::OnKeySystemCapabilitiesUpdated(
KeySystemCapabilities /*capabilities*/) {
auto cdms = CdmRegistryImpl::GetInstance()->GetRegisteredCdms();
base::Value::List cdm_list;
for (const auto& cdm_info : cdms) {
DCHECK(cdm_info.status != CdmInfo::Status::kUninitialized);
cdm_list.Append(CdmInfoToDict(cdm_info));
}
return MediaInternals::GetInstance()->SendUpdate(
SerializeUpdate("media.updateRegisteredCdms", cdm_list));
}
} // namespace content