Commit d5699bb4 authored by Chris Cunningham's avatar Chris Cunningham Committed by Commit Bot

Make mime type optional in media/ MimeUtil

ParseVideoCodecString() and ParseAudioCodecString() now accept
"" for mime_type. This affords reuse of for this complex parsing code
by WebCodecs, where encoded media is not containerized (no mime type).

The two existing callers of these functions require mime types, but they
already validate the mime type is present at a higher level.

Bug: 1113824, 1094191
Change-Id: Ifc3f0a30dd94e4979e184e8879d3f365b28d6ebf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2359408
Commit-Queue: Chrome Cunningham <chcunningham@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Reviewed-by: default avatarThomas Guilbert <tguilbert@chromium.org>
Auto-Submit: Chrome Cunningham <chcunningham@chromium.org>
Cr-Commit-Position: refs/heads/master@{#799190}
parent c27cab5b
......@@ -367,6 +367,10 @@ EmeCodec KeySystemsImpl::GetEmeCodecForString(
EmeMediaType media_type,
const std::string& container_mime_type,
const std::string& codec_string) const {
// Per spec, we should already reject empty mime types in
// GetSupportedCapabilities().
DCHECK(!container_mime_type.empty());
// 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.
......
......@@ -32,8 +32,12 @@ MEDIA_EXPORT void StripCodecs(std::vector<std::string>* codecs);
// Returns true if successfully parsed the given |mime_type| and |codec_id|,
// setting |out_*| arguments to the parsed video codec, profile, and level.
// |out_is_ambiguous| will be true when the codec string is incomplete such that
// some guessing was required to decide the codec, profile, or level.
// Empty string |mime_type| indicates "no mime type". |mime_type| should be
// provided whenever available for parsing and validation in combination with
// |codec_id|. |out_is_ambiguous| will be true when the codec string is
// incomplete such that some guessing was required to decide the codec, profile,
// or level.
//
// Returns false if parsing fails (invalid string, or unrecognized video codec),
// in which case values for |out_*| arguments are undefined.
MEDIA_EXPORT bool ParseVideoCodecString(const std::string& mime_type,
......@@ -45,9 +49,12 @@ MEDIA_EXPORT bool ParseVideoCodecString(const std::string& mime_type,
VideoColorSpace* out_colorspace);
// Returns true if successfully parsed the given |mime_type| and |codec_id|,
// setting |out_audio_codec| to found codec. |out_is_ambiguous| will be true
// when the codec string is incomplete such that some guessing was required to
// decide the codec.
// setting |out_audio_codec| to found codec. Empty string |mime_type| indicates
// "no mime type". |mime_type| should be provided whenever available for parsing
// and validation in combination with |codec_id|. |out_is_ambiguous| will be
// true when the codec string is incomplete such that some guessing was required
// to decide the codec.
//
// Returns false if parsing fails (invalid string, or unrecognized audio codec),
// in which case values for |out_*| arguments are undefined.
MEDIA_EXPORT bool ParseAudioCodecString(const std::string& mime_type,
......
......@@ -101,18 +101,18 @@ static bool ParseVp9CodecID(const std::string& mime_type_lower_case,
VideoCodecProfile* out_profile,
uint8_t* out_level,
VideoColorSpace* out_color_space) {
if (mime_type_lower_case == "video/mp4") {
// Only new style is allowed for mp4.
return ParseNewStyleVp9CodecID(codec_id, out_profile, out_level,
out_color_space);
} else if (mime_type_lower_case == "video/webm") {
if (ParseNewStyleVp9CodecID(codec_id, out_profile, out_level,
out_color_space)) {
return true;
}
if (ParseNewStyleVp9CodecID(codec_id, out_profile, out_level,
out_color_space)) {
// New style (e.g. vp09.00.10.08) is accepted with any mime type (including
// empty mime type).
return true;
}
// Legacy style (e.g. "vp9") is ambiguous about codec profile, and is only
// valid with video/webm for legacy reasons.
if (mime_type_lower_case == "video/webm")
return ParseLegacyVp9CodecID(codec_id, out_profile, out_level);
}
return false;
}
......@@ -453,8 +453,9 @@ bool MimeUtil::ParseVideoCodecString(const std::string& mime_type,
if (!ParseCodecStrings(base::ToLowerASCII(mime_type), codec_strings,
&parsed_results)) {
DVLOG(3) << __func__ << " Failed to parse mime/codec pair:" << mime_type
<< "; " << codec_id;
DVLOG(3) << __func__ << " Failed to parse mime/codec pair: "
<< (mime_type.empty() ? "<empty mime>" : mime_type) << "; "
<< codec_id;
return false;
}
......@@ -489,8 +490,9 @@ bool MimeUtil::ParseAudioCodecString(const std::string& mime_type,
if (!ParseCodecStrings(base::ToLowerASCII(mime_type), codec_strings,
&parsed_results)) {
DVLOG(3) << __func__ << " Failed to parse mime/codec pair:" << mime_type
<< "; " << codec_id;
DVLOG(3) << __func__ << " Failed to parse mime/codec pair:"
<< (mime_type.empty() ? "<empty mime>" : mime_type) << "; "
<< codec_id;
return false;
}
......@@ -670,46 +672,57 @@ bool MimeUtil::ParseCodecStrings(
std::vector<ParsedCodecResult>* out_results) const {
DCHECK(out_results);
// Reject unrecognized mime types.
auto it_media_format_map = media_format_map_.find(mime_type_lower_case);
if (it_media_format_map == media_format_map_.end()) {
DVLOG(3) << __func__ << " Unrecognized mime type: " << mime_type_lower_case;
// Nothing to parse.
if (mime_type_lower_case.empty() && codecs.empty())
return false;
}
const CodecSet& valid_codecs = it_media_format_map->second;
if (valid_codecs.empty()) {
// We get here if the mimetype does not expect a codecs parameter.
if (!codecs.empty()) {
// When mime type is provided, it may imply a codec or only be valid with
// certain codecs.
const CodecSet* valid_codecs_for_mime;
if (!mime_type_lower_case.empty()) {
// Reject unrecognized mime types.
auto it_media_format_map = media_format_map_.find(mime_type_lower_case);
if (it_media_format_map == media_format_map_.end()) {
DVLOG(3) << __func__
<< " Codecs unexpected for mime type:" << mime_type_lower_case;
<< " Unrecognized mime type: " << mime_type_lower_case;
return false;
}
// Determine implied codec for mime type.
ParsedCodecResult implied_result = MakeDefaultParsedCodecResult();
if (!GetDefaultCodec(mime_type_lower_case, &implied_result.codec)) {
NOTREACHED() << " Mime types must offer a default codec if no explicit "
"codecs are expected";
return false;
valid_codecs_for_mime = &it_media_format_map->second;
if (valid_codecs_for_mime->empty()) {
// We get here if the mimetype does not expect a codecs parameter.
if (!codecs.empty()) {
DVLOG(3) << __func__
<< " Codecs unexpected for mime type:" << mime_type_lower_case;
return false;
}
// Determine implied codec for mime type.
ParsedCodecResult implied_result = MakeDefaultParsedCodecResult();
if (!GetDefaultCodec(mime_type_lower_case, &implied_result.codec)) {
NOTREACHED() << " Mime types must offer a default codec if no explicit "
"codecs are expected";
return false;
}
out_results->push_back(implied_result);
return true;
}
out_results->push_back(implied_result);
return true;
}
if (codecs.empty()) {
// We get here if the mimetype expects to get a codecs parameter,
// but didn't get one. If |mime_type_lower_case| does not have a default
// codec, the string is considered ambiguous.
ParsedCodecResult implied_result = MakeDefaultParsedCodecResult();
implied_result.is_ambiguous =
!GetDefaultCodec(mime_type_lower_case, &implied_result.codec);
out_results->push_back(implied_result);
return true;
if (codecs.empty()) {
// We get here if the mimetype expects to get a codecs parameter,
// but didn't get one. If |mime_type_lower_case| does not have a default
// codec, the string is considered ambiguous.
ParsedCodecResult implied_result = MakeDefaultParsedCodecResult();
implied_result.is_ambiguous =
!GetDefaultCodec(mime_type_lower_case, &implied_result.codec);
out_results->push_back(implied_result);
return true;
}
}
// With empty cases handled, parse given codecs and check that they are valid
// for combining with given mime type.
// All empty cases handled above.
DCHECK(!codecs.empty());
for (std::string codec_string : codecs) {
ParsedCodecResult result;
......@@ -719,15 +732,17 @@ bool MimeUtil::ParseCodecStrings(
#endif
if (!ParseCodecHelper(mime_type_lower_case, codec_string, &result)) {
DVLOG(3) << __func__
<< " Failed to parse mime/codec pair: " << mime_type_lower_case
DVLOG(3) << __func__ << " Failed to parse mime/codec pair: "
<< (mime_type_lower_case.empty() ? "<empty mime>"
: mime_type_lower_case)
<< "; " << codec_string;
return false;
}
DCHECK_NE(INVALID_CODEC, result.codec);
// Fail if mime + codec is not a valid combination.
if (valid_codecs.find(result.codec) == valid_codecs.end()) {
// If mime type given, fail if mime + codec is not a valid combination.
if (!mime_type_lower_case.empty() &&
!valid_codecs_for_mime->contains(result.codec)) {
DVLOG(3) << __func__
<< " Incompatible mime/codec pair: " << mime_type_lower_case
<< "; " << codec_string;
......
......@@ -75,13 +75,14 @@ class MEDIA_EXPORT MimeUtil {
void SplitCodecs(const std::string& codecs,
std::vector<std::string>* codecs_out) const;
void StripCodecs(std::vector<std::string>* codecs) const;
bool ParseVideoCodecString(const std::string& mime_type,
const std::string& codec_id,
bool* out_is_ambiguous,
VideoCodec* out_codec,
VideoCodecProfile* out_profile,
uint8_t* out_level,
VideoColorSpace* out_color_space) const;
bool ParseVideoCodecString(
const std::string& mime_type, // fixme, make optional
const std::string& codec_id,
bool* out_is_ambiguous,
VideoCodec* out_codec,
VideoCodecProfile* out_profile,
uint8_t* out_level,
VideoColorSpace* out_color_space) const;
bool ParseAudioCodecString(const std::string& mime_type,
const std::string& codec_id,
bool* out_is_ambiguous,
......
......@@ -27,6 +27,8 @@ namespace media {
namespace internal {
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
// TODO(https://crbug.com/1117275): Remove conditioning of kUsePropCodecs when
// testing *parsing* functions.
const bool kUsePropCodecs = true;
#else
const bool kUsePropCodecs = false;
......@@ -295,6 +297,69 @@ TEST(MimeUtilTest, ParseVideoCodecString) {
&out_colorspace));
}
// Basic smoke test for API. More exhaustive codec string testing found in
// media_canplaytype_browsertest.cc.
TEST(MimeUtilTest, ParseVideoCodecString_NoMimeType) {
bool out_is_ambiguous;
VideoCodec out_codec;
VideoCodecProfile out_profile;
uint8_t out_level;
VideoColorSpace out_colorspace;
// Invalid to give empty codec without a mime type.
EXPECT_FALSE(ParseVideoCodecString("", "", &out_is_ambiguous, &out_codec,
&out_profile, &out_level,
&out_colorspace));
// Valid AVC string whenever proprietary codecs are supported.
EXPECT_TRUE(ParseVideoCodecString("", "avc3.42E01E", &out_is_ambiguous,
&out_codec, &out_profile, &out_level,
&out_colorspace));
EXPECT_FALSE(out_is_ambiguous);
EXPECT_EQ(kCodecH264, out_codec);
EXPECT_EQ(H264PROFILE_BASELINE, out_profile);
EXPECT_EQ(30, out_level);
EXPECT_EQ(VideoColorSpace::REC709(), out_colorspace);
// Valid VP9 string.
EXPECT_TRUE(ParseVideoCodecString("", "vp09.00.10.08", &out_is_ambiguous,
&out_codec, &out_profile, &out_level,
&out_colorspace));
EXPECT_FALSE(out_is_ambiguous);
EXPECT_EQ(kCodecVP9, out_codec);
EXPECT_EQ(VP9PROFILE_PROFILE0, out_profile);
EXPECT_EQ(10, out_level);
EXPECT_EQ(VideoColorSpace::REC709(), out_colorspace);
EXPECT_TRUE(ParseVideoCodecString("", "vp09.02.10.10.01.06.06.06",
&out_is_ambiguous, &out_codec, &out_profile,
&out_level, &out_colorspace));
EXPECT_FALSE(out_is_ambiguous);
EXPECT_EQ(kCodecVP9, out_codec);
EXPECT_EQ(VP9PROFILE_PROFILE2, out_profile);
EXPECT_EQ(10, out_level);
EXPECT_EQ(VideoColorSpace::REC601(), out_colorspace);
// Ambiguous AVC string (when proprietary codecs are supported).
EXPECT_TRUE(ParseVideoCodecString("", "avc3", &out_is_ambiguous, &out_codec,
&out_profile, &out_level, &out_colorspace));
EXPECT_TRUE(out_is_ambiguous);
EXPECT_EQ(kCodecH264, out_codec);
EXPECT_EQ(VIDEO_CODEC_PROFILE_UNKNOWN, out_profile);
EXPECT_EQ(0, out_level);
EXPECT_EQ(VideoColorSpace::REC709(), out_colorspace);
// Audio codecs codec is not valid for video API.
EXPECT_FALSE(ParseVideoCodecString("", "opus", &out_is_ambiguous, &out_codec,
&out_profile, &out_level,
&out_colorspace));
// Made up codec is invalid.
EXPECT_FALSE(ParseVideoCodecString("", "bogus", &out_is_ambiguous, &out_codec,
&out_profile, &out_level,
&out_colorspace));
}
TEST(MimeUtilTest, ParseAudioCodecString) {
bool out_is_ambiguous;
AudioCodec out_codec;
......@@ -352,6 +417,79 @@ TEST(MimeUtilTest, ParseAudioCodecString) {
&out_codec));
}
TEST(MimeUtilTest, ParseAudioCodecString_NoMimeType) {
bool out_is_ambiguous;
AudioCodec out_codec;
// Invalid to give empty codec without a mime type.
EXPECT_FALSE(ParseAudioCodecString("", "", &out_is_ambiguous, &out_codec));
// Valid Opus string.
EXPECT_TRUE(ParseAudioCodecString("", "opus", &out_is_ambiguous, &out_codec));
EXPECT_FALSE(out_is_ambiguous);
EXPECT_EQ(kCodecOpus, out_codec);
// Valid AAC string when proprietary codecs are supported.
EXPECT_TRUE(
ParseAudioCodecString("", "mp4a.40.2", &out_is_ambiguous, &out_codec));
EXPECT_FALSE(out_is_ambiguous);
EXPECT_EQ(kCodecAAC, out_codec);
// Valid FLAC string. Neither decoding nor demuxing is proprietary.
EXPECT_TRUE(ParseAudioCodecString("", "flac", &out_is_ambiguous, &out_codec));
EXPECT_FALSE(out_is_ambiguous);
EXPECT_EQ(kCodecFLAC, out_codec);
// Ambiguous AAC string.
// TODO(chcunningha): This can probably be allowed. I think we treat all
// MPEG4_AAC the same.
EXPECT_TRUE(
ParseAudioCodecString("", "mp4a.40", &out_is_ambiguous, &out_codec));
if (kUsePropCodecs) {
EXPECT_TRUE(out_is_ambiguous);
EXPECT_EQ(kCodecAAC, out_codec);
}
// Video codec is not valid for audio API.
EXPECT_FALSE(ParseAudioCodecString("", "vp09.00.10.08", &out_is_ambiguous,
&out_codec));
// Made up codec is also not valid.
EXPECT_FALSE(
ParseAudioCodecString("", "bogus", &out_is_ambiguous, &out_codec));
}
// MP3 is a weird case where we allow either the mime type, codec string, or
// both, and there are several valid codec strings.
TEST(MimeUtilTest, ParseAudioCodecString_Mp3) {
bool out_is_ambiguous;
AudioCodec out_codec;
EXPECT_TRUE(ParseAudioCodecString("audio/mpeg", "mp3", &out_is_ambiguous,
&out_codec));
EXPECT_FALSE(out_is_ambiguous);
EXPECT_EQ(kCodecMP3, out_codec);
EXPECT_TRUE(
ParseAudioCodecString("audio/mpeg", "", &out_is_ambiguous, &out_codec));
EXPECT_FALSE(out_is_ambiguous);
EXPECT_EQ(kCodecMP3, out_codec);
EXPECT_TRUE(ParseAudioCodecString("", "mp3", &out_is_ambiguous, &out_codec));
EXPECT_FALSE(out_is_ambiguous);
EXPECT_EQ(kCodecMP3, out_codec);
EXPECT_TRUE(
ParseAudioCodecString("", "mp4a.69", &out_is_ambiguous, &out_codec));
EXPECT_FALSE(out_is_ambiguous);
EXPECT_EQ(kCodecMP3, out_codec);
EXPECT_TRUE(
ParseAudioCodecString("", "mp4a.6B", &out_is_ambiguous, &out_codec));
EXPECT_FALSE(out_is_ambiguous);
EXPECT_EQ(kCodecMP3, out_codec);
}
// These codecs really only have one profile. Ensure that |out_profile| is
// correctly mapped.
TEST(MimeUtilTest, ParseVideoCodecString_SimpleCodecsHaveProfiles) {
......
......@@ -214,6 +214,8 @@ bool IsValidMimeType(const String& content_type, const String& prefix) {
if (!parsed_content_type.IsValid())
return false;
// Valid ParsedContentType implies we have a mime type.
DCHECK(parsed_content_type.MimeType());
if (!parsed_content_type.MimeType().StartsWith(prefix) &&
!parsed_content_type.MimeType().StartsWith(kApplicationMimeTypePrefix)) {
return false;
......
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