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