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( ...@@ -367,6 +367,10 @@ EmeCodec KeySystemsImpl::GetEmeCodecForString(
EmeMediaType media_type, EmeMediaType media_type,
const std::string& container_mime_type, const std::string& container_mime_type,
const std::string& codec_string) const { 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 // This is not checked because MimeUtil declares "vp9" and "vp9.0" as
// ambiguous, but they have always been supported by EME. // ambiguous, but they have always been supported by EME.
// TODO(xhwang): Find out whether we should fix MimeUtil about these cases. // 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); ...@@ -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|, // Returns true if successfully parsed the given |mime_type| and |codec_id|,
// setting |out_*| arguments to the parsed video codec, profile, and level. // 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 // Empty string |mime_type| indicates "no mime type". |mime_type| should be
// some guessing was required to decide the codec, profile, or level. // 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), // Returns false if parsing fails (invalid string, or unrecognized video codec),
// in which case values for |out_*| arguments are undefined. // in which case values for |out_*| arguments are undefined.
MEDIA_EXPORT bool ParseVideoCodecString(const std::string& mime_type, MEDIA_EXPORT bool ParseVideoCodecString(const std::string& mime_type,
...@@ -45,9 +49,12 @@ 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); VideoColorSpace* out_colorspace);
// Returns true if successfully parsed the given |mime_type| and |codec_id|, // 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 // setting |out_audio_codec| to found codec. Empty string |mime_type| indicates
// when the codec string is incomplete such that some guessing was required to // "no mime type". |mime_type| should be provided whenever available for parsing
// decide the codec. // 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), // Returns false if parsing fails (invalid string, or unrecognized audio codec),
// in which case values for |out_*| arguments are undefined. // in which case values for |out_*| arguments are undefined.
MEDIA_EXPORT bool ParseAudioCodecString(const std::string& mime_type, MEDIA_EXPORT bool ParseAudioCodecString(const std::string& mime_type,
......
...@@ -101,18 +101,18 @@ static bool ParseVp9CodecID(const std::string& mime_type_lower_case, ...@@ -101,18 +101,18 @@ static bool ParseVp9CodecID(const std::string& mime_type_lower_case,
VideoCodecProfile* out_profile, VideoCodecProfile* out_profile,
uint8_t* out_level, uint8_t* out_level,
VideoColorSpace* out_color_space) { VideoColorSpace* out_color_space) {
if (mime_type_lower_case == "video/mp4") { if (ParseNewStyleVp9CodecID(codec_id, out_profile, out_level,
// Only new style is allowed for mp4. out_color_space)) {
return ParseNewStyleVp9CodecID(codec_id, out_profile, out_level, // New style (e.g. vp09.00.10.08) is accepted with any mime type (including
out_color_space); // empty mime type).
} else if (mime_type_lower_case == "video/webm") { return true;
if (ParseNewStyleVp9CodecID(codec_id, out_profile, out_level, }
out_color_space)) {
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 ParseLegacyVp9CodecID(codec_id, out_profile, out_level);
}
return false; return false;
} }
...@@ -453,8 +453,9 @@ bool MimeUtil::ParseVideoCodecString(const std::string& mime_type, ...@@ -453,8 +453,9 @@ bool MimeUtil::ParseVideoCodecString(const std::string& mime_type,
if (!ParseCodecStrings(base::ToLowerASCII(mime_type), codec_strings, if (!ParseCodecStrings(base::ToLowerASCII(mime_type), codec_strings,
&parsed_results)) { &parsed_results)) {
DVLOG(3) << __func__ << " Failed to parse mime/codec pair:" << mime_type DVLOG(3) << __func__ << " Failed to parse mime/codec pair: "
<< "; " << codec_id; << (mime_type.empty() ? "<empty mime>" : mime_type) << "; "
<< codec_id;
return false; return false;
} }
...@@ -489,8 +490,9 @@ bool MimeUtil::ParseAudioCodecString(const std::string& mime_type, ...@@ -489,8 +490,9 @@ bool MimeUtil::ParseAudioCodecString(const std::string& mime_type,
if (!ParseCodecStrings(base::ToLowerASCII(mime_type), codec_strings, if (!ParseCodecStrings(base::ToLowerASCII(mime_type), codec_strings,
&parsed_results)) { &parsed_results)) {
DVLOG(3) << __func__ << " Failed to parse mime/codec pair:" << mime_type DVLOG(3) << __func__ << " Failed to parse mime/codec pair:"
<< "; " << codec_id; << (mime_type.empty() ? "<empty mime>" : mime_type) << "; "
<< codec_id;
return false; return false;
} }
...@@ -670,46 +672,57 @@ bool MimeUtil::ParseCodecStrings( ...@@ -670,46 +672,57 @@ bool MimeUtil::ParseCodecStrings(
std::vector<ParsedCodecResult>* out_results) const { std::vector<ParsedCodecResult>* out_results) const {
DCHECK(out_results); DCHECK(out_results);
// Reject unrecognized mime types. // Nothing to parse.
auto it_media_format_map = media_format_map_.find(mime_type_lower_case); if (mime_type_lower_case.empty() && codecs.empty())
if (it_media_format_map == media_format_map_.end()) {
DVLOG(3) << __func__ << " Unrecognized mime type: " << mime_type_lower_case;
return false; return false;
}
const CodecSet& valid_codecs = it_media_format_map->second; // When mime type is provided, it may imply a codec or only be valid with
if (valid_codecs.empty()) { // certain codecs.
// We get here if the mimetype does not expect a codecs parameter. const CodecSet* valid_codecs_for_mime;
if (!codecs.empty()) { 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__ DVLOG(3) << __func__
<< " Codecs unexpected for mime type:" << mime_type_lower_case; << " Unrecognized mime type: " << mime_type_lower_case;
return false; return false;
} }
// Determine implied codec for mime type. valid_codecs_for_mime = &it_media_format_map->second;
ParsedCodecResult implied_result = MakeDefaultParsedCodecResult(); if (valid_codecs_for_mime->empty()) {
if (!GetDefaultCodec(mime_type_lower_case, &implied_result.codec)) { // We get here if the mimetype does not expect a codecs parameter.
NOTREACHED() << " Mime types must offer a default codec if no explicit " if (!codecs.empty()) {
"codecs are expected"; DVLOG(3) << __func__
return false; << " 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()) { if (codecs.empty()) {
// We get here if the mimetype expects to get a codecs parameter, // 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 // but didn't get one. If |mime_type_lower_case| does not have a default
// codec, the string is considered ambiguous. // codec, the string is considered ambiguous.
ParsedCodecResult implied_result = MakeDefaultParsedCodecResult(); ParsedCodecResult implied_result = MakeDefaultParsedCodecResult();
implied_result.is_ambiguous = implied_result.is_ambiguous =
!GetDefaultCodec(mime_type_lower_case, &implied_result.codec); !GetDefaultCodec(mime_type_lower_case, &implied_result.codec);
out_results->push_back(implied_result); out_results->push_back(implied_result);
return true; return true;
}
} }
// With empty cases handled, parse given codecs and check that they are valid // All empty cases handled above.
// for combining with given mime type. DCHECK(!codecs.empty());
for (std::string codec_string : codecs) { for (std::string codec_string : codecs) {
ParsedCodecResult result; ParsedCodecResult result;
...@@ -719,15 +732,17 @@ bool MimeUtil::ParseCodecStrings( ...@@ -719,15 +732,17 @@ bool MimeUtil::ParseCodecStrings(
#endif #endif
if (!ParseCodecHelper(mime_type_lower_case, codec_string, &result)) { if (!ParseCodecHelper(mime_type_lower_case, codec_string, &result)) {
DVLOG(3) << __func__ DVLOG(3) << __func__ << " Failed to parse mime/codec pair: "
<< " Failed to parse mime/codec pair: " << mime_type_lower_case << (mime_type_lower_case.empty() ? "<empty mime>"
: mime_type_lower_case)
<< "; " << codec_string; << "; " << codec_string;
return false; return false;
} }
DCHECK_NE(INVALID_CODEC, result.codec); DCHECK_NE(INVALID_CODEC, result.codec);
// Fail if mime + codec is not a valid combination. // If mime type given, fail if mime + codec is not a valid combination.
if (valid_codecs.find(result.codec) == valid_codecs.end()) { if (!mime_type_lower_case.empty() &&
!valid_codecs_for_mime->contains(result.codec)) {
DVLOG(3) << __func__ DVLOG(3) << __func__
<< " Incompatible mime/codec pair: " << mime_type_lower_case << " Incompatible mime/codec pair: " << mime_type_lower_case
<< "; " << codec_string; << "; " << codec_string;
......
...@@ -75,13 +75,14 @@ class MEDIA_EXPORT MimeUtil { ...@@ -75,13 +75,14 @@ class MEDIA_EXPORT MimeUtil {
void SplitCodecs(const std::string& codecs, void SplitCodecs(const std::string& codecs,
std::vector<std::string>* codecs_out) const; std::vector<std::string>* codecs_out) const;
void StripCodecs(std::vector<std::string>* codecs) const; void StripCodecs(std::vector<std::string>* codecs) const;
bool ParseVideoCodecString(const std::string& mime_type, bool ParseVideoCodecString(
const std::string& codec_id, const std::string& mime_type, // fixme, make optional
bool* out_is_ambiguous, const std::string& codec_id,
VideoCodec* out_codec, bool* out_is_ambiguous,
VideoCodecProfile* out_profile, VideoCodec* out_codec,
uint8_t* out_level, VideoCodecProfile* out_profile,
VideoColorSpace* out_color_space) const; uint8_t* out_level,
VideoColorSpace* out_color_space) const;
bool ParseAudioCodecString(const std::string& mime_type, bool ParseAudioCodecString(const std::string& mime_type,
const std::string& codec_id, const std::string& codec_id,
bool* out_is_ambiguous, bool* out_is_ambiguous,
......
...@@ -27,6 +27,8 @@ namespace media { ...@@ -27,6 +27,8 @@ namespace media {
namespace internal { namespace internal {
#if BUILDFLAG(USE_PROPRIETARY_CODECS) #if BUILDFLAG(USE_PROPRIETARY_CODECS)
// TODO(https://crbug.com/1117275): Remove conditioning of kUsePropCodecs when
// testing *parsing* functions.
const bool kUsePropCodecs = true; const bool kUsePropCodecs = true;
#else #else
const bool kUsePropCodecs = false; const bool kUsePropCodecs = false;
...@@ -295,6 +297,69 @@ TEST(MimeUtilTest, ParseVideoCodecString) { ...@@ -295,6 +297,69 @@ TEST(MimeUtilTest, ParseVideoCodecString) {
&out_colorspace)); &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) { TEST(MimeUtilTest, ParseAudioCodecString) {
bool out_is_ambiguous; bool out_is_ambiguous;
AudioCodec out_codec; AudioCodec out_codec;
...@@ -352,6 +417,79 @@ TEST(MimeUtilTest, ParseAudioCodecString) { ...@@ -352,6 +417,79 @@ TEST(MimeUtilTest, ParseAudioCodecString) {
&out_codec)); &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 // These codecs really only have one profile. Ensure that |out_profile| is
// correctly mapped. // correctly mapped.
TEST(MimeUtilTest, ParseVideoCodecString_SimpleCodecsHaveProfiles) { TEST(MimeUtilTest, ParseVideoCodecString_SimpleCodecsHaveProfiles) {
......
...@@ -214,6 +214,8 @@ bool IsValidMimeType(const String& content_type, const String& prefix) { ...@@ -214,6 +214,8 @@ bool IsValidMimeType(const String& content_type, const String& prefix) {
if (!parsed_content_type.IsValid()) if (!parsed_content_type.IsValid())
return false; return false;
// Valid ParsedContentType implies we have a mime type.
DCHECK(parsed_content_type.MimeType());
if (!parsed_content_type.MimeType().StartsWith(prefix) && if (!parsed_content_type.MimeType().StartsWith(prefix) &&
!parsed_content_type.MimeType().StartsWith(kApplicationMimeTypePrefix)) { !parsed_content_type.MimeType().StartsWith(kApplicationMimeTypePrefix)) {
return false; 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