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_cluster_parser.h" 6 7 #include <vector> 8 9 #include "base/logging.h" 10 #include "base/sys_byteorder.h" 11 #include "media/base/buffers.h" 12 #include "media/base/decrypt_config.h" 13 #include "media/webm/webm_constants.h" 14 #include "media/webm/webm_crypto_helpers.h" 15 16 namespace media { 17 18 WebMClusterParser::TextTrackIterator::TextTrackIterator( 19 const TextTrackMap& text_track_map) : 20 iterator_(text_track_map.begin()), 21 iterator_end_(text_track_map.end()) { 22 } 23 24 WebMClusterParser::TextTrackIterator::TextTrackIterator( 25 const TextTrackIterator& rhs) : 26 iterator_(rhs.iterator_), 27 iterator_end_(rhs.iterator_end_) { 28 } 29 30 WebMClusterParser::TextTrackIterator::~TextTrackIterator() { 31 } 32 33 bool WebMClusterParser::TextTrackIterator::operator()( 34 int* track_num, 35 const BufferQueue** buffers) { 36 if (iterator_ == iterator_end_) { 37 *track_num = 0; 38 *buffers = NULL; 39 40 return false; 41 } 42 43 *track_num = iterator_->first; 44 *buffers = &iterator_->second.buffers(); 45 46 ++iterator_; 47 return true; 48 } 49 50 WebMClusterParser::WebMClusterParser( 51 int64 timecode_scale, int audio_track_num, int video_track_num, 52 const WebMTracksParser::TextTracks& text_tracks, 53 const std::set<int64>& ignored_tracks, 54 const std::string& audio_encryption_key_id, 55 const std::string& video_encryption_key_id, 56 const LogCB& log_cb) 57 : timecode_multiplier_(timecode_scale / 1000.0), 58 ignored_tracks_(ignored_tracks), 59 audio_encryption_key_id_(audio_encryption_key_id), 60 video_encryption_key_id_(video_encryption_key_id), 61 parser_(kWebMIdCluster, this), 62 last_block_timecode_(-1), 63 block_data_size_(-1), 64 block_duration_(-1), 65 block_add_id_(-1), 66 block_additional_data_size_(-1), 67 cluster_timecode_(-1), 68 cluster_start_time_(kNoTimestamp()), 69 cluster_ended_(false), 70 audio_(audio_track_num, false), 71 video_(video_track_num, true), 72 log_cb_(log_cb) { 73 for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin(); 74 it != text_tracks.end(); 75 ++it) { 76 text_track_map_.insert(std::make_pair(it->first, Track(it->first, false))); 77 } 78 } 79 80 WebMClusterParser::~WebMClusterParser() {} 81 82 void WebMClusterParser::Reset() { 83 last_block_timecode_ = -1; 84 cluster_timecode_ = -1; 85 cluster_start_time_ = kNoTimestamp(); 86 cluster_ended_ = false; 87 parser_.Reset(); 88 audio_.Reset(); 89 video_.Reset(); 90 ResetTextTracks(); 91 } 92 93 int WebMClusterParser::Parse(const uint8* buf, int size) { 94 audio_.Reset(); 95 video_.Reset(); 96 ResetTextTracks(); 97 98 int result = parser_.Parse(buf, size); 99 100 if (result < 0) { 101 cluster_ended_ = false; 102 return result; 103 } 104 105 cluster_ended_ = parser_.IsParsingComplete(); 106 if (cluster_ended_) { 107 // If there were no buffers in this cluster, set the cluster start time to 108 // be the |cluster_timecode_|. 109 if (cluster_start_time_ == kNoTimestamp()) { 110 DCHECK_GT(cluster_timecode_, -1); 111 cluster_start_time_ = base::TimeDelta::FromMicroseconds( 112 cluster_timecode_ * timecode_multiplier_); 113 } 114 115 // Reset the parser if we're done parsing so that 116 // it is ready to accept another cluster on the next 117 // call. 118 parser_.Reset(); 119 120 last_block_timecode_ = -1; 121 cluster_timecode_ = -1; 122 } 123 124 return result; 125 } 126 127 WebMClusterParser::TextTrackIterator 128 WebMClusterParser::CreateTextTrackIterator() const { 129 return TextTrackIterator(text_track_map_); 130 } 131 132 WebMParserClient* WebMClusterParser::OnListStart(int id) { 133 if (id == kWebMIdCluster) { 134 cluster_timecode_ = -1; 135 cluster_start_time_ = kNoTimestamp(); 136 } else if (id == kWebMIdBlockGroup) { 137 block_data_.reset(); 138 block_data_size_ = -1; 139 block_duration_ = -1; 140 } else if (id == kWebMIdBlockAdditions) { 141 block_add_id_ = -1; 142 block_additional_data_.reset(); 143 block_additional_data_size_ = -1; 144 } 145 146 return this; 147 } 148 149 bool WebMClusterParser::OnListEnd(int id) { 150 if (id != kWebMIdBlockGroup) 151 return true; 152 153 // Make sure the BlockGroup actually had a Block. 154 if (block_data_size_ == -1) { 155 MEDIA_LOG(log_cb_) << "Block missing from BlockGroup."; 156 return false; 157 } 158 159 bool result = ParseBlock(false, block_data_.get(), block_data_size_, 160 block_additional_data_.get(), 161 block_additional_data_size_, block_duration_); 162 block_data_.reset(); 163 block_data_size_ = -1; 164 block_duration_ = -1; 165 block_add_id_ = -1; 166 block_additional_data_.reset(); 167 block_additional_data_size_ = -1; 168 return result; 169 } 170 171 bool WebMClusterParser::OnUInt(int id, int64 val) { 172 int64* dst; 173 switch (id) { 174 case kWebMIdTimecode: 175 dst = &cluster_timecode_; 176 break; 177 case kWebMIdBlockDuration: 178 dst = &block_duration_; 179 break; 180 case kWebMIdBlockAddID: 181 dst = &block_add_id_; 182 break; 183 default: 184 return true; 185 } 186 if (*dst != -1) 187 return false; 188 *dst = val; 189 return true; 190 } 191 192 bool WebMClusterParser::ParseBlock(bool is_simple_block, const uint8* buf, 193 int size, const uint8* additional, 194 int additional_size, int duration) { 195 if (size < 4) 196 return false; 197 198 // Return an error if the trackNum > 127. We just aren't 199 // going to support large track numbers right now. 200 if (!(buf[0] & 0x80)) { 201 MEDIA_LOG(log_cb_) << "TrackNumber over 127 not supported"; 202 return false; 203 } 204 205 int track_num = buf[0] & 0x7f; 206 int timecode = buf[1] << 8 | buf[2]; 207 int flags = buf[3] & 0xff; 208 int lacing = (flags >> 1) & 0x3; 209 210 if (lacing) { 211 MEDIA_LOG(log_cb_) << "Lacing " << lacing << " is not supported yet."; 212 return false; 213 } 214 215 // Sign extend negative timecode offsets. 216 if (timecode & 0x8000) 217 timecode |= (-1 << 16); 218 219 const uint8* frame_data = buf + 4; 220 int frame_size = size - (frame_data - buf); 221 return OnBlock(is_simple_block, track_num, timecode, duration, flags, 222 frame_data, frame_size, additional, additional_size); 223 } 224 225 bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) { 226 switch (id) { 227 case kWebMIdSimpleBlock: 228 return ParseBlock(true, data, size, NULL, -1, -1); 229 230 case kWebMIdBlock: 231 if (block_data_) { 232 MEDIA_LOG(log_cb_) << "More than 1 Block in a BlockGroup is not " 233 "supported."; 234 return false; 235 } 236 block_data_.reset(new uint8[size]); 237 memcpy(block_data_.get(), data, size); 238 block_data_size_ = size; 239 return true; 240 241 case kWebMIdBlockAdditional: { 242 uint64 block_add_id = base::HostToNet64(block_add_id_); 243 if (block_additional_data_) { 244 // TODO(vigneshv): Technically, more than 1 BlockAdditional is allowed 245 // as per matroska spec. But for now we don't have a use case to 246 // support parsing of such files. Take a look at this again when such a 247 // case arises. 248 MEDIA_LOG(log_cb_) << "More than 1 BlockAdditional in a BlockGroup is " 249 "not supported."; 250 return false; 251 } 252 // First 8 bytes of side_data in DecoderBuffer is the BlockAddID 253 // element's value in Big Endian format. This is done to mimic ffmpeg 254 // demuxer's behavior. 255 block_additional_data_size_ = size + sizeof(block_add_id); 256 block_additional_data_.reset(new uint8[block_additional_data_size_]); 257 memcpy(block_additional_data_.get(), &block_add_id, 258 sizeof(block_add_id)); 259 memcpy(block_additional_data_.get() + 8, data, size); 260 return true; 261 } 262 263 default: 264 return true; 265 } 266 } 267 268 bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num, 269 int timecode, 270 int block_duration, 271 int flags, 272 const uint8* data, int size, 273 const uint8* additional, int additional_size) { 274 DCHECK_GE(size, 0); 275 if (cluster_timecode_ == -1) { 276 MEDIA_LOG(log_cb_) << "Got a block before cluster timecode."; 277 return false; 278 } 279 280 if (timecode < 0) { 281 MEDIA_LOG(log_cb_) << "Got a block with negative timecode offset " 282 << timecode; 283 return false; 284 } 285 286 if (last_block_timecode_ != -1 && timecode < last_block_timecode_) { 287 MEDIA_LOG(log_cb_) 288 << "Got a block with a timecode before the previous block."; 289 return false; 290 } 291 292 Track* track = NULL; 293 std::string encryption_key_id; 294 if (track_num == audio_.track_num()) { 295 track = &audio_; 296 encryption_key_id = audio_encryption_key_id_; 297 } else if (track_num == video_.track_num()) { 298 track = &video_; 299 encryption_key_id = video_encryption_key_id_; 300 } else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) { 301 return true; 302 } else if (Track* const text_track = FindTextTrack(track_num)) { 303 if (is_simple_block) // BlockGroup is required for WebVTT cues 304 return false; 305 if (block_duration < 0) // not specified 306 return false; 307 track = text_track; 308 } else { 309 MEDIA_LOG(log_cb_) << "Unexpected track number " << track_num; 310 return false; 311 } 312 313 last_block_timecode_ = timecode; 314 315 base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds( 316 (cluster_timecode_ + timecode) * timecode_multiplier_); 317 318 // The first bit of the flags is set when a SimpleBlock contains only 319 // keyframes. If this is a Block, then inspection of the payload is 320 // necessary to determine whether it contains a keyframe or not. 321 // http://www.matroska.org/technical/specs/index.html 322 bool is_keyframe = 323 is_simple_block ? (flags & 0x80) != 0 : track->IsKeyframe(data, size); 324 325 scoped_refptr<StreamParserBuffer> buffer = 326 StreamParserBuffer::CopyFrom(data, size, additional, additional_size, 327 is_keyframe); 328 329 // Every encrypted Block has a signal byte and IV prepended to it. Current 330 // encrypted WebM request for comments specification is here 331 // http://wiki.webmproject.org/encryption/webm-encryption-rfc 332 if (!encryption_key_id.empty()) { 333 scoped_ptr<DecryptConfig> config(WebMCreateDecryptConfig( 334 data, size, 335 reinterpret_cast<const uint8*>(encryption_key_id.data()), 336 encryption_key_id.size())); 337 if (!config) 338 return false; 339 buffer->set_decrypt_config(config.Pass()); 340 } 341 342 buffer->set_timestamp(timestamp); 343 if (cluster_start_time_ == kNoTimestamp()) 344 cluster_start_time_ = timestamp; 345 346 if (block_duration >= 0) { 347 buffer->set_duration(base::TimeDelta::FromMicroseconds( 348 block_duration * timecode_multiplier_)); 349 } 350 351 return track->AddBuffer(buffer); 352 } 353 354 WebMClusterParser::Track::Track(int track_num, bool is_video) 355 : track_num_(track_num), 356 is_video_(is_video) { 357 } 358 359 WebMClusterParser::Track::~Track() {} 360 361 bool WebMClusterParser::Track::AddBuffer( 362 const scoped_refptr<StreamParserBuffer>& buffer) { 363 DVLOG(2) << "AddBuffer() : " << track_num_ 364 << " ts " << buffer->timestamp().InSecondsF() 365 << " dur " << buffer->duration().InSecondsF() 366 << " kf " << buffer->IsKeyframe() 367 << " size " << buffer->data_size(); 368 369 buffers_.push_back(buffer); 370 return true; 371 } 372 373 void WebMClusterParser::Track::Reset() { 374 buffers_.clear(); 375 } 376 377 bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const { 378 // For now, assume that all blocks are keyframes for datatypes other than 379 // video. This is a valid assumption for Vorbis, WebVTT, & Opus. 380 if (!is_video_) 381 return true; 382 383 // Make sure the block is big enough for the minimal keyframe header size. 384 if (size < 7) 385 return false; 386 387 // The LSb of the first byte must be a 0 for a keyframe. 388 // http://tools.ietf.org/html/rfc6386 Section 19.1 389 if ((data[0] & 0x01) != 0) 390 return false; 391 392 // Verify VP8 keyframe startcode. 393 // http://tools.ietf.org/html/rfc6386 Section 19.1 394 if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) 395 return false; 396 397 return true; 398 } 399 400 void WebMClusterParser::ResetTextTracks() { 401 for (TextTrackMap::iterator it = text_track_map_.begin(); 402 it != text_track_map_.end(); 403 ++it) { 404 it->second.Reset(); 405 } 406 } 407 408 WebMClusterParser::Track* 409 WebMClusterParser::FindTextTrack(int track_num) { 410 const TextTrackMap::iterator it = text_track_map_.find(track_num); 411 412 if (it == text_track_map_.end()) 413 return NULL; 414 415 return &it->second; 416 } 417 418 } // namespace media 419