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 "media/formats/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/filters/webvtt_util.h" 14 #include "media/formats/webm/webm_constants.h" 15 #include "media/formats/webm/webm_crypto_helpers.h" 16 #include "media/formats/webm/webm_webvtt_parser.h" 17 18 namespace media { 19 20 WebMClusterParser::WebMClusterParser( 21 int64 timecode_scale, 22 int audio_track_num, 23 base::TimeDelta audio_default_duration, 24 int video_track_num, 25 base::TimeDelta video_default_duration, 26 const WebMTracksParser::TextTracks& text_tracks, 27 const std::set<int64>& ignored_tracks, 28 const std::string& audio_encryption_key_id, 29 const std::string& video_encryption_key_id, 30 const LogCB& log_cb) 31 : timecode_multiplier_(timecode_scale / 1000.0), 32 ignored_tracks_(ignored_tracks), 33 audio_encryption_key_id_(audio_encryption_key_id), 34 video_encryption_key_id_(video_encryption_key_id), 35 parser_(kWebMIdCluster, this), 36 last_block_timecode_(-1), 37 block_data_size_(-1), 38 block_duration_(-1), 39 block_add_id_(-1), 40 block_additional_data_size_(-1), 41 discard_padding_(-1), 42 cluster_timecode_(-1), 43 cluster_start_time_(kNoTimestamp()), 44 cluster_ended_(false), 45 audio_(audio_track_num, false, audio_default_duration, log_cb), 46 video_(video_track_num, true, video_default_duration, log_cb), 47 ready_buffer_upper_bound_(kNoTimestamp()), 48 log_cb_(log_cb) { 49 for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin(); 50 it != text_tracks.end(); 51 ++it) { 52 text_track_map_.insert(std::make_pair( 53 it->first, Track(it->first, false, kNoTimestamp(), log_cb_))); 54 } 55 } 56 57 WebMClusterParser::~WebMClusterParser() {} 58 59 void WebMClusterParser::Reset() { 60 last_block_timecode_ = -1; 61 cluster_timecode_ = -1; 62 cluster_start_time_ = kNoTimestamp(); 63 cluster_ended_ = false; 64 parser_.Reset(); 65 audio_.Reset(); 66 video_.Reset(); 67 ResetTextTracks(); 68 ready_buffer_upper_bound_ = kNoTimestamp(); 69 } 70 71 int WebMClusterParser::Parse(const uint8* buf, int size) { 72 audio_.ClearReadyBuffers(); 73 video_.ClearReadyBuffers(); 74 ClearTextTrackReadyBuffers(); 75 ready_buffer_upper_bound_ = kNoTimestamp(); 76 77 int result = parser_.Parse(buf, size); 78 79 if (result < 0) { 80 cluster_ended_ = false; 81 return result; 82 } 83 84 cluster_ended_ = parser_.IsParsingComplete(); 85 if (cluster_ended_) { 86 // If there were no buffers in this cluster, set the cluster start time to 87 // be the |cluster_timecode_|. 88 if (cluster_start_time_ == kNoTimestamp()) { 89 // If the cluster did not even have a |cluster_timecode_|, signal parse 90 // error. 91 if (cluster_timecode_ < 0) 92 return -1; 93 94 cluster_start_time_ = base::TimeDelta::FromMicroseconds( 95 cluster_timecode_ * timecode_multiplier_); 96 } 97 98 // Reset the parser if we're done parsing so that 99 // it is ready to accept another cluster on the next 100 // call. 101 parser_.Reset(); 102 103 last_block_timecode_ = -1; 104 cluster_timecode_ = -1; 105 } 106 107 return result; 108 } 109 110 const WebMClusterParser::BufferQueue& WebMClusterParser::GetAudioBuffers() { 111 if (ready_buffer_upper_bound_ == kNoTimestamp()) 112 UpdateReadyBuffers(); 113 114 return audio_.ready_buffers(); 115 } 116 117 const WebMClusterParser::BufferQueue& WebMClusterParser::GetVideoBuffers() { 118 if (ready_buffer_upper_bound_ == kNoTimestamp()) 119 UpdateReadyBuffers(); 120 121 return video_.ready_buffers(); 122 } 123 124 const WebMClusterParser::TextBufferQueueMap& 125 WebMClusterParser::GetTextBuffers() { 126 if (ready_buffer_upper_bound_ == kNoTimestamp()) 127 UpdateReadyBuffers(); 128 129 // Translate our |text_track_map_| into |text_buffers_map_|, inserting rows in 130 // the output only for non-empty ready_buffer() queues in |text_track_map_|. 131 text_buffers_map_.clear(); 132 for (TextTrackMap::const_iterator itr = text_track_map_.begin(); 133 itr != text_track_map_.end(); 134 ++itr) { 135 const BufferQueue& text_buffers = itr->second.ready_buffers(); 136 if (!text_buffers.empty()) 137 text_buffers_map_.insert(std::make_pair(itr->first, text_buffers)); 138 } 139 140 return text_buffers_map_; 141 } 142 143 WebMParserClient* WebMClusterParser::OnListStart(int id) { 144 if (id == kWebMIdCluster) { 145 cluster_timecode_ = -1; 146 cluster_start_time_ = kNoTimestamp(); 147 } else if (id == kWebMIdBlockGroup) { 148 block_data_.reset(); 149 block_data_size_ = -1; 150 block_duration_ = -1; 151 discard_padding_ = -1; 152 discard_padding_set_ = false; 153 } else if (id == kWebMIdBlockAdditions) { 154 block_add_id_ = -1; 155 block_additional_data_.reset(); 156 block_additional_data_size_ = -1; 157 } 158 159 return this; 160 } 161 162 bool WebMClusterParser::OnListEnd(int id) { 163 if (id != kWebMIdBlockGroup) 164 return true; 165 166 // Make sure the BlockGroup actually had a Block. 167 if (block_data_size_ == -1) { 168 MEDIA_LOG(log_cb_) << "Block missing from BlockGroup."; 169 return false; 170 } 171 172 bool result = ParseBlock(false, block_data_.get(), block_data_size_, 173 block_additional_data_.get(), 174 block_additional_data_size_, block_duration_, 175 discard_padding_set_ ? discard_padding_ : 0); 176 block_data_.reset(); 177 block_data_size_ = -1; 178 block_duration_ = -1; 179 block_add_id_ = -1; 180 block_additional_data_.reset(); 181 block_additional_data_size_ = -1; 182 discard_padding_ = -1; 183 discard_padding_set_ = false; 184 return result; 185 } 186 187 bool WebMClusterParser::OnUInt(int id, int64 val) { 188 int64* dst; 189 switch (id) { 190 case kWebMIdTimecode: 191 dst = &cluster_timecode_; 192 break; 193 case kWebMIdBlockDuration: 194 dst = &block_duration_; 195 break; 196 case kWebMIdBlockAddID: 197 dst = &block_add_id_; 198 break; 199 default: 200 return true; 201 } 202 if (*dst != -1) 203 return false; 204 *dst = val; 205 return true; 206 } 207 208 bool WebMClusterParser::ParseBlock(bool is_simple_block, const uint8* buf, 209 int size, const uint8* additional, 210 int additional_size, int duration, 211 int64 discard_padding) { 212 if (size < 4) 213 return false; 214 215 // Return an error if the trackNum > 127. We just aren't 216 // going to support large track numbers right now. 217 if (!(buf[0] & 0x80)) { 218 MEDIA_LOG(log_cb_) << "TrackNumber over 127 not supported"; 219 return false; 220 } 221 222 int track_num = buf[0] & 0x7f; 223 int timecode = buf[1] << 8 | buf[2]; 224 int flags = buf[3] & 0xff; 225 int lacing = (flags >> 1) & 0x3; 226 227 if (lacing) { 228 MEDIA_LOG(log_cb_) << "Lacing " << lacing << " is not supported yet."; 229 return false; 230 } 231 232 // Sign extend negative timecode offsets. 233 if (timecode & 0x8000) 234 timecode |= ~0xffff; 235 236 const uint8* frame_data = buf + 4; 237 int frame_size = size - (frame_data - buf); 238 return OnBlock(is_simple_block, track_num, timecode, duration, flags, 239 frame_data, frame_size, additional, additional_size, 240 discard_padding); 241 } 242 243 bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) { 244 switch (id) { 245 case kWebMIdSimpleBlock: 246 return ParseBlock(true, data, size, NULL, -1, -1, 0); 247 248 case kWebMIdBlock: 249 if (block_data_) { 250 MEDIA_LOG(log_cb_) << "More than 1 Block in a BlockGroup is not " 251 "supported."; 252 return false; 253 } 254 block_data_.reset(new uint8[size]); 255 memcpy(block_data_.get(), data, size); 256 block_data_size_ = size; 257 return true; 258 259 case kWebMIdBlockAdditional: { 260 uint64 block_add_id = base::HostToNet64(block_add_id_); 261 if (block_additional_data_) { 262 // TODO(vigneshv): Technically, more than 1 BlockAdditional is allowed 263 // as per matroska spec. But for now we don't have a use case to 264 // support parsing of such files. Take a look at this again when such a 265 // case arises. 266 MEDIA_LOG(log_cb_) << "More than 1 BlockAdditional in a BlockGroup is " 267 "not supported."; 268 return false; 269 } 270 // First 8 bytes of side_data in DecoderBuffer is the BlockAddID 271 // element's value in Big Endian format. This is done to mimic ffmpeg 272 // demuxer's behavior. 273 block_additional_data_size_ = size + sizeof(block_add_id); 274 block_additional_data_.reset(new uint8[block_additional_data_size_]); 275 memcpy(block_additional_data_.get(), &block_add_id, 276 sizeof(block_add_id)); 277 memcpy(block_additional_data_.get() + 8, data, size); 278 return true; 279 } 280 case kWebMIdDiscardPadding: { 281 if (discard_padding_set_ || size <= 0 || size > 8) 282 return false; 283 discard_padding_set_ = true; 284 285 // Read in the big-endian integer. 286 discard_padding_ = static_cast<int8>(data[0]); 287 for (int i = 1; i < size; ++i) 288 discard_padding_ = (discard_padding_ << 8) | data[i]; 289 290 return true; 291 } 292 default: 293 return true; 294 } 295 } 296 297 bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num, 298 int timecode, 299 int block_duration, 300 int flags, 301 const uint8* data, int size, 302 const uint8* additional, int additional_size, 303 int64 discard_padding) { 304 DCHECK_GE(size, 0); 305 if (cluster_timecode_ == -1) { 306 MEDIA_LOG(log_cb_) << "Got a block before cluster timecode."; 307 return false; 308 } 309 310 // TODO(acolwell): Should relative negative timecode offsets be rejected? Or 311 // only when the absolute timecode is negative? See http://crbug.com/271794 312 if (timecode < 0) { 313 MEDIA_LOG(log_cb_) << "Got a block with negative timecode offset " 314 << timecode; 315 return false; 316 } 317 318 if (last_block_timecode_ != -1 && timecode < last_block_timecode_) { 319 MEDIA_LOG(log_cb_) 320 << "Got a block with a timecode before the previous block."; 321 return false; 322 } 323 324 Track* track = NULL; 325 StreamParserBuffer::Type buffer_type = DemuxerStream::AUDIO; 326 std::string encryption_key_id; 327 if (track_num == audio_.track_num()) { 328 track = &audio_; 329 encryption_key_id = audio_encryption_key_id_; 330 } else if (track_num == video_.track_num()) { 331 track = &video_; 332 encryption_key_id = video_encryption_key_id_; 333 buffer_type = DemuxerStream::VIDEO; 334 } else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) { 335 return true; 336 } else if (Track* const text_track = FindTextTrack(track_num)) { 337 if (is_simple_block) // BlockGroup is required for WebVTT cues 338 return false; 339 if (block_duration < 0) // not specified 340 return false; 341 track = text_track; 342 buffer_type = DemuxerStream::TEXT; 343 } else { 344 MEDIA_LOG(log_cb_) << "Unexpected track number " << track_num; 345 return false; 346 } 347 348 last_block_timecode_ = timecode; 349 350 base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds( 351 (cluster_timecode_ + timecode) * timecode_multiplier_); 352 353 scoped_refptr<StreamParserBuffer> buffer; 354 if (buffer_type != DemuxerStream::TEXT) { 355 // The first bit of the flags is set when a SimpleBlock contains only 356 // keyframes. If this is a Block, then inspection of the payload is 357 // necessary to determine whether it contains a keyframe or not. 358 // http://www.matroska.org/technical/specs/index.html 359 bool is_keyframe = 360 is_simple_block ? (flags & 0x80) != 0 : track->IsKeyframe(data, size); 361 362 // Every encrypted Block has a signal byte and IV prepended to it. Current 363 // encrypted WebM request for comments specification is here 364 // http://wiki.webmproject.org/encryption/webm-encryption-rfc 365 scoped_ptr<DecryptConfig> decrypt_config; 366 int data_offset = 0; 367 if (!encryption_key_id.empty() && 368 !WebMCreateDecryptConfig( 369 data, size, 370 reinterpret_cast<const uint8*>(encryption_key_id.data()), 371 encryption_key_id.size(), 372 &decrypt_config, &data_offset)) { 373 return false; 374 } 375 376 // TODO(wolenetz/acolwell): Validate and use a common cross-parser TrackId 377 // type with remapped bytestream track numbers and allow multiple tracks as 378 // applicable. See https://crbug.com/341581. 379 buffer = StreamParserBuffer::CopyFrom( 380 data + data_offset, size - data_offset, 381 additional, additional_size, 382 is_keyframe, buffer_type, track_num); 383 384 if (decrypt_config) 385 buffer->set_decrypt_config(decrypt_config.Pass()); 386 } else { 387 std::string id, settings, content; 388 WebMWebVTTParser::Parse(data, size, &id, &settings, &content); 389 390 std::vector<uint8> side_data; 391 MakeSideData(id.begin(), id.end(), 392 settings.begin(), settings.end(), 393 &side_data); 394 395 // TODO(wolenetz/acolwell): Validate and use a common cross-parser TrackId 396 // type with remapped bytestream track numbers and allow multiple tracks as 397 // applicable. See https://crbug.com/341581. 398 buffer = StreamParserBuffer::CopyFrom( 399 reinterpret_cast<const uint8*>(content.data()), 400 content.length(), 401 &side_data[0], 402 side_data.size(), 403 true, buffer_type, track_num); 404 } 405 406 buffer->set_timestamp(timestamp); 407 if (cluster_start_time_ == kNoTimestamp()) 408 cluster_start_time_ = timestamp; 409 410 if (block_duration >= 0) { 411 buffer->set_duration(base::TimeDelta::FromMicroseconds( 412 block_duration * timecode_multiplier_)); 413 } else { 414 DCHECK_NE(buffer_type, DemuxerStream::TEXT); 415 buffer->set_duration(track->default_duration()); 416 } 417 418 if (discard_padding != 0) { 419 buffer->set_discard_padding(std::make_pair( 420 base::TimeDelta(), 421 base::TimeDelta::FromMicroseconds(discard_padding / 1000))); 422 } 423 424 return track->AddBuffer(buffer); 425 } 426 427 WebMClusterParser::Track::Track(int track_num, 428 bool is_video, 429 base::TimeDelta default_duration, 430 const LogCB& log_cb) 431 : track_num_(track_num), 432 is_video_(is_video), 433 default_duration_(default_duration), 434 estimated_next_frame_duration_(kNoTimestamp()), 435 log_cb_(log_cb) { 436 DCHECK(default_duration_ == kNoTimestamp() || 437 default_duration_ > base::TimeDelta()); 438 } 439 440 WebMClusterParser::Track::~Track() {} 441 442 base::TimeDelta WebMClusterParser::Track::GetReadyUpperBound() { 443 DCHECK(ready_buffers_.empty()); 444 if (last_added_buffer_missing_duration_) 445 return last_added_buffer_missing_duration_->GetDecodeTimestamp(); 446 447 return kInfiniteDuration(); 448 } 449 450 void WebMClusterParser::Track::ExtractReadyBuffers( 451 const base::TimeDelta before_timestamp) { 452 DCHECK(ready_buffers_.empty()); 453 DCHECK(base::TimeDelta() <= before_timestamp); 454 DCHECK(kNoTimestamp() != before_timestamp); 455 456 if (buffers_.empty()) 457 return; 458 459 if (buffers_.back()->GetDecodeTimestamp() < before_timestamp) { 460 // All of |buffers_| are ready. 461 ready_buffers_.swap(buffers_); 462 DVLOG(3) << __FUNCTION__ << " : " << track_num_ << " All " 463 << ready_buffers_.size() << " are ready: before upper bound ts " 464 << before_timestamp.InSecondsF(); 465 return; 466 } 467 468 // Not all of |buffers_| are ready yet. Move any that are ready to 469 // |ready_buffers_|. 470 while (true) { 471 const scoped_refptr<StreamParserBuffer>& buffer = buffers_.front(); 472 if (buffer->GetDecodeTimestamp() >= before_timestamp) 473 break; 474 ready_buffers_.push_back(buffer); 475 buffers_.pop_front(); 476 DCHECK(!buffers_.empty()); 477 } 478 479 DVLOG(3) << __FUNCTION__ << " : " << track_num_ << " Only " 480 << ready_buffers_.size() << " ready, " << buffers_.size() 481 << " at or after upper bound ts " << before_timestamp.InSecondsF(); 482 } 483 484 bool WebMClusterParser::Track::AddBuffer( 485 const scoped_refptr<StreamParserBuffer>& buffer) { 486 DVLOG(2) << "AddBuffer() : " << track_num_ 487 << " ts " << buffer->timestamp().InSecondsF() 488 << " dur " << buffer->duration().InSecondsF() 489 << " kf " << buffer->IsKeyframe() 490 << " size " << buffer->data_size(); 491 492 if (last_added_buffer_missing_duration_) { 493 base::TimeDelta derived_duration = 494 buffer->timestamp() - last_added_buffer_missing_duration_->timestamp(); 495 last_added_buffer_missing_duration_->set_duration(derived_duration); 496 497 DVLOG(2) << "AddBuffer() : applied derived duration to held-back buffer : " 498 << " ts " 499 << last_added_buffer_missing_duration_->timestamp().InSecondsF() 500 << " dur " 501 << last_added_buffer_missing_duration_->duration().InSecondsF() 502 << " kf " << last_added_buffer_missing_duration_->IsKeyframe() 503 << " size " << last_added_buffer_missing_duration_->data_size(); 504 scoped_refptr<StreamParserBuffer> updated_buffer = 505 last_added_buffer_missing_duration_; 506 last_added_buffer_missing_duration_ = NULL; 507 if (!QueueBuffer(updated_buffer)) 508 return false; 509 } 510 511 if (buffer->duration() == kNoTimestamp()) { 512 last_added_buffer_missing_duration_ = buffer; 513 DVLOG(2) << "AddBuffer() : holding back buffer that is missing duration"; 514 return true; 515 } 516 517 return QueueBuffer(buffer); 518 } 519 520 void WebMClusterParser::Track::ApplyDurationEstimateIfNeeded() { 521 if (!last_added_buffer_missing_duration_) 522 return; 523 524 last_added_buffer_missing_duration_->set_duration(GetDurationEstimate()); 525 526 DVLOG(2) << "ApplyDurationEstimateIfNeeded() : new dur : " 527 << " ts " 528 << last_added_buffer_missing_duration_->timestamp().InSecondsF() 529 << " dur " 530 << last_added_buffer_missing_duration_->duration().InSecondsF() 531 << " kf " << last_added_buffer_missing_duration_->IsKeyframe() 532 << " size " << last_added_buffer_missing_duration_->data_size(); 533 534 // Don't use the applied duration as a future estimation (don't use 535 // QueueBuffer() here.) 536 buffers_.push_back(last_added_buffer_missing_duration_); 537 last_added_buffer_missing_duration_ = NULL; 538 } 539 540 void WebMClusterParser::Track::ClearReadyBuffers() { 541 // Note that |buffers_| are kept and |estimated_next_frame_duration_| is not 542 // reset here. 543 ready_buffers_.clear(); 544 } 545 546 void WebMClusterParser::Track::Reset() { 547 ClearReadyBuffers(); 548 buffers_.clear(); 549 last_added_buffer_missing_duration_ = NULL; 550 } 551 552 bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const { 553 // For now, assume that all blocks are keyframes for datatypes other than 554 // video. This is a valid assumption for Vorbis, WebVTT, & Opus. 555 if (!is_video_) 556 return true; 557 558 // Make sure the block is big enough for the minimal keyframe header size. 559 if (size < 7) 560 return false; 561 562 // The LSb of the first byte must be a 0 for a keyframe. 563 // http://tools.ietf.org/html/rfc6386 Section 19.1 564 if ((data[0] & 0x01) != 0) 565 return false; 566 567 // Verify VP8 keyframe startcode. 568 // http://tools.ietf.org/html/rfc6386 Section 19.1 569 if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) 570 return false; 571 572 return true; 573 } 574 575 bool WebMClusterParser::Track::QueueBuffer( 576 const scoped_refptr<StreamParserBuffer>& buffer) { 577 DCHECK(!last_added_buffer_missing_duration_); 578 579 // WebMClusterParser::OnBlock() gives MEDIA_LOG and parse error on decreasing 580 // block timecode detection within a cluster. Therefore, we should not see 581 // those here. 582 base::TimeDelta previous_buffers_timestamp = buffers_.empty() ? 583 base::TimeDelta() : buffers_.back()->GetDecodeTimestamp(); 584 CHECK(previous_buffers_timestamp <= buffer->GetDecodeTimestamp()); 585 586 base::TimeDelta duration = buffer->duration(); 587 if (duration < base::TimeDelta() || duration == kNoTimestamp()) { 588 MEDIA_LOG(log_cb_) << "Invalid buffer duration: " << duration.InSecondsF(); 589 return false; 590 } 591 592 // The estimated frame duration is the minimum non-zero duration since the 593 // last initialization segment. The minimum is used to ensure frame durations 594 // aren't overestimated. 595 if (duration > base::TimeDelta()) { 596 if (estimated_next_frame_duration_ == kNoTimestamp()) { 597 estimated_next_frame_duration_ = duration; 598 } else { 599 estimated_next_frame_duration_ = 600 std::min(duration, estimated_next_frame_duration_); 601 } 602 } 603 604 buffers_.push_back(buffer); 605 return true; 606 } 607 608 base::TimeDelta WebMClusterParser::Track::GetDurationEstimate() { 609 base::TimeDelta duration = estimated_next_frame_duration_; 610 if (duration != kNoTimestamp()) { 611 DVLOG(3) << __FUNCTION__ << " : using estimated duration"; 612 } else { 613 DVLOG(3) << __FUNCTION__ << " : using hardcoded default duration"; 614 if (is_video_) { 615 duration = base::TimeDelta::FromMilliseconds( 616 kDefaultVideoBufferDurationInMs); 617 } else { 618 duration = base::TimeDelta::FromMilliseconds( 619 kDefaultAudioBufferDurationInMs); 620 } 621 } 622 623 DCHECK(duration > base::TimeDelta()); 624 DCHECK(duration != kNoTimestamp()); 625 return duration; 626 } 627 628 void WebMClusterParser::ClearTextTrackReadyBuffers() { 629 text_buffers_map_.clear(); 630 for (TextTrackMap::iterator it = text_track_map_.begin(); 631 it != text_track_map_.end(); 632 ++it) { 633 it->second.ClearReadyBuffers(); 634 } 635 } 636 637 void WebMClusterParser::ResetTextTracks() { 638 ClearTextTrackReadyBuffers(); 639 for (TextTrackMap::iterator it = text_track_map_.begin(); 640 it != text_track_map_.end(); 641 ++it) { 642 it->second.Reset(); 643 } 644 } 645 646 void WebMClusterParser::UpdateReadyBuffers() { 647 DCHECK(ready_buffer_upper_bound_ == kNoTimestamp()); 648 DCHECK(text_buffers_map_.empty()); 649 650 if (cluster_ended_) { 651 audio_.ApplyDurationEstimateIfNeeded(); 652 video_.ApplyDurationEstimateIfNeeded(); 653 // Per OnBlock(), all text buffers should already have valid durations, so 654 // there is no need to call ApplyDurationEstimateIfNeeded() on text tracks 655 // here. 656 ready_buffer_upper_bound_ = kInfiniteDuration(); 657 DCHECK(ready_buffer_upper_bound_ == audio_.GetReadyUpperBound()); 658 DCHECK(ready_buffer_upper_bound_ == video_.GetReadyUpperBound()); 659 } else { 660 ready_buffer_upper_bound_ = std::min(audio_.GetReadyUpperBound(), 661 video_.GetReadyUpperBound()); 662 DCHECK(base::TimeDelta() <= ready_buffer_upper_bound_); 663 DCHECK(kNoTimestamp() != ready_buffer_upper_bound_); 664 } 665 666 // Prepare each track's ready buffers for retrieval. 667 audio_.ExtractReadyBuffers(ready_buffer_upper_bound_); 668 video_.ExtractReadyBuffers(ready_buffer_upper_bound_); 669 for (TextTrackMap::iterator itr = text_track_map_.begin(); 670 itr != text_track_map_.end(); 671 ++itr) { 672 itr->second.ExtractReadyBuffers(ready_buffer_upper_bound_); 673 } 674 } 675 676 WebMClusterParser::Track* 677 WebMClusterParser::FindTextTrack(int track_num) { 678 const TextTrackMap::iterator it = text_track_map_.find(track_num); 679 680 if (it == text_track_map_.end()) 681 return NULL; 682 683 return &it->second; 684 } 685 686 } // namespace media 687