Chromium Code Reviews| 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 a762e2c40b34411594a5a30f0438e36f96d31a57..5b72a8d05ae18cd95ae034e5308ee1a08c8bd1ae 100644 |
| --- a/media/formats/webm/webm_cluster_parser.cc |
| +++ b/media/formats/webm/webm_cluster_parser.cc |
| @@ -15,10 +15,21 @@ |
| #include "media/formats/webm/webm_crypto_helpers.h" |
| #include "media/formats/webm/webm_webvtt_parser.h" |
| +// Arbitrarily-chosen numbers to estimate the duration of a buffer if none is |
| +// set and there is not enough information to get a better estimate. |
| +// TODO(wolenetz/acolwell): Parse audio codebook to determine missing audio |
| +// frame durations. See http://crbug.com/351166. |
| +static int kDefaultAudioBufferDurationInMs = 23; // Common 1k samples @44.1kHz |
| +static int kDefaultVideoBufferDurationInMs = 50; // Larger may reduce stalls |
|
acolwell GONE FROM CHROMIUM
2014/03/27 18:28:10
nit: This implies 20fps which is pretty uncommon.
wolenetz
2014/03/27 19:56:39
Ok. I'll use the longer of these (40) to help prev
|
| + |
| namespace media { |
| WebMClusterParser::WebMClusterParser( |
| - int64 timecode_scale, int audio_track_num, int video_track_num, |
| + int64 timecode_scale, |
| + int audio_track_num, |
| + base::TimeDelta audio_default_duration, |
| + int video_track_num, |
| + base::TimeDelta video_default_duration, |
| const WebMTracksParser::TextTracks& text_tracks, |
| const std::set<int64>& ignored_tracks, |
| const std::string& audio_encryption_key_id, |
| @@ -38,13 +49,14 @@ WebMClusterParser::WebMClusterParser( |
| cluster_timecode_(-1), |
| cluster_start_time_(kNoTimestamp()), |
| cluster_ended_(false), |
| - audio_(audio_track_num, false), |
| - video_(video_track_num, true), |
| + audio_(audio_track_num, false, audio_default_duration), |
| + video_(video_track_num, true, video_default_duration), |
| 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))); |
| + text_track_map_.insert(std::make_pair( |
| + it->first, Track(it->first, false, kNoTimestamp()))); |
| } |
| } |
| @@ -62,8 +74,8 @@ void WebMClusterParser::Reset() { |
| } |
| int WebMClusterParser::Parse(const uint8* buf, int size) { |
| - audio_.Reset(); |
| - video_.Reset(); |
| + audio_.ClearBuffersButKeepLastIfMissingDuration(); |
| + video_.ClearBuffersButKeepLastIfMissingDuration(); |
| ResetTextTracks(); |
| int result = parser_.Parse(buf, size); |
| @@ -99,6 +111,18 @@ int WebMClusterParser::Parse(const uint8* buf, int size) { |
| return result; |
| } |
| +const WebMClusterParser::BufferQueue& WebMClusterParser::GetAudioBuffers() { |
| + if (cluster_ended_) |
| + audio_.ApplyDurationDefaultOrEstimateIfNeeded(); |
| + return audio_.buffers(); |
| +} |
| + |
| +const WebMClusterParser::BufferQueue& WebMClusterParser::GetVideoBuffers() { |
| + if (cluster_ended_) |
| + video_.ApplyDurationDefaultOrEstimateIfNeeded(); |
| + return video_.buffers(); |
| +} |
| + |
| const WebMClusterParser::TextBufferQueueMap& |
| WebMClusterParser::GetTextBuffers() { |
| // Translate our |text_track_map_| into |text_buffers_map_|, inserting rows in |
| @@ -107,6 +131,9 @@ WebMClusterParser::GetTextBuffers() { |
| 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.ApplyDurationDefaultOrEstimateIfNeeded() here. |
| const BufferQueue& text_buffers = itr->second.buffers(); |
| if (!text_buffers.empty()) |
| text_buffers_map_.insert(std::make_pair(itr->first, text_buffers)); |
| @@ -390,9 +417,14 @@ bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num, |
| return track->AddBuffer(buffer); |
| } |
| -WebMClusterParser::Track::Track(int track_num, bool is_video) |
| +WebMClusterParser::Track::Track(int track_num, bool is_video, |
| + base::TimeDelta default_duration) |
| : track_num_(track_num), |
| - is_video_(is_video) { |
| + is_video_(is_video), |
| + default_duration_(default_duration), |
| + estimated_next_frame_duration_(kNoTimestamp()) { |
| + DCHECK(default_duration_ == kNoTimestamp() || |
| + default_duration_ > base::TimeDelta()); |
| } |
| WebMClusterParser::Track::~Track() {} |
| @@ -405,14 +437,83 @@ bool WebMClusterParser::Track::AddBuffer( |
| << " kf " << buffer->IsKeyframe() |
| << " size " << buffer->data_size(); |
| - buffers_.push_back(buffer); |
| - return true; |
| + if (last_added_buffer_missing_duration_) { |
| + base::TimeDelta derived_duration = |
| + buffer->timestamp() - last_added_buffer_missing_duration_->timestamp(); |
| + last_added_buffer_missing_duration_->set_duration(derived_duration); |
| + |
| + DVLOG(2) << "AddBuffer() : applied derived duration to held-back buffer : " |
| + << " ts " |
| + << last_added_buffer_missing_duration_->timestamp().InSecondsF() |
| + << " dur " |
| + << last_added_buffer_missing_duration_->duration().InSecondsF() |
| + << " kf " << last_added_buffer_missing_duration_->IsKeyframe() |
| + << " size " << last_added_buffer_missing_duration_->data_size(); |
| + scoped_refptr<StreamParserBuffer> updated_buffer = |
| + last_added_buffer_missing_duration_; |
| + last_added_buffer_missing_duration_ = NULL; |
| + if (!QueueBuffer(updated_buffer)) |
| + return false; |
| + } |
| + |
| + if (buffer->duration() == kNoTimestamp()) { |
| + last_added_buffer_missing_duration_ = buffer; |
| + DVLOG(2) << "AddBuffer() : holding back buffer that is missing duration"; |
| + return true; |
| + } |
| + |
| + return QueueBuffer(buffer); |
| } |
| -void WebMClusterParser::Track::Reset() { |
| +void WebMClusterParser::Track::ApplyDurationDefaultOrEstimateIfNeeded() { |
| + if (!last_added_buffer_missing_duration_) |
| + return; |
| + |
| + if (default_duration_ != kNoTimestamp()) { |
|
acolwell GONE FROM CHROMIUM
2014/03/27 18:28:10
nit: Move if chain to a helper function so we only
wolenetz
2014/03/27 19:56:39
Done (with a helper function).
|
| + DVLOG(3) << __FUNCTION__ << " : using TrackEntry DefaultDuration"; |
| + last_added_buffer_missing_duration_->set_duration(default_duration_); |
| + } else if (estimated_next_frame_duration_ != kNoTimestamp()) { |
| + DVLOG(3) << __FUNCTION__ << " : using estimated duration"; |
| + last_added_buffer_missing_duration_->set_duration( |
| + estimated_next_frame_duration_); |
| + } else { |
| + DVLOG(3) << __FUNCTION__ << " : using hardcoded default duration"; |
| + if (is_video_) { |
| + last_added_buffer_missing_duration_->set_duration( |
| + base::TimeDelta::FromMilliseconds(kDefaultVideoBufferDurationInMs)); |
| + } else { |
| + last_added_buffer_missing_duration_->set_duration( |
| + base::TimeDelta::FromMilliseconds(kDefaultAudioBufferDurationInMs)); |
| + } |
| + } |
| + |
| + DCHECK(last_added_buffer_missing_duration_->duration() > base::TimeDelta()); |
| + DCHECK(last_added_buffer_missing_duration_->duration() != kNoTimestamp()); |
| + |
| + DVLOG(2) << "ApplyDurationDefaultOrEstimateIfNeeded() : new dur : " |
| + << " ts " |
| + << last_added_buffer_missing_duration_->timestamp().InSecondsF() |
| + << " dur " |
| + << last_added_buffer_missing_duration_->duration().InSecondsF() |
| + << " kf " << last_added_buffer_missing_duration_->IsKeyframe() |
| + << " size " << last_added_buffer_missing_duration_->data_size(); |
| + // Don't use the applied duration as a future estimation (don't use |
| + // QueueBuffer() here.) |
| + buffers_.push_back(last_added_buffer_missing_duration_); |
| + 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::Reset() { |
| + ClearBuffersButKeepLastIfMissingDuration(); |
| + last_added_buffer_missing_duration_ = NULL; |
| +} |
| + |
| bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const { |
| // For now, assume that all blocks are keyframes for datatypes other than |
| // video. This is a valid assumption for Vorbis, WebVTT, & Opus. |
| @@ -436,6 +537,22 @@ bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const { |
| return true; |
| } |
| +bool WebMClusterParser::Track::QueueBuffer( |
| + const scoped_refptr<StreamParserBuffer>& buffer) { |
| + DCHECK(!last_added_buffer_missing_duration_); |
| + base::TimeDelta duration = buffer->duration(); |
| + if (duration < base::TimeDelta() || duration == kNoTimestamp()) { |
| + DVLOG(2) << "QueueBuffer() : Invalid buffer duration: " |
| + << duration.InSecondsF(); |
| + return false; |
| + } |
| + |
| + estimated_next_frame_duration_ = std::max(duration, |
| + estimated_next_frame_duration_); |
| + buffers_.push_back(buffer); |
| + return true; |
| +} |
| + |
| void WebMClusterParser::ResetTextTracks() { |
| text_buffers_map_.clear(); |
| for (TextTrackMap::iterator it = text_track_map_.begin(); |