1// Copyright (c) 2012 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/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/buffers.h"
11#include "media/webm/webm_constants.h"
12#include "media/webm/webm_content_encodings.h"
13
14namespace media {
15
16static TextKind CodecIdToTextKind(const std::string& codec_id) {
17  if (codec_id == kWebMCodecSubtitles)
18    return kTextSubtitles;
19
20  if (codec_id == kWebMCodecCaptions)
21    return kTextCaptions;
22
23  if (codec_id == kWebMCodecDescriptions)
24    return kTextDescriptions;
25
26  if (codec_id == kWebMCodecMetadata)
27    return kTextMetadata;
28
29  return kTextNone;
30}
31
32WebMTracksParser::WebMTracksParser(const LogCB& log_cb, bool ignore_text_tracks)
33    : track_type_(-1),
34      track_num_(-1),
35      track_uid_(-1),
36      seek_preroll_(-1),
37      codec_delay_(-1),
38      audio_track_num_(-1),
39      video_track_num_(-1),
40      ignore_text_tracks_(ignore_text_tracks),
41      log_cb_(log_cb),
42      audio_client_(log_cb),
43      video_client_(log_cb) {
44}
45
46WebMTracksParser::~WebMTracksParser() {}
47
48int WebMTracksParser::Parse(const uint8* buf, int size) {
49  track_type_ =-1;
50  track_num_ = -1;
51  track_uid_ = -1;
52  track_name_.clear();
53  track_language_.clear();
54  audio_track_num_ = -1;
55  audio_decoder_config_ = AudioDecoderConfig();
56  video_track_num_ = -1;
57  video_decoder_config_ = VideoDecoderConfig();
58  text_tracks_.clear();
59  ignored_tracks_.clear();
60
61  WebMListParser parser(kWebMIdTracks, this);
62  int result = parser.Parse(buf, size);
63
64  if (result <= 0)
65    return result;
66
67  // For now we do all or nothing parsing.
68  return parser.IsParsingComplete() ? result : 0;
69}
70
71WebMParserClient* WebMTracksParser::OnListStart(int id) {
72  if (id == kWebMIdContentEncodings) {
73    DCHECK(!track_content_encodings_client_.get());
74    track_content_encodings_client_.reset(
75        new WebMContentEncodingsClient(log_cb_));
76    return track_content_encodings_client_->OnListStart(id);
77  }
78
79  if (id == kWebMIdTrackEntry) {
80    track_type_ = -1;
81    track_num_ = -1;
82    track_name_.clear();
83    track_language_.clear();
84    codec_id_ = "";
85    codec_private_.clear();
86    audio_client_.Reset();
87    video_client_.Reset();
88    return this;
89  }
90
91  if (id == kWebMIdAudio)
92    return &audio_client_;
93
94  if (id == kWebMIdVideo)
95    return &video_client_;
96
97  return this;
98}
99
100bool WebMTracksParser::OnListEnd(int id) {
101  if (id == kWebMIdContentEncodings) {
102    DCHECK(track_content_encodings_client_.get());
103    return track_content_encodings_client_->OnListEnd(id);
104  }
105
106  if (id == kWebMIdTrackEntry) {
107    if (track_type_ == -1 || track_num_ == -1 || track_uid_ == -1) {
108      MEDIA_LOG(log_cb_) << "Missing TrackEntry data for "
109                         << " TrackType " << track_type_
110                         << " TrackNum " << track_num_
111                         << " TrackUID " << track_uid_;
112      return false;
113    }
114
115    if (track_type_ != kWebMTrackTypeAudio &&
116        track_type_ != kWebMTrackTypeVideo &&
117        track_type_ != kWebMTrackTypeSubtitlesOrCaptions &&
118        track_type_ != kWebMTrackTypeDescriptionsOrMetadata) {
119      MEDIA_LOG(log_cb_) << "Unexpected TrackType " << track_type_;
120      return false;
121    }
122
123    TextKind text_track_kind = kTextNone;
124    if (track_type_ == kWebMTrackTypeSubtitlesOrCaptions) {
125      text_track_kind = CodecIdToTextKind(codec_id_);
126      if (text_track_kind == kTextNone) {
127        MEDIA_LOG(log_cb_) << "Missing TrackEntry CodecID"
128                           << " TrackNum " << track_num_;
129        return false;
130      }
131
132      if (text_track_kind != kTextSubtitles &&
133          text_track_kind != kTextCaptions) {
134        MEDIA_LOG(log_cb_) << "Wrong TrackEntry CodecID"
135                           << " TrackNum " << track_num_;
136        return false;
137      }
138    } else if (track_type_ == kWebMTrackTypeDescriptionsOrMetadata) {
139      text_track_kind = CodecIdToTextKind(codec_id_);
140      if (text_track_kind == kTextNone) {
141        MEDIA_LOG(log_cb_) << "Missing TrackEntry CodecID"
142                           << " TrackNum " << track_num_;
143        return false;
144      }
145
146      if (text_track_kind != kTextDescriptions &&
147          text_track_kind != kTextMetadata) {
148        MEDIA_LOG(log_cb_) << "Wrong TrackEntry CodecID"
149                           << " TrackNum " << track_num_;
150        return false;
151      }
152    }
153
154    std::string encryption_key_id;
155    if (track_content_encodings_client_) {
156      DCHECK(!track_content_encodings_client_->content_encodings().empty());
157      // If we have multiple ContentEncoding in one track. Always choose the
158      // key id in the first ContentEncoding as the key id of the track.
159      encryption_key_id = track_content_encodings_client_->
160          content_encodings()[0]->encryption_key_id();
161    }
162
163    if (track_type_ == kWebMTrackTypeAudio) {
164      if (audio_track_num_ == -1) {
165        audio_track_num_ = track_num_;
166        audio_encryption_key_id_ = encryption_key_id;
167
168        DCHECK(!audio_decoder_config_.IsValidConfig());
169        if (!audio_client_.InitializeConfig(
170                codec_id_, codec_private_, seek_preroll_, codec_delay_,
171                !audio_encryption_key_id_.empty(), &audio_decoder_config_)) {
172          return false;
173        }
174      } else {
175        MEDIA_LOG(log_cb_) << "Ignoring audio track " << track_num_;
176        ignored_tracks_.insert(track_num_);
177      }
178    } else if (track_type_ == kWebMTrackTypeVideo) {
179      if (video_track_num_ == -1) {
180        video_track_num_ = track_num_;
181        video_encryption_key_id_ = encryption_key_id;
182
183        DCHECK(!video_decoder_config_.IsValidConfig());
184        if (!video_client_.InitializeConfig(
185                codec_id_, codec_private_, !video_encryption_key_id_.empty(),
186                &video_decoder_config_)) {
187          return false;
188        }
189      } else {
190        MEDIA_LOG(log_cb_) << "Ignoring video track " << track_num_;
191        ignored_tracks_.insert(track_num_);
192      }
193    } else if (track_type_ == kWebMTrackTypeSubtitlesOrCaptions ||
194               track_type_ == kWebMTrackTypeDescriptionsOrMetadata) {
195      if (ignore_text_tracks_) {
196        MEDIA_LOG(log_cb_) << "Ignoring text track " << track_num_;
197        ignored_tracks_.insert(track_num_);
198      } else {
199        std::string track_uid = base::Int64ToString(track_uid_);
200        text_tracks_[track_num_] = TextTrackConfig(text_track_kind,
201                                                   track_name_,
202                                                   track_language_,
203                                                   track_uid);
204      }
205    } else {
206      MEDIA_LOG(log_cb_) << "Unexpected TrackType " << track_type_;
207      return false;
208    }
209
210    track_type_ = -1;
211    track_num_ = -1;
212    track_uid_ = -1;
213    track_name_.clear();
214    track_language_.clear();
215    codec_id_ = "";
216    codec_private_.clear();
217    track_content_encodings_client_.reset();
218
219    audio_client_.Reset();
220    video_client_.Reset();
221    return true;
222  }
223
224  return true;
225}
226
227bool WebMTracksParser::OnUInt(int id, int64 val) {
228  int64* dst = NULL;
229
230  switch (id) {
231    case kWebMIdTrackNumber:
232      dst = &track_num_;
233      break;
234    case kWebMIdTrackType:
235      dst = &track_type_;
236      break;
237    case kWebMIdTrackUID:
238      dst = &track_uid_;
239      break;
240    case kWebMIdSeekPreRoll:
241      dst = &seek_preroll_;
242      break;
243    case kWebMIdCodecDelay:
244      dst = &codec_delay_;
245      break;
246    default:
247      return true;
248  }
249
250  if (*dst != -1) {
251    MEDIA_LOG(log_cb_) << "Multiple values for id " << std::hex << id
252                       << " specified";
253    return false;
254  }
255
256  *dst = val;
257  return true;
258}
259
260bool WebMTracksParser::OnFloat(int id, double val) {
261  return true;
262}
263
264bool WebMTracksParser::OnBinary(int id, const uint8* data, int size) {
265  if (id == kWebMIdCodecPrivate) {
266    if (!codec_private_.empty()) {
267      MEDIA_LOG(log_cb_) << "Multiple CodecPrivate fields in a track.";
268      return false;
269    }
270
271    codec_private_.assign(data, data + size);
272    return true;
273  }
274  return true;
275}
276
277bool WebMTracksParser::OnString(int id, const std::string& str) {
278  if (id == kWebMIdCodecID) {
279    if (!codec_id_.empty()) {
280      MEDIA_LOG(log_cb_) << "Multiple CodecID fields in a track";
281      return false;
282    }
283
284    codec_id_ = str;
285    return true;
286  }
287
288  if (id == kWebMIdName) {
289    track_name_ = str;
290    return true;
291  }
292
293  if (id == kWebMIdLanguage) {
294    track_language_ = str;
295    return true;
296  }
297
298  return true;
299}
300
301}  // namespace media
302