Index: media/formats/webm/webm_cluster_parser.cc |
diff --git a/media/formats/webm/webm_cluster_parser.cc b/media/formats/webm/webm_cluster_parser.cc |
index e206107c8b4f6d75b9b0205e401b3e38a8b8be48..81b8fdf436e541747e60fb552b9d5591d454f252 100644 |
--- a/media/formats/webm/webm_cluster_parser.cc |
+++ b/media/formats/webm/webm_cluster_parser.cc |
@@ -42,14 +42,15 @@ WebMClusterParser::WebMClusterParser( |
cluster_timecode_(-1), |
cluster_start_time_(kNoTimestamp()), |
cluster_ended_(false), |
- audio_(audio_track_num, false, audio_default_duration), |
- video_(video_track_num, true, video_default_duration), |
+ audio_(audio_track_num, false, audio_default_duration, log_cb), |
+ video_(video_track_num, true, video_default_duration, log_cb), |
+ ready_buffer_upper_bound_(kNoTimestamp()), |
log_cb_(log_cb) { |
for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin(); |
it != text_tracks.end(); |
++it) { |
text_track_map_.insert(std::make_pair( |
- it->first, Track(it->first, false, kNoTimestamp()))); |
+ it->first, Track(it->first, false, kNoTimestamp(), log_cb_))); |
} |
} |
@@ -64,12 +65,14 @@ void WebMClusterParser::Reset() { |
audio_.Reset(); |
video_.Reset(); |
ResetTextTracks(); |
+ ready_buffer_upper_bound_ = kNoTimestamp(); |
} |
int WebMClusterParser::Parse(const uint8* buf, int size) { |
- audio_.ClearBuffersButKeepLastIfMissingDuration(); |
- video_.ClearBuffersButKeepLastIfMissingDuration(); |
- ResetTextTracks(); |
+ audio_.ClearReadyBuffers(); |
+ video_.ClearReadyBuffers(); |
+ ClearTextTrackReadyBuffers(); |
+ ready_buffer_upper_bound_ = kNoTimestamp(); |
int result = parser_.Parse(buf, size); |
@@ -105,32 +108,23 @@ int WebMClusterParser::Parse(const uint8* buf, int size) { |
} |
const WebMClusterParser::BufferQueue& WebMClusterParser::GetAudioBuffers() { |
- if (cluster_ended_) |
- audio_.ApplyDurationEstimateIfNeeded(); |
- return audio_.buffers(); |
+ if (ready_buffer_upper_bound_ == kNoTimestamp()) |
+ UpdateReadyBuffers(); |
+ |
+ return audio_.ready_buffers(); |
} |
const WebMClusterParser::BufferQueue& WebMClusterParser::GetVideoBuffers() { |
- if (cluster_ended_) |
- video_.ApplyDurationEstimateIfNeeded(); |
- return video_.buffers(); |
+ if (ready_buffer_upper_bound_ == kNoTimestamp()) |
+ UpdateReadyBuffers(); |
+ |
+ return video_.ready_buffers(); |
} |
const WebMClusterParser::TextBufferQueueMap& |
WebMClusterParser::GetTextBuffers() { |
- // Translate our |text_track_map_| into |text_buffers_map_|, inserting rows in |
- // the output only for non-empty text buffer queues in |text_track_map_|. |
- 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
|
- for (TextTrackMap::const_iterator itr = text_track_map_.begin(); |
- itr != text_track_map_.end(); |
- ++itr) { |
- // Per OnBlock(), all text buffers should already have valid durations, so |
- // there is no need to call |
- // itr->second.ApplyDurationEstimateIfNeeded() here. |
- const BufferQueue& text_buffers = itr->second.buffers(); |
- if (!text_buffers.empty()) |
- text_buffers_map_.insert(std::make_pair(itr->first, text_buffers)); |
- } |
+ if (ready_buffer_upper_bound_ == kNoTimestamp()) |
+ UpdateReadyBuffers(); |
return text_buffers_map_; |
} |
@@ -413,18 +407,64 @@ bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num, |
return track->AddBuffer(buffer); |
} |
-WebMClusterParser::Track::Track(int track_num, bool is_video, |
- base::TimeDelta default_duration) |
+WebMClusterParser::Track::Track(int track_num, |
+ bool is_video, |
+ base::TimeDelta default_duration, |
+ const LogCB& log_cb) |
: track_num_(track_num), |
is_video_(is_video), |
default_duration_(default_duration), |
- estimated_next_frame_duration_(kNoTimestamp()) { |
+ estimated_next_frame_duration_(kNoTimestamp()), |
+ log_cb_(log_cb) { |
DCHECK(default_duration_ == kNoTimestamp() || |
default_duration_ > base::TimeDelta()); |
} |
WebMClusterParser::Track::~Track() {} |
+base::TimeDelta WebMClusterParser::Track::GetReadyUpperBound() { |
+ DCHECK(ready_buffers_.empty()); |
+ if (last_added_buffer_missing_duration_) |
+ return last_added_buffer_missing_duration_->GetDecodeTimestamp(); |
+ |
+ return kInfiniteDuration(); |
+} |
+ |
+const WebMClusterParser::BufferQueue& |
+WebMClusterParser::Track::ExtractReadyBuffers( |
+ const base::TimeDelta before_timestamp) { |
+ DCHECK(ready_buffers_.empty()); |
+ DCHECK(base::TimeDelta() <= before_timestamp); |
+ DCHECK(kNoTimestamp() != before_timestamp); |
+ |
+ 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.
|
+ if (buffers_.back()->GetDecodeTimestamp() < before_timestamp) { |
+ // All of |buffers_| are ready. |
+ ready_buffers_.swap(buffers_); |
+ DVLOG(3) << __FUNCTION__ << " : " << track_num_ << " All " |
+ << ready_buffers_.size() << " are ready: before upper bound ts " |
+ << before_timestamp.InSecondsF(); |
acolwell GONE FROM CHROMIUM
2014/04/23 22:36:39
nit:early return.
wolenetz
2014/04/25 20:04:21
Done.
|
+ } else { |
+ // Not all of |buffers_| are ready yet. Move any that are ready to |
+ // |ready_buffers_|. |
+ while (true) { |
+ const scoped_refptr<StreamParserBuffer>& buffer = buffers_.front(); |
+ if (buffer->GetDecodeTimestamp() >= before_timestamp) |
+ break; |
+ ready_buffers_.push_back(buffer); |
+ buffers_.pop_front(); |
+ DCHECK(!buffers_.empty()); |
+ } |
+ DVLOG(3) << __FUNCTION__ << " : " << track_num_ << " Only " |
+ << ready_buffers_.size() << " are ready, " << buffers_.size() |
+ << " are at or after upper bound ts " |
+ << before_timestamp.InSecondsF(); |
+ } |
+ } |
+ |
+ 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.
|
+} |
+ |
bool WebMClusterParser::Track::AddBuffer( |
const scoped_refptr<StreamParserBuffer>& buffer) { |
DVLOG(2) << "AddBuffer() : " << track_num_ |
@@ -481,14 +521,15 @@ void WebMClusterParser::Track::ApplyDurationEstimateIfNeeded() { |
last_added_buffer_missing_duration_ = NULL; |
} |
-void WebMClusterParser::Track::ClearBuffersButKeepLastIfMissingDuration() { |
- // Note that |estimated_next_frame_duration_| is not reset, so it can be |
- // reused on subsequent buffers added to this instance. |
- buffers_.clear(); |
+void WebMClusterParser::Track::ClearReadyBuffers() { |
+ // Note that |buffers_| are kept and |estimated_next_frame_duration_| is not |
+ // reset here. |
+ ready_buffers_.clear(); |
} |
void WebMClusterParser::Track::Reset() { |
- ClearBuffersButKeepLastIfMissingDuration(); |
+ ClearReadyBuffers(); |
+ buffers_.clear(); |
last_added_buffer_missing_duration_ = NULL; |
} |
@@ -518,10 +559,17 @@ bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const { |
bool WebMClusterParser::Track::QueueBuffer( |
const scoped_refptr<StreamParserBuffer>& buffer) { |
DCHECK(!last_added_buffer_missing_duration_); |
+ |
+ // WebMClusterParser::OnBlock() gives MEDIA_LOG and parse error on decreasing |
+ // block timecode detection within a cluster. Therefore, we should not see |
+ // those here. |
+ base::TimeDelta previous_buffers_timestamp = buffers_.empty() ? |
+ base::TimeDelta() : buffers_.back()->GetDecodeTimestamp(); |
+ CHECK(previous_buffers_timestamp <= buffer->GetDecodeTimestamp()); |
+ |
base::TimeDelta duration = buffer->duration(); |
if (duration < base::TimeDelta() || duration == kNoTimestamp()) { |
- DVLOG(2) << "QueueBuffer() : Invalid buffer duration: " |
- << duration.InSecondsF(); |
+ MEDIA_LOG(log_cb_) << "Invalid buffer duration: " << duration.InSecondsF(); |
return false; |
} |
@@ -561,15 +609,57 @@ base::TimeDelta WebMClusterParser::Track::GetDurationEstimate() { |
return duration; |
} |
-void WebMClusterParser::ResetTextTracks() { |
+void WebMClusterParser::ClearTextTrackReadyBuffers() { |
text_buffers_map_.clear(); |
for (TextTrackMap::iterator it = text_track_map_.begin(); |
it != text_track_map_.end(); |
++it) { |
+ it->second.ClearReadyBuffers(); |
+ } |
+} |
+ |
+void WebMClusterParser::ResetTextTracks() { |
+ ClearTextTrackReadyBuffers(); |
+ for (TextTrackMap::iterator it = text_track_map_.begin(); |
+ it != text_track_map_.end(); |
+ ++it) { |
it->second.Reset(); |
} |
} |
+void WebMClusterParser::UpdateReadyBuffers() { |
+ DCHECK(ready_buffer_upper_bound_ == kNoTimestamp()); |
+ DCHECK(text_buffers_map_.empty()); |
+ |
+ if (cluster_ended_) { |
+ audio_.ApplyDurationEstimateIfNeeded(); |
+ video_.ApplyDurationEstimateIfNeeded(); |
+ // Per OnBlock(), all text buffers should already have valid durations, so |
+ // there is no need to call ApplyDurationEstimateIfNeeded() on text tracks |
+ // here. |
+ ready_buffer_upper_bound_ = kInfiniteDuration(); |
+ DCHECK(ready_buffer_upper_bound_ == audio_.GetReadyUpperBound()); |
+ DCHECK(ready_buffer_upper_bound_ == video_.GetReadyUpperBound()); |
+ } else { |
+ ready_buffer_upper_bound_ = std::min(audio_.GetReadyUpperBound(), |
+ video_.GetReadyUpperBound()); |
+ DCHECK(base::TimeDelta() <= ready_buffer_upper_bound_); |
+ DCHECK(kNoTimestamp() != ready_buffer_upper_bound_); |
+ } |
+ |
+ // Prepare each track's ready buffers for retrieval. |
+ audio_.ExtractReadyBuffers(ready_buffer_upper_bound_); |
+ video_.ExtractReadyBuffers(ready_buffer_upper_bound_); |
+ for (TextTrackMap::iterator itr = text_track_map_.begin(); |
+ itr != text_track_map_.end(); |
+ ++itr) { |
+ const BufferQueue& text_buffers = itr->second.ExtractReadyBuffers( |
+ ready_buffer_upper_bound_); |
+ if (!text_buffers.empty()) |
+ 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
|
+ } |
+} |
+ |
WebMClusterParser::Track* |
WebMClusterParser::FindTextTrack(int track_num) { |
const TextTrackMap::iterator it = text_track_map_.find(track_num); |