1 // Copyright 2014 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/formats/webm/webm_tracks_parser.h"
6
7 #include "base/logging.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/string_util.h"
10 #include "media/base/buffers.h"
11 #include "media/formats/webm/webm_constants.h"
12 #include "media/formats/webm/webm_content_encodings.h"
13
14 namespace media {
15
CodecIdToTextKind(const std::string & codec_id)16 static TextKind CodecIdToTextKind(const std::string& codec_id) {
17 if (codec_id == kWebMCodecSubtitles)
18 return kTextSubtitles;
19
20 if (codec_id == kWebMCodecCaptions)
21 return kTextCaptions;
22
23 if (codec_id == kWebMCodecDescriptions)
24 return kTextDescriptions;
25
26 if (codec_id == kWebMCodecMetadata)
27 return kTextMetadata;
28
29 return kTextNone;
30 }
31
PrecisionCappedDefaultDuration(const double timecode_scale_in_us,const int64 duration_in_ns)32 static base::TimeDelta PrecisionCappedDefaultDuration(
33 const double timecode_scale_in_us, const int64 duration_in_ns) {
34 if (duration_in_ns <= 0)
35 return kNoTimestamp();
36
37 int64 mult = duration_in_ns / 1000;
38 mult /= timecode_scale_in_us;
39 if (mult == 0)
40 return kNoTimestamp();
41
42 mult = static_cast<double>(mult) * timecode_scale_in_us;
43 return base::TimeDelta::FromMicroseconds(mult);
44 }
45
WebMTracksParser(const LogCB & log_cb,bool ignore_text_tracks)46 WebMTracksParser::WebMTracksParser(const LogCB& log_cb, bool ignore_text_tracks)
47 : track_type_(-1),
48 track_num_(-1),
49 track_uid_(-1),
50 seek_preroll_(-1),
51 codec_delay_(-1),
52 default_duration_(-1),
53 audio_track_num_(-1),
54 audio_default_duration_(-1),
55 video_track_num_(-1),
56 video_default_duration_(-1),
57 ignore_text_tracks_(ignore_text_tracks),
58 log_cb_(log_cb),
59 audio_client_(log_cb),
60 video_client_(log_cb) {
61 }
62
~WebMTracksParser()63 WebMTracksParser::~WebMTracksParser() {}
64
Parse(const uint8 * buf,int size)65 int WebMTracksParser::Parse(const uint8* buf, int size) {
66 track_type_ =-1;
67 track_num_ = -1;
68 track_uid_ = -1;
69 default_duration_ = -1;
70 track_name_.clear();
71 track_language_.clear();
72 audio_track_num_ = -1;
73 audio_default_duration_ = -1;
74 audio_decoder_config_ = AudioDecoderConfig();
75 video_track_num_ = -1;
76 video_default_duration_ = -1;
77 video_decoder_config_ = VideoDecoderConfig();
78 text_tracks_.clear();
79 ignored_tracks_.clear();
80
81 WebMListParser parser(kWebMIdTracks, this);
82 int result = parser.Parse(buf, size);
83
84 if (result <= 0)
85 return result;
86
87 // For now we do all or nothing parsing.
88 return parser.IsParsingComplete() ? result : 0;
89 }
90
GetAudioDefaultDuration(const double timecode_scale_in_us) const91 base::TimeDelta WebMTracksParser::GetAudioDefaultDuration(
92 const double timecode_scale_in_us) const {
93 return PrecisionCappedDefaultDuration(timecode_scale_in_us,
94 audio_default_duration_);
95 }
96
GetVideoDefaultDuration(const double timecode_scale_in_us) const97 base::TimeDelta WebMTracksParser::GetVideoDefaultDuration(
98 const double timecode_scale_in_us) const {
99 return PrecisionCappedDefaultDuration(timecode_scale_in_us,
100 video_default_duration_);
101 }
102
OnListStart(int id)103 WebMParserClient* WebMTracksParser::OnListStart(int id) {
104 if (id == kWebMIdContentEncodings) {
105 DCHECK(!track_content_encodings_client_.get());
106 track_content_encodings_client_.reset(
107 new WebMContentEncodingsClient(log_cb_));
108 return track_content_encodings_client_->OnListStart(id);
109 }
110
111 if (id == kWebMIdTrackEntry) {
112 track_type_ = -1;
113 track_num_ = -1;
114 default_duration_ = -1;
115 track_name_.clear();
116 track_language_.clear();
117 codec_id_ = "";
118 codec_private_.clear();
119 audio_client_.Reset();
120 video_client_.Reset();
121 return this;
122 }
123
124 if (id == kWebMIdAudio)
125 return &audio_client_;
126
127 if (id == kWebMIdVideo)
128 return &video_client_;
129
130 return this;
131 }
132
OnListEnd(int id)133 bool WebMTracksParser::OnListEnd(int id) {
134 if (id == kWebMIdContentEncodings) {
135 DCHECK(track_content_encodings_client_.get());
136 return track_content_encodings_client_->OnListEnd(id);
137 }
138
139 if (id == kWebMIdTrackEntry) {
140 if (track_type_ == -1 || track_num_ == -1 || track_uid_ == -1) {
141 MEDIA_LOG(log_cb_) << "Missing TrackEntry data for "
142 << " TrackType " << track_type_
143 << " TrackNum " << track_num_
144 << " TrackUID " << track_uid_;
145 return false;
146 }
147
148 if (track_type_ != kWebMTrackTypeAudio &&
149 track_type_ != kWebMTrackTypeVideo &&
150 track_type_ != kWebMTrackTypeSubtitlesOrCaptions &&
151 track_type_ != kWebMTrackTypeDescriptionsOrMetadata) {
152 MEDIA_LOG(log_cb_) << "Unexpected TrackType " << track_type_;
153 return false;
154 }
155
156 TextKind text_track_kind = kTextNone;
157 if (track_type_ == kWebMTrackTypeSubtitlesOrCaptions) {
158 text_track_kind = CodecIdToTextKind(codec_id_);
159 if (text_track_kind == kTextNone) {
160 MEDIA_LOG(log_cb_) << "Missing TrackEntry CodecID"
161 << " TrackNum " << track_num_;
162 return false;
163 }
164
165 if (text_track_kind != kTextSubtitles &&
166 text_track_kind != kTextCaptions) {
167 MEDIA_LOG(log_cb_) << "Wrong TrackEntry CodecID"
168 << " TrackNum " << track_num_;
169 return false;
170 }
171 } else if (track_type_ == kWebMTrackTypeDescriptionsOrMetadata) {
172 text_track_kind = CodecIdToTextKind(codec_id_);
173 if (text_track_kind == kTextNone) {
174 MEDIA_LOG(log_cb_) << "Missing TrackEntry CodecID"
175 << " TrackNum " << track_num_;
176 return false;
177 }
178
179 if (text_track_kind != kTextDescriptions &&
180 text_track_kind != kTextMetadata) {
181 MEDIA_LOG(log_cb_) << "Wrong TrackEntry CodecID"
182 << " TrackNum " << track_num_;
183 return false;
184 }
185 }
186
187 std::string encryption_key_id;
188 if (track_content_encodings_client_) {
189 DCHECK(!track_content_encodings_client_->content_encodings().empty());
190 // If we have multiple ContentEncoding in one track. Always choose the
191 // key id in the first ContentEncoding as the key id of the track.
192 encryption_key_id = track_content_encodings_client_->
193 content_encodings()[0]->encryption_key_id();
194 }
195
196 if (track_type_ == kWebMTrackTypeAudio) {
197 if (audio_track_num_ == -1) {
198 audio_track_num_ = track_num_;
199 audio_encryption_key_id_ = encryption_key_id;
200
201 if (default_duration_ == 0) {
202 MEDIA_LOG(log_cb_) << "Illegal 0ns audio TrackEntry DefaultDuration";
203 return false;
204 }
205 audio_default_duration_ = default_duration_;
206
207 DCHECK(!audio_decoder_config_.IsValidConfig());
208 if (!audio_client_.InitializeConfig(
209 codec_id_, codec_private_, seek_preroll_, codec_delay_,
210 !audio_encryption_key_id_.empty(), &audio_decoder_config_)) {
211 return false;
212 }
213 } else {
214 MEDIA_LOG(log_cb_) << "Ignoring audio track " << track_num_;
215 ignored_tracks_.insert(track_num_);
216 }
217 } else if (track_type_ == kWebMTrackTypeVideo) {
218 if (video_track_num_ == -1) {
219 video_track_num_ = track_num_;
220 video_encryption_key_id_ = encryption_key_id;
221
222 if (default_duration_ == 0) {
223 MEDIA_LOG(log_cb_) << "Illegal 0ns video TrackEntry DefaultDuration";
224 return false;
225 }
226 video_default_duration_ = default_duration_;
227
228 DCHECK(!video_decoder_config_.IsValidConfig());
229 if (!video_client_.InitializeConfig(
230 codec_id_, codec_private_, !video_encryption_key_id_.empty(),
231 &video_decoder_config_)) {
232 return false;
233 }
234 } else {
235 MEDIA_LOG(log_cb_) << "Ignoring video track " << track_num_;
236 ignored_tracks_.insert(track_num_);
237 }
238 } else if (track_type_ == kWebMTrackTypeSubtitlesOrCaptions ||
239 track_type_ == kWebMTrackTypeDescriptionsOrMetadata) {
240 if (ignore_text_tracks_) {
241 MEDIA_LOG(log_cb_) << "Ignoring text track " << track_num_;
242 ignored_tracks_.insert(track_num_);
243 } else {
244 std::string track_uid = base::Int64ToString(track_uid_);
245 text_tracks_[track_num_] = TextTrackConfig(text_track_kind,
246 track_name_,
247 track_language_,
248 track_uid);
249 }
250 } else {
251 MEDIA_LOG(log_cb_) << "Unexpected TrackType " << track_type_;
252 return false;
253 }
254
255 track_type_ = -1;
256 track_num_ = -1;
257 track_uid_ = -1;
258 default_duration_ = -1;
259 track_name_.clear();
260 track_language_.clear();
261 codec_id_ = "";
262 codec_private_.clear();
263 track_content_encodings_client_.reset();
264
265 audio_client_.Reset();
266 video_client_.Reset();
267 return true;
268 }
269
270 return true;
271 }
272
OnUInt(int id,int64 val)273 bool WebMTracksParser::OnUInt(int id, int64 val) {
274 int64* dst = NULL;
275
276 switch (id) {
277 case kWebMIdTrackNumber:
278 dst = &track_num_;
279 break;
280 case kWebMIdTrackType:
281 dst = &track_type_;
282 break;
283 case kWebMIdTrackUID:
284 dst = &track_uid_;
285 break;
286 case kWebMIdSeekPreRoll:
287 dst = &seek_preroll_;
288 break;
289 case kWebMIdCodecDelay:
290 dst = &codec_delay_;
291 break;
292 case kWebMIdDefaultDuration:
293 dst = &default_duration_;
294 break;
295 default:
296 return true;
297 }
298
299 if (*dst != -1) {
300 MEDIA_LOG(log_cb_) << "Multiple values for id " << std::hex << id
301 << " specified";
302 return false;
303 }
304
305 *dst = val;
306 return true;
307 }
308
OnFloat(int id,double val)309 bool WebMTracksParser::OnFloat(int id, double val) {
310 return true;
311 }
312
OnBinary(int id,const uint8 * data,int size)313 bool WebMTracksParser::OnBinary(int id, const uint8* data, int size) {
314 if (id == kWebMIdCodecPrivate) {
315 if (!codec_private_.empty()) {
316 MEDIA_LOG(log_cb_) << "Multiple CodecPrivate fields in a track.";
317 return false;
318 }
319
320 codec_private_.assign(data, data + size);
321 return true;
322 }
323 return true;
324 }
325
OnString(int id,const std::string & str)326 bool WebMTracksParser::OnString(int id, const std::string& str) {
327 if (id == kWebMIdCodecID) {
328 if (!codec_id_.empty()) {
329 MEDIA_LOG(log_cb_) << "Multiple CodecID fields in a track";
330 return false;
331 }
332
333 codec_id_ = str;
334 return true;
335 }
336
337 if (id == kWebMIdName) {
338 track_name_ = str;
339 return true;
340 }
341
342 if (id == kWebMIdLanguage) {
343 track_language_ = str;
344 return true;
345 }
346
347 return true;
348 }
349
350 } // namespace media
351