Home | History | Annotate | Download | only in filters
      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