diff --git a/content/browser/media/media_canplaytype_browsertest.cc b/content/browser/media/media_canplaytype_browsertest.cc index 8f064df1f9f6..504863b65429 100644 --- a/content/browser/media/media_canplaytype_browsertest.cc +++ b/content/browser/media/media_canplaytype_browsertest.cc @@ -402,13 +402,20 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_mp4) { EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc3, mp4a.40\"'")); EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc1, avc3\"'")); - EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc1.4D401E\"'")); - EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc3.64001F\"'")); - EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"mp4a.40.2\"'")); + EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc1.42E01E\"'")); + EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc3.42E01E\"'")); + EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp4a.40.2\"'")); + EXPECT_EQ(kPropProbably, + CanPlay("'video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"'")); + EXPECT_EQ(kPropProbably, + CanPlay("'video/mp4; codecs=\"avc3.42E01E, mp4a.40.5\"'")); + + EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc1, mp4a.40.2\"'")); + EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc3, mp4a.40.2\"'")); EXPECT_EQ(kPropMaybe, - CanPlay("'video/mp4; codecs=\"avc1.4D401E, mp4a.40.2\"'")); + CanPlay("'video/mp4; codecs=\"avc1.42E01E, mp4a.40\"'")); EXPECT_EQ(kPropMaybe, - CanPlay("'video/mp4; codecs=\"avc3.64001F, mp4a.40.5\"'")); + CanPlay("'video/mp4; codecs=\"avc3.42E01E, mp4a.40\"'")); TestMPEGUnacceptableCombinations("video/mp4"); @@ -421,19 +428,26 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_mp4) { EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"avc3, mp4a.40\"'")); EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"avc1, avc3\"'")); - EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"avc1.4D401E\"'")); - EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"avc3.64001F\"'")); - EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"mp4a.40.2\"'")); + EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc1.42E01E\"'")); + EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc3.42E01E\"'")); + EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"mp4a.40.2\"'")); + EXPECT_EQ(kPropProbably, + CanPlay("'video/x-m4v; codecs=\"avc1.42E01E, mp4a.40.2\"'")); + EXPECT_EQ(kPropProbably, + CanPlay("'video/x-m4v; codecs=\"avc3.42E01E, mp4a.40.5\"'")); + + EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"avc1, mp4a.40.2\"'")); + EXPECT_EQ(kPropMaybe, CanPlay("'video/x-m4v; codecs=\"avc3, mp4a.40.2\"'")); EXPECT_EQ(kPropMaybe, - CanPlay("'video/x-m4v; codecs=\"avc1.4D401E, mp4a.40.2\"'")); + CanPlay("'video/x-m4v; codecs=\"avc1.42E01E, mp4a.40\"'")); EXPECT_EQ(kPropMaybe, - CanPlay("'video/x-m4v; codecs=\"avc3.64001F, mp4a.40.5\"'")); + CanPlay("'video/x-m4v; codecs=\"avc3.42E01E, mp4a.40\"'")); TestMPEGUnacceptableCombinations("video/x-m4v"); EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4'")); EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"mp4a.40\"'")); - EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4; codecs=\"mp4a.40.2\"'")); + EXPECT_EQ(kPropProbably, CanPlay("'audio/mp4; codecs=\"mp4a.40.2\"'")); EXPECT_EQ(kNot, CanPlay("'audio/mp4; codecs=\"avc1\"'")); EXPECT_EQ(kNot, CanPlay("'audio/mp4; codecs=\"avc3\"'")); @@ -447,7 +461,7 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_mp4) { EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a'")); EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"mp4a.40\"'")); - EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a; codecs=\"mp4a.40.2\"'")); + EXPECT_EQ(kPropProbably, CanPlay("'audio/x-m4a; codecs=\"mp4a.40.2\"'")); EXPECT_EQ(kNot, CanPlay("'audio/x-m4a; codecs=\"avc1\"'")); EXPECT_EQ(kNot, CanPlay("'audio/x-m4a; codecs=\"avc3\"'")); @@ -462,54 +476,80 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_mp4) { IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_HLS) { // HLS are supported only on Android IceCreamSandwich and above (API level 14) - std::string canPlayHLS = kNot; + std::string probablyCanPlayHLS = kNot; + std::string maybeCanPlayHLS = kNot; #if defined(OS_ANDROID) - if (base::android::BuildInfo::GetInstance()->sdk_int() > 13) - canPlayHLS = kMaybe; + if (base::android::BuildInfo::GetInstance()->sdk_int() > 13) { + probablyCanPlayHLS = kProbably; + maybeCanPlayHLS = kMaybe; + } #endif - EXPECT_EQ(canPlayHLS, CanPlay("'application/x-mpegurl'")); - - EXPECT_EQ(canPlayHLS, CanPlay("'application/x-mpegurl; codecs=\"avc1\"'")); - EXPECT_EQ(canPlayHLS, CanPlay("'application/x-mpegurl; codecs=\"avc3\"'")); - EXPECT_EQ(canPlayHLS, CanPlay("'application/x-mpegurl; codecs=\"mp4a.40\"'")); - EXPECT_EQ(canPlayHLS, + EXPECT_EQ(maybeCanPlayHLS, CanPlay("'application/x-mpegurl'")); + + EXPECT_EQ(maybeCanPlayHLS, + CanPlay("'application/x-mpegurl; codecs=\"avc1\"'")); + EXPECT_EQ(maybeCanPlayHLS, + CanPlay("'application/x-mpegurl; codecs=\"avc3\"'")); + EXPECT_EQ(maybeCanPlayHLS, + CanPlay("'application/x-mpegurl; codecs=\"mp4a.40\"'")); + EXPECT_EQ(maybeCanPlayHLS, CanPlay("'application/x-mpegurl; codecs=\"avc1, mp4a.40\"'")); - EXPECT_EQ(canPlayHLS, + EXPECT_EQ(maybeCanPlayHLS, CanPlay("'application/x-mpegurl; codecs=\"avc3, mp4a.40\"'")); - EXPECT_EQ(canPlayHLS, - CanPlay("'application/x-mpegurl; codecs=\"avc1.4D401E\"'")); - EXPECT_EQ(canPlayHLS, - CanPlay("'application/x-mpegurl; codecs=\"avc3.64001F\"'")); - EXPECT_EQ(canPlayHLS, + EXPECT_EQ(probablyCanPlayHLS, + CanPlay("'application/x-mpegurl; codecs=\"avc1.42E01E\"'")); + EXPECT_EQ(probablyCanPlayHLS, + CanPlay("'application/x-mpegurl; codecs=\"avc3.42E01E\"'")); + EXPECT_EQ(probablyCanPlayHLS, CanPlay("'application/x-mpegurl; codecs=\"mp4a.40.2\"'")); - EXPECT_EQ(canPlayHLS, - CanPlay("'application/x-mpegurl; codecs=\"avc1.4D401E, mp4a.40.2\"'")); - EXPECT_EQ(canPlayHLS, - CanPlay("'application/x-mpegurl; codecs=\"avc3.64001F, mp4a.40.5\"'")); + EXPECT_EQ(probablyCanPlayHLS, + CanPlay("'application/x-mpegurl; codecs=\"avc1.42E01E, mp4a.40.2\"'")); + EXPECT_EQ(probablyCanPlayHLS, + CanPlay("'application/x-mpegurl; codecs=\"avc3.42E01E, mp4a.40.5\"'")); + + EXPECT_EQ(maybeCanPlayHLS, + CanPlay("'application/x-mpegurl; codecs=\"avc1, mp4a.40.2\"'")); + EXPECT_EQ(maybeCanPlayHLS, + CanPlay("'application/x-mpegurl; codecs=\"avc3, mp4a.40.2\"'")); + EXPECT_EQ(maybeCanPlayHLS, + CanPlay("'application/x-mpegurl; codecs=\"avc1.42E01E, mp4a.40\"'")); + EXPECT_EQ(maybeCanPlayHLS, + CanPlay("'application/x-mpegurl; codecs=\"avc3.42E01E, mp4a.40\"'")); TestMPEGUnacceptableCombinations("application/x-mpegurl"); - EXPECT_EQ(canPlayHLS, CanPlay("'application/vnd.apple.mpegurl'")); + EXPECT_EQ(maybeCanPlayHLS, CanPlay("'application/vnd.apple.mpegurl'")); - EXPECT_EQ(canPlayHLS, + EXPECT_EQ(maybeCanPlayHLS, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1\"'")); - EXPECT_EQ(canPlayHLS, + EXPECT_EQ(maybeCanPlayHLS, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3\"'")); - EXPECT_EQ(canPlayHLS, + EXPECT_EQ(maybeCanPlayHLS, CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.40\"'")); - EXPECT_EQ(canPlayHLS, + EXPECT_EQ(maybeCanPlayHLS, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1, mp4a.40\"'")); - EXPECT_EQ(canPlayHLS, + EXPECT_EQ(maybeCanPlayHLS, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3, mp4a.40\"'")); - EXPECT_EQ(canPlayHLS, - CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1.4D401E\"'")); - EXPECT_EQ(canPlayHLS, - CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3.64001F\"'")); - EXPECT_EQ(canPlayHLS, + EXPECT_EQ(probablyCanPlayHLS, + CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1.42E01E\"'")); + EXPECT_EQ(probablyCanPlayHLS, + CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3.42E01E\"'")); + EXPECT_EQ(probablyCanPlayHLS, CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.40.2\"'")); + EXPECT_EQ(maybeCanPlayHLS, + CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1, mp4a.40.2\"'")); + EXPECT_EQ(maybeCanPlayHLS, + CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3, mp4a.40.2\"'")); + EXPECT_EQ(maybeCanPlayHLS, + CanPlay("'application/vnd.apple.mpegurl; " + "codecs=\"avc1.42E01E, mp4a.40\"'")); + EXPECT_EQ(maybeCanPlayHLS, + CanPlay("'application/vnd.apple.mpegurl; " + "codecs=\"avc3.42E01E, mp4a.40\"'")); + TestMPEGUnacceptableCombinations("application/vnd.apple.mpegurl"); } diff --git a/net/base/mime_util.cc b/net/base/mime_util.cc index b1bd3ab27570..0e799a64ccd3 100644 --- a/net/base/mime_util.cc +++ b/net/base/mime_util.cc @@ -11,6 +11,7 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -50,6 +51,25 @@ namespace net { // Singleton utility class for mime types. class MimeUtil : public PlatformMimeUtil { public: + enum Codec { + INVALID_CODEC, + PCM, + MP3, + MPEG2_AAC_LC, + MPEG2_AAC_MAIN, + MPEG2_AAC_SSR, + MPEG4_AAC_LC, + MPEG4_AAC_SBRv1, + VORBIS, + OPUS, + H264_BASELINE, + H264_MAIN, + H264_HIGH, + VP8, + VP9, + THEORA + }; + bool GetMimeTypeFromExtension(const base::FilePath::StringType& ext, std::string* mime_type) const; @@ -93,23 +113,27 @@ class MimeUtil : public PlatformMimeUtil { friend struct base::DefaultLazyInstanceTraits; typedef base::hash_set MimeMappings; - typedef std::map StrictMappings; - typedef std::vector MimeExpressionMappings; - typedef std::map - StrictExpressionMappings; + typedef base::hash_set CodecSet; + typedef std::map StrictMappings; + struct CodecEntry { + CodecEntry() : codec(INVALID_CODEC), is_ambiguous(true) {} + CodecEntry(Codec c, bool ambiguous) : codec(c), is_ambiguous(ambiguous) {} + Codec codec; + bool is_ambiguous; + }; + typedef std::map StringToCodecMappings; MimeUtil(); - // Returns true if |codecs| is nonempty and all the items in it are present in - // |supported_codecs|. - static bool AreSupportedCodecs(const MimeMappings& supported_codecs, - const std::vector& codecs); - // Returns true is |codecs| is nonempty and all the items in it match with the - // codecs expression in |supported_codecs|. - static bool AreSupportedCodecsWithProfile( - const MimeExpressionMappings& supported_codecs, - const std::vector& codecs); + // Returns IsSupported if all codec IDs in |codecs| are unambiguous + // and are supported by the platform. MayBeSupported is returned if + // at least one codec ID in |codecs| is ambiguous but all the codecs + // are supported by the platform. IsNotSupported is returned if at + // least one codec ID is not supported by the platform. + SupportsType AreSupportedCodecs( + const CodecSet& supported_codecs, + const std::vector& codecs) const; // For faster lookup, keep hash sets. void InitializeMimeTypeMaps(); @@ -118,18 +142,51 @@ class MimeUtil : public PlatformMimeUtil { bool include_platform_types, std::string* mime_type) const; + // Converts a codec ID into an Codec enum value and indicates + // whether the conversion was ambiguous. + // Returns true if this method was able to map |codec_id| to a specific + // Codec enum value. |codec| and |is_ambiguous| are only valid if true + // is returned. Otherwise their value is undefined after the call. + // |is_ambiguous| is true if |codec_id| did not have enough information to + // unambiguously determine the proper Codec enum value. If |is_ambiguous| + // is true |codec| contains the best guess for the intended Codec enum value. + bool StringToCodec(const std::string& codec_id, + Codec* codec, + bool* is_ambiguous) const; + + // Returns true if |codec| is supported by the platform. + // Note: This method will return false if the platform supports proprietary + // codecs but |allow_proprietary_codecs_| is set to false. + bool IsCodecSupported(Codec codec) const; + + // Returns true if |codec| refers to a proprietary codec. + bool IsCodecProprietary(Codec codec) const; + + // Returns true and sets |*default_codec| if |mime_type| has a + // default codec associated with it. + // Returns false otherwise and the value of |*default_codec| is undefined. + bool GetDefaultCodec(const std::string& mime_type, + Codec* default_codec) const; + + // Returns true if |mime_type| has a default codec associated with it + // and IsCodecSupported() returns true for that particular codec. + bool IsDefaultCodecSupported(const std::string& mime_type) const; + MimeMappings image_map_; MimeMappings media_map_; MimeMappings non_image_map_; MimeMappings unsupported_text_map_; MimeMappings javascript_map_; - MimeMappings codecs_map_; // A map of mime_types and hash map of the supported codecs for the mime_type. StrictMappings strict_format_map_; - // A map of MP4 mime_types which expect codecs with profile parameter and - // vector of supported codecs expressions for the mime_type. - StrictExpressionMappings strict_mp4_format_map_; + + // Keeps track of whether proprietary codec support should be + // advertised to callers. + bool allow_proprietary_codecs_; + + // Lookup table for string compare based string -> Codec mappings. + StringToCodecMappings string_to_codec_map_; }; // class MimeUtil // This variable is Leaky because we need to access it from WorkerPool threads. @@ -323,32 +380,6 @@ static const char* const proprietary_media_types[] = { "audio/mpeg", }; -// List of supported codecs when passed in with . -// This set of codecs is supported by all variations of Chromium. -// -// Refer to http://wiki.whatwg.org/wiki/Video_type_parameters#Browser_Support -// for more information. -// -// The codecs for WAV are integers as defined in Appendix A of RFC2361: -// http://tools.ietf.org/html/rfc2361 -static const char* const common_media_codecs[] = { -#if !defined(OS_ANDROID) // Android doesn't support Ogg Theora. - "theora", -#endif - "opus", - "vorbis", - "vp8", - "vp9", - "1" // WAVE_FORMAT_PCM. -}; - -// List of proprietary codecs only supported by Google Chrome. -static const char* const proprietary_media_codecs[] = { - "avc1", - "avc3", - "mp4a" -}; - // Note: // - does not include javascript types list (see supported_javascript_types) // - does not include types starting with "text/" (see @@ -430,23 +461,45 @@ static const char* const supported_javascript_types[] = { }; #if defined(OS_ANDROID) -static bool IsCodecSupportedOnAndroid(const std::string& codec) { - // Theora is not supported in Android - if (!codec.compare("theora")) - return false; +static bool IsCodecSupportedOnAndroid(MimeUtil::Codec codec) { + switch (codec) { + case MimeUtil::INVALID_CODEC: + return false; - // VP9 is supported only in KitKat+ (API Level 19). - if ((!codec.compare("vp9") || !codec.compare("vp9.0")) && - base::android::BuildInfo::GetInstance()->sdk_int() < 19) { - return false; - } + case MimeUtil::PCM: + case MimeUtil::MP3: + case MimeUtil::MPEG4_AAC_LC: + case MimeUtil::MPEG4_AAC_SBRv1: + case MimeUtil::H264_BASELINE: + case MimeUtil::VP8: + case MimeUtil::VORBIS: + return true; - // TODO(vigneshv): Change this similar to the VP9 check once Opus is - // supported on Android (http://crbug.com/318436). - if (!codec.compare("opus")) { - return false; + case MimeUtil::MPEG2_AAC_LC: + case MimeUtil::MPEG2_AAC_MAIN: + case MimeUtil::MPEG2_AAC_SSR: + // MPEG-2 variants of AAC are not supported on Android. + return false; + + case MimeUtil::H264_MAIN: + case MimeUtil::H264_HIGH: + // H.264 Main and High profiles are not supported on Android. + return false; + + case MimeUtil::VP9: + // VP9 is supported only in KitKat+ (API Level 19). + return base::android::BuildInfo::GetInstance()->sdk_int() >= 19; + + case MimeUtil::OPUS: + // TODO(vigneshv): Change this similar to the VP9 check once Opus is + // supported on Android (http://crbug.com/318436). + return false; + + case MimeUtil::THEORA: + return false; } - return true; + + return false; } static bool IsMimeTypeSupportedOnAndroid(const std::string& mimeType) { @@ -465,6 +518,24 @@ struct MediaFormatStrict { const char* codecs_list; }; +// Following is the list of RFC 6381 compliant codecs: +// 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 +// mp4a.6B - MPEG-1 audio +// mp4a.40.2 - MPEG-4 AAC LC +// mp4a.40.5 - MPEG-4 AAC SBRv1 +// +// avc1.42E0xx - H.264 Baseline +// avc1.4D40xx - H.264 Main +// avc1.6400xx - H.264 High +static const char kMP4AudioCodecsExpression[] = + "mp4a.66,mp4a.67,mp4a.68,mp4a.69,mp4a.6B,mp4a.40.2,mp4a.40.5"; +static const char kMP4VideoCodecsExpression[] = + "avc1.42E00A,avc1.4D400A,avc1.64000A," \ + "mp4a.66,mp4a.67,mp4a.68,mp4a.69,mp4a.6B,mp4a.40.2,mp4a.40.5"; + static const MediaFormatStrict format_codec_mappings[] = { { "video/webm", "opus,vorbis,vp8,vp8.0,vp9,vp9.0" }, { "audio/webm", "opus,vorbis" }, @@ -473,105 +544,82 @@ static const MediaFormatStrict format_codec_mappings[] = { { "video/ogg", "opus,theora,vorbis" }, { "audio/ogg", "opus,vorbis" }, { "application/ogg", "opus,theora,vorbis" }, - { "audio/mpeg", ",mp3" }, // Note: The comma before the 'mp3'results in an - // empty string codec ID and indicates - // a missing codecs= parameter is also valid. - // The presense of 'mp3' is not RFC compliant, - // but is common in the wild so it is a defacto - // standard. + { "audio/mpeg", "mp3" }, { "audio/mp3", "" }, - { "audio/x-mp3", "" } + { "audio/x-mp3", "" }, + { "audio/mp4", kMP4AudioCodecsExpression }, + { "audio/x-m4a", kMP4AudioCodecsExpression }, + { "video/mp4", kMP4VideoCodecsExpression }, + { "video/x-m4v", kMP4VideoCodecsExpression }, + { "application/x-mpegurl", kMP4VideoCodecsExpression }, + { "application/vnd.apple.mpegurl", kMP4VideoCodecsExpression } }; -// Following is the list of RFC 6381 compliant codecs: -// mp4a.6B - MPEG-1 audio -// mp4a.69 - MPEG-2 extension to MPEG-1 -// mp4a.67 - MPEG-2 AAC -// mp4a.40.2 - MPEG-4 AAC -// mp4a.40.5 - MPEG-4 HE-AAC -// -// avc1.42E0xx - H.264 Baseline -// avc1.4D40xx - H.264 Main -// avc1.6400xx - H.264 High +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. // -// Additionally, several non-RFC compliant codecs are allowed, due to their -// existing use on web. -// mp4a.40 -// avc1.xxxxxx -// avc3.xxxxxx -// mp4a.6x -static const char kProprietaryAudioCodecsExpression[] = - "mp4a.6?,mp4a.40,mp4a.40.?"; -static const char kProprietaryCodecsExpression[] = - "avc1,avc3,avc1.??????,avc3.??????,mp4a.6?,mp4a.40,mp4a.40.?"; - -static const MediaFormatStrict format_mp4_codec_mappings[] = { - { "audio/mp4", kProprietaryAudioCodecsExpression }, - { "audio/x-m4a", kProprietaryAudioCodecsExpression }, - { "video/mp4", kProprietaryCodecsExpression }, - { "video/x-m4v", kProprietaryCodecsExpression }, - { "application/x-mpegurl", kProprietaryCodecsExpression }, - { "application/vnd.apple.mpegurl", kProprietaryCodecsExpression } +// The "mp4a" strings come from RFC 6381. +static const CodecIDMappings kUnambiguousCodecIDs[] = { + { "1", MimeUtil::PCM }, // We only allow this for WAV so it isn't ambiguous. + { "mp3", MimeUtil::MP3 }, + { "mp4a.66", MimeUtil::MPEG2_AAC_MAIN }, + { "mp4a.67", MimeUtil::MPEG2_AAC_LC }, + { "mp4a.68", MimeUtil::MPEG2_AAC_SSR }, + { "mp4a.69", MimeUtil::MP3 }, + { "mp4a.6B", MimeUtil::MP3 }, + { "mp4a.40.2", MimeUtil::MPEG4_AAC_LC }, + { "mp4a.40.5", MimeUtil::MPEG4_AAC_SBRv1 }, + { "vorbis", MimeUtil::VORBIS }, + { "opus", MimeUtil::OPUS }, + { "vp8", MimeUtil::VP8 }, + { "vp8.0", MimeUtil::VP8 }, + { "vp9", MimeUtil::VP9 }, + { "vp9.0", MimeUtil::VP9 }, + { "theora", MimeUtil::THEORA } }; -MimeUtil::MimeUtil() { +// 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 kAmbiguousCodecIDs[] = { + { "mp4a.40", MimeUtil::MPEG4_AAC_LC }, + { "avc1", MimeUtil::H264_BASELINE }, + { "avc3", MimeUtil::H264_BASELINE }, +}; + +MimeUtil::MimeUtil() : allow_proprietary_codecs_(false) { InitializeMimeTypeMaps(); } -// static -bool MimeUtil::AreSupportedCodecs(const MimeMappings& supported_codecs, - const std::vector& codecs) { - if (supported_codecs.empty()) - return codecs.empty(); - - // If no codecs are specified in the mimetype, check to see if a missing - // codecs parameter is allowed. - if (codecs.empty()) - return supported_codecs.find(std::string()) != supported_codecs.end(); +SupportsType MimeUtil::AreSupportedCodecs( + const CodecSet& supported_codecs, + const std::vector& codecs) const { + DCHECK(!supported_codecs.empty()); + DCHECK(!codecs.empty()); + SupportsType result = IsSupported; for (size_t i = 0; i < codecs.size(); ++i) { - if (codecs[i].empty() || - supported_codecs.find(codecs[i]) == supported_codecs.end()) { - return false; + bool is_ambiguous = true; + Codec codec = INVALID_CODEC; + if (!StringToCodec(codecs[i], &codec, &is_ambiguous)) + return IsNotSupported; + + if (!IsCodecSupported(codec) || + supported_codecs.find(codec) == supported_codecs.end()) { + return IsNotSupported; } - } - return true; -} - -// Checks all the codecs present in the |codecs| against the entries in -// |supported_codecs|. Returns true only if |codecs| is non-empty and all the -// codecs match |supported_codecs| expressions. -bool MimeUtil::AreSupportedCodecsWithProfile( - const MimeExpressionMappings& supported_codecs, - const std::vector& codecs) { - DCHECK(!supported_codecs.empty()); - for (size_t i = 0; i < codecs.size(); ++i) { - bool codec_matched = false; - for (size_t j = 0; j < supported_codecs.size(); ++j) { - if (!MatchPattern(base::StringPiece(codecs[i]), - base::StringPiece(supported_codecs[j]))) { - continue; - } - // If suffix exists, check whether it is hexadecimal. - for (size_t wildcard_pos = supported_codecs[j].find('?'); - wildcard_pos != std::string::npos && - wildcard_pos < supported_codecs[j].length(); - wildcard_pos = supported_codecs[j].find('?', wildcard_pos + 1)) { - // Don't enforce case sensitivity, even though it's called for, as it - // would break some websites. - if (wildcard_pos >= codecs[i].length() || - !IsHexDigit(codecs[i].at(wildcard_pos))) { - return false; - } - } - codec_matched = true; - break; - } - if (!codec_matched) - return false; + if (is_ambiguous) + result = MayBeSupported; } - return !codecs.empty(); + + return result; } void MimeUtil::InitializeMimeTypeMaps() { @@ -595,6 +643,8 @@ void MimeUtil::InitializeMimeTypeMaps() { non_image_map_.insert(common_media_types[i]); } #if defined(USE_PROPRIETARY_CODECS) + allow_proprietary_codecs_ = true; + for (size_t i = 0; i < arraysize(proprietary_media_types); ++i) non_image_map_.insert(proprietary_media_types[i]); #endif @@ -615,17 +665,15 @@ void MimeUtil::InitializeMimeTypeMaps() { for (size_t i = 0; i < arraysize(supported_javascript_types); ++i) javascript_map_.insert(supported_javascript_types[i]); - for (size_t i = 0; i < arraysize(common_media_codecs); ++i) { -#if defined(OS_ANDROID) - if (!IsCodecSupportedOnAndroid(common_media_codecs[i])) - continue; -#endif - codecs_map_.insert(common_media_codecs[i]); + for (size_t i = 0; i < arraysize(kUnambiguousCodecIDs); ++i) { + string_to_codec_map_[kUnambiguousCodecIDs[i].codec_id] = + CodecEntry(kUnambiguousCodecIDs[i].codec, false); + } + + for (size_t i = 0; i < arraysize(kAmbiguousCodecIDs); ++i) { + string_to_codec_map_[kAmbiguousCodecIDs[i].codec_id] = + CodecEntry(kAmbiguousCodecIDs[i].codec, true); } -#if defined(USE_PROPRIETARY_CODECS) - for (size_t i = 0; i < arraysize(proprietary_media_codecs); ++i) - codecs_map_.insert(proprietary_media_codecs[i]); -#endif // Initialize the strict supported media types. for (size_t i = 0; i < arraysize(format_codec_mappings); ++i) { @@ -634,25 +682,16 @@ void MimeUtil::InitializeMimeTypeMaps() { &mime_type_codecs, false); - MimeMappings codecs; + CodecSet codecs; for (size_t j = 0; j < mime_type_codecs.size(); ++j) { -#if defined(OS_ANDROID) - if (!IsCodecSupportedOnAndroid(mime_type_codecs[j])) - continue; -#endif - codecs.insert(mime_type_codecs[j]); + Codec codec = INVALID_CODEC; + bool is_ambiguous = true; + CHECK(StringToCodec(mime_type_codecs[j], &codec, &is_ambiguous)); + DCHECK(!is_ambiguous); + codecs.insert(codec); } - strict_format_map_[format_codec_mappings[i].mime_type] = codecs; - } - for (size_t i = 0; i < arraysize(format_mp4_codec_mappings); ++i) { - std::vector mime_type_codecs; - ParseCodecString( - format_mp4_codec_mappings[i].codecs_list, &mime_type_codecs, false); - MimeExpressionMappings codecs; - for (size_t j = 0; j < mime_type_codecs.size(); ++j) - codecs.push_back(mime_type_codecs[j]); - strict_mp4_format_map_[format_mp4_codec_mappings[i].mime_type] = codecs; + strict_format_map_[format_codec_mappings[i].mime_type] = codecs; } } @@ -810,7 +849,15 @@ bool MimeUtil::IsValidTopLevelMimeType(const std::string& type_string) const { bool MimeUtil::AreSupportedMediaCodecs( const std::vector& codecs) const { - return AreSupportedCodecs(codecs_map_, codecs); + for (size_t i = 0; i < codecs.size(); ++i) { + Codec codec = INVALID_CODEC; + bool is_ambiguous = true; + if (!StringToCodec(codecs[i], &codec, &is_ambiguous) || + !IsCodecSupported(codec)) { + return false; + } + } + return true; } void MimeUtil::ParseCodecString(const std::string& codecs, @@ -834,10 +881,7 @@ void MimeUtil::ParseCodecString(const std::string& codecs, } bool MimeUtil::IsStrictMediaMimeType(const std::string& mime_type) const { - if (strict_format_map_.find(mime_type) == strict_format_map_.end() && - strict_mp4_format_map_.find(mime_type) == strict_mp4_format_map_.end()) - return false; - return true; + return strict_format_map_.find(mime_type) != strict_format_map_.end(); } SupportsType MimeUtil::IsSupportedStrictMediaMimeType( @@ -845,22 +889,28 @@ SupportsType MimeUtil::IsSupportedStrictMediaMimeType( const std::vector& codecs) const { StrictMappings::const_iterator it_strict_map = strict_format_map_.find(mime_type); - if ((it_strict_map != strict_format_map_.end()) && - AreSupportedCodecs(it_strict_map->second, codecs)) { - return IsSupported; - } + if (it_strict_map == strict_format_map_.end()) + return codecs.empty() ? MayBeSupported : IsNotSupported; - StrictExpressionMappings::const_iterator it_expression_map = - strict_mp4_format_map_.find(mime_type); - if ((it_expression_map != strict_mp4_format_map_.end()) && - AreSupportedCodecsWithProfile(it_expression_map->second, codecs)) { - return MayBeSupported; + if (it_strict_map->second.empty()) { + // We get here if the mimetype does not expect a codecs parameter. + return (codecs.empty() && IsDefaultCodecSupported(mime_type)) ? + IsSupported : IsNotSupported; } - if (codecs.empty()) - return MayBeSupported; + if (codecs.empty()) { + // We get here if the mimetype expects to get a codecs parameter, + // but didn't get one. If |mime_type| 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, &default_codec)) + return MayBeSupported; + + return IsCodecSupported(default_codec) ? IsSupported : IsNotSupported; + } - return IsNotSupported; + return AreSupportedCodecs(it_strict_map->second, codecs); } void MimeUtil::RemoveProprietaryMediaTypesAndCodecsForTests() { @@ -868,8 +918,129 @@ void MimeUtil::RemoveProprietaryMediaTypesAndCodecsForTests() { non_image_map_.erase(proprietary_media_types[i]); media_map_.erase(proprietary_media_types[i]); } - for (size_t i = 0; i < arraysize(proprietary_media_codecs); ++i) - codecs_map_.erase(proprietary_media_codecs[i]); + allow_proprietary_codecs_ = false; +} + +static bool IsValidH264Level(const std::string& level_str) { + uint32 level; + if (level_str.size() != 2 || !base::HexStringToUInt(level_str, &level)) + return false; + + // Valid levels taken from Table A-1 in ISO-14496-10. + // Essentially |level_str| is toHex(10 * level). + return ((level >= 10 && level <= 13) || + (level >= 20 && level <= 22) || + (level >= 30 && level <= 32) || + (level >= 40 && level <= 42) || + (level >= 50 && level <= 51)); +} + +// Handle parsing H.264 codec IDs as outlined in RFC 6381 +// avc1.42E0xx - H.264 Baseline +// avc1.4D40xx - H.264 Main +// avc1.6400xx - H.264 High +// +// avc1.xxxxxx & avc3.xxxxxx are considered ambiguous forms that +// are trying to signal H.264 Baseline. +static bool ParseH264CodecID(const std::string& codec_id, + MimeUtil::Codec* codec, + bool* is_ambiguous) { + // Make sure we have avc1.xxxxxx or avc3.xxxxxx + if (codec_id.size() != 11 || + (!StartsWithASCII(codec_id, "avc1.", true) && + !StartsWithASCII(codec_id, "avc3.", true))) { + return false; + } + + std::string profile = StringToUpperASCII(codec_id.substr(5, 4)); + if (profile == "42E0") { + *codec = MimeUtil::H264_BASELINE; + } else if (profile == "4D40") { + *codec = MimeUtil::H264_MAIN; + } else if (profile == "6400") { + *codec = MimeUtil::H264_HIGH; + } else { + *codec = MimeUtil::H264_BASELINE; + *is_ambiguous = true; + return true; + } + + *is_ambiguous = !IsValidH264Level(StringToUpperASCII(codec_id.substr(9))); + return true; +} + +bool MimeUtil::StringToCodec(const std::string& codec_id, + Codec* codec, + bool* is_ambiguous) const { + 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 + // an H.264 codec ID because currently those are the only ones that can't be + // stored in the |string_to_codec_map_| and require parsing. + return ParseH264CodecID(codec_id, codec, is_ambiguous); +} + +bool MimeUtil::IsCodecSupported(Codec codec) const { + DCHECK_NE(codec, INVALID_CODEC); + +#if defined(OS_ANDROID) + if (!IsCodecSupportedOnAndroid(codec)) + return false; +#endif + + return allow_proprietary_codecs_ || !IsCodecProprietary(codec); +} + +bool MimeUtil::IsCodecProprietary(Codec codec) const { + switch (codec) { + case INVALID_CODEC: + case MP3: + case MPEG2_AAC_LC: + case MPEG2_AAC_MAIN: + case MPEG2_AAC_SSR: + case MPEG4_AAC_LC: + case MPEG4_AAC_SBRv1: + case H264_BASELINE: + case H264_MAIN: + case H264_HIGH: + return true; + + case PCM: + case VORBIS: + case OPUS: + case VP8: + case VP9: + case THEORA: + return false; + } + + return true; +} + +bool MimeUtil::GetDefaultCodec(const std::string& mime_type, + Codec* default_codec) const { + if (mime_type == "audio/mpeg" || + mime_type == "audio/mp3" || + mime_type == "audio/x-mp3") { + *default_codec = MimeUtil::MP3; + return true; + } + + return false; +} + + +bool MimeUtil::IsDefaultCodecSupported(const std::string& mime_type) const { + Codec default_codec = Codec::INVALID_CODEC; + if (!GetDefaultCodec(mime_type, &default_codec)) + return false; + return IsCodecSupported(default_codec); } //----------------------------------------------------------------------------