1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "media/filters/stream_parser_factory.h" 6 7 #include "base/command_line.h" 8 #include "base/metrics/histogram.h" 9 #include "base/strings/string_number_conversions.h" 10 #include "base/strings/string_util.h" 11 #include "media/base/media_log.h" 12 #include "media/base/media_switches.h" 13 #include "media/webm/webm_stream_parser.h" 14 15 #if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS) 16 #include "media/mp4/es_descriptor.h" 17 #include "media/mp4/mp4_stream_parser.h" 18 #endif 19 20 namespace media { 21 22 typedef bool (*CodecIDValidatorFunction)( 23 const std::string& codecs_id, const media::LogCB& log_cb); 24 25 struct CodecInfo { 26 enum Type { 27 UNKNOWN, 28 AUDIO, 29 VIDEO 30 }; 31 enum HistogramTag { 32 HISTOGRAM_UNKNOWN, 33 HISTOGRAM_VP8, 34 HISTOGRAM_VP9, 35 HISTOGRAM_VORBIS, 36 HISTOGRAM_H264, 37 HISTOGRAM_MPEG2AAC, 38 HISTOGRAM_MPEG4AAC, 39 HISTOGRAM_EAC3, 40 HISTOGRAM_MAX // Must be the last entry. 41 }; 42 43 const char* pattern; 44 Type type; 45 CodecIDValidatorFunction validator; 46 HistogramTag tag; 47 }; 48 49 typedef media::StreamParser* (*ParserFactoryFunction)( 50 const std::vector<std::string>& codecs, 51 const media::LogCB& log_cb); 52 53 struct SupportedTypeInfo { 54 const char* type; 55 const ParserFactoryFunction factory_function; 56 const CodecInfo** codecs; 57 }; 58 59 static const CodecInfo kVP8CodecInfo = { "vp8", CodecInfo::VIDEO, NULL, 60 CodecInfo::HISTOGRAM_VP8 }; 61 static const CodecInfo kVP9CodecInfo = { "vp9", CodecInfo::VIDEO, NULL, 62 CodecInfo::HISTOGRAM_VP9 }; 63 static const CodecInfo kVorbisCodecInfo = { "vorbis", CodecInfo::AUDIO, NULL, 64 CodecInfo::HISTOGRAM_VORBIS }; 65 66 static const CodecInfo* kVideoWebMCodecs[] = { 67 &kVP8CodecInfo, 68 #if !defined(OS_ANDROID) 69 // TODO(wonsik): crbug.com/285016 query Android platform for codec 70 // capabilities. 71 &kVP9CodecInfo, 72 #endif 73 &kVorbisCodecInfo, 74 NULL 75 }; 76 77 static const CodecInfo* kAudioWebMCodecs[] = { 78 &kVorbisCodecInfo, 79 NULL 80 }; 81 82 static media::StreamParser* BuildWebMParser( 83 const std::vector<std::string>& codecs, 84 const media::LogCB& log_cb) { 85 return new media::WebMStreamParser(); 86 } 87 88 #if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS) 89 // AAC Object Type IDs that Chrome supports. 90 static const int kAACLCObjectType = 2; 91 static const int kAACSBRObjectType = 5; 92 93 static int GetMP4AudioObjectType(const std::string& codec_id, 94 const media::LogCB& log_cb) { 95 int audio_object_type; 96 std::vector<std::string> tokens; 97 if (Tokenize(codec_id, ".", &tokens) != 3 || 98 tokens[0] != "mp4a" || tokens[1] != "40" || 99 !base::HexStringToInt(tokens[2], &audio_object_type)) { 100 MEDIA_LOG(log_cb) << "Malformed mimetype codec '" << codec_id << "'"; 101 return -1; 102 } 103 104 105 return audio_object_type; 106 } 107 108 bool ValidateMP4ACodecID(const std::string& codec_id, 109 const media::LogCB& log_cb) { 110 int audio_object_type = GetMP4AudioObjectType(codec_id, log_cb); 111 if (audio_object_type == kAACLCObjectType || 112 audio_object_type == kAACSBRObjectType) { 113 return true; 114 } 115 116 MEDIA_LOG(log_cb) << "Unsupported audio object type " 117 << "0x" << std::hex << audio_object_type 118 << " in codec '" << codec_id << "'"; 119 return false; 120 } 121 122 static const CodecInfo kH264CodecInfo = { "avc1.*", CodecInfo::VIDEO, NULL, 123 CodecInfo::HISTOGRAM_H264 }; 124 static const CodecInfo kMPEG4AACCodecInfo = { "mp4a.40.*", CodecInfo::AUDIO, 125 &ValidateMP4ACodecID, 126 CodecInfo::HISTOGRAM_MPEG4AAC }; 127 static const CodecInfo kMPEG2AACLCCodecInfo = { "mp4a.67", CodecInfo::AUDIO, 128 NULL, 129 CodecInfo::HISTOGRAM_MPEG2AAC }; 130 131 #if defined(ENABLE_EAC3_PLAYBACK) 132 static const CodecInfo kEAC3CodecInfo = { "mp4a.a6", CodecInfo::AUDIO, NULL, 133 CodecInfo::HISTOGRAM_EAC3 }; 134 #endif 135 136 static const CodecInfo* kVideoMP4Codecs[] = { 137 &kH264CodecInfo, 138 &kMPEG4AACCodecInfo, 139 &kMPEG2AACLCCodecInfo, 140 NULL 141 }; 142 143 static const CodecInfo* kAudioMP4Codecs[] = { 144 &kMPEG4AACCodecInfo, 145 &kMPEG2AACLCCodecInfo, 146 #if defined(ENABLE_EAC3_PLAYBACK) 147 &kEAC3CodecInfo, 148 #endif 149 NULL 150 }; 151 152 static media::StreamParser* BuildMP4Parser( 153 const std::vector<std::string>& codecs, const media::LogCB& log_cb) { 154 std::set<int> audio_object_types; 155 bool has_sbr = false; 156 #if defined(ENABLE_EAC3_PLAYBACK) 157 bool enable_eac3 = CommandLine::ForCurrentProcess()->HasSwitch( 158 switches::kEnableEac3Playback); 159 #endif 160 for (size_t i = 0; i < codecs.size(); ++i) { 161 std::string codec_id = codecs[i]; 162 if (MatchPattern(codec_id, kMPEG2AACLCCodecInfo.pattern)) { 163 audio_object_types.insert(media::mp4::kISO_13818_7_AAC_LC); 164 } else if (MatchPattern(codec_id, kMPEG4AACCodecInfo.pattern)) { 165 int audio_object_type = GetMP4AudioObjectType(codec_id, log_cb); 166 DCHECK_GT(audio_object_type, 0); 167 168 audio_object_types.insert(media::mp4::kISO_14496_3); 169 170 if (audio_object_type == kAACSBRObjectType) { 171 has_sbr = true; 172 break; 173 } 174 #if defined(ENABLE_EAC3_PLAYBACK) 175 } else if (enable_eac3 && MatchPattern(codec_id, kEAC3CodecInfo.pattern)) { 176 audio_object_types.insert(media::mp4::kEAC3); 177 #endif 178 } 179 } 180 181 return new media::mp4::MP4StreamParser(audio_object_types, has_sbr); 182 } 183 #endif 184 185 static const SupportedTypeInfo kSupportedTypeInfo[] = { 186 { "video/webm", &BuildWebMParser, kVideoWebMCodecs }, 187 { "audio/webm", &BuildWebMParser, kAudioWebMCodecs }, 188 #if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS) 189 { "video/mp4", &BuildMP4Parser, kVideoMP4Codecs }, 190 { "audio/mp4", &BuildMP4Parser, kAudioMP4Codecs }, 191 #endif 192 }; 193 194 // Verify that |codec_info| is supported on this platform. 195 // 196 // Returns true if |codec_info| is a valid audio/video codec and is allowed. 197 // |audio_codecs| has |codec_info|.tag added to its list if |codec_info| is an 198 // audio codec. |audio_codecs| may be NULL, in which case it is not updated. 199 // |video_codecs| has |codec_info|.tag added to its list if |codec_info| is a 200 // video codec. |video_codecs| may be NULL, in which case it is not updated. 201 // 202 // Returns false otherwise, and |audio_codecs| and |video_codecs| not touched. 203 static bool VerifyCodec( 204 const CodecInfo* codec_info, 205 std::vector<CodecInfo::HistogramTag>* audio_codecs, 206 std::vector<CodecInfo::HistogramTag>* video_codecs) { 207 switch (codec_info->type) { 208 case CodecInfo::AUDIO: 209 #if defined(ENABLE_EAC3_PLAYBACK) 210 if (codec_info->tag == CodecInfo::HISTOGRAM_EAC3) { 211 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); 212 if (!cmd_line->HasSwitch(switches::kEnableEac3Playback)) 213 return false; 214 } 215 #endif 216 if (audio_codecs) 217 audio_codecs->push_back(codec_info->tag); 218 return true; 219 case CodecInfo::VIDEO: 220 if (video_codecs) 221 video_codecs->push_back(codec_info->tag); 222 return true; 223 default: 224 // Not audio or video, so skip it. 225 DVLOG(1) << "CodecInfo type of " << codec_info->type 226 << " should not be specified in a SupportedTypes list"; 227 return false; 228 } 229 } 230 231 // Checks to see if the specified |type| and |codecs| list are supported. 232 // 233 // Returns true if |type| and all codecs listed in |codecs| are supported. 234 // |factory_function| contains a function that can build a StreamParser for this 235 // type. Value may be NULL, in which case it is not touched. 236 // |audio_codecs| is updated with the appropriate HistogramTags for matching 237 // audio codecs specified in |codecs|. Value may be NULL, in which case it is 238 // not touched. 239 // |video_codecs| is updated with the appropriate HistogramTags for matching 240 // video codecs specified in |codecs|. Value may be NULL, in which case it is 241 // not touched. 242 // 243 // Returns false otherwise. The values of |factory_function|, |audio_codecs|, 244 // and |video_codecs| are undefined. 245 static bool CheckTypeAndCodecs( 246 const std::string& type, 247 const std::vector<std::string>& codecs, 248 const media::LogCB& log_cb, 249 ParserFactoryFunction* factory_function, 250 std::vector<CodecInfo::HistogramTag>* audio_codecs, 251 std::vector<CodecInfo::HistogramTag>* video_codecs) { 252 DCHECK_GT(codecs.size(), 0u); 253 254 // Search for the SupportedTypeInfo for |type|. 255 for (size_t i = 0; i < arraysize(kSupportedTypeInfo); ++i) { 256 const SupportedTypeInfo& type_info = kSupportedTypeInfo[i]; 257 if (type == type_info.type) { 258 // Make sure all the codecs specified in |codecs| are 259 // in the supported type info. 260 for (size_t j = 0; j < codecs.size(); ++j) { 261 // Search the type info for a match. 262 bool found_codec = false; 263 std::string codec_id = codecs[j]; 264 for (int k = 0; type_info.codecs[k]; ++k) { 265 if (MatchPattern(codec_id, type_info.codecs[k]->pattern) && 266 (!type_info.codecs[k]->validator || 267 type_info.codecs[k]->validator(codec_id, log_cb))) { 268 found_codec = 269 VerifyCodec(type_info.codecs[k], audio_codecs, video_codecs); 270 break; // Since only 1 pattern will match, no need to check others. 271 } 272 } 273 if (!found_codec) { 274 MEDIA_LOG(log_cb) << "Codec '" << codec_id 275 << "' is not supported for '" << type << "'"; 276 return false; 277 } 278 } 279 280 if (factory_function) 281 *factory_function = type_info.factory_function; 282 283 // All codecs were supported by this |type|. 284 return true; 285 } 286 } 287 288 // |type| didn't match any of the supported types. 289 return false; 290 } 291 292 bool StreamParserFactory::IsTypeSupported( 293 const std::string& type, const std::vector<std::string>& codecs) { 294 return CheckTypeAndCodecs(type, codecs, media::LogCB(), NULL, NULL, NULL); 295 } 296 297 scoped_ptr<media::StreamParser> StreamParserFactory::Create( 298 const std::string& type, 299 const std::vector<std::string>& codecs, 300 const media::LogCB& log_cb, 301 bool* has_audio, 302 bool* has_video) { 303 scoped_ptr<media::StreamParser> stream_parser; 304 ParserFactoryFunction factory_function; 305 std::vector<CodecInfo::HistogramTag> audio_codecs; 306 std::vector<CodecInfo::HistogramTag> video_codecs; 307 *has_audio = false; 308 *has_video = false; 309 310 if (CheckTypeAndCodecs(type, 311 codecs, 312 log_cb, 313 &factory_function, 314 &audio_codecs, 315 &video_codecs)) { 316 *has_audio = !audio_codecs.empty(); 317 *has_video = !video_codecs.empty(); 318 319 // Log the number of codecs specified, as well as the details on each one. 320 UMA_HISTOGRAM_COUNTS_100("Media.MSE.NumberOfTracks", codecs.size()); 321 for (size_t i = 0; i < audio_codecs.size(); ++i) { 322 UMA_HISTOGRAM_ENUMERATION( 323 "Media.MSE.AudioCodec", audio_codecs[i], CodecInfo::HISTOGRAM_MAX); 324 } 325 for (size_t i = 0; i < video_codecs.size(); ++i) { 326 UMA_HISTOGRAM_ENUMERATION( 327 "Media.MSE.VideoCodec", video_codecs[i], CodecInfo::HISTOGRAM_MAX); 328 } 329 330 stream_parser.reset(factory_function(codecs, log_cb)); 331 } 332 333 return stream_parser.Pass(); 334 } 335 336 } // namespace media 337