Index: media/base/mime_util.cc |
diff --git a/media/base/mime_util.cc b/media/base/mime_util.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3ed33ca5260852aa98254e3c00d9d5fcec3f8134 |
--- /dev/null |
+++ b/media/base/mime_util.cc |
@@ -0,0 +1,441 @@ |
+// Copyright 2014 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.h" |
+ |
+#include <map> |
+ |
+#include "base/lazy_instance.h" |
+#include "base/strings/string_split.h" |
+#include "base/strings/string_util.h" |
+ |
+#if defined(OS_ANDROID) |
+#include "base/android/build_info.h" |
+#endif |
+ |
+namespace media { |
+ |
+class MimeUtil { |
+ public: |
+ bool IsSupportedMediaMimeType(const std::string& mime_type) const; |
+ |
+ bool AreSupportedMediaCodecs(const std::vector<std::string>& codecs) const; |
+ |
+ void ParseCodecString(const std::string& codecs, |
+ std::vector<std::string>* codecs_out, |
+ bool strip); |
+ |
+ bool IsStrictMediaMimeType(const std::string& mime_type) const; |
+ SupportsType IsSupportedStrictMediaMimeType( |
+ const std::string& mime_type, |
+ const std::vector<std::string>& codecs) const; |
+ |
+ void RemoveProprietaryMediaTypesAndCodecsForTests(); |
+ |
+ private: |
+ friend struct base::DefaultLazyInstanceTraits<MimeUtil>; |
+ |
+ typedef base::hash_set<std::string> MimeMappings; |
+ typedef std::map<std::string, MimeMappings> StrictMappings; |
+ |
+ typedef std::vector<std::string> MimeExpressionMappings; |
+ typedef std::map<std::string, MimeExpressionMappings> |
+ StrictExpressionMappings; |
+ |
+ 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<std::string>& 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<std::string>& codecs); |
+ |
+ // For faster lookup, keep hash sets. |
+ void InitializeMimeTypeMaps(); |
+ |
+ MimeMappings media_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_; |
+}; |
+ |
+// This variable is Leaky because we need to access it from WorkerPool threads. |
+static base::LazyInstance<MimeUtil>::Leaky g_mime_util = |
+ LAZY_INSTANCE_INITIALIZER; |
+ |
+ |
+// A list of media types: http://en.wikipedia.org/wiki/Internet_media_type |
+// A comprehensive mime type list: http://plugindoc.mozdev.org/winmime.php |
+// This set of codecs is supported by all variations of Chromium. |
+static const char* const common_media_types[] = { |
+ // Ogg. |
+ "audio/ogg", |
+ "application/ogg", |
+#if !defined(OS_ANDROID) // Android doesn't support Ogg Theora. |
+ "video/ogg", |
+#endif |
+ |
+ // WebM. |
+ "video/webm", |
+ "audio/webm", |
+ |
+ // Wav. |
+ "audio/wav", |
+ "audio/x-wav", |
+ |
+#if defined(OS_ANDROID) |
+ // HLS. Supported by Android ICS and above. |
+ "application/vnd.apple.mpegurl", |
+ "application/x-mpegurl", |
+#endif |
+}; |
+ |
+// List of proprietary types only supported by Google Chrome. |
+static const char* const proprietary_media_types[] = { |
+ // MPEG-4. |
+ "video/mp4", |
+ "video/x-m4v", |
+ "audio/mp4", |
+ "audio/x-m4a", |
+ |
+ // MP3. |
+ "audio/mp3", |
+ "audio/x-mp3", |
+ "audio/mpeg", |
+}; |
+ |
+// List of supported codecs when passed in with <source type="...">. |
+// 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" |
+}; |
+ |
+#if defined(OS_ANDROID) |
+static bool IsCodecSupportedOnAndroid(const std::string& codec) { |
+ // Theora is not supported in Android |
+ if (!codec.compare("theora")) |
+ 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; |
+ } |
+ |
+ // 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; |
+ } |
+ return true; |
+} |
+ |
+static bool IsMimeTypeSupportedOnAndroid(const std::string& mimeType) { |
+ // HLS codecs are supported in ICS and above (API level 14) |
+ if ((!mimeType.compare("application/vnd.apple.mpegurl") || |
+ !mimeType.compare("application/x-mpegurl")) && |
+ base::android::BuildInfo::GetInstance()->sdk_int() < 14) { |
+ return false; |
+ } |
+ return true; |
+} |
+#endif |
+ |
+struct MediaFormatStrict { |
+ const char* mime_type; |
+ const char* codecs_list; |
+}; |
+ |
+static const MediaFormatStrict format_codec_mappings[] = { |
+ { "video/webm", "opus,vorbis,vp8,vp8.0,vp9,vp9.0" }, |
+ { "audio/webm", "opus,vorbis" }, |
+ { "audio/wav", "1" }, |
+ { "audio/x-wav", "1" }, |
+ { "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/mp3", "" }, |
+ { "audio/x-mp3", "" } |
+}; |
+ |
+// 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 |
+// |
+// 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 } |
+}; |
+ |
+MimeUtil::MimeUtil() { |
+ InitializeMimeTypeMaps(); |
+} |
+ |
+// static |
+bool MimeUtil::AreSupportedCodecs(const MimeMappings& supported_codecs, |
+ const std::vector<std::string>& 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(); |
+ |
+ for (size_t i = 0; i < codecs.size(); ++i) { |
+ if (codecs[i].empty() || |
+ supported_codecs.find(codecs[i]) == supported_codecs.end()) { |
+ return false; |
+ } |
+ } |
+ |
+ 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<std::string>& 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; |
+ } |
+ return !codecs.empty(); |
+} |
+ |
+void MimeUtil::InitializeMimeTypeMaps() { |
+ // Initialize the supported media types. |
+ for (size_t i = 0; i < arraysize(common_media_types); ++i) { |
+#if defined(OS_ANDROID) |
+ if (!IsMimeTypeSupportedOnAndroid(common_media_types[i])) |
+ continue; |
+#endif |
+ media_map_.insert(common_media_types[i]); |
+ } |
+#if defined(USE_PROPRIETARY_CODECS) |
+ for (size_t i = 0; i < arraysize(proprietary_media_types); ++i) |
+ media_map_.insert(proprietary_media_types[i]); |
+#endif |
+ |
+ 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]); |
+ } |
+#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) { |
+ std::vector<std::string> mime_type_codecs; |
+ ParseCodecString(format_codec_mappings[i].codecs_list, |
+ &mime_type_codecs, |
+ false); |
+ |
+ MimeMappings 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]); |
+ } |
+ strict_format_map_[format_codec_mappings[i].mime_type] = codecs; |
+ } |
+ for (size_t i = 0; i < arraysize(format_mp4_codec_mappings); ++i) { |
+ std::vector<std::string> 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; |
+ } |
+} |
+ |
+bool MimeUtil::IsSupportedMediaMimeType(const std::string& mime_type) const { |
+ return media_map_.find(mime_type) != media_map_.end(); |
+} |
+ |
+bool MimeUtil::AreSupportedMediaCodecs( |
+ const std::vector<std::string>& codecs) const { |
+ return AreSupportedCodecs(codecs_map_, codecs); |
+} |
+ |
+void MimeUtil::ParseCodecString(const std::string& codecs, |
+ std::vector<std::string>* codecs_out, |
+ bool strip) { |
+ std::string no_quote_codecs; |
+ base::TrimString(codecs, "\"", &no_quote_codecs); |
+ base::SplitString(no_quote_codecs, ',', codecs_out); |
+ |
+ 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); |
+ } |
+} |
+ |
+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; |
+} |
+ |
+SupportsType MimeUtil::IsSupportedStrictMediaMimeType( |
+ const std::string& mime_type, |
+ const std::vector<std::string>& 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; |
+ } |
+ |
+ 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 (codecs.empty()) |
+ return MayBeSupported; |
+ |
+ return IsNotSupported; |
+} |
+ |
+void MimeUtil::RemoveProprietaryMediaTypesAndCodecsForTests() { |
+ for (size_t i = 0; i < arraysize(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]); |
+} |
+ |
+//---------------------------------------------------------------------------- |
+// Wrappers for the singleton |
+//---------------------------------------------------------------------------- |
+ |
+bool IsSupportedMediaMimeType(const std::string& mime_type) { |
+ return g_mime_util.Get().IsSupportedMediaMimeType(mime_type); |
+} |
+ |
+bool AreSupportedMediaCodecs(const std::vector<std::string>& codecs) { |
+ return g_mime_util.Get().AreSupportedMediaCodecs(codecs); |
+} |
+ |
+bool IsStrictMediaMimeType(const std::string& mime_type) { |
+ return g_mime_util.Get().IsStrictMediaMimeType(mime_type); |
+} |
+ |
+SupportsType IsSupportedStrictMediaMimeType( |
+ const std::string& mime_type, |
+ const std::vector<std::string>& codecs) { |
+ return g_mime_util.Get().IsSupportedStrictMediaMimeType(mime_type, codecs); |
+} |
+ |
+void ParseCodecString(const std::string& codecs, |
+ std::vector<std::string>* codecs_out, |
+ const bool strip) { |
+ g_mime_util.Get().ParseCodecString(codecs, codecs_out, strip); |
+} |
+ |
+void RemoveProprietaryMediaTypesAndCodecsForTests() { |
+ g_mime_util.Get().RemoveProprietaryMediaTypesAndCodecsForTests(); |
+} |
+ |
+} // namespace media |