| Index: media/filters/source_buffer_stream.cc
 | 
| diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc
 | 
| index 75c9c31fd344fab814e09c442fa15429f1422097..2fcb6ab1440801ffde64facf1c52adbdf74cc47b 100644
 | 
| --- a/media/filters/source_buffer_stream.cc
 | 
| +++ b/media/filters/source_buffer_stream.cc
 | 
| @@ -19,18 +19,19 @@ namespace media {
 | 
|  
 | 
|  namespace {
 | 
|  
 | 
| -enum {
 | 
| -  // An arbitrarily-chosen number to estimate the duration of a buffer if none
 | 
| -  // is set and there's not enough information to get a better estimate.
 | 
| -  kDefaultBufferDurationInMs = 125,
 | 
| -
 | 
| -  // Limit the number of MEDIA_LOG() logs for splice buffer generation warnings
 | 
| -  // and successes. Though these values are high enough to possibly exhaust the
 | 
| -  // media internals event cache (along with other events), these logs are
 | 
| -  // important for debugging splice generation.
 | 
| -  kMaxSpliceGenerationWarningLogs = 50,
 | 
| -  kMaxSpliceGenerationSuccessLogs = 20,
 | 
| -};
 | 
| +// An arbitrarily-chosen number to estimate the duration of a buffer if none is
 | 
| +// set and there's not enough information to get a better estimate.
 | 
| +const int kDefaultBufferDurationInMs = 125;
 | 
| +
 | 
| +// Limit the number of MEDIA_LOG() logs for splice buffer generation warnings
 | 
| +// and successes. Though these values are high enough to possibly exhaust the
 | 
| +// media internals event cache (along with other events), these logs are
 | 
| +// important for debugging splice generation.
 | 
| +const int kMaxSpliceGenerationWarningLogs = 50;
 | 
| +const int kMaxSpliceGenerationSuccessLogs = 20;
 | 
| +
 | 
| +// Limit the number of MEDIA_LOG() logs for track buffer time gaps.
 | 
| +const int kMaxTrackBufferGapWarningLogs = 20;
 | 
|  
 | 
|  // Helper method that returns true if |ranges| is sorted in increasing order,
 | 
|  // false otherwise.
 | 
| @@ -134,6 +135,7 @@ SourceBufferStream::SourceBufferStream(const AudioDecoderConfig& audio_config,
 | 
|        end_of_stream_(false),
 | 
|        seek_buffer_timestamp_(kNoTimestamp()),
 | 
|        selected_range_(NULL),
 | 
| +      just_exhausted_track_buffer_(false),
 | 
|        media_segment_start_time_(kNoDecodeTimestamp()),
 | 
|        range_for_next_append_(ranges_.end()),
 | 
|        new_media_segment_(false),
 | 
| @@ -147,7 +149,8 @@ SourceBufferStream::SourceBufferStream(const AudioDecoderConfig& audio_config,
 | 
|        pending_buffers_complete_(false),
 | 
|        splice_frames_enabled_(splice_frames_enabled),
 | 
|        num_splice_generation_warning_logs_(0),
 | 
| -      num_splice_generation_success_logs_(0) {
 | 
| +      num_splice_generation_success_logs_(0),
 | 
| +      num_track_buffer_gap_warning_logs_(0) {
 | 
|    DCHECK(audio_config.IsValidConfig());
 | 
|    audio_configs_.push_back(audio_config);
 | 
|  }
 | 
| @@ -162,6 +165,7 @@ SourceBufferStream::SourceBufferStream(const VideoDecoderConfig& video_config,
 | 
|        end_of_stream_(false),
 | 
|        seek_buffer_timestamp_(kNoTimestamp()),
 | 
|        selected_range_(NULL),
 | 
| +      just_exhausted_track_buffer_(false),
 | 
|        media_segment_start_time_(kNoDecodeTimestamp()),
 | 
|        range_for_next_append_(ranges_.end()),
 | 
|        new_media_segment_(false),
 | 
| @@ -175,7 +179,8 @@ SourceBufferStream::SourceBufferStream(const VideoDecoderConfig& video_config,
 | 
|        pending_buffers_complete_(false),
 | 
|        splice_frames_enabled_(splice_frames_enabled),
 | 
|        num_splice_generation_warning_logs_(0),
 | 
| -      num_splice_generation_success_logs_(0) {
 | 
| +      num_splice_generation_success_logs_(0),
 | 
| +      num_track_buffer_gap_warning_logs_(0) {
 | 
|    DCHECK(video_config.IsValidConfig());
 | 
|    video_configs_.push_back(video_config);
 | 
|  }
 | 
| @@ -191,6 +196,7 @@ SourceBufferStream::SourceBufferStream(const TextTrackConfig& text_config,
 | 
|        end_of_stream_(false),
 | 
|        seek_buffer_timestamp_(kNoTimestamp()),
 | 
|        selected_range_(NULL),
 | 
| +      just_exhausted_track_buffer_(false),
 | 
|        media_segment_start_time_(kNoDecodeTimestamp()),
 | 
|        range_for_next_append_(ranges_.end()),
 | 
|        new_media_segment_(false),
 | 
| @@ -204,7 +210,8 @@ SourceBufferStream::SourceBufferStream(const TextTrackConfig& text_config,
 | 
|        pending_buffers_complete_(false),
 | 
|        splice_frames_enabled_(splice_frames_enabled),
 | 
|        num_splice_generation_warning_logs_(0),
 | 
| -      num_splice_generation_success_logs_(0) {}
 | 
| +      num_splice_generation_success_logs_(0),
 | 
| +      num_track_buffer_gap_warning_logs_(0) {}
 | 
|  
 | 
|  SourceBufferStream::~SourceBufferStream() {
 | 
|    while (!ranges_.empty()) {
 | 
| @@ -533,6 +540,7 @@ void SourceBufferStream::ResetSeekState() {
 | 
|    track_buffer_.clear();
 | 
|    config_change_pending_ = false;
 | 
|    last_output_buffer_timestamp_ = kNoDecodeTimestamp();
 | 
| +  just_exhausted_track_buffer_ = false;
 | 
|    splice_buffers_index_ = 0;
 | 
|    pending_buffer_ = NULL;
 | 
|    pending_buffers_complete_ = false;
 | 
| @@ -1136,12 +1144,15 @@ SourceBufferStream::Status SourceBufferStream::GetNextBufferInternal(
 | 
|      DVLOG(3) << __FUNCTION__ << " Next buffer coming from track_buffer_";
 | 
|      *out_buffer = next_buffer;
 | 
|      track_buffer_.pop_front();
 | 
| +    WarnIfTrackBufferExhaustionSkipsForward(*out_buffer);
 | 
|      last_output_buffer_timestamp_ = (*out_buffer)->GetDecodeTimestamp();
 | 
|  
 | 
|      // If the track buffer becomes empty, then try to set the selected range
 | 
|      // based on the timestamp of this buffer being returned.
 | 
| -    if (track_buffer_.empty())
 | 
| +    if (track_buffer_.empty()) {
 | 
| +      just_exhausted_track_buffer_ = true;
 | 
|        SetSelectedRangeIfNeeded(last_output_buffer_timestamp_);
 | 
| +    }
 | 
|  
 | 
|      return kSuccess;
 | 
|    }
 | 
| @@ -1165,10 +1176,34 @@ SourceBufferStream::Status SourceBufferStream::GetNextBufferInternal(
 | 
|    }
 | 
|  
 | 
|    CHECK(selected_range_->GetNextBuffer(out_buffer));
 | 
| +  WarnIfTrackBufferExhaustionSkipsForward(*out_buffer);
 | 
|    last_output_buffer_timestamp_ = (*out_buffer)->GetDecodeTimestamp();
 | 
|    return kSuccess;
 | 
|  }
 | 
|  
 | 
| +void SourceBufferStream::WarnIfTrackBufferExhaustionSkipsForward(
 | 
| +    const scoped_refptr<StreamParserBuffer>& next_buffer) {
 | 
| +  if (!just_exhausted_track_buffer_)
 | 
| +    return;
 | 
| +
 | 
| +  just_exhausted_track_buffer_ = false;
 | 
| +  DCHECK(next_buffer->is_key_frame());
 | 
| +  DecodeTimestamp next_output_buffer_timestamp =
 | 
| +      next_buffer->GetDecodeTimestamp();
 | 
| +  base::TimeDelta delta =
 | 
| +      next_output_buffer_timestamp - last_output_buffer_timestamp_;
 | 
| +  DCHECK_GE(delta, base::TimeDelta());
 | 
| +  if (delta > GetMaxInterbufferDistance()) {
 | 
| +    LIMITED_MEDIA_LOG(DEBUG, media_log_, num_track_buffer_gap_warning_logs_,
 | 
| +                      kMaxTrackBufferGapWarningLogs)
 | 
| +        << "Media append that overlapped current playback position caused time "
 | 
| +           "gap in playing "
 | 
| +        << GetStreamTypeName() << " stream because the next keyframe is "
 | 
| +        << delta.InMilliseconds() << "ms beyond last overlapped frame. Media "
 | 
| +                                     "may appear temporarily frozen.";
 | 
| +  }
 | 
| +}
 | 
| +
 | 
|  DecodeTimestamp SourceBufferStream::GetNextBufferTimestamp() {
 | 
|    if (!track_buffer_.empty())
 | 
|      return track_buffer_.front()->GetDecodeTimestamp();
 | 
| 
 |