Commit 865dfbdd authored by Xiaohan Wang's avatar Xiaohan Wang Committed by Commit Bot

media: Refactor codec string to EmeCodec conversion

Instead of stripping the codec string and table look-up by stripped codec
string, use MimeUtil ParseAudioCodecString() and ParseVideoCodecString() to
convert the codec string to media AudioCodec/VideoCodec, then have dedicated
functions to convert media codecs to EmeCodecs.

Bug: 707127
Test: All existing tests pass.
Change-Id: I9a69a76bd8acc6ef7cb23ef6630c258df3f5347f
Reviewed-on: https://chromium-review.googlesource.com/c/1277613
Commit-Queue: Xiaohan Wang <xhwang@chromium.org>
Reviewed-by: default avatarChrome Cunningham <chcunningham@chromium.org>
Reviewed-by: default avatarDan Sanders <sandersd@chromium.org>
Cr-Commit-Position: refs/heads/master@{#600033}
parent 546926a4
......@@ -20,6 +20,7 @@
#include "media/base/media.h"
#include "media/base/media_client.h"
#include "media/base/media_switches.h"
#include "media/base/mime_util.h"
#include "media/media_buildflags.h"
#include "third_party/widevine/cdm/widevine_cdm_common.h"
......@@ -51,50 +52,67 @@ static const MimeTypeToCodecs kMimeTypeToCodecsMap[] = {
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
}; // namespace media
struct NameToCodec {
const char* name;
EmeCodec codec;
};
EmeCodec ToAudioEmeCodec(AudioCodec codec) {
switch (codec) {
case kCodecAAC:
return EME_CODEC_AAC;
case kCodecVorbis:
return EME_CODEC_VORBIS;
case kCodecFLAC:
return EME_CODEC_FLAC;
case kCodecOpus:
return EME_CODEC_OPUS;
case kCodecEAC3:
return EME_CODEC_EAC3;
case kCodecAC3:
return EME_CODEC_AC3;
case kCodecMpegHAudio:
return EME_CODEC_MPEG_H_AUDIO;
default:
DVLOG(1) << "Unsupported AudioCodec " << codec;
return EME_CODEC_NONE;
}
}
// Mapping between codec names and enum values.
// TODO(crbug.com/724362): Convert codecs into enums directly to avoid this map.
static const NameToCodec kCodecMap[] = {
{"opus", EME_CODEC_OPUS}, // Opus
{"vorbis", EME_CODEC_VORBIS}, // Vorbis
{"vp8", EME_CODEC_VP8}, // VP8
{"vp8.0", EME_CODEC_VP8}, // VP8
{"vp9", EME_CODEC_VP9_PROFILE0}, // Legacy VP9
{"vp9.0", EME_CODEC_VP9_PROFILE0}, // Legacy VP9
// "vp09.00.*" is replaced by "vp09-profile0" to avoid ambiguity. See
// OverrideVp9Profile0() in key_system_config_selector.cc.
{"vp09-profile0", EME_CODEC_VP9_PROFILE0}, // VP9 profile 0
{"vp09", EME_CODEC_VP9_PROFILES123}, // VP9 profile 1/2/3
{"flac", EME_CODEC_FLAC}, // FLAC
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
{"mp4a", EME_CODEC_AAC}, // AAC.
#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
{"ac-3", EME_CODEC_AC3}, // AC3.
{"ec-3", EME_CODEC_EAC3}, // EAC3.
#endif
#if BUILDFLAG(ENABLE_MPEG_H_AUDIO_DEMUXING)
{"mhm1", EME_CODEC_MPEG_H_AUDIO}, // MPEG-H Audio.
#endif
{"avc1", EME_CODEC_AVC1}, // AVC1 for MP4 and MP2T
{"avc3", EME_CODEC_AVC1}, // AVC3 for MP4 and MP2T
#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
{"hev1", EME_CODEC_HEVC}, // HEV1.
{"hvc1", EME_CODEC_HEVC}, // HVC1.
#endif
#if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
{"dva1", EME_CODEC_DOLBY_VISION_AVC}, // DolbyVision AVC
{"dvav", EME_CODEC_DOLBY_VISION_AVC}, // DolbyVision AVC
#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
{"dvh1", EME_CODEC_DOLBY_VISION_HEVC}, // DolbyVision HEVC
{"dvhe", EME_CODEC_DOLBY_VISION_HEVC}, // DolbyVision HEVC
#endif
#endif
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
};
EmeCodec ToVideoEmeCodec(VideoCodec codec, VideoCodecProfile profile) {
switch (codec) {
case kCodecH264:
return EME_CODEC_AVC1;
case kCodecVP8:
return EME_CODEC_VP8;
case kCodecVP9:
// ParseVideoCodecString() returns VIDEO_CODEC_PROFILE_UNKNOWN for "vp9"
// and "vp9.0". Since these codecs are essentially the same as profile 0,
// return EME_CODEC_VP9_PROFILE0.
if (profile == VP9PROFILE_PROFILE0 ||
profile == VIDEO_CODEC_PROFILE_UNKNOWN) {
return EME_CODEC_VP9_PROFILE0;
} else if (profile == VP9PROFILE_PROFILE1 ||
profile == VP9PROFILE_PROFILE2 ||
profile == VP9PROFILE_PROFILE3) {
return EME_CODEC_VP9_PROFILES123;
} else {
return EME_CODEC_NONE;
}
case kCodecHEVC:
return EME_CODEC_HEVC;
case kCodecDolbyVision:
// Only profiles 0, 4, 5 and 7 are valid. Profile 0 is encoded based on
// AVC while profile 4, 5 and 7 are based on HEVC.
if (profile == DOLBYVISION_PROFILE0) {
return EME_CODEC_DOLBY_VISION_AVC;
} else if (profile == DOLBYVISION_PROFILE4 ||
profile == DOLBYVISION_PROFILE5 ||
profile == DOLBYVISION_PROFILE7) {
return EME_CODEC_DOLBY_VISION_HEVC;
} else {
return EME_CODEC_NONE;
}
default:
DVLOG(1) << "Unsupported VideoCodec " << codec;
return EME_CODEC_NONE;
}
}
class ClearKeyProperties : public KeySystemProperties {
public:
......@@ -210,10 +228,11 @@ class KeySystemsImpl : public KeySystems {
std::string GetKeySystemNameForUMA(const std::string& key_system) const;
// These two functions are for testing purpose only.
void AddCodecMask(EmeMediaType media_type,
void AddCodecMaskForTesting(EmeMediaType media_type,
const std::string& codec,
uint32_t mask);
void AddMimeTypeCodecMask(const std::string& mime_type, uint32_t mask);
void AddMimeTypeCodecMaskForTesting(const std::string& mime_type,
uint32_t mask);
// Implementation of KeySystems interface.
bool IsSupportedKeySystem(const std::string& key_system) const override;
......@@ -276,14 +295,21 @@ class KeySystemsImpl : public KeySystems {
// Potentially pass EmeMediaType and a container enum.
SupportedCodecs GetCodecMaskForMimeType(
const std::string& container_mime_type) const;
EmeCodec GetCodecForString(const std::string& codec) const;
// Converts a full |codec_string| (e.g. vp09.02.10.10) to an EmeCodec. Returns
// EME_CODEC_NONE is the |codec_string| is invalid or not supported by EME.
EmeCodec GetEmeCodecForString(EmeMediaType media_type,
const std::string& container_mime_type,
const std::string& codec_string) const;
// Map from key system string to KeySystemProperties instance.
KeySystemPropertiesMap key_system_properties_map_;
// This member should only be modified by RegisterMimeType().
MimeTypeToCodecsMap mime_type_to_codecs_map_;
CodecMap codec_map_;
// For unit test only.
CodecMap codec_map_for_testing_;
SupportedCodecs audio_codec_mask_;
SupportedCodecs video_codec_mask_;
......@@ -305,11 +331,6 @@ KeySystemsImpl* KeySystemsImpl::GetInstance() {
KeySystemsImpl::KeySystemsImpl()
: audio_codec_mask_(EME_CODEC_AUDIO_ALL),
video_codec_mask_(EME_CODEC_VIDEO_ALL) {
for (size_t i = 0; i < arraysize(kCodecMap); ++i) {
const std::string& name = kCodecMap[i].name;
DCHECK(!codec_map_.count(name));
codec_map_[name] = kCodecMap[i].codec;
}
for (size_t i = 0; i < arraysize(kMimeTypeToCodecsMap); ++i) {
RegisterMimeType(kMimeTypeToCodecsMap[i].mime_type,
kMimeTypeToCodecsMap[i].codecs);
......@@ -331,11 +352,44 @@ SupportedCodecs KeySystemsImpl::GetCodecMaskForMimeType(
return iter->second;
}
EmeCodec KeySystemsImpl::GetCodecForString(const std::string& codec) const {
auto iter = codec_map_.find(codec);
if (iter != codec_map_.end())
EmeCodec KeySystemsImpl::GetEmeCodecForString(
EmeMediaType media_type,
const std::string& container_mime_type,
const std::string& codec_string) const {
// This is not checked because MimeUtil declares "vp9" and "vp9.0" as
// ambiguous, but they have always been supported by EME.
// TODO(xhwang): Find out whether we should fix MimeUtil about these cases.
bool is_ambiguous = true;
// For testing only.
auto iter = codec_map_for_testing_.find(codec_string);
if (iter != codec_map_for_testing_.end())
return iter->second;
return EME_CODEC_NONE;
if (media_type == EmeMediaType::AUDIO) {
AudioCodec audio_codec = kUnknownAudioCodec;
ParseAudioCodecString(container_mime_type, codec_string, &is_ambiguous,
&audio_codec);
DVLOG(3) << "Audio codec = " << audio_codec;
return ToAudioEmeCodec(audio_codec);
}
DCHECK_EQ(media_type, EmeMediaType::VIDEO);
// In general EmeCodec doesn't care about codec profiles and assumes the same
// level of profile support as Chromium, which is checked in
// KeySystemConfigSelector::IsSupportedContentType(). However, there are a few
// exceptions where we need to know the profile. For example, for VP9, there
// are older CDMs only supporting profile 0, hence EmeCodec differentiate
// between VP9 profile 0 and higher profiles.
VideoCodec video_codec = kUnknownVideoCodec;
VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN;
uint8_t level = 0;
VideoColorSpace color_space;
ParseVideoCodecString(container_mime_type, codec_string, &is_ambiguous,
&video_codec, &profile, &level, &color_space);
DVLOG(3) << "Video codec = " << video_codec << ", profile = " << profile;
return ToVideoEmeCodec(video_codec, profile);
}
void KeySystemsImpl::UpdateIfNeeded() {
......@@ -532,12 +586,12 @@ std::string KeySystemsImpl::GetKeySystemNameForUMA(
return kUnknownKeySystemNameForUMA;
}
void KeySystemsImpl::AddCodecMask(EmeMediaType media_type,
void KeySystemsImpl::AddCodecMaskForTesting(EmeMediaType media_type,
const std::string& codec,
uint32_t mask) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!codec_map_.count(codec));
codec_map_[codec] = static_cast<EmeCodec>(mask);
DCHECK(!codec_map_for_testing_.count(codec));
codec_map_for_testing_[codec] = static_cast<EmeCodec>(mask);
if (media_type == EmeMediaType::AUDIO) {
audio_codec_mask_ |= mask;
} else {
......@@ -545,7 +599,8 @@ void KeySystemsImpl::AddCodecMask(EmeMediaType media_type,
}
}
void KeySystemsImpl::AddMimeTypeCodecMask(const std::string& mime_type,
void KeySystemsImpl::AddMimeTypeCodecMaskForTesting(
const std::string& mime_type,
uint32_t codecs_mask) {
RegisterMimeType(mime_type, static_cast<EmeCodec>(codecs_mask));
}
......@@ -623,9 +678,10 @@ EmeConfigRule KeySystemsImpl::GetContentTypeConfigRule(
// no | any | NOT_SUPPORTED
EmeConfigRule support = EmeConfigRule::SUPPORTED;
for (size_t i = 0; i < codecs.size(); i++) {
EmeCodec codec = GetCodecForString(codecs[i]);
EmeCodec codec =
GetEmeCodecForString(media_type, container_mime_type, codecs[i]);
if (codec == EME_CODEC_NONE) {
DVLOG(2) << "Unknown stripped codec string \"" << codecs[i] << "\"";
DVLOG(2) << "Unsupported codec string \"" << codecs[i] << "\"";
return EmeConfigRule::NOT_SUPPORTED;
}
......@@ -743,15 +799,17 @@ bool CanUseAesDecryptor(const std::string& key_system) {
// "media" where "UNIT_TEST" is not defined. So we need to specify
// "MEDIA_EXPORT" here again so that they are visible to tests.
MEDIA_EXPORT void AddCodecMask(EmeMediaType media_type,
MEDIA_EXPORT void AddCodecMaskForTesting(EmeMediaType media_type,
const std::string& codec,
uint32_t mask) {
KeySystemsImpl::GetInstance()->AddCodecMask(media_type, codec, mask);
KeySystemsImpl::GetInstance()->AddCodecMaskForTesting(media_type, codec,
mask);
}
MEDIA_EXPORT void AddMimeTypeCodecMask(const std::string& mime_type,
MEDIA_EXPORT void AddMimeTypeCodecMaskForTesting(const std::string& mime_type,
uint32_t mask) {
KeySystemsImpl::GetInstance()->AddMimeTypeCodecMask(mime_type, mask);
KeySystemsImpl::GetInstance()->AddMimeTypeCodecMaskForTesting(mime_type,
mask);
}
} // namespace media
......@@ -44,7 +44,7 @@ class MEDIA_EXPORT KeySystems {
const std::string& key_system,
EncryptionMode encryption_scheme) const = 0;
// Returns the configuration rule for supporting a container and list of
// Returns the configuration rule for supporting a container and a list of
// codecs.
virtual EmeConfigRule GetContentTypeConfigRule(
const std::string& key_system,
......@@ -94,12 +94,12 @@ MEDIA_EXPORT bool CanUseAesDecryptor(const std::string& key_system);
#if defined(UNIT_TEST)
// Helper functions to add container/codec types for testing purposes.
// Call AddCodecMask() first to ensure the mask values passed to
// AddMimeTypeCodecMask() already exist.
MEDIA_EXPORT void AddCodecMask(EmeMediaType media_type,
// Call AddCodecMaskForTesting() first to ensure the mask values passed to
// AddMimeTypeCodecMaskForTesting() already exist.
MEDIA_EXPORT void AddCodecMaskForTesting(EmeMediaType media_type,
const std::string& codec,
uint32_t mask);
MEDIA_EXPORT void AddMimeTypeCodecMask(const std::string& mime_type,
MEDIA_EXPORT void AddMimeTypeCodecMaskForTesting(const std::string& mime_type,
uint32_t mask);
#endif // defined(UNIT_TEST)
......
......@@ -228,12 +228,12 @@ void AddContainerAndCodecMasksForTest() {
if (is_test_masks_added)
return;
AddCodecMask(EmeMediaType::AUDIO, "fooaudio", TEST_CODEC_FOO_AUDIO);
AddCodecMask(EmeMediaType::VIDEO, "foovideo", TEST_CODEC_FOO_VIDEO);
AddCodecMask(EmeMediaType::VIDEO, "securefoovideo",
AddCodecMaskForTesting(EmeMediaType::AUDIO, "fooaudio", TEST_CODEC_FOO_AUDIO);
AddCodecMaskForTesting(EmeMediaType::VIDEO, "foovideo", TEST_CODEC_FOO_VIDEO);
AddCodecMaskForTesting(EmeMediaType::VIDEO, "securefoovideo",
TEST_CODEC_FOO_SECURE_VIDEO);
AddMimeTypeCodecMask("audio/foo", TEST_CODEC_FOO_AUDIO_ALL);
AddMimeTypeCodecMask("video/foo", TEST_CODEC_FOO_VIDEO_ALL);
AddMimeTypeCodecMaskForTesting("audio/foo", TEST_CODEC_FOO_AUDIO_ALL);
AddMimeTypeCodecMaskForTesting("video/foo", TEST_CODEC_FOO_VIDEO_ALL);
is_test_masks_added = true;
}
......
......@@ -167,16 +167,6 @@ bool IsSupportedMediaType(const std::string& container_mime_type,
return (support_result == IsSupported);
}
// Replaces any codec in |codecs| starting with "vp09.00." with "vp09-profile0".
void OverrideVp9Profile0(std::vector<std::string>* codecs) {
for (std::string& codec : *codecs) {
if (base::StartsWith(codec, "vp09.00.", base::CompareCase::SENSITIVE)) {
DVLOG(3) << "Replace codec " << codec << " with vp09-profile0";
codec = "vp09-profile0";
}
}
}
} // namespace
struct KeySystemConfigSelector::SelectionRequest {
......@@ -358,20 +348,13 @@ bool KeySystemConfigSelector::IsSupportedContentType(
return false;
}
// Before checking CDM support, split and strip the |codecs| because
// |key_systems_| does not handle extended codecs. Note that extended codec
// information was checked above.
std::vector<std::string> stripped_codec_vector;
SplitCodecs(codecs, &stripped_codec_vector);
// Since both "vp09.00.*" and "vp09.01.*" will be stripped to "vp09", before
// stripping codecs, replace "vp09.00.*" with "vp09-profile0" to avoid
// ambiguity.
OverrideVp9Profile0(&stripped_codec_vector);
StripCodecs(&stripped_codec_vector);
// Check that |container_mime_type| and |codecs| are supported by the CDM.
// Before checking CDM support, split |codecs| into a vector of codecs.
std::vector<std::string> codec_vector;
SplitCodecs(codecs, &codec_vector);
// Check that |container_lower| and |codec_vector| are supported by the CDM.
EmeConfigRule codecs_rule = key_systems_->GetContentTypeConfigRule(
key_system, media_type, container_lower, stripped_codec_vector);
key_system, media_type, container_lower, codec_vector);
if (!config_state->IsRuleSupported(codecs_rule)) {
DVLOG(3) << "Container mime type and codecs are not supported by CDM";
return false;
......
......@@ -247,8 +247,6 @@ class FakeKeySystems : public KeySystems {
for (const std::string& codec : codecs) {
DCHECK(IsValidCodec(codec)) << "Invalid codec should not be passed in";
DCHECK_NE(codec, kExtendedVideoCodec)
<< "Extended codec should already been stripped";
if (codec == kUnsupportedCodec ||
!IsCompatibleWithEmeMediaType(media_type, codec)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment