- add sources.
[platform/framework/web/crosswalk.git] / src / media / filters / stream_parser_factory.cc
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/mp3/mp3_stream_parser.h"
14 #include "media/webm/webm_stream_parser.h"
15
16 #if defined(USE_PROPRIETARY_CODECS)
17 #if defined(ENABLE_MPEG2TS_STREAM_PARSER)
18 #include "media/mp2t/mp2t_stream_parser.h"
19 #endif
20 #include "media/mp4/es_descriptor.h"
21 #include "media/mp4/mp4_stream_parser.h"
22 #endif
23
24 namespace media {
25
26 typedef bool (*CodecIDValidatorFunction)(
27     const std::string& codecs_id, const LogCB& log_cb);
28
29 struct CodecInfo {
30   enum Type {
31     UNKNOWN,
32     AUDIO,
33     VIDEO
34   };
35
36   // Update tools/metrics/histograms/histograms.xml if new values are added.
37   enum HistogramTag {
38     HISTOGRAM_UNKNOWN,
39     HISTOGRAM_VP8,
40     HISTOGRAM_VP9,
41     HISTOGRAM_VORBIS,
42     HISTOGRAM_H264,
43     HISTOGRAM_MPEG2AAC,
44     HISTOGRAM_MPEG4AAC,
45     HISTOGRAM_EAC3,
46     HISTOGRAM_MP3,
47     HISTOGRAM_OPUS,
48     HISTOGRAM_MAX  // Must be the last entry.
49   };
50
51   const char* pattern;
52   Type type;
53   CodecIDValidatorFunction validator;
54   HistogramTag tag;
55 };
56
57 typedef StreamParser* (*ParserFactoryFunction)(
58     const std::vector<std::string>& codecs,
59     const LogCB& log_cb);
60
61 struct SupportedTypeInfo {
62   const char* type;
63   const ParserFactoryFunction factory_function;
64   const CodecInfo** codecs;
65 };
66
67 static const CodecInfo kVP8CodecInfo = { "vp8", CodecInfo::VIDEO, NULL,
68                                          CodecInfo::HISTOGRAM_VP8 };
69 #if !defined(OS_ANDROID)
70 static const CodecInfo kVP9CodecInfo = { "vp9", CodecInfo::VIDEO, NULL,
71                                          CodecInfo::HISTOGRAM_VP9 };
72 #endif
73 static const CodecInfo kVorbisCodecInfo = { "vorbis", CodecInfo::AUDIO, NULL,
74                                             CodecInfo::HISTOGRAM_VORBIS };
75 static const CodecInfo kOpusCodecInfo = { "opus", CodecInfo::AUDIO, NULL,
76                                           CodecInfo::HISTOGRAM_OPUS };
77
78 static const CodecInfo* kVideoWebMCodecs[] = {
79   &kVP8CodecInfo,
80 #if !defined(OS_ANDROID)
81   // TODO(wonsik): crbug.com/285016 query Android platform for codec
82   // capabilities.
83   &kVP9CodecInfo,
84 #endif
85   &kVorbisCodecInfo,
86   &kOpusCodecInfo,
87   NULL
88 };
89
90 static const CodecInfo* kAudioWebMCodecs[] = {
91   &kVorbisCodecInfo,
92   &kOpusCodecInfo,
93   NULL
94 };
95
96 static StreamParser* BuildWebMParser(
97     const std::vector<std::string>& codecs,
98     const LogCB& log_cb) {
99   return new WebMStreamParser();
100 }
101
102 #if defined(USE_PROPRIETARY_CODECS)
103 // AAC Object Type IDs that Chrome supports.
104 static const int kAACLCObjectType = 2;
105 static const int kAACSBRObjectType = 5;
106
107 static int GetMP4AudioObjectType(const std::string& codec_id,
108                                  const LogCB& log_cb) {
109   int audio_object_type;
110   std::vector<std::string> tokens;
111   if (Tokenize(codec_id, ".", &tokens) != 3 ||
112       tokens[0] != "mp4a" || tokens[1] != "40" ||
113       !base::HexStringToInt(tokens[2], &audio_object_type)) {
114     MEDIA_LOG(log_cb) << "Malformed mimetype codec '" << codec_id << "'";
115     return -1;
116   }
117
118
119   return audio_object_type;
120 }
121
122 bool ValidateMP4ACodecID(const std::string& codec_id, const LogCB& log_cb) {
123   int audio_object_type = GetMP4AudioObjectType(codec_id, log_cb);
124   if (audio_object_type == kAACLCObjectType ||
125       audio_object_type == kAACSBRObjectType) {
126     return true;
127   }
128
129   MEDIA_LOG(log_cb) << "Unsupported audio object type "
130                     << "0x" << std::hex << audio_object_type
131                     << " in codec '" << codec_id << "'";
132   return false;
133 }
134
135 static const CodecInfo kH264AVC1CodecInfo = { "avc1.*", CodecInfo::VIDEO, NULL,
136                                               CodecInfo::HISTOGRAM_H264 };
137 static const CodecInfo kH264AVC3CodecInfo = { "avc3.*", CodecInfo::VIDEO, NULL,
138                                               CodecInfo::HISTOGRAM_H264 };
139 static const CodecInfo kMPEG4AACCodecInfo = { "mp4a.40.*", CodecInfo::AUDIO,
140                                               &ValidateMP4ACodecID,
141                                               CodecInfo::HISTOGRAM_MPEG4AAC };
142 static const CodecInfo kMPEG2AACLCCodecInfo = { "mp4a.67", CodecInfo::AUDIO,
143                                                 NULL,
144                                                 CodecInfo::HISTOGRAM_MPEG2AAC };
145
146 #if defined(ENABLE_EAC3_PLAYBACK)
147 static const CodecInfo kEAC3CodecInfo = { "mp4a.a6", CodecInfo::AUDIO, NULL,
148                                           CodecInfo::HISTOGRAM_EAC3 };
149 #endif
150
151 static const CodecInfo* kVideoMP4Codecs[] = {
152   &kH264AVC1CodecInfo,
153   &kH264AVC3CodecInfo,
154   &kMPEG4AACCodecInfo,
155   &kMPEG2AACLCCodecInfo,
156   NULL
157 };
158
159 static const CodecInfo* kAudioMP4Codecs[] = {
160   &kMPEG4AACCodecInfo,
161   &kMPEG2AACLCCodecInfo,
162 #if defined(ENABLE_EAC3_PLAYBACK)
163   &kEAC3CodecInfo,
164 #endif
165   NULL
166 };
167
168 static StreamParser* BuildMP4Parser(
169     const std::vector<std::string>& codecs, const LogCB& log_cb) {
170   std::set<int> audio_object_types;
171
172   bool has_sbr = false;
173 #if defined(ENABLE_EAC3_PLAYBACK)
174   bool enable_eac3 = CommandLine::ForCurrentProcess()->HasSwitch(
175       switches::kEnableEac3Playback);
176 #endif
177   for (size_t i = 0; i < codecs.size(); ++i) {
178     std::string codec_id = codecs[i];
179     if (MatchPattern(codec_id, kMPEG2AACLCCodecInfo.pattern)) {
180       audio_object_types.insert(mp4::kISO_13818_7_AAC_LC);
181     } else if (MatchPattern(codec_id, kMPEG4AACCodecInfo.pattern)) {
182       int audio_object_type = GetMP4AudioObjectType(codec_id, log_cb);
183       DCHECK_GT(audio_object_type, 0);
184
185       audio_object_types.insert(mp4::kISO_14496_3);
186
187       if (audio_object_type == kAACSBRObjectType) {
188         has_sbr = true;
189         break;
190       }
191 #if defined(ENABLE_EAC3_PLAYBACK)
192     } else if (enable_eac3 && MatchPattern(codec_id, kEAC3CodecInfo.pattern)) {
193       audio_object_types.insert(mp4::kEAC3);
194 #endif
195     }
196   }
197
198   return new mp4::MP4StreamParser(audio_object_types, has_sbr);
199 }
200
201 static const CodecInfo kMP3CodecInfo = { NULL, CodecInfo::AUDIO, NULL,
202                                          CodecInfo::HISTOGRAM_MP3 };
203
204 static const CodecInfo* kAudioMP3Codecs[] = {
205   &kMP3CodecInfo,
206   NULL
207 };
208
209 static StreamParser* BuildMP3Parser(
210     const std::vector<std::string>& codecs, const LogCB& log_cb) {
211   return new MP3StreamParser();
212 }
213
214 #if defined(ENABLE_MPEG2TS_STREAM_PARSER)
215 static const CodecInfo* kVideoMP2TCodecs[] = {
216   &kH264CodecInfo,
217   &kMPEG4AACCodecInfo,
218   &kMPEG2AACLCCodecInfo,
219   NULL
220 };
221
222 static StreamParser* BuildMP2TParser(
223     const std::vector<std::string>& codecs, const media::LogCB& log_cb) {
224   return new media::mp2t::Mp2tStreamParser();
225 }
226 #endif
227 #endif
228
229
230 static const SupportedTypeInfo kSupportedTypeInfo[] = {
231   { "video/webm", &BuildWebMParser, kVideoWebMCodecs },
232   { "audio/webm", &BuildWebMParser, kAudioWebMCodecs },
233 #if defined(USE_PROPRIETARY_CODECS)
234   { "audio/mpeg", &BuildMP3Parser, kAudioMP3Codecs },
235   { "video/mp4", &BuildMP4Parser, kVideoMP4Codecs },
236   { "audio/mp4", &BuildMP4Parser, kAudioMP4Codecs },
237 #if defined(ENABLE_MPEG2TS_STREAM_PARSER)
238   { "video/mp2t", &BuildMP2TParser, kVideoMP2TCodecs },
239 #endif
240 #endif
241 };
242
243 // Verify that |codec_info| is supported on this platform.
244 //
245 // Returns true if |codec_info| is a valid audio/video codec and is allowed.
246 // |audio_codecs| has |codec_info|.tag added to its list if |codec_info| is an
247 // audio codec. |audio_codecs| may be NULL, in which case it is not updated.
248 // |video_codecs| has |codec_info|.tag added to its list if |codec_info| is a
249 // video codec. |video_codecs| may be NULL, in which case it is not updated.
250 //
251 // Returns false otherwise, and |audio_codecs| and |video_codecs| not touched.
252 static bool VerifyCodec(
253     const CodecInfo* codec_info,
254     std::vector<CodecInfo::HistogramTag>* audio_codecs,
255     std::vector<CodecInfo::HistogramTag>* video_codecs) {
256   switch (codec_info->type) {
257     case CodecInfo::AUDIO:
258 #if defined(ENABLE_EAC3_PLAYBACK)
259       if (codec_info->tag == CodecInfo::HISTOGRAM_EAC3) {
260         const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
261         if (!cmd_line->HasSwitch(switches::kEnableEac3Playback))
262           return false;
263       }
264 #endif
265       if (codec_info->tag == CodecInfo::HISTOGRAM_OPUS) {
266         const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
267         if (!cmd_line->HasSwitch(switches::kEnableOpusPlayback))
268           return false;
269       }
270       if (audio_codecs)
271         audio_codecs->push_back(codec_info->tag);
272       return true;
273     case CodecInfo::VIDEO:
274       if (video_codecs)
275         video_codecs->push_back(codec_info->tag);
276       return true;
277     default:
278       // Not audio or video, so skip it.
279       DVLOG(1) << "CodecInfo type of " << codec_info->type
280                << " should not be specified in a SupportedTypes list";
281       return false;
282   }
283 }
284
285 // Checks to see if the specified |type| and |codecs| list are supported.
286 //
287 // Returns true if |type| and all codecs listed in |codecs| are supported.
288 // |factory_function| contains a function that can build a StreamParser for this
289 // type. Value may be NULL, in which case it is not touched.
290 // |audio_codecs| is updated with the appropriate HistogramTags for matching
291 // audio codecs specified in |codecs|. Value may be NULL, in which case it is
292 // not touched.
293 // |video_codecs| is updated with the appropriate HistogramTags for matching
294 // video codecs specified in |codecs|. Value may be NULL, in which case it is
295 // not touched.
296 //
297 // Returns false otherwise. The values of |factory_function|, |audio_codecs|,
298 // and |video_codecs| are undefined.
299 static bool CheckTypeAndCodecs(
300     const std::string& type,
301     const std::vector<std::string>& codecs,
302     const LogCB& log_cb,
303     ParserFactoryFunction* factory_function,
304     std::vector<CodecInfo::HistogramTag>* audio_codecs,
305     std::vector<CodecInfo::HistogramTag>* video_codecs) {
306
307   // Search for the SupportedTypeInfo for |type|.
308   for (size_t i = 0; i < arraysize(kSupportedTypeInfo); ++i) {
309     const SupportedTypeInfo& type_info = kSupportedTypeInfo[i];
310     if (type == type_info.type) {
311       if (codecs.empty()) {
312
313 #if defined(USE_PROPRIETARY_CODECS)
314         if (type_info.codecs == kAudioMP3Codecs &&
315             !CommandLine::ForCurrentProcess()->HasSwitch(
316                 switches::kEnableMP3StreamParser)) {
317           DVLOG(1) << "MP3StreamParser is not enabled.";
318           return false;
319         }
320 #endif
321
322         const CodecInfo* codec_info = type_info.codecs[0];
323         if (codec_info && !codec_info->pattern &&
324             VerifyCodec(codec_info, audio_codecs, video_codecs)) {
325
326           if (factory_function)
327             *factory_function = type_info.factory_function;
328           return true;
329         }
330
331         MEDIA_LOG(log_cb) << "A codecs parameter must be provided for '"
332                           << type << "'";
333         return false;
334       }
335
336       // Make sure all the codecs specified in |codecs| are
337       // in the supported type info.
338       for (size_t j = 0; j < codecs.size(); ++j) {
339         // Search the type info for a match.
340         bool found_codec = false;
341         std::string codec_id = codecs[j];
342         for (int k = 0; type_info.codecs[k]; ++k) {
343           if (MatchPattern(codec_id, type_info.codecs[k]->pattern) &&
344               (!type_info.codecs[k]->validator ||
345                type_info.codecs[k]->validator(codec_id, log_cb))) {
346             found_codec =
347                 VerifyCodec(type_info.codecs[k], audio_codecs, video_codecs);
348             break;  // Since only 1 pattern will match, no need to check others.
349           }
350         }
351
352         if (!found_codec) {
353           MEDIA_LOG(log_cb) << "Codec '" << codec_id
354                             << "' is not supported for '" << type << "'";
355           return false;
356         }
357       }
358
359       if (factory_function)
360         *factory_function = type_info.factory_function;
361
362       // All codecs were supported by this |type|.
363       return true;
364     }
365   }
366
367   // |type| didn't match any of the supported types.
368   return false;
369 }
370
371 bool StreamParserFactory::IsTypeSupported(
372     const std::string& type, const std::vector<std::string>& codecs) {
373   return CheckTypeAndCodecs(type, codecs, LogCB(), NULL, NULL, NULL);
374 }
375
376 scoped_ptr<StreamParser> StreamParserFactory::Create(
377     const std::string& type,
378     const std::vector<std::string>& codecs,
379     const LogCB& log_cb,
380     bool* has_audio,
381     bool* has_video) {
382   scoped_ptr<StreamParser> stream_parser;
383   ParserFactoryFunction factory_function;
384   std::vector<CodecInfo::HistogramTag> audio_codecs;
385   std::vector<CodecInfo::HistogramTag> video_codecs;
386   *has_audio = false;
387   *has_video = false;
388
389   if (CheckTypeAndCodecs(type,
390                          codecs,
391                          log_cb,
392                          &factory_function,
393                          &audio_codecs,
394                          &video_codecs)) {
395     *has_audio = !audio_codecs.empty();
396     *has_video = !video_codecs.empty();
397
398     // Log the number of codecs specified, as well as the details on each one.
399     UMA_HISTOGRAM_COUNTS_100("Media.MSE.NumberOfTracks", codecs.size());
400     for (size_t i = 0; i < audio_codecs.size(); ++i) {
401       UMA_HISTOGRAM_ENUMERATION(
402           "Media.MSE.AudioCodec", audio_codecs[i], CodecInfo::HISTOGRAM_MAX);
403     }
404     for (size_t i = 0; i < video_codecs.size(); ++i) {
405       UMA_HISTOGRAM_ENUMERATION(
406           "Media.MSE.VideoCodec", video_codecs[i], CodecInfo::HISTOGRAM_MAX);
407     }
408
409     stream_parser.reset(factory_function(codecs, log_cb));
410   }
411
412   return stream_parser.Pass();
413 }
414
415 }  // namespace media