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/media_util.h"
11 #include "media/base/timestamp_constants.h"
12 #include "media/formats/webm/webm_constants.h"
13 #include "media/formats/webm/webm_content_encodings.h"
14
15 namespace media {
16
CodecIdToTextKind(const std::string & codec_id)17 static TextKind CodecIdToTextKind(const std::string& codec_id) {
18 if (codec_id == kWebMCodecSubtitles)
19 return kTextSubtitles;
20
21 if (codec_id == kWebMCodecCaptions)
22 return kTextCaptions;
23
24 if (codec_id == kWebMCodecDescriptions)
25 return kTextDescriptions;
26
27 if (codec_id == kWebMCodecMetadata)
28 return kTextMetadata;
29
30 return kTextNone;
31 }
32
WebMTracksParser(MediaLog * media_log,bool ignore_text_tracks)33 WebMTracksParser::WebMTracksParser(MediaLog* media_log, bool ignore_text_tracks)
34 : ignore_text_tracks_(ignore_text_tracks),
35 media_log_(media_log),
36 audio_client_(media_log),
37 video_client_(media_log) {
38 Reset();
39 }
40
41 WebMTracksParser::~WebMTracksParser() = default;
42
PrecisionCappedDefaultDuration(const int64_t timecode_scale_in_ns,const int64_t duration_in_ns) const43 base::TimeDelta WebMTracksParser::PrecisionCappedDefaultDuration(
44 const int64_t timecode_scale_in_ns,
45 const int64_t duration_in_ns) const {
46 DCHECK_GT(timecode_scale_in_ns, 0);
47 if (duration_in_ns <= 0)
48 return kNoTimestamp;
49
50 // Calculate the (integral) number of complete |timecode_scale_in_ns|
51 // intervals that fit within |duration_in_ns|.
52 int64_t intervals = duration_in_ns / timecode_scale_in_ns;
53
54 int64_t result_us = (intervals * timecode_scale_in_ns) / 1000;
55 if (result_us == 0)
56 return kNoTimestamp;
57
58 return base::TimeDelta::FromMicroseconds(result_us);
59 }
60
Reset()61 void WebMTracksParser::Reset() {
62 ResetTrackEntry();
63 reset_on_next_parse_ = false;
64 audio_track_num_ = -1;
65 audio_default_duration_ = -1;
66 audio_decoder_config_ = AudioDecoderConfig();
67 video_track_num_ = -1;
68 video_default_duration_ = -1;
69 video_decoder_config_ = VideoDecoderConfig();
70 text_tracks_.clear();
71 ignored_tracks_.clear();
72 detected_audio_track_count_ = 0;
73 detected_video_track_count_ = 0;
74 detected_text_track_count_ = 0;
75 media_tracks_.reset(new MediaTracks());
76 }
77
ResetTrackEntry()78 void WebMTracksParser::ResetTrackEntry() {
79 track_type_ = -1;
80 track_num_ = -1;
81 track_name_.clear();
82 track_language_.clear();
83 codec_id_ = "";
84 codec_private_.clear();
85 seek_preroll_ = -1;
86 codec_delay_ = -1;
87 default_duration_ = -1;
88 audio_client_.Reset();
89 video_client_.Reset();
90 }
91
Parse(const uint8_t * buf,int size)92 int WebMTracksParser::Parse(const uint8_t* buf, int size) {
93 if (reset_on_next_parse_)
94 Reset();
95
96 reset_on_next_parse_ = true;
97
98 WebMListParser parser(kWebMIdTracks, this);
99 int result = parser.Parse(buf, size);
100
101 if (result <= 0)
102 return result;
103
104 // For now we do all or nothing parsing.
105 return parser.IsParsingComplete() ? result : 0;
106 }
107
GetAudioDefaultDuration(const int64_t timecode_scale_in_ns) const108 base::TimeDelta WebMTracksParser::GetAudioDefaultDuration(
109 const int64_t timecode_scale_in_ns) const {
110 return PrecisionCappedDefaultDuration(timecode_scale_in_ns,
111 audio_default_duration_);
112 }
113
GetVideoDefaultDuration(const int64_t timecode_scale_in_ns) const114 base::TimeDelta WebMTracksParser::GetVideoDefaultDuration(
115 const int64_t timecode_scale_in_ns) const {
116 return PrecisionCappedDefaultDuration(timecode_scale_in_ns,
117 video_default_duration_);
118 }
119
OnListStart(int id)120 WebMParserClient* WebMTracksParser::OnListStart(int id) {
121 if (id == kWebMIdContentEncodings) {
122 if (track_content_encodings_client_) {
123 MEDIA_LOG(ERROR, media_log_) << "Multiple ContentEncodings lists";
124 return NULL;
125 }
126
127 track_content_encodings_client_.reset(
128 new WebMContentEncodingsClient(media_log_));
129 return track_content_encodings_client_->OnListStart(id);
130 }
131
132 if (id == kWebMIdTrackEntry) {
133 ResetTrackEntry();
134 return this;
135 }
136
137 if (id == kWebMIdAudio)
138 return &audio_client_;
139
140 if (id == kWebMIdVideo)
141 return &video_client_;
142
143 return this;
144 }
145
OnListEnd(int id)146 bool WebMTracksParser::OnListEnd(int id) {
147 if (id == kWebMIdContentEncodings) {
148 DCHECK(track_content_encodings_client_.get());
149 return track_content_encodings_client_->OnListEnd(id);
150 }
151
152 if (id == kWebMIdTrackEntry) {
153 if (track_type_ == -1 || track_num_ == -1) {
154 MEDIA_LOG(ERROR, media_log_) << "Missing TrackEntry data for "
155 << " TrackType " << track_type_
156 << " TrackNum " << track_num_;
157 return false;
158 }
159
160 if (track_type_ != kWebMTrackTypeAudio &&
161 track_type_ != kWebMTrackTypeVideo &&
162 track_type_ != kWebMTrackTypeSubtitlesOrCaptions &&
163 track_type_ != kWebMTrackTypeDescriptionsOrMetadata) {
164 MEDIA_LOG(ERROR, media_log_) << "Unexpected TrackType " << track_type_;
165 return false;
166 }
167
168 TextKind text_track_kind = kTextNone;
169 if (track_type_ == kWebMTrackTypeSubtitlesOrCaptions) {
170 text_track_kind = CodecIdToTextKind(codec_id_);
171 if (text_track_kind == kTextNone) {
172 MEDIA_LOG(ERROR, media_log_) << "Missing TrackEntry CodecID"
173 << " TrackNum " << track_num_;
174 return false;
175 }
176
177 if (text_track_kind != kTextSubtitles &&
178 text_track_kind != kTextCaptions) {
179 MEDIA_LOG(ERROR, media_log_) << "Wrong TrackEntry CodecID"
180 << " TrackNum " << track_num_;
181 return false;
182 }
183 } else if (track_type_ == kWebMTrackTypeDescriptionsOrMetadata) {
184 text_track_kind = CodecIdToTextKind(codec_id_);
185 if (text_track_kind == kTextNone) {
186 MEDIA_LOG(ERROR, media_log_) << "Missing TrackEntry CodecID"
187 << " TrackNum " << track_num_;
188 return false;
189 }
190
191 if (text_track_kind != kTextDescriptions &&
192 text_track_kind != kTextMetadata) {
193 MEDIA_LOG(ERROR, media_log_) << "Wrong TrackEntry CodecID"
194 << " TrackNum " << track_num_;
195 return false;
196 }
197 }
198
199 std::string encryption_key_id;
200 if (track_content_encodings_client_) {
201 DCHECK(!track_content_encodings_client_->content_encodings().empty());
202 // If we have multiple ContentEncoding in one track. Always choose the
203 // key id in the first ContentEncoding as the key id of the track.
204 encryption_key_id = track_content_encodings_client_->
205 content_encodings()[0]->encryption_key_id();
206 }
207
208 EncryptionScheme encryption_scheme = encryption_key_id.empty()
209 ? EncryptionScheme::kUnencrypted
210 : EncryptionScheme::kCenc;
211
212 if (track_type_ == kWebMTrackTypeAudio) {
213 detected_audio_track_count_++;
214 if (audio_track_num_ == -1) {
215 audio_track_num_ = track_num_;
216 audio_encryption_key_id_ = encryption_key_id;
217
218 if (default_duration_ == 0) {
219 MEDIA_LOG(ERROR, media_log_) << "Illegal 0ns audio TrackEntry "
220 "DefaultDuration";
221 return false;
222 }
223 audio_default_duration_ = default_duration_;
224
225 DCHECK(!audio_decoder_config_.IsValidConfig());
226 if (!audio_client_.InitializeConfig(
227 codec_id_, codec_private_, seek_preroll_, codec_delay_,
228 encryption_scheme, &audio_decoder_config_)) {
229 return false;
230 }
231 media_tracks_->AddAudioTrack(
232 audio_decoder_config_,
233 static_cast<StreamParser::TrackId>(track_num_),
234 MediaTrack::Kind("main"), MediaTrack::Label(track_name_),
235 MediaTrack::Language(track_language_));
236 } else {
237 MEDIA_LOG(DEBUG, media_log_) << "Ignoring audio track " << track_num_;
238 ignored_tracks_.insert(track_num_);
239 }
240 } else if (track_type_ == kWebMTrackTypeVideo) {
241 detected_video_track_count_++;
242 if (video_track_num_ == -1) {
243 video_track_num_ = track_num_;
244 video_encryption_key_id_ = encryption_key_id;
245
246 if (default_duration_ == 0) {
247 MEDIA_LOG(ERROR, media_log_) << "Illegal 0ns video TrackEntry "
248 "DefaultDuration";
249 return false;
250 }
251 video_default_duration_ = default_duration_;
252
253 DCHECK(!video_decoder_config_.IsValidConfig());
254 if (!video_client_.InitializeConfig(codec_id_, codec_private_,
255 encryption_scheme,
256 &video_decoder_config_)) {
257 return false;
258 }
259 media_tracks_->AddVideoTrack(
260 video_decoder_config_,
261 static_cast<StreamParser::TrackId>(track_num_),
262 MediaTrack::Kind("main"), MediaTrack::Label(track_name_),
263 MediaTrack::Language(track_language_));
264 } else {
265 MEDIA_LOG(DEBUG, media_log_) << "Ignoring video track " << track_num_;
266 ignored_tracks_.insert(track_num_);
267 }
268 } else if (track_type_ == kWebMTrackTypeSubtitlesOrCaptions ||
269 track_type_ == kWebMTrackTypeDescriptionsOrMetadata) {
270 detected_text_track_count_++;
271 if (ignore_text_tracks_) {
272 MEDIA_LOG(DEBUG, media_log_) << "Ignoring text track " << track_num_;
273 ignored_tracks_.insert(track_num_);
274 } else {
275 std::string track_num = base::NumberToString(track_num_);
276 text_tracks_[track_num_] = TextTrackConfig(
277 text_track_kind, track_name_, track_language_, track_num);
278 }
279 } else {
280 MEDIA_LOG(ERROR, media_log_) << "Unexpected TrackType " << track_type_;
281 return false;
282 }
283
284 track_type_ = -1;
285 track_num_ = -1;
286 default_duration_ = -1;
287 track_name_.clear();
288 track_language_.clear();
289 codec_id_ = "";
290 codec_private_.clear();
291 track_content_encodings_client_.reset();
292
293 audio_client_.Reset();
294 video_client_.Reset();
295 return true;
296 }
297
298 return true;
299 }
300
OnUInt(int id,int64_t val)301 bool WebMTracksParser::OnUInt(int id, int64_t val) {
302 int64_t* dst = NULL;
303
304 switch (id) {
305 case kWebMIdTrackNumber:
306 dst = &track_num_;
307 break;
308 case kWebMIdTrackType:
309 dst = &track_type_;
310 break;
311 case kWebMIdSeekPreRoll:
312 dst = &seek_preroll_;
313 break;
314 case kWebMIdCodecDelay:
315 dst = &codec_delay_;
316 break;
317 case kWebMIdDefaultDuration:
318 dst = &default_duration_;
319 break;
320 default:
321 return true;
322 }
323
324 if (*dst != -1) {
325 MEDIA_LOG(ERROR, media_log_) << "Multiple values for id " << std::hex << id
326 << " specified";
327 return false;
328 }
329
330 *dst = val;
331 return true;
332 }
333
OnFloat(int id,double val)334 bool WebMTracksParser::OnFloat(int id, double val) {
335 return true;
336 }
337
OnBinary(int id,const uint8_t * data,int size)338 bool WebMTracksParser::OnBinary(int id, const uint8_t* data, int size) {
339 if (id == kWebMIdCodecPrivate) {
340 if (!codec_private_.empty()) {
341 MEDIA_LOG(ERROR, media_log_)
342 << "Multiple CodecPrivate fields in a track.";
343 return false;
344 }
345 codec_private_.assign(data, data + size);
346 return true;
347 }
348 return true;
349 }
350
OnString(int id,const std::string & str)351 bool WebMTracksParser::OnString(int id, const std::string& str) {
352 if (id == kWebMIdCodecID) {
353 if (!codec_id_.empty()) {
354 MEDIA_LOG(ERROR, media_log_) << "Multiple CodecID fields in a track";
355 return false;
356 }
357
358 // This element is specified to be printable ASCII (0x20-0x7F). Here, we
359 // allow also 0x01-0x1F.
360 if (!base::IsStringASCII(str)) {
361 MEDIA_LOG(ERROR, media_log_)
362 << "Tracks CodecID element value must be an ASCII string";
363 return false;
364 }
365
366 codec_id_ = str;
367 return true;
368 }
369
370 if (id == kWebMIdName) {
371 // This element is specified to be printable ASCII (0x20-0x7F). Here, we
372 // allow also 0x01-0x1F.
373 if (!base::IsStringASCII(str)) {
374 MEDIA_LOG(ERROR, media_log_)
375 << "Tracks Name element value must be an ASCII string";
376 return false;
377 }
378 track_name_ = str;
379 return true;
380 }
381
382 if (id == kWebMIdLanguage) {
383 // Check that the language string is in ISO 639-2 format (3 letter code of a
384 // language, all lower-case letters).
385 if (str.size() != 3 || str[0] < 'a' || str[0] > 'z' || str[1] < 'a' ||
386 str[1] > 'z' || str[2] < 'a' || str[2] > 'z') {
387 VLOG(2) << "Ignoring kWebMIdLanguage (not ISO 639-2 compliant): " << str;
388 track_language_ = "und";
389 } else {
390 track_language_ = str;
391 }
392 return true;
393 }
394
395 return true;
396 }
397
398 } // namespace media
399