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.
5 #include "media/filters/stream_parser_factory.h"
7 #include "base/command_line.h"
8 #include "base/metrics/histogram.h"
9 #include "base/strings/pattern.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_split.h"
12 #include "base/strings/string_util.h"
13 #include "media/base/media_switches.h"
14 #include "media/formats/mpeg/adts_stream_parser.h"
15 #include "media/formats/mpeg/mpeg1_audio_stream_parser.h"
16 #include "media/formats/webm/webm_stream_parser.h"
18 #if defined(OS_ANDROID)
19 #include "base/android/build_info.h"
22 #if defined(USE_PROPRIETARY_CODECS)
23 #if defined(ENABLE_MPEG2TS_STREAM_PARSER)
24 #include "media/formats/mp2t/mp2t_stream_parser.h"
26 #include "media/formats/mp4/es_descriptor.h"
27 #include "media/formats/mp4/mp4_stream_parser.h"
32 typedef bool (*CodecIDValidatorFunction
)(
33 const std::string
& codecs_id
, const LogCB
& log_cb
);
42 // Update tools/metrics/histograms/histograms.xml if new values are added.
54 HISTOGRAM_MAX
= HISTOGRAM_OPUS
// Must be equal to largest logged entry.
59 CodecIDValidatorFunction validator
;
63 typedef StreamParser
* (*ParserFactoryFunction
)(
64 const std::vector
<std::string
>& codecs
,
67 struct SupportedTypeInfo
{
69 const ParserFactoryFunction factory_function
;
70 const CodecInfo
** codecs
;
73 static const CodecInfo kVP8CodecInfo
= { "vp8", CodecInfo::VIDEO
, NULL
,
74 CodecInfo::HISTOGRAM_VP8
};
75 static const CodecInfo kVP9CodecInfo
= { "vp9", CodecInfo::VIDEO
, NULL
,
76 CodecInfo::HISTOGRAM_VP9
};
77 static const CodecInfo kVorbisCodecInfo
= { "vorbis", CodecInfo::AUDIO
, NULL
,
78 CodecInfo::HISTOGRAM_VORBIS
};
79 static const CodecInfo kOpusCodecInfo
= { "opus", CodecInfo::AUDIO
, NULL
,
80 CodecInfo::HISTOGRAM_OPUS
};
82 static const CodecInfo
* kVideoWebMCodecs
[] = {
90 static const CodecInfo
* kAudioWebMCodecs
[] = {
96 static StreamParser
* BuildWebMParser(
97 const std::vector
<std::string
>& codecs
,
98 const LogCB
& log_cb
) {
99 return new WebMStreamParser();
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 static const int kAACPSObjectType
= 29;
108 static int GetMP4AudioObjectType(const std::string
& codec_id
,
109 const LogCB
& log_cb
) {
110 // From RFC 6381 section 3.3 (ISO Base Media File Format Name Space):
111 // When the first element of a ['codecs' parameter value] is 'mp4a' ...,
112 // the second element is a hexadecimal representation of the MP4 Registration
113 // Authority ObjectTypeIndication (OTI). Note that MP4RA uses a leading "0x"
114 // with these values, which is omitted here and hence implied.
115 std::vector
<base::StringPiece
> tokens
= base::SplitStringPiece(
116 codec_id
, ".", base::KEEP_WHITESPACE
, base::SPLIT_WANT_NONEMPTY
);
117 if (tokens
.size() == 3 && tokens
[0] == "mp4a" && tokens
[1] == "40") {
118 // From RFC 6381 section 3.3:
119 // One of the OTI values for 'mp4a' is 40 (identifying MPEG-4 audio). For
120 // this value, the third element identifies the audio ObjectTypeIndication
121 // (OTI) ... expressed as a decimal number.
122 int audio_object_type
;
123 if (base::StringToInt(tokens
[2], &audio_object_type
))
124 return audio_object_type
;
127 MEDIA_LOG(DEBUG
, log_cb
) << "Malformed mimetype codec '" << codec_id
<< "'";
131 bool ValidateMP4ACodecID(const std::string
& codec_id
, const LogCB
& log_cb
) {
132 int audio_object_type
= GetMP4AudioObjectType(codec_id
, log_cb
);
133 if (audio_object_type
== kAACLCObjectType
||
134 audio_object_type
== kAACSBRObjectType
||
135 audio_object_type
== kAACPSObjectType
) {
139 MEDIA_LOG(DEBUG
, log_cb
) << "Unsupported audio object type "
140 << audio_object_type
<< " in codec '" << codec_id
145 static const CodecInfo kH264AVC1CodecInfo
= { "avc1.*", CodecInfo::VIDEO
, NULL
,
146 CodecInfo::HISTOGRAM_H264
};
147 static const CodecInfo kH264AVC3CodecInfo
= { "avc3.*", CodecInfo::VIDEO
, NULL
,
148 CodecInfo::HISTOGRAM_H264
};
149 static const CodecInfo kMPEG4AACCodecInfo
= { "mp4a.40.*", CodecInfo::AUDIO
,
150 &ValidateMP4ACodecID
,
151 CodecInfo::HISTOGRAM_MPEG4AAC
};
152 static const CodecInfo kMPEG2AACLCCodecInfo
= { "mp4a.67", CodecInfo::AUDIO
,
154 CodecInfo::HISTOGRAM_MPEG2AAC
};
156 static const CodecInfo
* kVideoMP4Codecs
[] = {
160 &kMPEG2AACLCCodecInfo
,
164 static const CodecInfo
* kAudioMP4Codecs
[] = {
166 &kMPEG2AACLCCodecInfo
,
170 static StreamParser
* BuildMP4Parser(
171 const std::vector
<std::string
>& codecs
, const LogCB
& log_cb
) {
172 std::set
<int> audio_object_types
;
174 bool has_sbr
= false;
175 for (size_t i
= 0; i
< codecs
.size(); ++i
) {
176 std::string codec_id
= codecs
[i
];
177 if (base::MatchPattern(codec_id
, kMPEG2AACLCCodecInfo
.pattern
)) {
178 audio_object_types
.insert(mp4::kISO_13818_7_AAC_LC
);
179 } else if (base::MatchPattern(codec_id
, kMPEG4AACCodecInfo
.pattern
)) {
180 int audio_object_type
= GetMP4AudioObjectType(codec_id
, log_cb
);
181 DCHECK_GT(audio_object_type
, 0);
183 audio_object_types
.insert(mp4::kISO_14496_3
);
185 if (audio_object_type
== kAACSBRObjectType
||
186 audio_object_type
== kAACPSObjectType
) {
193 return new mp4::MP4StreamParser(audio_object_types
, has_sbr
);
196 static const CodecInfo kMP3CodecInfo
= { NULL
, CodecInfo::AUDIO
, NULL
,
197 CodecInfo::HISTOGRAM_MP3
};
199 static const CodecInfo
* kAudioMP3Codecs
[] = {
204 static StreamParser
* BuildMP3Parser(
205 const std::vector
<std::string
>& codecs
, const LogCB
& log_cb
) {
206 return new MPEG1AudioStreamParser();
209 static const CodecInfo kADTSCodecInfo
= { NULL
, CodecInfo::AUDIO
, NULL
,
210 CodecInfo::HISTOGRAM_MPEG4AAC
};
211 static const CodecInfo
* kAudioADTSCodecs
[] = {
216 static StreamParser
* BuildADTSParser(
217 const std::vector
<std::string
>& codecs
, const LogCB
& log_cb
) {
218 return new ADTSStreamParser();
221 #if defined(ENABLE_MPEG2TS_STREAM_PARSER)
222 static const CodecInfo
* kVideoMP2TCodecs
[] = {
226 &kMPEG2AACLCCodecInfo
,
230 static StreamParser
* BuildMP2TParser(
231 const std::vector
<std::string
>& codecs
, const media::LogCB
& log_cb
) {
232 bool has_sbr
= false;
233 for (size_t i
= 0; i
< codecs
.size(); ++i
) {
234 std::string codec_id
= codecs
[i
];
235 if (base::MatchPattern(codec_id
, kMPEG4AACCodecInfo
.pattern
)) {
236 int audio_object_type
= GetMP4AudioObjectType(codec_id
, log_cb
);
237 if (audio_object_type
== kAACSBRObjectType
||
238 audio_object_type
== kAACPSObjectType
) {
244 return new media::mp2t::Mp2tStreamParser(has_sbr
);
250 static const SupportedTypeInfo kSupportedTypeInfo
[] = {
251 { "video/webm", &BuildWebMParser
, kVideoWebMCodecs
},
252 { "audio/webm", &BuildWebMParser
, kAudioWebMCodecs
},
253 #if defined(USE_PROPRIETARY_CODECS)
254 { "audio/aac", &BuildADTSParser
, kAudioADTSCodecs
},
255 { "audio/mpeg", &BuildMP3Parser
, kAudioMP3Codecs
},
256 { "video/mp4", &BuildMP4Parser
, kVideoMP4Codecs
},
257 { "audio/mp4", &BuildMP4Parser
, kAudioMP4Codecs
},
258 #if defined(ENABLE_MPEG2TS_STREAM_PARSER)
259 { "video/mp2t", &BuildMP2TParser
, kVideoMP2TCodecs
},
264 // Verify that |codec_info| is supported on this platform.
266 // Returns true if |codec_info| is a valid audio/video codec and is allowed.
267 // |audio_codecs| has |codec_info|.tag added to its list if |codec_info| is an
268 // audio codec. |audio_codecs| may be NULL, in which case it is not updated.
269 // |video_codecs| has |codec_info|.tag added to its list if |codec_info| is a
270 // video codec. |video_codecs| may be NULL, in which case it is not updated.
272 // Returns false otherwise, and |audio_codecs| and |video_codecs| not touched.
273 static bool VerifyCodec(
274 const CodecInfo
* codec_info
,
275 std::vector
<CodecInfo::HistogramTag
>* audio_codecs
,
276 std::vector
<CodecInfo::HistogramTag
>* video_codecs
) {
277 switch (codec_info
->type
) {
278 case CodecInfo::AUDIO
:
280 audio_codecs
->push_back(codec_info
->tag
);
282 case CodecInfo::VIDEO
:
283 #if defined(OS_ANDROID)
284 // VP9 is only supported on KitKat+ (API Level 19).
285 if (codec_info
->tag
== CodecInfo::HISTOGRAM_VP9
&&
286 base::android::BuildInfo::GetInstance()->sdk_int() < 19) {
289 // Opus is only supported on Lollipop+ (API Level 21).
290 if (codec_info
->tag
== CodecInfo::HISTOGRAM_OPUS
&&
291 base::android::BuildInfo::GetInstance()->sdk_int() < 21) {
296 video_codecs
->push_back(codec_info
->tag
);
299 // Not audio or video, so skip it.
300 DVLOG(1) << "CodecInfo type of " << codec_info
->type
301 << " should not be specified in a SupportedTypes list";
306 // Checks to see if the specified |type| and |codecs| list are supported.
308 // Returns true if |type| and all codecs listed in |codecs| are supported.
309 // |factory_function| contains a function that can build a StreamParser for this
310 // type. Value may be NULL, in which case it is not touched.
311 // |audio_codecs| is updated with the appropriate HistogramTags for matching
312 // audio codecs specified in |codecs|. Value may be NULL, in which case it is
314 // |video_codecs| is updated with the appropriate HistogramTags for matching
315 // video codecs specified in |codecs|. Value may be NULL, in which case it is
318 // Returns false otherwise. The values of |factory_function|, |audio_codecs|,
319 // and |video_codecs| are undefined.
320 static bool CheckTypeAndCodecs(
321 const std::string
& type
,
322 const std::vector
<std::string
>& codecs
,
324 ParserFactoryFunction
* factory_function
,
325 std::vector
<CodecInfo::HistogramTag
>* audio_codecs
,
326 std::vector
<CodecInfo::HistogramTag
>* video_codecs
) {
328 // Search for the SupportedTypeInfo for |type|.
329 for (size_t i
= 0; i
< arraysize(kSupportedTypeInfo
); ++i
) {
330 const SupportedTypeInfo
& type_info
= kSupportedTypeInfo
[i
];
331 if (type
== type_info
.type
) {
332 if (codecs
.empty()) {
333 const CodecInfo
* codec_info
= type_info
.codecs
[0];
334 if (codec_info
&& !codec_info
->pattern
&&
335 VerifyCodec(codec_info
, audio_codecs
, video_codecs
)) {
337 if (factory_function
)
338 *factory_function
= type_info
.factory_function
;
342 MEDIA_LOG(DEBUG
, log_cb
) << "A codecs parameter must be provided for '"
347 // Make sure all the codecs specified in |codecs| are
348 // in the supported type info.
349 for (size_t j
= 0; j
< codecs
.size(); ++j
) {
350 // Search the type info for a match.
351 bool found_codec
= false;
352 std::string codec_id
= codecs
[j
];
353 for (int k
= 0; type_info
.codecs
[k
]; ++k
) {
354 if (base::MatchPattern(codec_id
, type_info
.codecs
[k
]->pattern
) &&
355 (!type_info
.codecs
[k
]->validator
||
356 type_info
.codecs
[k
]->validator(codec_id
, log_cb
))) {
358 VerifyCodec(type_info
.codecs
[k
], audio_codecs
, video_codecs
);
359 break; // Since only 1 pattern will match, no need to check others.
364 MEDIA_LOG(DEBUG
, log_cb
) << "Codec '" << codec_id
365 << "' is not supported for '" << type
<< "'";
370 if (factory_function
)
371 *factory_function
= type_info
.factory_function
;
373 // All codecs were supported by this |type|.
378 // |type| didn't match any of the supported types.
382 bool StreamParserFactory::IsTypeSupported(
383 const std::string
& type
, const std::vector
<std::string
>& codecs
) {
384 return CheckTypeAndCodecs(type
, codecs
, LogCB(), NULL
, NULL
, NULL
);
387 scoped_ptr
<StreamParser
> StreamParserFactory::Create(
388 const std::string
& type
,
389 const std::vector
<std::string
>& codecs
,
393 scoped_ptr
<StreamParser
> stream_parser
;
394 ParserFactoryFunction factory_function
;
395 std::vector
<CodecInfo::HistogramTag
> audio_codecs
;
396 std::vector
<CodecInfo::HistogramTag
> video_codecs
;
400 if (CheckTypeAndCodecs(type
,
406 *has_audio
= !audio_codecs
.empty();
407 *has_video
= !video_codecs
.empty();
409 // Log the number of codecs specified, as well as the details on each one.
410 UMA_HISTOGRAM_COUNTS_100("Media.MSE.NumberOfTracks", codecs
.size());
411 for (size_t i
= 0; i
< audio_codecs
.size(); ++i
) {
412 UMA_HISTOGRAM_ENUMERATION("Media.MSE.AudioCodec",
414 CodecInfo::HISTOGRAM_MAX
+ 1);
416 for (size_t i
= 0; i
< video_codecs
.size(); ++i
) {
417 UMA_HISTOGRAM_ENUMERATION("Media.MSE.VideoCodec",
419 CodecInfo::HISTOGRAM_MAX
+ 1);
422 stream_parser
.reset(factory_function(codecs
, log_cb
));
425 return stream_parser
.Pass();