From 781ab43e9fa29cb43ef7d55755cdd394c7474a7e Mon Sep 17 00:00:00 2001 From: "acolwell@chromium.org" Date: Tue, 29 Jul 2014 23:35:55 +0000 Subject: [PATCH] Refactor net::MimeUtil media code to return "probably" for codecs the platform likely supports. This patch amends the behaviour set for mp4 mime types Now browser returns "probably" for codecs which are confirmed to be shipped with Chrome. CodecPrameter| Before| Now -------------------------------- avc1.42E0xx -| maybe | probably avc1.4D40xx -| maybe | probably (non-Android) avc1.6400xx -| maybe | probably (non-Android) avc3.xxxxxx -| maybe | probably mp4a.6B -----| maybe | probably mp4a.69 -----| maybe | probably mp4a.67 -----| maybe | probably (non-Android) mp4a.40.2 ---| maybe | probably mp4a.40.5 ---| maybe | probably BUG=388317 TEST=MediaCanPlayTypeTest.* Review URL: https://codereview.chromium.org/422573005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@286333 0039d316-1c4b-4281-b951-d872f2087c98 --- .../media/media_canplaytype_browsertest.cc | 124 ++-- net/base/mime_util.cc | 545 ++++++++++++------ 2 files changed, 440 insertions(+), 229 deletions(-) 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); } //----------------------------------------------------------------------------