| // 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 "cobalt/media/base/mime_util_internal.h" |
| |
| #include "base/command_line.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_split.h" |
| #include "base/string_util.h" |
| #include "build/build_config.h" |
| #include "cobalt/media/base/media.h" |
| #include "cobalt/media/base/video_codecs.h" |
| |
| #if defined(OS_ANDROID) |
| #include "base/android/build_info.h" |
| #include "cobalt/media/base/android/media_codec_util.h" |
| #endif |
| |
| namespace cobalt { |
| namespace media { |
| namespace internal { |
| |
| struct CodecIDMappings { |
| const char* const codec_id; |
| MimeUtil::Codec codec; |
| }; |
| |
| // List of codec IDs that provide enough information to determine the |
| // codec and profile being requested. |
| // |
| // The "mp4a" strings come from RFC 6381. |
| static const CodecIDMappings kUnambiguousCodecStringMap[] = { |
| {"1", MimeUtil::PCM}, // We only allow this for WAV so it isn't ambiguous. |
| // 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}, |
| // 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}, |
| {"vp8", MimeUtil::VP8}, |
| {"vp8.0", MimeUtil::VP8}, |
| {"theora", MimeUtil::THEORA}}; |
| |
| // List of codec IDs that are ambiguous and don't provide |
| // enough information to determine the codec and profile. |
| // The codec in these entries indicate the codec and profile |
| // we assume the user is trying to indicate. |
| static const CodecIDMappings kAmbiguousCodecStringMap[] = { |
| {"mp4a.40", MimeUtil::MPEG4_AAC}, |
| {"avc1", MimeUtil::H264}, |
| {"avc3", MimeUtil::H264}, |
| // avc1/avc3.XXXXXX may be ambiguous; handled by ParseAVCCodecId(). |
| }; |
| |
| static const char kHexString[] = "0123456789ABCDEF"; |
| static char IntToHex(int i) { |
| DCHECK_GE(i, 0) << i << " not a hex value"; |
| DCHECK_LE(i, 15) << i << " not a hex value"; |
| return kHexString[i]; |
| } |
| |
| static std::string TranslateLegacyAvc1CodecIds(const std::string& codec_id) { |
| // Special handling for old, pre-RFC 6381 format avc1 strings, which are still |
| // being used by some HLS apps to preserve backward compatibility with older |
| // iOS devices. The old format was avc1.<profile>.<level> |
| // Where <profile> is H.264 profile_idc encoded as a decimal number, i.e. |
| // 66 is baseline profile (0x42) |
| // 77 is main profile (0x4d) |
| // 100 is high profile (0x64) |
| // And <level> is H.264 level multiplied by 10, also encoded as decimal number |
| // E.g. <level> 31 corresponds to H.264 level 3.1 |
| // See, for example, http://qtdevseed.apple.com/qadrift/testcases/tc-0133.php |
| uint32_t level_start = 0; |
| std::string result; |
| if (StartsWithASCII(codec_id, "avc1.66.", true)) { |
| level_start = 8; |
| result = "avc1.4200"; |
| } else if (StartsWithASCII(codec_id, "avc1.77.", true)) { |
| level_start = 8; |
| result = "avc1.4D00"; |
| } else if (StartsWithASCII(codec_id, "avc1.100.", true)) { |
| level_start = 9; |
| result = "avc1.6400"; |
| } |
| |
| uint32_t level = 0; |
| if (level_start > 0 && |
| base::StringToUint(codec_id.substr(level_start), &level) && level < 256) { |
| // This is a valid legacy avc1 codec id - return the codec id translated |
| // into RFC 6381 format. |
| result.push_back(IntToHex(level >> 4)); |
| result.push_back(IntToHex(level & 0xf)); |
| return result; |
| } |
| |
| // This is not a valid legacy avc1 codec id - return the original codec id. |
| return codec_id; |
| } |
| |
| 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 <= 51)); |
| } |
| |
| // Handle parsing of vp9 codec IDs. |
| static bool ParseVp9CodecID(const std::string& mime_type_lower_case, |
| const std::string& codec_id, |
| VideoCodecProfile* profile) { |
| if (mime_type_lower_case == "video/webm") { |
| if (codec_id == "vp9" || codec_id == "vp9.0") { |
| // Profile is not included in the codec string. Assuming profile 0 to be |
| // backward compatible. |
| *profile = VP9PROFILE_PROFILE0; |
| return true; |
| } |
| // TODO(kqyang): Should we support new codec string in WebM? |
| return false; |
| } else if (mime_type_lower_case == "audio/webm") { |
| return false; |
| } |
| |
| std::vector<std::string> fields; |
| base::SplitString(codec_id, '.', &fields); |
| if (fields.size() < 1) return false; |
| |
| if (fields[0] != "vp09") return false; |
| |
| if (fields.size() > 8) return false; |
| |
| std::vector<int> values; |
| for (size_t i = 1; i < fields.size(); ++i) { |
| // Missing value is not allowed. |
| if (fields[i] == "") return false; |
| int value; |
| if (!base::StringToInt(fields[i], &value)) return false; |
| if (value < 0) return false; |
| values.push_back(value); |
| } |
| |
| // The spec specifies 8 fields (7 values excluding the first codec field). |
| // We do not allow missing fields. |
| if (values.size() < 7) return false; |
| |
| const int profile_idc = values[0]; |
| switch (profile_idc) { |
| case 0: |
| *profile = VP9PROFILE_PROFILE0; |
| break; |
| case 1: |
| *profile = VP9PROFILE_PROFILE1; |
| break; |
| case 2: |
| *profile = VP9PROFILE_PROFILE2; |
| break; |
| case 3: |
| *profile = VP9PROFILE_PROFILE3; |
| break; |
| default: |
| return false; |
| } |
| |
| const int bit_depth = values[2]; |
| if (bit_depth != 8 && bit_depth != 10 && bit_depth != 12) return false; |
| |
| const int color_space = values[3]; |
| if (color_space > 7) return false; |
| |
| const int chroma_subsampling = values[4]; |
| if (chroma_subsampling > 3) return false; |
| |
| const int transfer_function = values[5]; |
| if (transfer_function > 1) return false; |
| |
| const int video_full_range_flag = values[6]; |
| if (video_full_range_flag > 1) return false; |
| |
| return true; |
| } |
| |
| MimeUtil::MimeUtil() : allow_proprietary_codecs_(false) { |
| #if defined(OS_ANDROID) |
| platform_info_.is_unified_media_pipeline_enabled = |
| IsUnifiedMediaPipelineEnabled(); |
| // When the unified media pipeline is enabled, we need support for both GPU |
| // video decoders and MediaCodec; indicated by HasPlatformDecoderSupport(). |
| // When the Android pipeline is used, we only need access to MediaCodec. |
| platform_info_.has_platform_decoders = ArePlatformDecodersAvailable(); |
| platform_info_.has_platform_vp8_decoder = |
| MediaCodecUtil::IsVp8DecoderAvailable(); |
| platform_info_.has_platform_vp9_decoder = |
| MediaCodecUtil::IsVp9DecoderAvailable(); |
| platform_info_.supports_opus = PlatformHasOpusSupport(); |
| #endif |
| |
| InitializeMimeTypeMaps(); |
| } |
| |
| MimeUtil::~MimeUtil() {} |
| |
| VideoCodec MimeUtilToVideoCodec(MimeUtil::Codec codec) { |
| switch (codec) { |
| case MimeUtil::H264: |
| return kCodecH264; |
| case MimeUtil::HEVC: |
| return kCodecHEVC; |
| case MimeUtil::VP8: |
| return kCodecVP8; |
| case MimeUtil::VP9: |
| return kCodecVP9; |
| case MimeUtil::THEORA: |
| return kCodecTheora; |
| default: |
| break; |
| } |
| return kUnknownVideoCodec; |
| } |
| |
| SupportsType MimeUtil::AreSupportedCodecs( |
| const CodecSet& supported_codecs, const std::vector<std::string>& codecs, |
| const std::string& mime_type_lower_case, bool is_encrypted) const { |
| DCHECK(!supported_codecs.empty()); |
| DCHECK(!codecs.empty()); |
| |
| SupportsType result = IsSupported; |
| for (size_t i = 0; i < codecs.size(); ++i) { |
| bool is_ambiguous = true; |
| Codec codec = INVALID_CODEC; |
| VideoCodecProfile video_profile = VIDEO_CODEC_PROFILE_UNKNOWN; |
| uint8_t video_level = 0; |
| if (!StringToCodec(mime_type_lower_case, codecs[i], &codec, &is_ambiguous, |
| &video_profile, &video_level, is_encrypted)) { |
| return IsNotSupported; |
| } |
| |
| VideoCodec video_codec = MimeUtilToVideoCodec(codec); |
| |
| if (!IsCodecSupported(codec, mime_type_lower_case, is_encrypted) || |
| supported_codecs.find(codec) == supported_codecs.end()) { |
| return IsNotSupported; |
| } |
| |
| if (is_ambiguous) result = MayBeSupported; |
| } |
| |
| return result; |
| } |
| |
| void MimeUtil::InitializeMimeTypeMaps() { |
| #if defined(USE_PROPRIETARY_CODECS) |
| allow_proprietary_codecs_ = true; |
| #endif |
| |
| for (size_t i = 0; i < arraysize(kUnambiguousCodecStringMap); ++i) { |
| string_to_codec_map_[kUnambiguousCodecStringMap[i].codec_id] = |
| CodecEntry(kUnambiguousCodecStringMap[i].codec, false); |
| } |
| |
| for (size_t i = 0; i < arraysize(kAmbiguousCodecStringMap); ++i) { |
| string_to_codec_map_[kAmbiguousCodecStringMap[i].codec_id] = |
| CodecEntry(kAmbiguousCodecStringMap[i].codec, true); |
| } |
| |
| 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. |
| // TODO(ddorwin): Replace insert() calls with initializer_list when allowed. |
| void MimeUtil::AddSupportedMediaFormats() { |
| CodecSet implicit_codec; |
| CodecSet wav_codecs; |
| wav_codecs.insert(PCM); |
| |
| CodecSet ogg_audio_codecs; |
| ogg_audio_codecs.insert(OPUS); |
| ogg_audio_codecs.insert(VORBIS); |
| CodecSet ogg_video_codecs; |
| #if !defined(OS_ANDROID) |
| ogg_video_codecs.insert(THEORA); |
| #endif // !defined(OS_ANDROID) |
| CodecSet ogg_codecs(ogg_audio_codecs); |
| ogg_codecs.insert(ogg_video_codecs.begin(), ogg_video_codecs.end()); |
| |
| CodecSet webm_audio_codecs; |
| webm_audio_codecs.insert(OPUS); |
| webm_audio_codecs.insert(VORBIS); |
| CodecSet webm_video_codecs; |
| webm_video_codecs.insert(VP8); |
| webm_video_codecs.insert(VP9); |
| CodecSet webm_codecs(webm_audio_codecs); |
| webm_codecs.insert(webm_video_codecs.begin(), webm_video_codecs.end()); |
| |
| CodecSet mp3_codecs; |
| mp3_codecs.insert(MP3); |
| |
| CodecSet aac; |
| aac.insert(MPEG2_AAC); |
| aac.insert(MPEG4_AAC); |
| |
| CodecSet avc_and_aac(aac); |
| avc_and_aac.insert(H264); |
| |
| CodecSet mp4_audio_codecs(aac); |
| mp4_audio_codecs.insert(MP3); |
| mp4_audio_codecs.insert(AC3); |
| mp4_audio_codecs.insert(EAC3); |
| |
| CodecSet mp4_video_codecs; |
| mp4_video_codecs.insert(H264); |
| mp4_video_codecs.insert(HEVC); |
| // Only VP9 with valid codec string vp09.xx.xx.xx.xx.xx.xx.xx is supported. |
| // See ParseVp9CodecID for details. |
| mp4_video_codecs.insert(VP9); |
| CodecSet mp4_codecs(mp4_audio_codecs); |
| mp4_codecs.insert(mp4_video_codecs.begin(), mp4_video_codecs.end()); |
| |
| AddContainerWithCodecs("audio/wav", wav_codecs, false); |
| AddContainerWithCodecs("audio/x-wav", wav_codecs, false); |
| AddContainerWithCodecs("audio/webm", webm_audio_codecs, false); |
| DCHECK(!webm_video_codecs.empty()); |
| AddContainerWithCodecs("video/webm", webm_codecs, false); |
| AddContainerWithCodecs("audio/ogg", ogg_audio_codecs, false); |
| // 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, false); |
| // TODO(ddorwin): Should the application type support Opus? |
| AddContainerWithCodecs("application/ogg", ogg_codecs, false); |
| |
| AddContainerWithCodecs("audio/mpeg", mp3_codecs, true); // Allow "mp3". |
| AddContainerWithCodecs("audio/mp3", implicit_codec, true); |
| AddContainerWithCodecs("audio/x-mp3", implicit_codec, true); |
| AddContainerWithCodecs("audio/aac", implicit_codec, true); // AAC / ADTS. |
| AddContainerWithCodecs("audio/mp4", mp4_audio_codecs, true); |
| DCHECK(!mp4_video_codecs.empty()); |
| AddContainerWithCodecs("video/mp4", mp4_codecs, true); |
| // These strings are supported for backwards compatibility only and thus only |
| // support the codecs needed for compatibility. |
| AddContainerWithCodecs("audio/x-m4a", aac, true); |
| AddContainerWithCodecs("video/x-m4v", avc_and_aac, true); |
| |
| // TODO(ddorwin): Exactly which codecs should be supported? |
| DCHECK(!mp4_video_codecs.empty()); |
| AddContainerWithCodecs("video/mp2t", mp4_codecs, true); |
| #if defined(OS_ANDROID) |
| // HTTP Live Streaming (HLS). |
| // TODO(ddorwin): Is any MP3 codec string variant included in real queries? |
| CodecSet hls_codecs(avc_and_aac); |
| hls_codecs.insert(MP3); |
| AddContainerWithCodecs("application/x-mpegurl", hls_codecs, true); |
| AddContainerWithCodecs("application/vnd.apple.mpegurl", hls_codecs, true); |
| #endif // defined(OS_ANDROID) |
| } |
| |
| void MimeUtil::AddContainerWithCodecs(const std::string& mime_type, |
| const CodecSet& codecs, |
| bool is_proprietary_mime_type) { |
| media_format_map_[mime_type] = codecs; |
| |
| if (is_proprietary_mime_type) |
| proprietary_media_containers_.push_back(mime_type); |
| } |
| |
| bool MimeUtil::IsSupportedMediaMimeType(const std::string& mime_type) const { |
| return media_format_map_.find(StringToLowerASCII(mime_type)) != |
| media_format_map_.end(); |
| } |
| |
| void MimeUtil::ParseCodecString(const std::string& codecs, |
| std::vector<std::string>* codecs_out, |
| bool strip) { |
| std::string trimmed_codecs; |
| TrimString(codecs, "\"", &trimmed_codecs); |
| base::SplitString(trimmed_codecs, ',', codecs_out); |
| |
| // Convert empty or all-whitespace input to 0 results. |
| if (codecs_out->size() == 1 && (*codecs_out)[0].empty()) codecs_out->clear(); |
| |
| if (!strip) return; |
| |
| // Strip everything past the first '.' |
| for (std::vector<std::string>::iterator it = codecs_out->begin(); |
| it != codecs_out->end(); ++it) { |
| size_t found = it->find_first_of('.'); |
| if (found != std::string::npos) it->resize(found); |
| } |
| } |
| |
| 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 = StringToLowerASCII(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()) return IsNotSupported; |
| |
| if (it_media_format_map->second.empty()) { |
| // We get here if the mimetype does not expect a codecs parameter. |
| return (codecs.empty() && IsDefaultCodecSupportedLowerCase( |
| mime_type_lower_case, is_encrypted)) |
| ? IsSupported |
| : 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 (!GetDefaultCodecLowerCase(mime_type_lower_case, &default_codec)) |
| return MayBeSupported; |
| |
| return IsCodecSupported(default_codec, mime_type_lower_case, is_encrypted) |
| ? IsSupported |
| : IsNotSupported; |
| } |
| |
| if (mime_type_lower_case == "video/mp2t") { |
| std::vector<std::string> codecs_to_check; |
| for (size_t i = 0; i < codecs.size(); ++i) { |
| codecs_to_check.push_back(TranslateLegacyAvc1CodecIds(codecs[i])); |
| } |
| return AreSupportedCodecs(it_media_format_map->second, codecs_to_check, |
| mime_type_lower_case, is_encrypted); |
| } |
| |
| return AreSupportedCodecs(it_media_format_map->second, codecs, |
| mime_type_lower_case, is_encrypted); |
| } |
| |
| void MimeUtil::RemoveProprietaryMediaTypesAndCodecs() { |
| for (size_t i = 0; i < proprietary_media_containers_.size(); ++i) |
| media_format_map_.erase(proprietary_media_containers_[i]); |
| allow_proprietary_codecs_ = false; |
| } |
| |
| // static |
| bool MimeUtil::IsCodecSupportedOnPlatform( |
| Codec codec, const std::string& mime_type_lower_case, bool is_encrypted, |
| const PlatformInfo& platform_info) { |
| DCHECK_NE(mime_type_lower_case, ""); |
| |
| // Encrypted block support is never available without platform decoders. |
| if (is_encrypted && !platform_info.has_platform_decoders) return false; |
| |
| // 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 AC3: |
| case EAC3: |
| case THEORA: |
| return false; |
| |
| // ---------------------------------------------------------------------- |
| // The remaining codecs may be supported depending on platform abilities. |
| // ---------------------------------------------------------------------- |
| |
| case PCM: |
| case MP3: |
| case MPEG4_AAC: |
| case VORBIS: |
| // These codecs are always supported; via a platform decoder (when used |
| // with MSE/EME), a software decoder (the unified pipeline), or with |
| // MediaPlayer. |
| DCHECK(!is_encrypted || platform_info.has_platform_decoders); |
| return true; |
| |
| case MPEG2_AAC: |
| // MPEG-2 variants of AAC are not supported on Android unless the unified |
| // media pipeline can be used and the container is not HLS. These codecs |
| // will be decoded in software. See https:crbug.com/544268 for details. |
| if (mime_type_lower_case == "application/x-mpegurl" || |
| mime_type_lower_case == "application/vnd.apple.mpegurl") { |
| return false; |
| } |
| return !is_encrypted && platform_info.is_unified_media_pipeline_enabled; |
| |
| case OPUS: |
| // If clear, the unified pipeline can always decode Opus in software. |
| if (!is_encrypted && platform_info.is_unified_media_pipeline_enabled) |
| return true; |
| |
| // Otherwise, platform support is required. |
| if (!platform_info.supports_opus) return false; |
| |
| // MediaPlayer does not support Opus in ogg containers. |
| if (EndsWith(mime_type_lower_case, "ogg", true)) { |
| return false; |
| } |
| |
| DCHECK(!is_encrypted || platform_info.has_platform_decoders); |
| return true; |
| |
| case H264: |
| // When content is not encrypted we fall back to MediaPlayer, thus we |
| // always support H264. For EME we need MediaCodec. |
| return !is_encrypted || platform_info.has_platform_decoders; |
| |
| case HEVC: |
| if (platform_info.is_unified_media_pipeline_enabled && |
| !platform_info.has_platform_decoders) { |
| return false; |
| } |
| |
| #if defined(OS_ANDROID) |
| // HEVC/H.265 is supported in Lollipop+ (API Level 21), according to |
| // http://developer.android.com/reference/android/media/MediaFormat.html |
| return base::android::BuildInfo::GetInstance()->sdk_int() >= 21; |
| #else |
| return true; |
| #endif // defined(OS_ANDROID) |
| |
| case VP8: |
| // If clear, the unified pipeline can always decode VP8 in software. |
| if (!is_encrypted && platform_info.is_unified_media_pipeline_enabled) |
| return true; |
| |
| if (is_encrypted) return platform_info.has_platform_vp8_decoder; |
| |
| // MediaPlayer can always play VP8. Note: This is incorrect for MSE, but |
| // MSE does not use this code. http://crbug.com/587303. |
| return true; |
| |
| case VP9: { |
| // If clear, the unified pipeline can always decode VP9 in software. |
| if (!is_encrypted && platform_info.is_unified_media_pipeline_enabled) |
| return true; |
| |
| if (!platform_info.has_platform_vp9_decoder) return false; |
| |
| // Encrypted content is demuxed so the container is irrelevant. |
| if (is_encrypted) return true; |
| |
| // MediaPlayer only supports VP9 in WebM. |
| return mime_type_lower_case == "video/webm"; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool MimeUtil::StringToCodec(const std::string& mime_type_lower_case, |
| const std::string& codec_id, Codec* codec, |
| bool* is_ambiguous, VideoCodecProfile* out_profile, |
| uint8_t* out_level, bool is_encrypted) const { |
| DCHECK(out_profile); |
| DCHECK(out_level); |
| *out_profile = VIDEO_CODEC_PROFILE_UNKNOWN; |
| *out_level = 0; |
| |
| StringToCodecMappings::const_iterator itr = |
| string_to_codec_map_.find(codec_id); |
| if (itr != string_to_codec_map_.end()) { |
| *codec = itr->second.codec; |
| *is_ambiguous = itr->second.is_ambiguous; |
| return true; |
| } |
| |
| // If |codec_id| is not in |string_to_codec_map_|, then we assume that it is |
| // either H.264 or HEVC/H.265 codec ID because currently those are the only |
| // ones that are not added to the |string_to_codec_map_| and require parsing. |
| if (ParseAVCCodecId(codec_id, out_profile, out_level)) { |
| *codec = MimeUtil::H264; |
| switch (*out_profile) { |
| // HIGH10PROFILE is supported through fallback to the ffmpeg decoder |
| // which is not available on Android, or if FFMPEG is not used. |
| #if !defined(MEDIA_DISABLE_FFMPEG) && !defined(OS_ANDROID) |
| case H264PROFILE_HIGH10PROFILE: |
| if (is_encrypted) { |
| // FFmpeg is not generally used for encrypted videos, so we do not |
| // know whether 10-bit is supported. |
| *is_ambiguous = true; |
| break; |
| } |
| // Fall through. |
| #endif |
| |
| case H264PROFILE_BASELINE: |
| case H264PROFILE_MAIN: |
| case H264PROFILE_HIGH: |
| *is_ambiguous = !IsValidH264Level(*out_level); |
| break; |
| default: |
| *is_ambiguous = true; |
| } |
| return true; |
| } |
| |
| if (ParseVp9CodecID(mime_type_lower_case, codec_id, out_profile)) { |
| *codec = MimeUtil::VP9; |
| *out_level = 1; |
| switch (*out_profile) { |
| case VP9PROFILE_PROFILE0: |
| // Profile 0 should always be supported if VP9 is supported. |
| *is_ambiguous = false; |
| break; |
| default: |
| // We don't know if the underlying platform supports these profiles. |
| // Need to add platform level querying to get supported profiles |
| // (crbug/604566). |
| *is_ambiguous = true; |
| } |
| return true; |
| } |
| |
| if (ParseHEVCCodecId(codec_id, out_profile, out_level)) { |
| *codec = MimeUtil::HEVC; |
| *is_ambiguous = false; |
| return true; |
| } |
| |
| DVLOG(4) << __func__ << ": Unrecognized codec id " << codec_id; |
| return false; |
| } |
| |
| bool MimeUtil::IsCodecSupported(Codec codec, |
| const std::string& mime_type_lower_case, |
| bool is_encrypted) const { |
| DCHECK_NE(codec, INVALID_CODEC); |
| |
| #if defined(OS_ANDROID) |
| if (!IsCodecSupportedOnPlatform(codec, mime_type_lower_case, is_encrypted, |
| platform_info_)) { |
| return false; |
| } |
| #endif |
| |
| return allow_proprietary_codecs_ || !IsCodecProprietary(codec); |
| } |
| |
| bool MimeUtil::IsCodecProprietary(Codec codec) const { |
| switch (codec) { |
| case INVALID_CODEC: |
| case AC3: |
| case EAC3: |
| case MP3: |
| case MPEG2_AAC: |
| case MPEG4_AAC: |
| case H264: |
| case HEVC: |
| return true; |
| |
| case PCM: |
| case VORBIS: |
| case OPUS: |
| case VP8: |
| case VP9: |
| case THEORA: |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool MimeUtil::GetDefaultCodecLowerCase(const std::string& mime_type_lower_case, |
| Codec* default_codec) const { |
| if (mime_type_lower_case == "audio/mpeg" || |
| mime_type_lower_case == "audio/mp3" || |
| mime_type_lower_case == "audio/x-mp3") { |
| *default_codec = MimeUtil::MP3; |
| return true; |
| } |
| |
| if (mime_type_lower_case == "audio/aac") { |
| *default_codec = MimeUtil::MPEG4_AAC; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool MimeUtil::IsDefaultCodecSupportedLowerCase( |
| const std::string& mime_type_lower_case, bool is_encrypted) const { |
| Codec default_codec = INVALID_CODEC; |
| if (!GetDefaultCodecLowerCase(mime_type_lower_case, &default_codec)) |
| return false; |
| return IsCodecSupported(default_codec, mime_type_lower_case, is_encrypted); |
| } |
| |
| } // namespace internal |
| } // namespace media |
| } // namespace cobalt |