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