| 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
|
|
|