OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/formats/webm/webm_cluster_parser.h" | 5 #include "media/formats/webm/webm_cluster_parser.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/sys_byteorder.h" | 10 #include "base/sys_byteorder.h" |
(...skipping 24 matching lines...) Expand all Loading... |
35 parser_(kWebMIdCluster, this), | 35 parser_(kWebMIdCluster, this), |
36 last_block_timecode_(-1), | 36 last_block_timecode_(-1), |
37 block_data_size_(-1), | 37 block_data_size_(-1), |
38 block_duration_(-1), | 38 block_duration_(-1), |
39 block_add_id_(-1), | 39 block_add_id_(-1), |
40 block_additional_data_size_(-1), | 40 block_additional_data_size_(-1), |
41 discard_padding_(-1), | 41 discard_padding_(-1), |
42 cluster_timecode_(-1), | 42 cluster_timecode_(-1), |
43 cluster_start_time_(kNoTimestamp()), | 43 cluster_start_time_(kNoTimestamp()), |
44 cluster_ended_(false), | 44 cluster_ended_(false), |
45 audio_(audio_track_num, false, audio_default_duration), | 45 audio_(audio_track_num, false, audio_default_duration, log_cb), |
46 video_(video_track_num, true, video_default_duration), | 46 video_(video_track_num, true, video_default_duration, log_cb), |
| 47 ready_buffer_upper_bound_(kNoTimestamp()), |
47 log_cb_(log_cb) { | 48 log_cb_(log_cb) { |
48 for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin(); | 49 for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin(); |
49 it != text_tracks.end(); | 50 it != text_tracks.end(); |
50 ++it) { | 51 ++it) { |
51 text_track_map_.insert(std::make_pair( | 52 text_track_map_.insert(std::make_pair( |
52 it->first, Track(it->first, false, kNoTimestamp()))); | 53 it->first, Track(it->first, false, kNoTimestamp(), log_cb_))); |
53 } | 54 } |
54 } | 55 } |
55 | 56 |
56 WebMClusterParser::~WebMClusterParser() {} | 57 WebMClusterParser::~WebMClusterParser() {} |
57 | 58 |
58 void WebMClusterParser::Reset() { | 59 void WebMClusterParser::Reset() { |
59 last_block_timecode_ = -1; | 60 last_block_timecode_ = -1; |
60 cluster_timecode_ = -1; | 61 cluster_timecode_ = -1; |
61 cluster_start_time_ = kNoTimestamp(); | 62 cluster_start_time_ = kNoTimestamp(); |
62 cluster_ended_ = false; | 63 cluster_ended_ = false; |
63 parser_.Reset(); | 64 parser_.Reset(); |
64 audio_.Reset(); | 65 audio_.Reset(); |
65 video_.Reset(); | 66 video_.Reset(); |
66 ResetTextTracks(); | 67 ResetTextTracks(); |
| 68 ready_buffer_upper_bound_ = kNoTimestamp(); |
67 } | 69 } |
68 | 70 |
69 int WebMClusterParser::Parse(const uint8* buf, int size) { | 71 int WebMClusterParser::Parse(const uint8* buf, int size) { |
70 audio_.ClearBuffersButKeepLastIfMissingDuration(); | 72 audio_.ClearReadyBuffers(); |
71 video_.ClearBuffersButKeepLastIfMissingDuration(); | 73 video_.ClearReadyBuffers(); |
72 ResetTextTracks(); | 74 ClearTextTrackReadyBuffers(); |
| 75 ready_buffer_upper_bound_ = kNoTimestamp(); |
73 | 76 |
74 int result = parser_.Parse(buf, size); | 77 int result = parser_.Parse(buf, size); |
75 | 78 |
76 if (result < 0) { | 79 if (result < 0) { |
77 cluster_ended_ = false; | 80 cluster_ended_ = false; |
78 return result; | 81 return result; |
79 } | 82 } |
80 | 83 |
81 cluster_ended_ = parser_.IsParsingComplete(); | 84 cluster_ended_ = parser_.IsParsingComplete(); |
82 if (cluster_ended_) { | 85 if (cluster_ended_) { |
(...skipping 15 matching lines...) Expand all Loading... |
98 parser_.Reset(); | 101 parser_.Reset(); |
99 | 102 |
100 last_block_timecode_ = -1; | 103 last_block_timecode_ = -1; |
101 cluster_timecode_ = -1; | 104 cluster_timecode_ = -1; |
102 } | 105 } |
103 | 106 |
104 return result; | 107 return result; |
105 } | 108 } |
106 | 109 |
107 const WebMClusterParser::BufferQueue& WebMClusterParser::GetAudioBuffers() { | 110 const WebMClusterParser::BufferQueue& WebMClusterParser::GetAudioBuffers() { |
108 if (cluster_ended_) | 111 if (ready_buffer_upper_bound_ == kNoTimestamp()) |
109 audio_.ApplyDurationEstimateIfNeeded(); | 112 UpdateReadyBuffers(); |
110 return audio_.buffers(); | 113 |
| 114 return audio_.ready_buffers(); |
111 } | 115 } |
112 | 116 |
113 const WebMClusterParser::BufferQueue& WebMClusterParser::GetVideoBuffers() { | 117 const WebMClusterParser::BufferQueue& WebMClusterParser::GetVideoBuffers() { |
114 if (cluster_ended_) | 118 if (ready_buffer_upper_bound_ == kNoTimestamp()) |
115 video_.ApplyDurationEstimateIfNeeded(); | 119 UpdateReadyBuffers(); |
116 return video_.buffers(); | 120 |
| 121 return video_.ready_buffers(); |
117 } | 122 } |
118 | 123 |
119 const WebMClusterParser::TextBufferQueueMap& | 124 const WebMClusterParser::TextBufferQueueMap& |
120 WebMClusterParser::GetTextBuffers() { | 125 WebMClusterParser::GetTextBuffers() { |
| 126 if (ready_buffer_upper_bound_ == kNoTimestamp()) |
| 127 UpdateReadyBuffers(); |
| 128 |
121 // Translate our |text_track_map_| into |text_buffers_map_|, inserting rows in | 129 // Translate our |text_track_map_| into |text_buffers_map_|, inserting rows in |
122 // the output only for non-empty text buffer queues in |text_track_map_|. | 130 // the output only for non-empty ready_buffer() queues in |text_track_map_|. |
123 text_buffers_map_.clear(); | 131 text_buffers_map_.clear(); |
124 for (TextTrackMap::const_iterator itr = text_track_map_.begin(); | 132 for (TextTrackMap::const_iterator itr = text_track_map_.begin(); |
125 itr != text_track_map_.end(); | 133 itr != text_track_map_.end(); |
126 ++itr) { | 134 ++itr) { |
127 // Per OnBlock(), all text buffers should already have valid durations, so | 135 const BufferQueue& text_buffers = itr->second.ready_buffers(); |
128 // there is no need to call | |
129 // itr->second.ApplyDurationEstimateIfNeeded() here. | |
130 const BufferQueue& text_buffers = itr->second.buffers(); | |
131 if (!text_buffers.empty()) | 136 if (!text_buffers.empty()) |
132 text_buffers_map_.insert(std::make_pair(itr->first, text_buffers)); | 137 text_buffers_map_.insert(std::make_pair(itr->first, text_buffers)); |
133 } | 138 } |
134 | 139 |
135 return text_buffers_map_; | 140 return text_buffers_map_; |
136 } | 141 } |
137 | 142 |
138 WebMParserClient* WebMClusterParser::OnListStart(int id) { | 143 WebMParserClient* WebMClusterParser::OnListStart(int id) { |
139 if (id == kWebMIdCluster) { | 144 if (id == kWebMIdCluster) { |
140 cluster_timecode_ = -1; | 145 cluster_timecode_ = -1; |
(...skipping 265 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
406 } | 411 } |
407 | 412 |
408 if (discard_padding != 0) { | 413 if (discard_padding != 0) { |
409 buffer->set_discard_padding(base::TimeDelta::FromMicroseconds( | 414 buffer->set_discard_padding(base::TimeDelta::FromMicroseconds( |
410 discard_padding / 1000)); | 415 discard_padding / 1000)); |
411 } | 416 } |
412 | 417 |
413 return track->AddBuffer(buffer); | 418 return track->AddBuffer(buffer); |
414 } | 419 } |
415 | 420 |
416 WebMClusterParser::Track::Track(int track_num, bool is_video, | 421 WebMClusterParser::Track::Track(int track_num, |
417 base::TimeDelta default_duration) | 422 bool is_video, |
| 423 base::TimeDelta default_duration, |
| 424 const LogCB& log_cb) |
418 : track_num_(track_num), | 425 : track_num_(track_num), |
419 is_video_(is_video), | 426 is_video_(is_video), |
420 default_duration_(default_duration), | 427 default_duration_(default_duration), |
421 estimated_next_frame_duration_(kNoTimestamp()) { | 428 estimated_next_frame_duration_(kNoTimestamp()), |
| 429 log_cb_(log_cb) { |
422 DCHECK(default_duration_ == kNoTimestamp() || | 430 DCHECK(default_duration_ == kNoTimestamp() || |
423 default_duration_ > base::TimeDelta()); | 431 default_duration_ > base::TimeDelta()); |
424 } | 432 } |
425 | 433 |
426 WebMClusterParser::Track::~Track() {} | 434 WebMClusterParser::Track::~Track() {} |
427 | 435 |
| 436 base::TimeDelta WebMClusterParser::Track::GetReadyUpperBound() { |
| 437 DCHECK(ready_buffers_.empty()); |
| 438 if (last_added_buffer_missing_duration_) |
| 439 return last_added_buffer_missing_duration_->GetDecodeTimestamp(); |
| 440 |
| 441 return kInfiniteDuration(); |
| 442 } |
| 443 |
| 444 void WebMClusterParser::Track::ExtractReadyBuffers( |
| 445 const base::TimeDelta before_timestamp) { |
| 446 DCHECK(ready_buffers_.empty()); |
| 447 DCHECK(base::TimeDelta() <= before_timestamp); |
| 448 DCHECK(kNoTimestamp() != before_timestamp); |
| 449 |
| 450 if (buffers_.empty()) |
| 451 return; |
| 452 |
| 453 if (buffers_.back()->GetDecodeTimestamp() < before_timestamp) { |
| 454 // All of |buffers_| are ready. |
| 455 ready_buffers_.swap(buffers_); |
| 456 DVLOG(3) << __FUNCTION__ << " : " << track_num_ << " All " |
| 457 << ready_buffers_.size() << " are ready: before upper bound ts " |
| 458 << before_timestamp.InSecondsF(); |
| 459 return; |
| 460 } |
| 461 |
| 462 // Not all of |buffers_| are ready yet. Move any that are ready to |
| 463 // |ready_buffers_|. |
| 464 while (true) { |
| 465 const scoped_refptr<StreamParserBuffer>& buffer = buffers_.front(); |
| 466 if (buffer->GetDecodeTimestamp() >= before_timestamp) |
| 467 break; |
| 468 ready_buffers_.push_back(buffer); |
| 469 buffers_.pop_front(); |
| 470 DCHECK(!buffers_.empty()); |
| 471 } |
| 472 |
| 473 DVLOG(3) << __FUNCTION__ << " : " << track_num_ << " Only " |
| 474 << ready_buffers_.size() << " ready, " << buffers_.size() |
| 475 << " at or after upper bound ts " << before_timestamp.InSecondsF(); |
| 476 } |
| 477 |
428 bool WebMClusterParser::Track::AddBuffer( | 478 bool WebMClusterParser::Track::AddBuffer( |
429 const scoped_refptr<StreamParserBuffer>& buffer) { | 479 const scoped_refptr<StreamParserBuffer>& buffer) { |
430 DVLOG(2) << "AddBuffer() : " << track_num_ | 480 DVLOG(2) << "AddBuffer() : " << track_num_ |
431 << " ts " << buffer->timestamp().InSecondsF() | 481 << " ts " << buffer->timestamp().InSecondsF() |
432 << " dur " << buffer->duration().InSecondsF() | 482 << " dur " << buffer->duration().InSecondsF() |
433 << " kf " << buffer->IsKeyframe() | 483 << " kf " << buffer->IsKeyframe() |
434 << " size " << buffer->data_size(); | 484 << " size " << buffer->data_size(); |
435 | 485 |
436 if (last_added_buffer_missing_duration_) { | 486 if (last_added_buffer_missing_duration_) { |
437 base::TimeDelta derived_duration = | 487 base::TimeDelta derived_duration = |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
474 << last_added_buffer_missing_duration_->duration().InSecondsF() | 524 << last_added_buffer_missing_duration_->duration().InSecondsF() |
475 << " kf " << last_added_buffer_missing_duration_->IsKeyframe() | 525 << " kf " << last_added_buffer_missing_duration_->IsKeyframe() |
476 << " size " << last_added_buffer_missing_duration_->data_size(); | 526 << " size " << last_added_buffer_missing_duration_->data_size(); |
477 | 527 |
478 // Don't use the applied duration as a future estimation (don't use | 528 // Don't use the applied duration as a future estimation (don't use |
479 // QueueBuffer() here.) | 529 // QueueBuffer() here.) |
480 buffers_.push_back(last_added_buffer_missing_duration_); | 530 buffers_.push_back(last_added_buffer_missing_duration_); |
481 last_added_buffer_missing_duration_ = NULL; | 531 last_added_buffer_missing_duration_ = NULL; |
482 } | 532 } |
483 | 533 |
484 void WebMClusterParser::Track::ClearBuffersButKeepLastIfMissingDuration() { | 534 void WebMClusterParser::Track::ClearReadyBuffers() { |
485 // Note that |estimated_next_frame_duration_| is not reset, so it can be | 535 // Note that |buffers_| are kept and |estimated_next_frame_duration_| is not |
486 // reused on subsequent buffers added to this instance. | 536 // reset here. |
487 buffers_.clear(); | 537 ready_buffers_.clear(); |
488 } | 538 } |
489 | 539 |
490 void WebMClusterParser::Track::Reset() { | 540 void WebMClusterParser::Track::Reset() { |
491 ClearBuffersButKeepLastIfMissingDuration(); | 541 ClearReadyBuffers(); |
| 542 buffers_.clear(); |
492 last_added_buffer_missing_duration_ = NULL; | 543 last_added_buffer_missing_duration_ = NULL; |
493 } | 544 } |
494 | 545 |
495 bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const { | 546 bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const { |
496 // For now, assume that all blocks are keyframes for datatypes other than | 547 // For now, assume that all blocks are keyframes for datatypes other than |
497 // video. This is a valid assumption for Vorbis, WebVTT, & Opus. | 548 // video. This is a valid assumption for Vorbis, WebVTT, & Opus. |
498 if (!is_video_) | 549 if (!is_video_) |
499 return true; | 550 return true; |
500 | 551 |
501 // Make sure the block is big enough for the minimal keyframe header size. | 552 // Make sure the block is big enough for the minimal keyframe header size. |
502 if (size < 7) | 553 if (size < 7) |
503 return false; | 554 return false; |
504 | 555 |
505 // The LSb of the first byte must be a 0 for a keyframe. | 556 // The LSb of the first byte must be a 0 for a keyframe. |
506 // http://tools.ietf.org/html/rfc6386 Section 19.1 | 557 // http://tools.ietf.org/html/rfc6386 Section 19.1 |
507 if ((data[0] & 0x01) != 0) | 558 if ((data[0] & 0x01) != 0) |
508 return false; | 559 return false; |
509 | 560 |
510 // Verify VP8 keyframe startcode. | 561 // Verify VP8 keyframe startcode. |
511 // http://tools.ietf.org/html/rfc6386 Section 19.1 | 562 // http://tools.ietf.org/html/rfc6386 Section 19.1 |
512 if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) | 563 if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) |
513 return false; | 564 return false; |
514 | 565 |
515 return true; | 566 return true; |
516 } | 567 } |
517 | 568 |
518 bool WebMClusterParser::Track::QueueBuffer( | 569 bool WebMClusterParser::Track::QueueBuffer( |
519 const scoped_refptr<StreamParserBuffer>& buffer) { | 570 const scoped_refptr<StreamParserBuffer>& buffer) { |
520 DCHECK(!last_added_buffer_missing_duration_); | 571 DCHECK(!last_added_buffer_missing_duration_); |
| 572 |
| 573 // WebMClusterParser::OnBlock() gives MEDIA_LOG and parse error on decreasing |
| 574 // block timecode detection within a cluster. Therefore, we should not see |
| 575 // those here. |
| 576 base::TimeDelta previous_buffers_timestamp = buffers_.empty() ? |
| 577 base::TimeDelta() : buffers_.back()->GetDecodeTimestamp(); |
| 578 CHECK(previous_buffers_timestamp <= buffer->GetDecodeTimestamp()); |
| 579 |
521 base::TimeDelta duration = buffer->duration(); | 580 base::TimeDelta duration = buffer->duration(); |
522 if (duration < base::TimeDelta() || duration == kNoTimestamp()) { | 581 if (duration < base::TimeDelta() || duration == kNoTimestamp()) { |
523 DVLOG(2) << "QueueBuffer() : Invalid buffer duration: " | 582 MEDIA_LOG(log_cb_) << "Invalid buffer duration: " << duration.InSecondsF(); |
524 << duration.InSecondsF(); | |
525 return false; | 583 return false; |
526 } | 584 } |
527 | 585 |
528 // The estimated frame duration is the minimum non-zero duration since the | 586 // The estimated frame duration is the minimum non-zero duration since the |
529 // last initialization segment. The minimum is used to ensure frame durations | 587 // last initialization segment. The minimum is used to ensure frame durations |
530 // aren't overestimated. | 588 // aren't overestimated. |
531 if (duration > base::TimeDelta()) { | 589 if (duration > base::TimeDelta()) { |
532 if (estimated_next_frame_duration_ == kNoTimestamp()) { | 590 if (estimated_next_frame_duration_ == kNoTimestamp()) { |
533 estimated_next_frame_duration_ = duration; | 591 estimated_next_frame_duration_ = duration; |
534 } else { | 592 } else { |
(...skipping 19 matching lines...) Expand all Loading... |
554 duration = base::TimeDelta::FromMilliseconds( | 612 duration = base::TimeDelta::FromMilliseconds( |
555 kDefaultAudioBufferDurationInMs); | 613 kDefaultAudioBufferDurationInMs); |
556 } | 614 } |
557 } | 615 } |
558 | 616 |
559 DCHECK(duration > base::TimeDelta()); | 617 DCHECK(duration > base::TimeDelta()); |
560 DCHECK(duration != kNoTimestamp()); | 618 DCHECK(duration != kNoTimestamp()); |
561 return duration; | 619 return duration; |
562 } | 620 } |
563 | 621 |
| 622 void WebMClusterParser::ClearTextTrackReadyBuffers() { |
| 623 text_buffers_map_.clear(); |
| 624 for (TextTrackMap::iterator it = text_track_map_.begin(); |
| 625 it != text_track_map_.end(); |
| 626 ++it) { |
| 627 it->second.ClearReadyBuffers(); |
| 628 } |
| 629 } |
| 630 |
564 void WebMClusterParser::ResetTextTracks() { | 631 void WebMClusterParser::ResetTextTracks() { |
565 text_buffers_map_.clear(); | 632 ClearTextTrackReadyBuffers(); |
566 for (TextTrackMap::iterator it = text_track_map_.begin(); | 633 for (TextTrackMap::iterator it = text_track_map_.begin(); |
567 it != text_track_map_.end(); | 634 it != text_track_map_.end(); |
568 ++it) { | 635 ++it) { |
569 it->second.Reset(); | 636 it->second.Reset(); |
570 } | 637 } |
571 } | 638 } |
572 | 639 |
| 640 void WebMClusterParser::UpdateReadyBuffers() { |
| 641 DCHECK(ready_buffer_upper_bound_ == kNoTimestamp()); |
| 642 DCHECK(text_buffers_map_.empty()); |
| 643 |
| 644 if (cluster_ended_) { |
| 645 audio_.ApplyDurationEstimateIfNeeded(); |
| 646 video_.ApplyDurationEstimateIfNeeded(); |
| 647 // Per OnBlock(), all text buffers should already have valid durations, so |
| 648 // there is no need to call ApplyDurationEstimateIfNeeded() on text tracks |
| 649 // here. |
| 650 ready_buffer_upper_bound_ = kInfiniteDuration(); |
| 651 DCHECK(ready_buffer_upper_bound_ == audio_.GetReadyUpperBound()); |
| 652 DCHECK(ready_buffer_upper_bound_ == video_.GetReadyUpperBound()); |
| 653 } else { |
| 654 ready_buffer_upper_bound_ = std::min(audio_.GetReadyUpperBound(), |
| 655 video_.GetReadyUpperBound()); |
| 656 DCHECK(base::TimeDelta() <= ready_buffer_upper_bound_); |
| 657 DCHECK(kNoTimestamp() != ready_buffer_upper_bound_); |
| 658 } |
| 659 |
| 660 // Prepare each track's ready buffers for retrieval. |
| 661 audio_.ExtractReadyBuffers(ready_buffer_upper_bound_); |
| 662 video_.ExtractReadyBuffers(ready_buffer_upper_bound_); |
| 663 for (TextTrackMap::iterator itr = text_track_map_.begin(); |
| 664 itr != text_track_map_.end(); |
| 665 ++itr) { |
| 666 itr->second.ExtractReadyBuffers(ready_buffer_upper_bound_); |
| 667 } |
| 668 } |
| 669 |
573 WebMClusterParser::Track* | 670 WebMClusterParser::Track* |
574 WebMClusterParser::FindTextTrack(int track_num) { | 671 WebMClusterParser::FindTextTrack(int track_num) { |
575 const TextTrackMap::iterator it = text_track_map_.find(track_num); | 672 const TextTrackMap::iterator it = text_track_map_.find(track_num); |
576 | 673 |
577 if (it == text_track_map_.end()) | 674 if (it == text_track_map_.end()) |
578 return NULL; | 675 return NULL; |
579 | 676 |
580 return &it->second; | 677 return &it->second; |
581 } | 678 } |
582 | 679 |
583 } // namespace media | 680 } // namespace media |
OLD | NEW |