Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(261)

Side by Side Diff: media/formats/webm/webm_cluster_parser.cc

Issue 239343007: MSE: Make WebMClusterParser hold back buffers at or beyond buffer missing duration (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix some errors in comments. Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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() {
121 // Translate our |text_track_map_| into |text_buffers_map_|, inserting rows in 126 if (ready_buffer_upper_bound_ == kNoTimestamp())
122 // the output only for non-empty text buffer queues in |text_track_map_|. 127 UpdateReadyBuffers();
123 text_buffers_map_.clear();
acolwell GONE FROM CHROMIUM 2014/04/23 22:36:39 Why move this code? It seems like this is more con
wolenetz 2014/04/25 20:04:21 This was just an optimization to iterate through t
124 for (TextTrackMap::const_iterator itr = text_track_map_.begin();
125 itr != text_track_map_.end();
126 ++itr) {
127 // Per OnBlock(), all text buffers should already have valid durations, so
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())
132 text_buffers_map_.insert(std::make_pair(itr->first, text_buffers));
133 }
134 128
135 return text_buffers_map_; 129 return text_buffers_map_;
136 } 130 }
137 131
138 WebMParserClient* WebMClusterParser::OnListStart(int id) { 132 WebMParserClient* WebMClusterParser::OnListStart(int id) {
139 if (id == kWebMIdCluster) { 133 if (id == kWebMIdCluster) {
140 cluster_timecode_ = -1; 134 cluster_timecode_ = -1;
141 cluster_start_time_ = kNoTimestamp(); 135 cluster_start_time_ = kNoTimestamp();
142 } else if (id == kWebMIdBlockGroup) { 136 } else if (id == kWebMIdBlockGroup) {
143 block_data_.reset(); 137 block_data_.reset();
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after
406 } 400 }
407 401
408 if (discard_padding != 0) { 402 if (discard_padding != 0) {
409 buffer->set_discard_padding(base::TimeDelta::FromMicroseconds( 403 buffer->set_discard_padding(base::TimeDelta::FromMicroseconds(
410 discard_padding / 1000)); 404 discard_padding / 1000));
411 } 405 }
412 406
413 return track->AddBuffer(buffer); 407 return track->AddBuffer(buffer);
414 } 408 }
415 409
416 WebMClusterParser::Track::Track(int track_num, bool is_video, 410 WebMClusterParser::Track::Track(int track_num,
417 base::TimeDelta default_duration) 411 bool is_video,
412 base::TimeDelta default_duration,
413 const LogCB& log_cb)
418 : track_num_(track_num), 414 : track_num_(track_num),
419 is_video_(is_video), 415 is_video_(is_video),
420 default_duration_(default_duration), 416 default_duration_(default_duration),
421 estimated_next_frame_duration_(kNoTimestamp()) { 417 estimated_next_frame_duration_(kNoTimestamp()),
418 log_cb_(log_cb) {
422 DCHECK(default_duration_ == kNoTimestamp() || 419 DCHECK(default_duration_ == kNoTimestamp() ||
423 default_duration_ > base::TimeDelta()); 420 default_duration_ > base::TimeDelta());
424 } 421 }
425 422
426 WebMClusterParser::Track::~Track() {} 423 WebMClusterParser::Track::~Track() {}
427 424
425 base::TimeDelta WebMClusterParser::Track::GetReadyUpperBound() {
426 DCHECK(ready_buffers_.empty());
427 if (last_added_buffer_missing_duration_)
428 return last_added_buffer_missing_duration_->GetDecodeTimestamp();
429
430 return kInfiniteDuration();
431 }
432
433 const WebMClusterParser::BufferQueue&
434 WebMClusterParser::Track::ExtractReadyBuffers(
435 const base::TimeDelta before_timestamp) {
436 DCHECK(ready_buffers_.empty());
437 DCHECK(base::TimeDelta() <= before_timestamp);
438 DCHECK(kNoTimestamp() != before_timestamp);
439
440 if (!buffers_.empty()) {
acolwell GONE FROM CHROMIUM 2014/04/23 22:36:39 nit: reverse condition and early return.
wolenetz 2014/04/25 20:04:21 Done.
441 if (buffers_.back()->GetDecodeTimestamp() < before_timestamp) {
442 // All of |buffers_| are ready.
443 ready_buffers_.swap(buffers_);
444 DVLOG(3) << __FUNCTION__ << " : " << track_num_ << " All "
445 << ready_buffers_.size() << " are ready: before upper bound ts "
446 << before_timestamp.InSecondsF();
acolwell GONE FROM CHROMIUM 2014/04/23 22:36:39 nit:early return.
wolenetz 2014/04/25 20:04:21 Done.
447 } else {
448 // Not all of |buffers_| are ready yet. Move any that are ready to
449 // |ready_buffers_|.
450 while (true) {
451 const scoped_refptr<StreamParserBuffer>& buffer = buffers_.front();
452 if (buffer->GetDecodeTimestamp() >= before_timestamp)
453 break;
454 ready_buffers_.push_back(buffer);
455 buffers_.pop_front();
456 DCHECK(!buffers_.empty());
457 }
458 DVLOG(3) << __FUNCTION__ << " : " << track_num_ << " Only "
459 << ready_buffers_.size() << " are ready, " << buffers_.size()
460 << " are at or after upper bound ts "
461 << before_timestamp.InSecondsF();
462 }
463 }
464
465 return ready_buffers_;
acolwell GONE FROM CHROMIUM 2014/04/23 22:36:39 nit: looks like this is valuable at only one call
wolenetz 2014/04/25 20:04:21 Done.
466 }
467
428 bool WebMClusterParser::Track::AddBuffer( 468 bool WebMClusterParser::Track::AddBuffer(
429 const scoped_refptr<StreamParserBuffer>& buffer) { 469 const scoped_refptr<StreamParserBuffer>& buffer) {
430 DVLOG(2) << "AddBuffer() : " << track_num_ 470 DVLOG(2) << "AddBuffer() : " << track_num_
431 << " ts " << buffer->timestamp().InSecondsF() 471 << " ts " << buffer->timestamp().InSecondsF()
432 << " dur " << buffer->duration().InSecondsF() 472 << " dur " << buffer->duration().InSecondsF()
433 << " kf " << buffer->IsKeyframe() 473 << " kf " << buffer->IsKeyframe()
434 << " size " << buffer->data_size(); 474 << " size " << buffer->data_size();
435 475
436 if (last_added_buffer_missing_duration_) { 476 if (last_added_buffer_missing_duration_) {
437 base::TimeDelta derived_duration = 477 base::TimeDelta derived_duration =
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
474 << last_added_buffer_missing_duration_->duration().InSecondsF() 514 << last_added_buffer_missing_duration_->duration().InSecondsF()
475 << " kf " << last_added_buffer_missing_duration_->IsKeyframe() 515 << " kf " << last_added_buffer_missing_duration_->IsKeyframe()
476 << " size " << last_added_buffer_missing_duration_->data_size(); 516 << " size " << last_added_buffer_missing_duration_->data_size();
477 517
478 // Don't use the applied duration as a future estimation (don't use 518 // Don't use the applied duration as a future estimation (don't use
479 // QueueBuffer() here.) 519 // QueueBuffer() here.)
480 buffers_.push_back(last_added_buffer_missing_duration_); 520 buffers_.push_back(last_added_buffer_missing_duration_);
481 last_added_buffer_missing_duration_ = NULL; 521 last_added_buffer_missing_duration_ = NULL;
482 } 522 }
483 523
484 void WebMClusterParser::Track::ClearBuffersButKeepLastIfMissingDuration() { 524 void WebMClusterParser::Track::ClearReadyBuffers() {
485 // Note that |estimated_next_frame_duration_| is not reset, so it can be 525 // Note that |buffers_| are kept and |estimated_next_frame_duration_| is not
486 // reused on subsequent buffers added to this instance. 526 // reset here.
487 buffers_.clear(); 527 ready_buffers_.clear();
488 } 528 }
489 529
490 void WebMClusterParser::Track::Reset() { 530 void WebMClusterParser::Track::Reset() {
491 ClearBuffersButKeepLastIfMissingDuration(); 531 ClearReadyBuffers();
532 buffers_.clear();
492 last_added_buffer_missing_duration_ = NULL; 533 last_added_buffer_missing_duration_ = NULL;
493 } 534 }
494 535
495 bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const { 536 bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const {
496 // For now, assume that all blocks are keyframes for datatypes other than 537 // For now, assume that all blocks are keyframes for datatypes other than
497 // video. This is a valid assumption for Vorbis, WebVTT, & Opus. 538 // video. This is a valid assumption for Vorbis, WebVTT, & Opus.
498 if (!is_video_) 539 if (!is_video_)
499 return true; 540 return true;
500 541
501 // Make sure the block is big enough for the minimal keyframe header size. 542 // Make sure the block is big enough for the minimal keyframe header size.
502 if (size < 7) 543 if (size < 7)
503 return false; 544 return false;
504 545
505 // The LSb of the first byte must be a 0 for a keyframe. 546 // The LSb of the first byte must be a 0 for a keyframe.
506 // http://tools.ietf.org/html/rfc6386 Section 19.1 547 // http://tools.ietf.org/html/rfc6386 Section 19.1
507 if ((data[0] & 0x01) != 0) 548 if ((data[0] & 0x01) != 0)
508 return false; 549 return false;
509 550
510 // Verify VP8 keyframe startcode. 551 // Verify VP8 keyframe startcode.
511 // http://tools.ietf.org/html/rfc6386 Section 19.1 552 // http://tools.ietf.org/html/rfc6386 Section 19.1
512 if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) 553 if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a)
513 return false; 554 return false;
514 555
515 return true; 556 return true;
516 } 557 }
517 558
518 bool WebMClusterParser::Track::QueueBuffer( 559 bool WebMClusterParser::Track::QueueBuffer(
519 const scoped_refptr<StreamParserBuffer>& buffer) { 560 const scoped_refptr<StreamParserBuffer>& buffer) {
520 DCHECK(!last_added_buffer_missing_duration_); 561 DCHECK(!last_added_buffer_missing_duration_);
562
563 // WebMClusterParser::OnBlock() gives MEDIA_LOG and parse error on decreasing
564 // block timecode detection within a cluster. Therefore, we should not see
565 // those here.
566 base::TimeDelta previous_buffers_timestamp = buffers_.empty() ?
567 base::TimeDelta() : buffers_.back()->GetDecodeTimestamp();
568 CHECK(previous_buffers_timestamp <= buffer->GetDecodeTimestamp());
569
521 base::TimeDelta duration = buffer->duration(); 570 base::TimeDelta duration = buffer->duration();
522 if (duration < base::TimeDelta() || duration == kNoTimestamp()) { 571 if (duration < base::TimeDelta() || duration == kNoTimestamp()) {
523 DVLOG(2) << "QueueBuffer() : Invalid buffer duration: " 572 MEDIA_LOG(log_cb_) << "Invalid buffer duration: " << duration.InSecondsF();
524 << duration.InSecondsF();
525 return false; 573 return false;
526 } 574 }
527 575
528 // The estimated frame duration is the minimum non-zero duration since the 576 // The estimated frame duration is the minimum non-zero duration since the
529 // last initialization segment. The minimum is used to ensure frame durations 577 // last initialization segment. The minimum is used to ensure frame durations
530 // aren't overestimated. 578 // aren't overestimated.
531 if (duration > base::TimeDelta()) { 579 if (duration > base::TimeDelta()) {
532 if (estimated_next_frame_duration_ == kNoTimestamp()) { 580 if (estimated_next_frame_duration_ == kNoTimestamp()) {
533 estimated_next_frame_duration_ = duration; 581 estimated_next_frame_duration_ = duration;
534 } else { 582 } else {
(...skipping 19 matching lines...) Expand all
554 duration = base::TimeDelta::FromMilliseconds( 602 duration = base::TimeDelta::FromMilliseconds(
555 kDefaultAudioBufferDurationInMs); 603 kDefaultAudioBufferDurationInMs);
556 } 604 }
557 } 605 }
558 606
559 DCHECK(duration > base::TimeDelta()); 607 DCHECK(duration > base::TimeDelta());
560 DCHECK(duration != kNoTimestamp()); 608 DCHECK(duration != kNoTimestamp());
561 return duration; 609 return duration;
562 } 610 }
563 611
612 void WebMClusterParser::ClearTextTrackReadyBuffers() {
613 text_buffers_map_.clear();
614 for (TextTrackMap::iterator it = text_track_map_.begin();
615 it != text_track_map_.end();
616 ++it) {
617 it->second.ClearReadyBuffers();
618 }
619 }
620
564 void WebMClusterParser::ResetTextTracks() { 621 void WebMClusterParser::ResetTextTracks() {
565 text_buffers_map_.clear(); 622 ClearTextTrackReadyBuffers();
566 for (TextTrackMap::iterator it = text_track_map_.begin(); 623 for (TextTrackMap::iterator it = text_track_map_.begin();
567 it != text_track_map_.end(); 624 it != text_track_map_.end();
568 ++it) { 625 ++it) {
569 it->second.Reset(); 626 it->second.Reset();
570 } 627 }
571 } 628 }
572 629
630 void WebMClusterParser::UpdateReadyBuffers() {
631 DCHECK(ready_buffer_upper_bound_ == kNoTimestamp());
632 DCHECK(text_buffers_map_.empty());
633
634 if (cluster_ended_) {
635 audio_.ApplyDurationEstimateIfNeeded();
636 video_.ApplyDurationEstimateIfNeeded();
637 // Per OnBlock(), all text buffers should already have valid durations, so
638 // there is no need to call ApplyDurationEstimateIfNeeded() on text tracks
639 // here.
640 ready_buffer_upper_bound_ = kInfiniteDuration();
641 DCHECK(ready_buffer_upper_bound_ == audio_.GetReadyUpperBound());
642 DCHECK(ready_buffer_upper_bound_ == video_.GetReadyUpperBound());
643 } else {
644 ready_buffer_upper_bound_ = std::min(audio_.GetReadyUpperBound(),
645 video_.GetReadyUpperBound());
646 DCHECK(base::TimeDelta() <= ready_buffer_upper_bound_);
647 DCHECK(kNoTimestamp() != ready_buffer_upper_bound_);
648 }
649
650 // Prepare each track's ready buffers for retrieval.
651 audio_.ExtractReadyBuffers(ready_buffer_upper_bound_);
652 video_.ExtractReadyBuffers(ready_buffer_upper_bound_);
653 for (TextTrackMap::iterator itr = text_track_map_.begin();
654 itr != text_track_map_.end();
655 ++itr) {
656 const BufferQueue& text_buffers = itr->second.ExtractReadyBuffers(
657 ready_buffer_upper_bound_);
658 if (!text_buffers.empty())
659 text_buffers_map_.insert(std::make_pair(itr->first, text_buffers));
acolwell GONE FROM CHROMIUM 2014/04/23 22:36:39 Why are these staged here? I would expect these to
wolenetz 2014/04/25 20:04:21 Done (see my reply to GetTextBuffers() comment, ab
660 }
661 }
662
573 WebMClusterParser::Track* 663 WebMClusterParser::Track*
574 WebMClusterParser::FindTextTrack(int track_num) { 664 WebMClusterParser::FindTextTrack(int track_num) {
575 const TextTrackMap::iterator it = text_track_map_.find(track_num); 665 const TextTrackMap::iterator it = text_track_map_.find(track_num);
576 666
577 if (it == text_track_map_.end()) 667 if (it == text_track_map_.end())
578 return NULL; 668 return NULL;
579 669
580 return &it->second; 670 return &it->second;
581 } 671 }
582 672
583 } // namespace media 673 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698