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