| Index: media/base/mime_util_internal.cc
|
| diff --git a/media/base/mime_util_internal.cc b/media/base/mime_util_internal.cc
|
| index f5f35f1cb813d8306475a8f6545b219ae69ac068..e8933a0473d4496c0e7bac35ab30123a7d76963e 100644
|
| --- a/media/base/mime_util_internal.cc
|
| +++ b/media/base/mime_util_internal.cc
|
| @@ -182,68 +182,64 @@ VideoCodec MimeUtilToVideoCodec(MimeUtil::Codec codec) {
|
| }
|
|
|
| SupportsType MimeUtil::AreSupportedCodecs(
|
| - const CodecSet& supported_codecs,
|
| - const std::vector<std::string>& codecs,
|
| + const std::vector<ParsedCodecResult>& parsed_codecs,
|
| const std::string& mime_type_lower_case,
|
| bool is_encrypted) const {
|
| - DCHECK(!supported_codecs.empty());
|
| - DCHECK(!codecs.empty());
|
| + DCHECK(!parsed_codecs.empty());
|
| DCHECK_EQ(base::ToLowerASCII(mime_type_lower_case), mime_type_lower_case);
|
|
|
| SupportsType combined_result = IsSupported;
|
|
|
| - for (size_t i = 0; i < codecs.size(); ++i) {
|
| - // Parse the string.
|
| - bool ambiguous_codec_string = false;
|
| - Codec codec = INVALID_CODEC;
|
| - VideoCodecProfile video_profile = VIDEO_CODEC_PROFILE_UNKNOWN;
|
| - uint8_t video_level = 0;
|
| - VideoColorSpace color_space;
|
| - if (!ParseCodecString(mime_type_lower_case, codecs[i], &codec,
|
| - &ambiguous_codec_string, &video_profile, &video_level,
|
| - &color_space)) {
|
| - DVLOG(2) << __func__ << " Failed to parse codec string:" << codecs[i];
|
| - return IsNotSupported;
|
| - }
|
| -
|
| - // Bail if codec not in supported list for given container.
|
| - if (supported_codecs.find(codec) == supported_codecs.end()) {
|
| - DVLOG(2) << __func__ << " Codec " << codecs[i]
|
| - << " not supported in container " << mime_type_lower_case;
|
| - return IsNotSupported;
|
| - }
|
| -
|
| + for (const auto& parsed_codec : parsed_codecs) {
|
| // Make conservative guesses to resolve ambiguity before checking platform
|
| - // support. H264 and VP9 are the only allowed ambiguous video codec. DO NOT
|
| - // ADD SUPPORT FOR MORE AMIBIGUOUS STRINGS.
|
| - if (codec == MimeUtil::H264 && ambiguous_codec_string) {
|
| - if (video_profile == VIDEO_CODEC_PROFILE_UNKNOWN)
|
| - video_profile = H264PROFILE_BASELINE;
|
| - if (!IsValidH264Level(video_level))
|
| - video_level = 10;
|
| - } else if (codec == MimeUtil::VP9 && video_level == 0) {
|
| - // Original VP9 content type (codecs="vp9") does not specify the level.
|
| - // TODO(chcunningham): Mark this string as ambiguous when new multi-part
|
| - // VP9 content type is published.
|
| - video_level = 10;
|
| + // support. Historically we allowed some ambiguity in H264 and VP9 codec
|
| + // strings, so we must continue to allow going forward. DO NOT ADD NEW
|
| + // SUPPORT FOR MORE AMBIGUOUS STRINGS.
|
| + VideoCodecProfile video_profile = parsed_codec.video_profile;
|
| + uint8_t video_level = parsed_codec.video_level;
|
| + if (parsed_codec.is_ambiguous) {
|
| + switch (parsed_codec.codec) {
|
| + case MimeUtil::H264:
|
| + if (video_profile == VIDEO_CODEC_PROFILE_UNKNOWN)
|
| + video_profile = H264PROFILE_BASELINE;
|
| + if (!IsValidH264Level(video_level))
|
| + video_level = 10;
|
| + break;
|
| + case MimeUtil::VP9:
|
| + if (video_profile == VIDEO_CODEC_PROFILE_UNKNOWN)
|
| + video_profile = VP9PROFILE_PROFILE0;
|
| + if (video_level == 0)
|
| + video_level = 10;
|
| + break;
|
| + case MimeUtil::MPEG4_AAC:
|
| + // Nothing to do for AAC; no notion of profile / level to guess.
|
| + break;
|
| + default:
|
| + NOTREACHED()
|
| + << "Only VP9, H264, and AAC codec strings can be ambiguous.";
|
| + }
|
| }
|
|
|
| // Check platform support.
|
| - SupportsType result =
|
| - IsCodecSupported(mime_type_lower_case, codec, video_profile,
|
| - video_level, color_space, is_encrypted);
|
| + SupportsType result = IsCodecSupported(
|
| + mime_type_lower_case, parsed_codec.codec, video_profile, video_level,
|
| + parsed_codec.video_color_space, is_encrypted);
|
| if (result == IsNotSupported) {
|
| - DVLOG(2) << __func__ << " Codec " << codecs[i]
|
| - << " not supported by platform";
|
| + DVLOG(2) << __func__ << " Codec " << parsed_codec.codec
|
| + << " not supported by platform.";
|
| return IsNotSupported;
|
| }
|
|
|
| // If any codec is "MayBeSupported", return Maybe for the combined result.
|
| - // Downgrade to MayBeSupported if we had to guess the meaning of one of the
|
| - // codec strings.
|
| if (result == MayBeSupported ||
|
| - (result == IsSupported && ambiguous_codec_string))
|
| + // Downgrade to MayBeSupported if we had to guess the meaning of one of
|
| + // the codec strings. Do not downgrade for VP9 because we historically
|
| + // returned "Probably" for the old "vp9" string and cannot change to
|
| + // returning "Maybe" as this will break sites.
|
| + (result == IsSupported && parsed_codec.is_ambiguous &&
|
| + parsed_codec.codec != MimeUtil::VP9)) {
|
| combined_result = MayBeSupported;
|
| + }
|
| }
|
|
|
| return combined_result;
|
| @@ -408,53 +404,109 @@ void MimeUtil::SplitCodecsToVector(const std::string& codecs,
|
| }
|
| }
|
|
|
| +bool MimeUtil::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) {
|
| + DCHECK(out_is_ambiguous);
|
| + DCHECK(out_codec);
|
| + DCHECK(out_profile);
|
| + DCHECK(out_level);
|
| + DCHECK(out_color_space);
|
| +
|
| + // Internal parsing API expects a vector of codecs.
|
| + std::vector<ParsedCodecResult> parsed_results;
|
| + std::vector<std::string> codec_strings;
|
| + if (!codec_id.empty())
|
| + codec_strings.push_back(codec_id);
|
| +
|
| + if (!ParseCodecStrings(base::ToLowerASCII(mime_type), codec_strings,
|
| + &parsed_results)) {
|
| + DVLOG(3) << __func__ << " Failed to parse mime/codec pair:" << mime_type
|
| + << "; " << codec_id;
|
| + return false;
|
| + }
|
| +
|
| + CHECK_EQ(1U, parsed_results.size());
|
| + *out_is_ambiguous = parsed_results[0].is_ambiguous;
|
| + *out_codec = MimeUtilToVideoCodec(parsed_results[0].codec);
|
| + *out_profile = parsed_results[0].video_profile;
|
| + *out_level = parsed_results[0].video_level;
|
| + *out_color_space = parsed_results[0].video_color_space;
|
| +
|
| + if (*out_codec == kUnknownVideoCodec) {
|
| + DVLOG(3) << __func__ << " Codec string " << codec_id
|
| + << " is not a VIDEO codec.";
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool MimeUtil::ParseAudioCodecString(const std::string& mime_type,
|
| + const std::string& codec_id,
|
| + bool* out_is_ambiguous,
|
| + AudioCodec* out_codec) {
|
| + DCHECK(out_is_ambiguous);
|
| + DCHECK(out_codec);
|
| +
|
| + // Internal parsing API expects a vector of codecs.
|
| + std::vector<ParsedCodecResult> parsed_results;
|
| + std::vector<std::string> codec_strings;
|
| + if (!codec_id.empty())
|
| + codec_strings.push_back(codec_id);
|
| +
|
| + if (!ParseCodecStrings(base::ToLowerASCII(mime_type), codec_strings,
|
| + &parsed_results)) {
|
| + DVLOG(3) << __func__ << " Failed to parse mime/codec pair:" << mime_type
|
| + << "; " << codec_id;
|
| + return false;
|
| + }
|
| +
|
| + CHECK_EQ(1U, parsed_results.size());
|
| + *out_is_ambiguous = parsed_results[0].is_ambiguous;
|
| + *out_codec = MimeUtilToAudioCodec(parsed_results[0].codec);
|
| +
|
| + if (*out_codec == kUnknownAudioCodec) {
|
| + DVLOG(3) << __func__ << " Codec string " << codec_id
|
| + << " is not an AUDIO codec.";
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| SupportsType MimeUtil::IsSupportedMediaFormat(
|
| const std::string& mime_type,
|
| const std::vector<std::string>& codecs,
|
| bool is_encrypted) const {
|
| const std::string mime_type_lower_case = base::ToLowerASCII(mime_type);
|
| - MediaFormatMappings::const_iterator 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;
|
| + std::vector<ParsedCodecResult> parsed_results;
|
| + if (!ParseCodecStrings(mime_type_lower_case, codecs, &parsed_results)) {
|
| + DVLOG(3) << __func__ << " Media format unsupported; codec parsing failed "
|
| + << mime_type << " " << base::JoinString(codecs, ",");
|
| return IsNotSupported;
|
| }
|
|
|
| - if (it_media_format_map->second.empty()) {
|
| - // We get here if the mimetype does not expect a codecs parameter.
|
| - if (codecs.empty()) {
|
| - return IsDefaultCodecSupported(mime_type_lower_case, is_encrypted);
|
| - } else {
|
| - return IsNotSupported;
|
| - }
|
| - }
|
| -
|
| - 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 best we can do is say "maybe" because we don't have enough
|
| - // information.
|
| - Codec default_codec = INVALID_CODEC;
|
| - if (!GetDefaultCodec(mime_type_lower_case, &default_codec))
|
| - return MayBeSupported;
|
| -
|
| - return IsSimpleCodecSupported(mime_type_lower_case, default_codec,
|
| - is_encrypted);
|
| + if (parsed_results.empty()) {
|
| + NOTREACHED() << __func__ << " Successful parsing should output results.";
|
| + return IsNotSupported;
|
| }
|
|
|
| -#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
|
| - if (mime_type_lower_case == "video/mp2t") {
|
| - std::vector<std::string> codecs_to_check;
|
| - for (const auto& codec_id : codecs) {
|
| - codecs_to_check.push_back(TranslateLegacyAvc1CodecIds(codec_id));
|
| - }
|
| - return AreSupportedCodecs(it_media_format_map->second, codecs_to_check,
|
| - mime_type_lower_case, is_encrypted);
|
| + // We get here if the mime type expects to get a codecs parameter
|
| + // but none was provided and no default codec was implied. In this case
|
| + // the best we can do is say "maybe" because we don't have enough
|
| + // information.
|
| + if (codecs.empty() && parsed_results.size() == 1 &&
|
| + parsed_results[0].codec == INVALID_CODEC) {
|
| + DCHECK(parsed_results[0].is_ambiguous);
|
| + return MayBeSupported;
|
| }
|
| -#endif
|
|
|
| - return AreSupportedCodecs(it_media_format_map->second, codecs,
|
| - mime_type_lower_case, is_encrypted);
|
| + return AreSupportedCodecs(parsed_results, mime_type_lower_case, is_encrypted);
|
| }
|
|
|
| void MimeUtil::RemoveProprietaryMediaTypesAndCodecs() {
|
| @@ -601,33 +653,107 @@ bool MimeUtil::IsCodecSupportedOnAndroid(
|
| return false;
|
| }
|
|
|
| -bool MimeUtil::ParseCodecString(const std::string& mime_type_lower_case,
|
| +// Make a default ParsedCodecResult. Values should indicate "unspecified"
|
| +// where possible. Color space is an exception where we choose a default value
|
| +// because most codec strings will not describe a color space.
|
| +MimeUtil::ParsedCodecResult MakeDefaultParsedCodecResult() {
|
| + return {
|
| + MimeUtil::INVALID_CODEC, false, VIDEO_CODEC_PROFILE_UNKNOWN, 0,
|
| + // We choose 709 as default color space elsewhere, so defaulting to 709
|
| + // here as well. See here for context: https://crrev.com/1221903003/
|
| + VideoColorSpace::REC709()};
|
| +}
|
| +
|
| +bool MimeUtil::ParseCodecStrings(
|
| + const std::string& mime_type_lower_case,
|
| + const std::vector<std::string>& codecs,
|
| + std::vector<ParsedCodecResult>* out_results) const {
|
| + DCHECK(out_results);
|
| +
|
| + // Reject unrecognized mime types.
|
| + MediaFormatMappings::const_iterator 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;
|
| + 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()) {
|
| + 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;
|
| + }
|
| +
|
| + 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.
|
| + for (std::string codec_string : codecs) {
|
| + ParsedCodecResult result;
|
| +
|
| +#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
|
| + if (mime_type_lower_case == "video/mp2t")
|
| + codec_string = TranslateLegacyAvc1CodecIds(codec_string);
|
| +#endif
|
| +
|
| + if (!ParseCodecHelper(mime_type_lower_case, codec_string, &result)) {
|
| + DVLOG(3) << __func__
|
| + << " Failed to parse mime/codec pair: " << 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()) {
|
| + DVLOG(3) << __func__
|
| + << " Incompatible mime/codec pair: " << mime_type_lower_case
|
| + << "; " << codec_string;
|
| + return false;
|
| + }
|
| +
|
| + out_results->push_back(result);
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool MimeUtil::ParseCodecHelper(const std::string& mime_type_lower_case,
|
| const std::string& codec_id,
|
| - Codec* codec,
|
| - bool* ambiguous_codec_string,
|
| - VideoCodecProfile* out_profile,
|
| - uint8_t* out_level,
|
| - VideoColorSpace* out_color_space) const {
|
| + ParsedCodecResult* out_result) const {
|
| DCHECK_EQ(base::ToLowerASCII(mime_type_lower_case), mime_type_lower_case);
|
| - DCHECK(codec);
|
| - DCHECK(out_profile);
|
| - DCHECK(out_level);
|
| -
|
| - *codec = INVALID_CODEC;
|
| - *ambiguous_codec_string = false;
|
| - *out_profile = VIDEO_CODEC_PROFILE_UNKNOWN;
|
| - *out_level = 0;
|
| + DCHECK(out_result);
|
|
|
| - // Most codec strings do not yet specify color. We choose 709 as default color
|
| - // space elsewhere, so defaulting to 709 here as well. See here for context:
|
| - // https://crrev.com/1221903003/
|
| - *out_color_space = VideoColorSpace::REC709();
|
| + *out_result = MakeDefaultParsedCodecResult();
|
|
|
| + // Simple codecs can be found in the codec map.
|
| std::map<std::string, Codec>::const_iterator itr =
|
| GetStringToCodecMap().find(codec_id);
|
| if (itr != GetStringToCodecMap().end()) {
|
| - *codec = itr->second;
|
| -
|
| + out_result->codec = itr->second;
|
| return true;
|
| }
|
|
|
| @@ -636,12 +762,12 @@ bool MimeUtil::ParseCodecString(const std::string& mime_type_lower_case,
|
| // INCREASE PLACES WHERE |ambiguous_codec_string| = true.
|
| // NOTE: avc1/avc3.XXXXXX may be ambiguous handled after ParseAVCCodecId().
|
| if (codec_id == "avc1" || codec_id == "avc3") {
|
| - *codec = MimeUtil::H264;
|
| - *ambiguous_codec_string = true;
|
| + out_result->codec = MimeUtil::H264;
|
| + out_result->is_ambiguous = true;
|
| return true;
|
| } else if (codec_id == "mp4a.40") {
|
| - *codec = MimeUtil::MPEG4_AAC;
|
| - *ambiguous_codec_string = true;
|
| + out_result->codec = MimeUtil::MPEG4_AAC;
|
| + out_result->is_ambiguous = true;
|
| return true;
|
| }
|
|
|
| @@ -649,34 +775,43 @@ bool MimeUtil::ParseCodecString(const std::string& mime_type_lower_case,
|
| // either VP9, H.264 or HEVC/H.265 codec ID because currently those are the
|
| // only ones that are not added to the |kStringToCodecMap| and require
|
| // parsing.
|
| + VideoCodecProfile* out_profile = &out_result->video_profile;
|
| + uint8_t* out_level = &out_result->video_level;
|
| + VideoColorSpace* out_color_space = &out_result->video_color_space;
|
| if (ParseVp9CodecID(mime_type_lower_case, codec_id, out_profile, out_level,
|
| out_color_space)) {
|
| - *codec = MimeUtil::VP9;
|
| + out_result->codec = MimeUtil::VP9;
|
| + // Original VP9 codec string did not describe the profile.
|
| + if (out_result->video_profile == VIDEO_CODEC_PROFILE_UNKNOWN) {
|
| + // New VP9 string should never be ambiguous.
|
| + DCHECK(!base::StartsWith(codec_id, "vp09", base::CompareCase::SENSITIVE));
|
| + out_result->is_ambiguous = true;
|
| + }
|
| return true;
|
| }
|
|
|
| if (ParseAVCCodecId(codec_id, out_profile, out_level)) {
|
| - *codec = MimeUtil::H264;
|
| + out_result->codec = MimeUtil::H264;
|
| // Allowed string ambiguity since 2014. DO NOT ADD NEW CASES FOR AMBIGUITY.
|
| - *ambiguous_codec_string = !IsValidH264Level(*out_level);
|
| + out_result->is_ambiguous = !IsValidH264Level(*out_level);
|
| return true;
|
| }
|
|
|
| #if BUILDFLAG(ENABLE_HEVC_DEMUXING)
|
| if (ParseHEVCCodecId(codec_id, out_profile, out_level)) {
|
| - *codec = MimeUtil::HEVC;
|
| + out_result->codec = MimeUtil::HEVC;
|
| return true;
|
| }
|
| #endif
|
|
|
| #if BUILDFLAG(ENABLE_DOLBY_VISION_DEMUXING)
|
| if (ParseDolbyVisionCodecId(codec_id, out_profile, out_level)) {
|
| - *codec = MimeUtil::DOLBY_VISION;
|
| + out_result->codec = MimeUtil::DOLBY_VISION;
|
| return true;
|
| }
|
| #endif
|
|
|
| - DVLOG(2) << __func__ << ": Unrecognized codec id " << codec_id;
|
| + DVLOG(2) << __func__ << ": Unrecognized codec id \"" << codec_id << "\"";
|
| return false;
|
| }
|
|
|
| @@ -820,6 +955,9 @@ bool MimeUtil::IsCodecProprietary(Codec codec) const {
|
|
|
| bool MimeUtil::GetDefaultCodec(const std::string& mime_type,
|
| Codec* default_codec) const {
|
| + // Codecs below are unambiguously implied by the mime type string. DO NOT add
|
| + // default codecs for ambiguous mime types.
|
| +
|
| if (mime_type == "audio/mpeg" || mime_type == "audio/mp3" ||
|
| mime_type == "audio/x-mp3") {
|
| *default_codec = MimeUtil::MP3;
|
| @@ -839,13 +977,5 @@ bool MimeUtil::GetDefaultCodec(const std::string& mime_type,
|
| return false;
|
| }
|
|
|
| -SupportsType MimeUtil::IsDefaultCodecSupported(const std::string& mime_type,
|
| - bool is_encrypted) const {
|
| - Codec default_codec = Codec::INVALID_CODEC;
|
| - if (!GetDefaultCodec(mime_type, &default_codec))
|
| - return IsNotSupported;
|
| - return IsSimpleCodecSupported(mime_type, default_codec, is_encrypted);
|
| -}
|
| -
|
| } // namespace internal
|
| } // namespace media
|
|
|