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() { |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |