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