OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/filters/chunk_demuxer.h" | 5 #include "media/filters/chunk_demuxer.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <deque> | 8 #include <deque> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/callback_helpers.h" | 11 #include "base/callback_helpers.h" |
12 #include "base/location.h" | 12 #include "base/location.h" |
13 #include "base/logging.h" | |
14 #include "base/message_loop_proxy.h" | 13 #include "base/message_loop_proxy.h" |
15 #include "base/string_number_conversions.h" | |
16 #include "base/string_util.h" | |
17 #include "media/base/audio_decoder_config.h" | 14 #include "media/base/audio_decoder_config.h" |
18 #include "media/base/stream_parser_buffer.h" | 15 #include "media/base/stream_parser_buffer.h" |
19 #include "media/base/video_decoder_config.h" | 16 #include "media/base/video_decoder_config.h" |
20 #if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS) | 17 #include "media/filters/stream_parser_factory.h" |
21 #include "media/mp4/es_descriptor.h" | |
22 #include "media/mp4/mp4_stream_parser.h" | |
23 #endif | |
24 #include "media/webm/webm_stream_parser.h" | |
25 | 18 |
26 using base::TimeDelta; | 19 using base::TimeDelta; |
27 | 20 |
28 namespace media { | 21 namespace media { |
29 | 22 |
30 struct CodecInfo { | |
31 const char* pattern; | |
32 DemuxerStream::Type type; | |
33 }; | |
34 | |
35 typedef StreamParser* (*ParserFactoryFunction)( | |
36 const std::vector<std::string>& codecs, const LogCB& log_cb); | |
37 | |
38 struct SupportedTypeInfo { | |
39 const char* type; | |
40 const ParserFactoryFunction factory_function; | |
41 const CodecInfo** codecs; | |
42 }; | |
43 | |
44 static const CodecInfo kVP8CodecInfo = { "vp8", DemuxerStream::VIDEO }; | |
45 static const CodecInfo kVorbisCodecInfo = { "vorbis", DemuxerStream::AUDIO }; | |
46 | |
47 static const CodecInfo* kVideoWebMCodecs[] = { | |
48 &kVP8CodecInfo, | |
49 &kVorbisCodecInfo, | |
50 NULL | |
51 }; | |
52 | |
53 static const CodecInfo* kAudioWebMCodecs[] = { | |
54 &kVorbisCodecInfo, | |
55 NULL | |
56 }; | |
57 | |
58 static StreamParser* BuildWebMParser(const std::vector<std::string>& codecs, | |
59 const LogCB& log_cb) { | |
60 return new WebMStreamParser(); | |
61 } | |
62 | |
63 #if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS) | |
64 static const CodecInfo kH264CodecInfo = { "avc1.*", DemuxerStream::VIDEO }; | |
65 static const CodecInfo kMPEG4AACCodecInfo = { | |
66 "mp4a.40.*", DemuxerStream::AUDIO | |
67 }; | |
68 | |
69 static const CodecInfo kMPEG2AACLCCodecInfo = { | |
70 "mp4a.67", DemuxerStream::AUDIO | |
71 }; | |
72 | |
73 static const CodecInfo* kVideoMP4Codecs[] = { | |
74 &kH264CodecInfo, | |
75 &kMPEG4AACCodecInfo, | |
76 &kMPEG2AACLCCodecInfo, | |
77 NULL | |
78 }; | |
79 | |
80 static const CodecInfo* kAudioMP4Codecs[] = { | |
81 &kMPEG4AACCodecInfo, | |
82 &kMPEG2AACLCCodecInfo, | |
83 NULL | |
84 }; | |
85 | |
86 // AAC Object Type IDs that Chrome supports. | |
87 static const int kAACLCObjectType = 2; | |
88 static const int kAACSBRObjectType = 5; | |
89 | |
90 static StreamParser* BuildMP4Parser(const std::vector<std::string>& codecs, | |
91 const LogCB& log_cb) { | |
92 std::set<int> audio_object_types; | |
93 bool has_sbr = false; | |
94 for (size_t i = 0; i < codecs.size(); ++i) { | |
95 if (MatchPattern(codecs[i], kMPEG2AACLCCodecInfo.pattern)) { | |
96 audio_object_types.insert(mp4::kISO_13818_7_AAC_LC); | |
97 } else if (MatchPattern(codecs[i], kMPEG4AACCodecInfo.pattern)) { | |
98 std::vector<std::string> tokens; | |
99 int audio_object_type; | |
100 if (Tokenize(codecs[i], ".", &tokens) != 3 || | |
101 !base::HexStringToInt(tokens[2], &audio_object_type)) { | |
102 MEDIA_LOG(log_cb) << "Malformed mimetype codec '" << codecs[i] << "'"; | |
103 return NULL; | |
104 } | |
105 | |
106 if (audio_object_type != kAACLCObjectType && | |
107 audio_object_type != kAACSBRObjectType) { | |
108 MEDIA_LOG(log_cb) << "Unsupported audio object type " | |
109 << "0x" << std::hex << audio_object_type | |
110 << " in codec '" << codecs[i] << "'"; | |
111 return NULL; | |
112 } | |
113 | |
114 audio_object_types.insert(mp4::kISO_14496_3); | |
115 | |
116 if (audio_object_type == kAACSBRObjectType) { | |
117 has_sbr = true; | |
118 break; | |
119 } | |
120 } | |
121 } | |
122 | |
123 return new mp4::MP4StreamParser(audio_object_types, has_sbr); | |
124 } | |
125 #endif | |
126 | |
127 static const SupportedTypeInfo kSupportedTypeInfo[] = { | |
128 { "video/webm", &BuildWebMParser, kVideoWebMCodecs }, | |
129 { "audio/webm", &BuildWebMParser, kAudioWebMCodecs }, | |
130 #if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS) | |
131 { "video/mp4", &BuildMP4Parser, kVideoMP4Codecs }, | |
132 { "audio/mp4", &BuildMP4Parser, kAudioMP4Codecs }, | |
133 #endif | |
134 }; | |
135 | |
136 // Checks to see if the specified |type| and |codecs| list are supported. | |
137 // Returns true if |type| and all codecs listed in |codecs| are supported. | |
138 // |factory_function| contains a function that can build a StreamParser | |
139 // for this type. | |
140 // |has_audio| is true if an audio codec was specified. | |
141 // |has_video| is true if a video codec was specified. | |
142 // Returns false otherwise. The values of |factory_function|, |has_audio|, | |
143 // and |has_video| are undefined. | |
144 static bool IsSupported(const std::string& type, | |
145 std::vector<std::string>& codecs, | |
146 const LogCB& log_cb, | |
147 ParserFactoryFunction* factory_function, | |
148 bool* has_audio, | |
149 bool* has_video) { | |
150 *factory_function = NULL; | |
151 *has_audio = false; | |
152 *has_video = false; | |
153 | |
154 // Search for the SupportedTypeInfo for |type|. | |
155 for (size_t i = 0; i < arraysize(kSupportedTypeInfo); ++i) { | |
156 const SupportedTypeInfo& type_info = kSupportedTypeInfo[i]; | |
157 if (type == type_info.type) { | |
158 // Make sure all the codecs specified in |codecs| are | |
159 // in the supported type info. | |
160 for (size_t j = 0; j < codecs.size(); ++j) { | |
161 // Search the type info for a match. | |
162 bool found_codec = false; | |
163 DemuxerStream::Type codec_type = DemuxerStream::UNKNOWN; | |
164 | |
165 for (int k = 0; type_info.codecs[k]; ++k) { | |
166 if (MatchPattern(codecs[j], type_info.codecs[k]->pattern)) { | |
167 found_codec = true; | |
168 codec_type = type_info.codecs[k]->type; | |
169 break; | |
170 } | |
171 } | |
172 | |
173 if (!found_codec) { | |
174 MEDIA_LOG(log_cb) << "Codec '" << codecs[j] | |
175 <<"' is not supported for '" << type << "'"; | |
176 return false; | |
177 } | |
178 | |
179 switch (codec_type) { | |
180 case DemuxerStream::AUDIO: | |
181 *has_audio = true; | |
182 break; | |
183 case DemuxerStream::VIDEO: | |
184 *has_video = true; | |
185 break; | |
186 default: | |
187 MEDIA_LOG(log_cb) << "Unsupported codec type '"<< codec_type | |
188 << "' for " << codecs[j]; | |
189 return false; | |
190 } | |
191 } | |
192 | |
193 *factory_function = type_info.factory_function; | |
194 | |
195 // All codecs were supported by this |type|. | |
196 return true; | |
197 } | |
198 } | |
199 | |
200 // |type| didn't match any of the supported types. | |
201 return false; | |
202 } | |
203 | 23 |
204 class ChunkDemuxerStream : public DemuxerStream { | 24 class ChunkDemuxerStream : public DemuxerStream { |
205 public: | 25 public: |
206 typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue; | 26 typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue; |
207 typedef std::deque<ReadCB> ReadCBQueue; | 27 typedef std::deque<ReadCB> ReadCBQueue; |
208 typedef std::deque<base::Closure> ClosureQueue; | 28 typedef std::deque<base::Closure> ClosureQueue; |
209 | 29 |
210 ChunkDemuxerStream(const AudioDecoderConfig& audio_config, | 30 ChunkDemuxerStream(const AudioDecoderConfig& audio_config, |
211 const LogCB& log_cb); | 31 const LogCB& log_cb); |
212 ChunkDemuxerStream(const VideoDecoderConfig& video_config, | 32 ChunkDemuxerStream(const VideoDecoderConfig& video_config, |
(...skipping 497 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
710 std::vector<std::string>& codecs) { | 530 std::vector<std::string>& codecs) { |
711 DCHECK_GT(codecs.size(), 0u); | 531 DCHECK_GT(codecs.size(), 0u); |
712 base::AutoLock auto_lock(lock_); | 532 base::AutoLock auto_lock(lock_); |
713 | 533 |
714 if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) || | 534 if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) || |
715 stream_parser_map_.count(id) > 0u) | 535 stream_parser_map_.count(id) > 0u) |
716 return kReachedIdLimit; | 536 return kReachedIdLimit; |
717 | 537 |
718 bool has_audio = false; | 538 bool has_audio = false; |
719 bool has_video = false; | 539 bool has_video = false; |
720 ParserFactoryFunction factory_function = NULL; | 540 scoped_ptr<media::StreamParser> stream_parser( |
721 std::string error; | 541 StreamParserFactory::Create(type, codecs, log_cb_, |
722 if (!IsSupported(type, codecs, log_cb_, &factory_function, &has_audio, | 542 &has_audio, &has_video)); |
723 &has_video)) { | 543 |
724 return kNotSupported; | 544 if (!stream_parser) |
725 } | 545 return ChunkDemuxer::kNotSupported; |
726 | 546 |
727 if ((has_audio && !source_id_audio_.empty()) || | 547 if ((has_audio && !source_id_audio_.empty()) || |
728 (has_video && !source_id_video_.empty())) | 548 (has_video && !source_id_video_.empty())) |
729 return kReachedIdLimit; | 549 return kReachedIdLimit; |
730 | 550 |
731 StreamParser::NewBuffersCB audio_cb; | 551 StreamParser::NewBuffersCB audio_cb; |
732 StreamParser::NewBuffersCB video_cb; | 552 StreamParser::NewBuffersCB video_cb; |
733 | 553 |
734 scoped_ptr<StreamParser> stream_parser(factory_function(codecs, log_cb_)); | |
735 if (!stream_parser) | |
736 return kNotSupported; | |
737 | |
738 if (has_audio) { | 554 if (has_audio) { |
739 source_id_audio_ = id; | 555 source_id_audio_ = id; |
740 audio_cb = base::Bind(&ChunkDemuxer::OnAudioBuffers, | 556 audio_cb = base::Bind(&ChunkDemuxer::OnAudioBuffers, |
741 base::Unretained(this)); | 557 base::Unretained(this)); |
742 } | 558 } |
743 | 559 |
744 if (has_video) { | 560 if (has_video) { |
745 source_id_video_ = id; | 561 source_id_video_ = id; |
746 video_cb = base::Bind(&ChunkDemuxer::OnVideoBuffers, | 562 video_cb = base::Bind(&ChunkDemuxer::OnVideoBuffers, |
747 base::Unretained(this)); | 563 base::Unretained(this)); |
(...skipping 587 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1335 | 1151 |
1336 Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges() const { | 1152 Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges() const { |
1337 if (audio_ && !video_) | 1153 if (audio_ && !video_) |
1338 return audio_->GetBufferedRanges(duration_); | 1154 return audio_->GetBufferedRanges(duration_); |
1339 else if (!audio_ && video_) | 1155 else if (!audio_ && video_) |
1340 return video_->GetBufferedRanges(duration_); | 1156 return video_->GetBufferedRanges(duration_); |
1341 return ComputeIntersection(); | 1157 return ComputeIntersection(); |
1342 } | 1158 } |
1343 | 1159 |
1344 } // namespace media | 1160 } // namespace media |
OLD | NEW |