123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976 |
- // Copyright 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "media/base/mime_util_internal.h"
- #include "base/command_line.h"
- #include "base/feature_list.h"
- #include "base/logging.h"
- #include "base/no_destructor.h"
- #include "base/notreached.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/string_split.h"
- #include "base/strings/string_util.h"
- #include "build/build_config.h"
- #include "media/base/media.h"
- #include "media/base/media_client.h"
- #include "media/base/media_switches.h"
- #include "media/base/supported_types.h"
- #include "media/base/video_codecs.h"
- #include "media/base/video_color_space.h"
- #include "media/media_buildflags.h"
- #if BUILDFLAG(IS_ANDROID)
- #include "base/android/build_info.h"
- // TODO(dalecurtis): This include is not allowed by media/base since
- // media/base/android is technically a different component. We should move
- // mime_util*.{cc,h} out of media/base to fix this.
- #include "media/base/android/media_codec_util.h" // nogncheck
- #endif
- namespace media {
- namespace internal {
- // A map from codec string to MimeUtil::Codec.
- using StringToCodecMap = base::flat_map<std::string, MimeUtil::Codec>;
- // Wrapped to avoid static initializer startup cost.
- const StringToCodecMap& GetStringToCodecMap() {
- static const base::NoDestructor<StringToCodecMap> kStringToCodecMap({
- // We only allow this for WAV so it isn't ambiguous.
- {"1", MimeUtil::PCM},
- // avc1/avc3.XXXXXX may be unambiguous; handled by
- // ParseAVCCodecId(). hev1/hvc1.XXXXXX may be unambiguous;
- // handled by ParseHEVCCodecID(). vp9, vp9.0,
- // vp09.xx.xx.xx.xx.xx.xx.xx may be unambiguous; handled by
- // ParseVp9CodecID().
- {"mp3", MimeUtil::MP3},
- // Following is the list of RFC 6381 compliant audio codec
- // strings:
- // mp4a.66 - MPEG-2 AAC MAIN
- // mp4a.67 - MPEG-2 AAC LC
- // mp4a.68 - MPEG-2 AAC SSR
- // mp4a.69 - MPEG-2 extension to MPEG-1 (MP3)
- // mp4a.6B - MPEG-1 audio (MP3)
- // mp4a.40.2 - MPEG-4 AAC LC
- // mp4a.40.02 - MPEG-4 AAC LC (leading 0 in aud-oti for
- // compatibility)
- // mp4a.40.5 - MPEG-4 HE-AAC v1 (AAC LC + SBR)
- // mp4a.40.05 - MPEG-4 HE-AAC v1 (AAC LC + SBR) (leading 0
- // in aud-oti for compatibility)
- // mp4a.40.29 - MPEG-4 HE-AAC v2 (AAC LC + SBR + PS)
- {"mp4a.66", MimeUtil::MPEG2_AAC},
- {"mp4a.67", MimeUtil::MPEG2_AAC},
- {"mp4a.68", MimeUtil::MPEG2_AAC},
- {"mp4a.69", MimeUtil::MP3},
- {"mp4a.6B", MimeUtil::MP3},
- {"mp4a.40.2", MimeUtil::MPEG4_AAC},
- {"mp4a.40.02", MimeUtil::MPEG4_AAC},
- {"mp4a.40.5", MimeUtil::MPEG4_AAC},
- {"mp4a.40.05", MimeUtil::MPEG4_AAC},
- {"mp4a.40.29", MimeUtil::MPEG4_AAC},
- {"mp4a.40.42", MimeUtil::MPEG4_XHE_AAC},
- // TODO(servolk): Strictly speaking only mp4a.A5 and mp4a.A6
- // codec ids are valid according to RFC 6381 section 3.3, 3.4.
- // Lower-case oti (mp4a.a5 and mp4a.a6) should be rejected. But
- // we used to allow those in older versions of Chromecast
- // firmware and some apps (notably MPL) depend on those codec
- // types being supported, so they should be allowed for now
- // (crbug.com/564960).
- {"ac-3", MimeUtil::AC3},
- {"mp4a.a5", MimeUtil::AC3},
- {"mp4a.A5", MimeUtil::AC3},
- {"ec-3", MimeUtil::EAC3},
- {"mp4a.a6", MimeUtil::EAC3},
- {"mp4a.A6", MimeUtil::EAC3},
- {"vorbis", MimeUtil::VORBIS},
- {"opus", MimeUtil::OPUS},
- {"flac", MimeUtil::FLAC},
- {"vp8", MimeUtil::VP8},
- {"vp8.0", MimeUtil::VP8},
- {"theora", MimeUtil::THEORA},
- {"dtsc", MimeUtil::DTS},
- {"mp4a.a9", MimeUtil::DTS},
- {"mp4a.A9", MimeUtil::DTS},
- {"dtsx", MimeUtil::DTSXP2},
- {"mp4a.b2", MimeUtil::DTSXP2},
- {"mp4a.B2", MimeUtil::DTSXP2},
- });
- return *kStringToCodecMap;
- }
- static bool ParseVp9CodecID(const std::string& mime_type_lower_case,
- const std::string& codec_id,
- VideoCodecProfile* out_profile,
- uint8_t* out_level,
- VideoColorSpace* out_color_space) {
- 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;
- }
- static bool IsValidH264Level(uint8_t level_idc) {
- // Valid levels taken from Table A-1 in ISO/IEC 14496-10.
- // Level_idc represents the standard level represented as decimal number
- // multiplied by ten, e.g. level_idc==32 corresponds to level==3.2
- return ((level_idc >= 10 && level_idc <= 13) ||
- (level_idc >= 20 && level_idc <= 22) ||
- (level_idc >= 30 && level_idc <= 32) ||
- (level_idc >= 40 && level_idc <= 42) ||
- (level_idc >= 50 && level_idc <= 52) ||
- (level_idc >= 60 && level_idc <= 62));
- }
- // 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.
- static 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()};
- }
- MimeUtil::MimeUtil() {
- #if BUILDFLAG(IS_ANDROID)
- #if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
- platform_info_.has_platform_dv_decoder =
- MediaCodecUtil::IsDolbyVisionDecoderAvailable();
- #endif
- platform_info_.has_platform_vp8_decoder =
- MediaCodecUtil::IsVp8DecoderAvailable();
- platform_info_.has_platform_vp9_decoder =
- MediaCodecUtil::IsVp9DecoderAvailable();
- #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
- platform_info_.has_platform_hevc_decoder =
- MediaCodecUtil::IsHEVCDecoderAvailable();
- #endif
- platform_info_.has_platform_opus_decoder =
- MediaCodecUtil::IsOpusDecoderAvailable();
- #endif // BUILDFLAG(IS_ANDROID)
- InitializeMimeTypeMaps();
- }
- MimeUtil::~MimeUtil() = default;
- AudioCodec MimeUtilToAudioCodec(MimeUtil::Codec codec) {
- switch (codec) {
- case MimeUtil::PCM:
- return AudioCodec::kPCM;
- case MimeUtil::MP3:
- return AudioCodec::kMP3;
- case MimeUtil::AC3:
- return AudioCodec::kAC3;
- case MimeUtil::EAC3:
- return AudioCodec::kEAC3;
- case MimeUtil::MPEG2_AAC:
- case MimeUtil::MPEG4_AAC:
- case MimeUtil::MPEG4_XHE_AAC:
- return AudioCodec::kAAC;
- case MimeUtil::MPEG_H_AUDIO:
- return AudioCodec::kMpegHAudio;
- case MimeUtil::VORBIS:
- return AudioCodec::kVorbis;
- case MimeUtil::OPUS:
- return AudioCodec::kOpus;
- case MimeUtil::FLAC:
- return AudioCodec::kFLAC;
- case MimeUtil::DTS:
- return AudioCodec::kDTS;
- case MimeUtil::DTSXP2:
- return AudioCodec::kDTSXP2;
- default:
- break;
- }
- return AudioCodec::kUnknown;
- }
- VideoCodec MimeUtilToVideoCodec(MimeUtil::Codec codec) {
- switch (codec) {
- case MimeUtil::AV1:
- return VideoCodec::kAV1;
- case MimeUtil::H264:
- return VideoCodec::kH264;
- case MimeUtil::HEVC:
- return VideoCodec::kHEVC;
- case MimeUtil::VP8:
- return VideoCodec::kVP8;
- case MimeUtil::VP9:
- return VideoCodec::kVP9;
- case MimeUtil::THEORA:
- return VideoCodec::kTheora;
- case MimeUtil::DOLBY_VISION:
- return VideoCodec::kDolbyVision;
- default:
- break;
- }
- return VideoCodec::kUnknown;
- }
- SupportsType MimeUtil::AreSupportedCodecs(
- const std::vector<ParsedCodecResult>& parsed_codecs,
- const std::string& mime_type_lower_case,
- bool is_encrypted) const {
- DCHECK(!parsed_codecs.empty());
- DCHECK_EQ(base::ToLowerASCII(mime_type_lower_case), mime_type_lower_case);
- SupportsType combined_result = SupportsType::kSupported;
- for (const auto& parsed_codec : parsed_codecs) {
- // Make conservative guesses to resolve ambiguity before checking platform
- // 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, parsed_codec.codec, video_profile, video_level,
- parsed_codec.video_color_space, is_encrypted);
- if (result == SupportsType::kNotSupported) {
- DVLOG(2) << __func__ << ": Codec " << parsed_codec.codec
- << " not supported by platform.";
- return SupportsType::kNotSupported;
- }
- // If any codec is "kMaybeSupported", return Maybe for the combined result.
- if (result == SupportsType::kMaybeSupported ||
- // Downgrade to kMaybeSupported 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 == SupportsType::kSupported && parsed_codec.is_ambiguous &&
- parsed_codec.codec != MimeUtil::VP9)) {
- combined_result = SupportsType::kMaybeSupported;
- }
- }
- return combined_result;
- }
- void MimeUtil::InitializeMimeTypeMaps() {
- AddSupportedMediaFormats();
- }
- // Each call to AddContainerWithCodecs() contains a media type
- // (https://en.wikipedia.org/wiki/Media_type) and corresponding media codec(s)
- // supported by these types/containers.
- void MimeUtil::AddSupportedMediaFormats() {
- const CodecSet wav_codecs{PCM};
- const CodecSet ogg_audio_codecs{FLAC, OPUS, VORBIS};
- CodecSet ogg_video_codecs{VP8};
- #if BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
- ogg_video_codecs.emplace(THEORA);
- #endif // BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
- CodecSet ogg_codecs(ogg_audio_codecs);
- ogg_codecs.insert(ogg_video_codecs.begin(), ogg_video_codecs.end());
- const CodecSet webm_audio_codecs{OPUS, VORBIS};
- CodecSet webm_video_codecs{VP8, VP9};
- #if BUILDFLAG(ENABLE_AV1_DECODER)
- webm_video_codecs.emplace(AV1);
- #endif
- CodecSet webm_codecs(webm_audio_codecs);
- webm_codecs.insert(webm_video_codecs.begin(), webm_video_codecs.end());
- const CodecSet mp3_codecs{MP3};
- CodecSet mp4_audio_codecs{FLAC, MP3, OPUS};
- // Only VP9 with valid codec string vp09.xx.xx.xx.xx.xx.xx.xx is supported.
- // See ParseVp9CodecID for details.
- CodecSet mp4_video_codecs;
- mp4_video_codecs.emplace(VP9);
- #if BUILDFLAG(USE_PROPRIETARY_CODECS)
- const CodecSet aac{MPEG2_AAC, MPEG4_AAC, MPEG4_XHE_AAC};
- mp4_audio_codecs.insert(aac.begin(), aac.end());
- CodecSet avc_and_aac(aac);
- avc_and_aac.emplace(H264);
- #if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
- mp4_audio_codecs.emplace(AC3);
- mp4_audio_codecs.emplace(EAC3);
- #endif // BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
- #if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
- mp4_audio_codecs.emplace(MPEG_H_AUDIO);
- #endif // BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
- mp4_video_codecs.emplace(H264);
- #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
- mp4_video_codecs.emplace(HEVC);
- #endif // BUILDFLAG(ENABLE_PLATFORM_HEVC)
- #if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
- mp4_video_codecs.emplace(DOLBY_VISION);
- #endif // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
- #endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
- #if BUILDFLAG(ENABLE_AV1_DECODER)
- mp4_video_codecs.emplace(AV1);
- #endif
- #if BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
- mp4_audio_codecs.emplace(DTS);
- mp4_audio_codecs.emplace(DTSXP2);
- #endif // BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
- CodecSet mp4_codecs(mp4_audio_codecs);
- mp4_codecs.insert(mp4_video_codecs.begin(), mp4_video_codecs.end());
- const CodecSet implicit_codec;
- AddContainerWithCodecs("audio/wav", wav_codecs);
- AddContainerWithCodecs("audio/x-wav", wav_codecs);
- AddContainerWithCodecs("audio/webm", webm_audio_codecs);
- DCHECK(!webm_video_codecs.empty());
- AddContainerWithCodecs("video/webm", webm_codecs);
- AddContainerWithCodecs("audio/ogg", ogg_audio_codecs);
- // video/ogg is only supported if an appropriate video codec is supported.
- // Note: This assumes such codecs cannot be later excluded.
- if (!ogg_video_codecs.empty())
- AddContainerWithCodecs("video/ogg", ogg_codecs);
- // TODO(ddorwin): Should the application type support Opus?
- AddContainerWithCodecs("application/ogg", ogg_codecs);
- AddContainerWithCodecs("audio/flac", implicit_codec);
- AddContainerWithCodecs("audio/mpeg", mp3_codecs); // Allow "mp3".
- AddContainerWithCodecs("audio/mp3", implicit_codec);
- AddContainerWithCodecs("audio/x-mp3", implicit_codec);
- AddContainerWithCodecs("audio/mp4", mp4_audio_codecs);
- DCHECK(!mp4_video_codecs.empty());
- AddContainerWithCodecs("video/mp4", mp4_codecs);
- #if BUILDFLAG(USE_PROPRIETARY_CODECS)
- AddContainerWithCodecs("audio/aac", implicit_codec); // AAC / ADTS.
- // These strings are supported for backwards compatibility only and thus only
- // support the codecs needed for compatibility.
- AddContainerWithCodecs("audio/x-m4a", aac);
- AddContainerWithCodecs("video/x-m4v", avc_and_aac);
- CodecSet video_3gpp_codecs(aac);
- video_3gpp_codecs.emplace(H264);
- AddContainerWithCodecs("video/3gpp", video_3gpp_codecs);
- #if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
- CodecSet mp2t_codecs{H264, MPEG2_AAC, MPEG4_AAC, MP3};
- AddContainerWithCodecs("video/mp2t", mp2t_codecs);
- #endif // BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
- #if BUILDFLAG(IS_ANDROID)
- if (base::FeatureList::IsEnabled(kCanPlayHls)) {
- // HTTP Live Streaming (HLS).
- CodecSet hls_codecs{H264,
- // TODO(ddorwin): Is any MP3 codec string variant
- // included in real queries?
- MP3,
- // Android HLS only supports MPEG4_AAC (missing demuxer
- // support for MPEG2_AAC)
- MPEG4_AAC};
- AddContainerWithCodecs("application/x-mpegurl", hls_codecs);
- AddContainerWithCodecs("application/vnd.apple.mpegurl", hls_codecs);
- AddContainerWithCodecs("audio/mpegurl", hls_codecs);
- // Not documented by Apple, but unfortunately used extensively by Apple and
- // others for both audio-only and audio+video playlists. See
- // https://crbug.com/675552 for details and examples.
- AddContainerWithCodecs("audio/x-mpegurl", hls_codecs);
- }
- #endif // BUILDFLAG(IS_ANDROID)
- #endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
- }
- void MimeUtil::AddContainerWithCodecs(const std::string& mime_type,
- const CodecSet& codecs) {
- media_format_map_[mime_type] = codecs;
- }
- bool MimeUtil::IsSupportedMediaMimeType(const std::string& mime_type) const {
- return media_format_map_.contains(base::ToLowerASCII(mime_type));
- }
- void MimeUtil::SplitCodecs(const std::string& codecs,
- std::vector<std::string>* codecs_out) const {
- *codecs_out =
- base::SplitString(base::TrimString(codecs, "\"", base::TRIM_ALL), ",",
- base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
- // Convert empty or all-whitespace input to 0 results.
- if (codecs_out->size() == 1 && (*codecs_out)[0].empty())
- codecs_out->clear();
- }
- void MimeUtil::StripCodecs(std::vector<std::string>* codecs) const {
- // Strip everything past the first '.'
- for (auto it = codecs->begin(); it != codecs->end(); ++it) {
- size_t found = it->find_first_of('.');
- if (found != std::string::npos)
- it->resize(found);
- }
- }
- 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) const {
- 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.empty() ? "<empty mime>" : 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 == VideoCodec::kUnknown) {
- 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) const {
- 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.empty() ? "<empty mime>" : 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 == AudioCodec::kUnknown) {
- 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);
- 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 SupportsType::kNotSupported;
- }
- if (parsed_results.empty()) {
- NOTREACHED() << __func__ << " Successful parsing should output results.";
- return SupportsType::kNotSupported;
- }
- // 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 SupportsType::kMaybeSupported;
- }
- return AreSupportedCodecs(parsed_results, mime_type_lower_case, is_encrypted);
- }
- // static
- bool MimeUtil::IsCodecSupportedOnAndroid(
- Codec codec,
- const std::string& mime_type_lower_case,
- bool is_encrypted,
- VideoCodecProfile video_profile,
- const PlatformInfo& platform_info) {
- DVLOG(3) << __func__;
- DCHECK_NE(mime_type_lower_case, "");
- // NOTE: We do not account for Media Source Extensions (MSE) within these
- // checks since it has its own isTypeSupported() which will handle platform
- // specific codec rejections. See http://crbug.com/587303.
- switch (codec) {
- // ----------------------------------------------------------------------
- // The following codecs are never supported.
- // ----------------------------------------------------------------------
- case INVALID_CODEC:
- case THEORA:
- return false;
- // ----------------------------------------------------------------------
- // The remaining codecs may be supported depending on platform abilities.
- // ----------------------------------------------------------------------
- case AV1:
- return BUILDFLAG(ENABLE_AV1_DECODER);
- case MPEG2_AAC:
- // MPEG2_AAC cannot be used in HLS (mpegurl suffix), but this is enforced
- // in the parsing step by excluding MPEG2_AAC from the list of
- // valid codecs to be used with HLS mime types.
- DCHECK(!base::EndsWith(mime_type_lower_case, "mpegurl",
- base::CompareCase::SENSITIVE));
- [[fallthrough]];
- case PCM:
- case MP3:
- case MPEG4_AAC:
- case FLAC:
- case VORBIS:
- // These codecs are always supported; via a platform decoder (when used
- // with MSE/EME) or with a software decoder (the unified pipeline).
- return true;
- case MPEG4_XHE_AAC:
- return true;
- case MPEG_H_AUDIO:
- return false;
- case OPUS:
- // If clear, the unified pipeline can always decode Opus in software.
- if (!is_encrypted)
- return true;
- // Otherwise, platform support is required.
- if (!platform_info.has_platform_opus_decoder) {
- DVLOG(3) << "Platform does not support opus";
- return false;
- }
- return true;
- case H264:
- return true;
- case HEVC:
- #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
- return platform_info.has_platform_hevc_decoder;
- #else
- return false;
- #endif // BUILDFLAG(ENABLE_PLATFORM_HEVC)
- case VP8:
- // If clear, the unified pipeline can always decode VP8 in software.
- return is_encrypted ? platform_info.has_platform_vp8_decoder : true;
- case VP9: {
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kReportVp9AsAnUnsupportedMimeType)) {
- return false;
- }
- // If clear, the unified pipeline can always decode VP9.0,1 in software.
- // If we don't know the profile, then support is ambiguous, but default to
- // true for historical reasons.
- if (!is_encrypted && (video_profile == VP9PROFILE_PROFILE0 ||
- video_profile == VP9PROFILE_PROFILE1 ||
- video_profile == VIDEO_CODEC_PROFILE_UNKNOWN)) {
- return true;
- }
- if (!platform_info.has_platform_vp9_decoder)
- return false;
- return true;
- }
- case DOLBY_VISION:
- #if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
- return platform_info.has_platform_dv_decoder;
- #else
- return false;
- #endif
- case AC3:
- case EAC3:
- #if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
- return true;
- #else
- return false;
- #endif
- case DTS:
- case DTSXP2:
- #if BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
- return true;
- #else
- return false;
- #endif
- }
- return false;
- }
- 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);
- // Nothing to parse.
- if (mime_type_lower_case.empty() && codecs.empty())
- return false;
- // 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__
- << " Unrecognized mime type: " << mime_type_lower_case;
- 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;
- }
- 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;
- }
- }
- // All empty cases handled above.
- DCHECK(!codecs.empty());
- 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.empty() ? "<empty mime>"
- : mime_type_lower_case)
- << "; " << codec_string;
- return false;
- }
- DCHECK_NE(INVALID_CODEC, result.codec);
- // 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;
- return false;
- }
- out_results->push_back(result);
- }
- return true;
- }
- bool MimeUtil::ParseCodecHelper(const std::string& mime_type_lower_case,
- const std::string& codec_id,
- ParsedCodecResult* out_result) const {
- DCHECK_EQ(base::ToLowerASCII(mime_type_lower_case), mime_type_lower_case);
- DCHECK(out_result);
- *out_result = MakeDefaultParsedCodecResult();
- // Simple codecs can be found in the codec map.
- auto itr = GetStringToCodecMap().find(codec_id);
- if (itr != GetStringToCodecMap().end()) {
- out_result->codec = itr->second;
- // Even "simple" video codecs should have an associated profile.
- if (MimeUtilToVideoCodec(out_result->codec) != VideoCodec::kUnknown) {
- switch (out_result->codec) {
- case Codec::VP8:
- out_result->video_profile = VP8PROFILE_ANY;
- break;
- case Codec::THEORA:
- out_result->video_profile = THEORAPROFILE_ANY;
- break;
- default:
- NOTREACHED();
- }
- }
- return true;
- }
- // Check codec string against short list of allowed ambiguous codecs.
- // Hard-coded to discourage expansion. DO NOT ADD TO THIS LIST. DO NOT
- // INCREASE PLACES WHERE |ambiguous_codec_string| = true.
- // NOTE: avc1/avc3.XXXXXX may be ambiguous handled after ParseAVCCodecId().
- if (codec_id == "avc1" || codec_id == "avc3") {
- out_result->codec = MimeUtil::H264;
- out_result->is_ambiguous = true;
- return true;
- } else if (codec_id == "mp4a.40") {
- out_result->codec = MimeUtil::MPEG4_AAC;
- out_result->is_ambiguous = true;
- return true;
- }
- // If |codec_id| is not in |kStringToCodecMap|, then we assume that it is
- // 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)) {
- 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 BUILDFLAG(ENABLE_AV1_DECODER)
- if (ParseAv1CodecId(codec_id, out_profile, out_level, out_color_space)) {
- out_result->codec = MimeUtil::AV1;
- return true;
- }
- #endif
- if (ParseAVCCodecId(codec_id, out_profile, out_level)) {
- out_result->codec = MimeUtil::H264;
- // Allowed string ambiguity since 2014. DO NOT ADD NEW CASES FOR AMBIGUITY.
- out_result->is_ambiguous = !IsValidH264Level(*out_level);
- return true;
- }
- #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
- if (ParseHEVCCodecId(codec_id, out_profile, out_level)) {
- out_result->codec = MimeUtil::HEVC;
- return true;
- }
- #endif
- #if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
- if (ParseDolbyVisionCodecId(codec_id, out_profile, out_level)) {
- out_result->codec = MimeUtil::DOLBY_VISION;
- return true;
- }
- #endif
- #if BUILDFLAG(ENABLE_PLATFORM_MPEG_H_AUDIO)
- if (base::StartsWith(codec_id, "mhm1.", base::CompareCase::SENSITIVE) ||
- base::StartsWith(codec_id, "mha1.", base::CompareCase::SENSITIVE)) {
- out_result->codec = MimeUtil::MPEG_H_AUDIO;
- return true;
- }
- #endif
- DVLOG(2) << __func__ << ": Unrecognized codec id \"" << codec_id << "\"";
- return false;
- }
- SupportsType MimeUtil::IsCodecSupported(const std::string& mime_type_lower_case,
- Codec codec,
- VideoCodecProfile video_profile,
- uint8_t video_level,
- const VideoColorSpace& color_space,
- bool is_encrypted) const {
- DVLOG(3) << __func__;
- DCHECK_EQ(base::ToLowerASCII(mime_type_lower_case), mime_type_lower_case);
- DCHECK_NE(codec, INVALID_CODEC);
- VideoCodec video_codec = MimeUtilToVideoCodec(codec);
- if (video_codec != VideoCodec::kUnknown &&
- // Theora and VP8 do not have profiles/levels.
- video_codec != VideoCodec::kTheora && video_codec != VideoCodec::kVP8 &&
- // TODO(dalecurtis): AV1 has levels, but they aren't supported yet;
- // http://crbug.com/784993
- video_codec != VideoCodec::kAV1) {
- DCHECK_NE(video_profile, VIDEO_CODEC_PROFILE_UNKNOWN);
- DCHECK_GT(video_level, 0);
- }
- // Check for cases of ambiguous platform support.
- // TODO(chcunningham): DELETE THIS. Platform should know its capabilities.
- // Answer should come from MediaClient.
- bool ambiguous_platform_support = false;
- if (codec == MimeUtil::H264) {
- switch (video_profile) {
- // Always supported
- case H264PROFILE_BASELINE:
- case H264PROFILE_MAIN:
- case H264PROFILE_HIGH:
- break;
- // HIGH10PROFILE is supported through fallback to the ffmpeg decoder
- // which is not available on Android, or if FFMPEG is not used.
- #if BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
- case H264PROFILE_HIGH10PROFILE:
- // FFmpeg is not generally used for encrypted videos, so we do not
- // know whether 10-bit is supported.
- ambiguous_platform_support = is_encrypted;
- break;
- #endif
- default:
- ambiguous_platform_support = true;
- }
- }
- AudioCodec audio_codec = MimeUtilToAudioCodec(codec);
- if (audio_codec != AudioCodec::kUnknown) {
- AudioCodecProfile audio_profile = AudioCodecProfile::kUnknown;
- if (codec == MPEG4_XHE_AAC)
- audio_profile = AudioCodecProfile::kXHE_AAC;
- if (!IsSupportedAudioType({audio_codec, audio_profile, false}))
- return SupportsType::kNotSupported;
- }
- if (video_codec != VideoCodec::kUnknown) {
- if (!IsSupportedVideoType(
- {video_codec, video_profile, video_level, color_space})) {
- return SupportsType::kNotSupported;
- }
- }
- #if BUILDFLAG(IS_ANDROID)
- // TODO(chcunningham): Delete this. Android platform support should be
- // handled by (android specific) media::IsSupportedVideoType() above.
- if (!IsCodecSupportedOnAndroid(codec, mime_type_lower_case, is_encrypted,
- video_profile, platform_info_)) {
- return SupportsType::kNotSupported;
- }
- #endif
- return ambiguous_platform_support ? SupportsType::kMaybeSupported
- : SupportsType::kSupported;
- }
- 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;
- return true;
- }
- if (mime_type == "audio/aac") {
- *default_codec = MimeUtil::MPEG4_AAC;
- return true;
- }
- if (mime_type == "audio/flac") {
- *default_codec = MimeUtil::FLAC;
- return true;
- }
- return false;
- }
- } // namespace internal
- } // namespace media
|