Upstreaming browser/ui/uikit_ui_util from iOS.
[chromium-blink-merge.git] / media / formats / webm / webm_tracks_parser.cc
blob0c2cd8ef4b1373853d76625172ef85ac0f6447c9
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.
5 #include "media/formats/webm/webm_tracks_parser.h"
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"
14 namespace media {
16 static TextKind CodecIdToTextKind(const std::string& codec_id) {
17 if (codec_id == kWebMCodecSubtitles)
18 return kTextSubtitles;
20 if (codec_id == kWebMCodecCaptions)
21 return kTextCaptions;
23 if (codec_id == kWebMCodecDescriptions)
24 return kTextDescriptions;
26 if (codec_id == kWebMCodecMetadata)
27 return kTextMetadata;
29 return kTextNone;
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();
37 int64 mult = duration_in_ns / 1000;
38 mult /= timecode_scale_in_us;
39 if (mult == 0)
40 return kNoTimestamp();
42 mult = static_cast<double>(mult) * timecode_scale_in_us;
43 return base::TimeDelta::FromMicroseconds(mult);
46 WebMTracksParser::WebMTracksParser(const scoped_refptr<MediaLog>& media_log,
47 bool ignore_text_tracks)
48 : track_type_(-1),
49 track_num_(-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 media_log_(media_log),
59 audio_client_(media_log),
60 video_client_(media_log) {
63 WebMTracksParser::~WebMTracksParser() {}
65 int WebMTracksParser::Parse(const uint8* buf, int size) {
66 track_type_ =-1;
67 track_num_ = -1;
68 default_duration_ = -1;
69 track_name_.clear();
70 track_language_.clear();
71 audio_track_num_ = -1;
72 audio_default_duration_ = -1;
73 audio_decoder_config_ = AudioDecoderConfig();
74 video_track_num_ = -1;
75 video_default_duration_ = -1;
76 video_decoder_config_ = VideoDecoderConfig();
77 text_tracks_.clear();
78 ignored_tracks_.clear();
80 WebMListParser parser(kWebMIdTracks, this);
81 int result = parser.Parse(buf, size);
83 if (result <= 0)
84 return result;
86 // For now we do all or nothing parsing.
87 return parser.IsParsingComplete() ? result : 0;
90 base::TimeDelta WebMTracksParser::GetAudioDefaultDuration(
91 const double timecode_scale_in_us) const {
92 return PrecisionCappedDefaultDuration(timecode_scale_in_us,
93 audio_default_duration_);
96 base::TimeDelta WebMTracksParser::GetVideoDefaultDuration(
97 const double timecode_scale_in_us) const {
98 return PrecisionCappedDefaultDuration(timecode_scale_in_us,
99 video_default_duration_);
102 WebMParserClient* WebMTracksParser::OnListStart(int id) {
103 if (id == kWebMIdContentEncodings) {
104 DCHECK(!track_content_encodings_client_.get());
105 track_content_encodings_client_.reset(
106 new WebMContentEncodingsClient(media_log_));
107 return track_content_encodings_client_->OnListStart(id);
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;
123 if (id == kWebMIdAudio)
124 return &audio_client_;
126 if (id == kWebMIdVideo)
127 return &video_client_;
129 return this;
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);
138 if (id == kWebMIdTrackEntry) {
139 if (track_type_ == -1 || track_num_ == -1) {
140 MEDIA_LOG(ERROR, media_log_) << "Missing TrackEntry data for "
141 << " TrackType " << track_type_
142 << " TrackNum " << track_num_;
143 return false;
146 if (track_type_ != kWebMTrackTypeAudio &&
147 track_type_ != kWebMTrackTypeVideo &&
148 track_type_ != kWebMTrackTypeSubtitlesOrCaptions &&
149 track_type_ != kWebMTrackTypeDescriptionsOrMetadata) {
150 MEDIA_LOG(ERROR, media_log_) << "Unexpected TrackType " << track_type_;
151 return false;
154 TextKind text_track_kind = kTextNone;
155 if (track_type_ == kWebMTrackTypeSubtitlesOrCaptions) {
156 text_track_kind = CodecIdToTextKind(codec_id_);
157 if (text_track_kind == kTextNone) {
158 MEDIA_LOG(ERROR, media_log_) << "Missing TrackEntry CodecID"
159 << " TrackNum " << track_num_;
160 return false;
163 if (text_track_kind != kTextSubtitles &&
164 text_track_kind != kTextCaptions) {
165 MEDIA_LOG(ERROR, media_log_) << "Wrong TrackEntry CodecID"
166 << " TrackNum " << track_num_;
167 return false;
169 } else if (track_type_ == kWebMTrackTypeDescriptionsOrMetadata) {
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;
177 if (text_track_kind != kTextDescriptions &&
178 text_track_kind != kTextMetadata) {
179 MEDIA_LOG(ERROR, media_log_) << "Wrong TrackEntry CodecID"
180 << " TrackNum " << track_num_;
181 return false;
185 std::string encryption_key_id;
186 if (track_content_encodings_client_) {
187 DCHECK(!track_content_encodings_client_->content_encodings().empty());
188 // If we have multiple ContentEncoding in one track. Always choose the
189 // key id in the first ContentEncoding as the key id of the track.
190 encryption_key_id = track_content_encodings_client_->
191 content_encodings()[0]->encryption_key_id();
194 if (track_type_ == kWebMTrackTypeAudio) {
195 if (audio_track_num_ == -1) {
196 audio_track_num_ = track_num_;
197 audio_encryption_key_id_ = encryption_key_id;
199 if (default_duration_ == 0) {
200 MEDIA_LOG(ERROR, media_log_) << "Illegal 0ns audio TrackEntry "
201 "DefaultDuration";
202 return false;
204 audio_default_duration_ = default_duration_;
206 DCHECK(!audio_decoder_config_.IsValidConfig());
207 if (!audio_client_.InitializeConfig(
208 codec_id_, codec_private_, seek_preroll_, codec_delay_,
209 !audio_encryption_key_id_.empty(), &audio_decoder_config_)) {
210 return false;
212 } else {
213 MEDIA_LOG(DEBUG, media_log_) << "Ignoring audio track " << track_num_;
214 ignored_tracks_.insert(track_num_);
216 } else if (track_type_ == kWebMTrackTypeVideo) {
217 if (video_track_num_ == -1) {
218 video_track_num_ = track_num_;
219 video_encryption_key_id_ = encryption_key_id;
221 if (default_duration_ == 0) {
222 MEDIA_LOG(ERROR, media_log_) << "Illegal 0ns video TrackEntry "
223 "DefaultDuration";
224 return false;
226 video_default_duration_ = default_duration_;
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;
234 } else {
235 MEDIA_LOG(DEBUG, media_log_) << "Ignoring video track " << track_num_;
236 ignored_tracks_.insert(track_num_);
238 } else if (track_type_ == kWebMTrackTypeSubtitlesOrCaptions ||
239 track_type_ == kWebMTrackTypeDescriptionsOrMetadata) {
240 if (ignore_text_tracks_) {
241 MEDIA_LOG(DEBUG, media_log_) << "Ignoring text track " << track_num_;
242 ignored_tracks_.insert(track_num_);
243 } else {
244 std::string track_num = base::Int64ToString(track_num_);
245 text_tracks_[track_num_] = TextTrackConfig(
246 text_track_kind, track_name_, track_language_, track_num);
248 } else {
249 MEDIA_LOG(ERROR, media_log_) << "Unexpected TrackType " << track_type_;
250 return false;
253 track_type_ = -1;
254 track_num_ = -1;
255 default_duration_ = -1;
256 track_name_.clear();
257 track_language_.clear();
258 codec_id_ = "";
259 codec_private_.clear();
260 track_content_encodings_client_.reset();
262 audio_client_.Reset();
263 video_client_.Reset();
264 return true;
267 return true;
270 bool WebMTracksParser::OnUInt(int id, int64 val) {
271 int64* dst = NULL;
273 switch (id) {
274 case kWebMIdTrackNumber:
275 dst = &track_num_;
276 break;
277 case kWebMIdTrackType:
278 dst = &track_type_;
279 break;
280 case kWebMIdSeekPreRoll:
281 dst = &seek_preroll_;
282 break;
283 case kWebMIdCodecDelay:
284 dst = &codec_delay_;
285 break;
286 case kWebMIdDefaultDuration:
287 dst = &default_duration_;
288 break;
289 default:
290 return true;
293 if (*dst != -1) {
294 MEDIA_LOG(ERROR, media_log_) << "Multiple values for id " << std::hex << id
295 << " specified";
296 return false;
299 *dst = val;
300 return true;
303 bool WebMTracksParser::OnFloat(int id, double val) {
304 return true;
307 bool WebMTracksParser::OnBinary(int id, const uint8* data, int size) {
308 if (id == kWebMIdCodecPrivate) {
309 if (!codec_private_.empty()) {
310 MEDIA_LOG(ERROR, media_log_)
311 << "Multiple CodecPrivate fields in a track.";
312 return false;
314 codec_private_.assign(data, data + size);
315 return true;
317 return true;
320 bool WebMTracksParser::OnString(int id, const std::string& str) {
321 if (id == kWebMIdCodecID) {
322 if (!codec_id_.empty()) {
323 MEDIA_LOG(ERROR, media_log_) << "Multiple CodecID fields in a track";
324 return false;
327 codec_id_ = str;
328 return true;
331 if (id == kWebMIdName) {
332 track_name_ = str;
333 return true;
336 if (id == kWebMIdLanguage) {
337 track_language_ = str;
338 return true;
341 return true;
344 } // namespace media