Cap sourceBuffered() on duration and truncate duration on EoS

Adds logic + test to truncate the duration on Media Source streams when
EndOfStream is signalled. Also adds some logic to make sure buffered streams
never exceed the reported duration.

BUG=139899

Review URL: https://chromiumcodereview.appspot.com/10829108

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149702 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/media/base/demuxer_stream.h b/media/base/demuxer_stream.h
index 65457c3..30ef1ec7 100644
--- a/media/base/demuxer_stream.h
+++ b/media/base/demuxer_stream.h
@@ -64,9 +64,6 @@
   // if type() != VIDEO.
   virtual const VideoDecoderConfig& video_decoder_config() = 0;
 
-  // Returns time ranges known to have been seen by this stream.
-  virtual Ranges<base::TimeDelta> GetBufferedRanges() = 0;
-
   // Returns the type of stream.
   virtual Type type() = 0;
 
diff --git a/media/base/ranges.h b/media/base/ranges.h
index 82bf7a0..f42b34a 100644
--- a/media/base/ranges.h
+++ b/media/base/ranges.h
@@ -39,7 +39,7 @@
   void clear();
 
   // Computes the intersection between this range and |other|.
-  Ranges<T> IntersectionWith(const Ranges<T>& other);
+  Ranges<T> IntersectionWith(const Ranges<T>& other) const;
 
  private:
   // Wrapper around DCHECK_LT allowing comparisons of operator<<'able T's.
@@ -133,7 +133,7 @@
 }
 
 template<class T>
-Ranges<T> Ranges<T>::IntersectionWith(const Ranges<T>& other) {
+Ranges<T> Ranges<T>::IntersectionWith(const Ranges<T>& other) const {
   Ranges<T> result;
 
   size_t i = 0;
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index b1c2389..e866da3d 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -176,6 +176,9 @@
   // Returns true if buffers were successfully added.
   bool Append(const StreamParser::BufferQueue& buffers);
 
+  // Returns the range of buffered data in this stream, capped at |duration|.
+  Ranges<TimeDelta> GetBufferedRanges(base::TimeDelta duration) const;
+
   // Signal to the stream that buffers handed in through subsequent calls to
   // Append() belong to a media segment that starts at |start_timestamp|.
   void OnNewMediaSegment(TimeDelta start_timestamp);
@@ -199,7 +202,6 @@
   virtual void EnableBitstreamConverter() OVERRIDE;
   virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE;
   virtual const VideoDecoderConfig& video_decoder_config() OVERRIDE;
-  virtual Ranges<TimeDelta> GetBufferedRanges() OVERRIDE;
 
  protected:
   virtual ~ChunkDemuxerStream();
@@ -318,9 +320,20 @@
   return true;
 }
 
-Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedRanges() {
+Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedRanges(
+    base::TimeDelta duration) const {
   base::AutoLock auto_lock(lock_);
-  return stream_->GetBufferedTime();
+  Ranges<TimeDelta> range = stream_->GetBufferedTime();
+
+  if (range.size() == 0u)
+    return range;
+
+  // Clamp the end of the stream's buffered ranges to fit within the duration.
+  // This can be done by intersecting the stream's range with the valid time
+  // range.
+  Ranges<TimeDelta> valid_time_range;
+  valid_time_range.Add(range.start(0), duration);
+  return range.IntersectionWith(valid_time_range);
 }
 
 bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config) {
@@ -671,12 +684,12 @@
 
   if (id == source_id_audio_ && id != source_id_video_) {
     // Only include ranges that have been buffered in |audio_|
-    return audio_ ? audio_->GetBufferedRanges() : Ranges<TimeDelta>();
+    return audio_ ? audio_->GetBufferedRanges(duration_) : Ranges<TimeDelta>();
   }
 
   if (id != source_id_audio_ && id == source_id_video_) {
     // Only include ranges that have been buffered in |video_|
-    return video_ ? video_->GetBufferedRanges() : Ranges<TimeDelta>();
+    return video_ ? video_->GetBufferedRanges(duration_) : Ranges<TimeDelta>();
   }
 
   return ComputeIntersection();
@@ -689,8 +702,8 @@
     return Ranges<TimeDelta>();
 
   // Include ranges that have been buffered in both |audio_| and |video_|.
-  Ranges<TimeDelta> audio_ranges = audio_->GetBufferedRanges();
-  Ranges<TimeDelta> video_ranges = video_->GetBufferedRanges();
+  Ranges<TimeDelta> audio_ranges = audio_->GetBufferedRanges(duration_);
+  Ranges<TimeDelta> video_ranges = video_->GetBufferedRanges(duration_);
   Ranges<TimeDelta> result = audio_ranges.IntersectionWith(video_ranges);
 
   if (state_ == ENDED && result.size() > 0) {
@@ -763,13 +776,7 @@
       std::swap(cb, seek_cb_);
     }
 
-    if (audio_ && !video_) {
-      ranges = audio_->GetBufferedRanges();
-    } else if (!audio_ && video_) {
-      ranges = video_->GetBufferedRanges();
-    } else {
-      ranges = ComputeIntersection();
-    }
+    ranges = GetBufferedRanges();
   }
 
   for (size_t i = 0; i < ranges.size(); ++i)
@@ -825,10 +832,12 @@
   if (video_)
     video_->EndOfStream();
 
-  if (status != PIPELINE_OK)
+  if (status != PIPELINE_OK) {
     ReportError_Locked(status);
-  else
+  } else {
     ChangeState_Locked(ENDED);
+    DecreaseDurationIfNecessary();
+  }
 
   return true;
 }
@@ -1121,7 +1130,7 @@
   if (buffers.back()->GetTimestamp() <= duration_)
     return;
 
-  Ranges<TimeDelta> ranges = stream->GetBufferedRanges();
+  Ranges<TimeDelta> ranges = stream->GetBufferedRanges(kInfiniteDuration());
   DCHECK_GT(ranges.size(), 0u);
 
   base::TimeDelta last_timestamp_buffered = ranges.end(ranges.size() - 1);
@@ -1129,4 +1138,22 @@
     UpdateDuration(last_timestamp_buffered);
 }
 
+void ChunkDemuxer::DecreaseDurationIfNecessary() {
+  Ranges<TimeDelta> ranges = GetBufferedRanges();
+  if (ranges.size() == 0u)
+    return;
+
+  base::TimeDelta last_timestamp_buffered = ranges.end(ranges.size() - 1);
+  if (last_timestamp_buffered < duration_)
+    UpdateDuration(last_timestamp_buffered);
+}
+
+Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges() const {
+  if (audio_ && !video_)
+    return audio_->GetBufferedRanges(duration_);
+  else if (!audio_ && video_)
+    return video_->GetBufferedRanges(duration_);
+  return ComputeIntersection();
+}
+
 }  // namespace media
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index fb53bca5..fe8e671 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -143,9 +143,16 @@
       const StreamParser::BufferQueue& buffers,
       const scoped_refptr<ChunkDemuxerStream>& stream);
 
+  // Decreases |duration_| if the buffered region is less than |duration_| when
+  // EndOfStream() is called.
+  void DecreaseDurationIfNecessary();
+
   // Sets |duration_| to |new_duration| and notifies |host_|.
   void UpdateDuration(base::TimeDelta new_duration);
 
+  // Returns the ranges representing the buffered data in the demuxer.
+  Ranges<base::TimeDelta> GetBufferedRanges() const;
+
   mutable base::Lock lock_;
   State state_;
 
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index 1570bea..71ebe9a 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -47,6 +47,8 @@
 
 static const char* kSourceId = "SourceId";
 static const char* kDefaultFirstClusterRange = "{ [0,46) }";
+static const int kDefaultFirstClusterEndTimestamp = 66;
+static const int kDefaultSecondClusterEndTimestamp = 132;
 
 base::TimeDelta kDefaultDuration() {
   return base::TimeDelta::FromMilliseconds(201224);
@@ -1100,6 +1102,8 @@
   end_of_stream_helper_1.CheckIfReadDonesWereCalled(false);
   end_of_stream_helper_2.CheckIfReadDonesWereCalled(false);
 
+  EXPECT_CALL(host_, SetDuration(
+      base::TimeDelta::FromMilliseconds(kVideoBlockDuration)));
   demuxer_->EndOfStream(PIPELINE_OK);
 
   end_of_stream_helper_1.CheckIfReadDonesWereCalled(true);
@@ -1139,6 +1143,8 @@
   EXPECT_TRUE(video_read_done_1);
   end_of_stream_helper_1.CheckIfReadDonesWereCalled(false);
 
+  EXPECT_CALL(host_, SetDuration(
+      base::TimeDelta::FromMilliseconds(kVideoBlockDuration)));
   EXPECT_TRUE(demuxer_->EndOfStream(PIPELINE_OK));
 
   end_of_stream_helper_1.CheckIfReadDonesWereCalled(true);
@@ -1697,6 +1703,7 @@
 
   CheckExpectedRanges("{ [0,90) }");
 
+  EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(100)));
   demuxer_->EndOfStream(PIPELINE_OK);
 
   CheckExpectedRanges("{ [0,100) }");
@@ -1813,6 +1820,7 @@
 
   // Make sure that end of stream fails because there is a gap between
   // the current position(0) and the end of the appended data.
+  EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(50)));
   ASSERT_FALSE(demuxer_->EndOfStream(PIPELINE_OK));
 
   // Seek to an time that is inside the last ranges for both streams
@@ -1863,6 +1871,8 @@
   demuxer_->StartWaitingForSeek();
 
   ASSERT_TRUE(AppendData(cluster_b->data(), cluster_b->size()));
+  EXPECT_CALL(host_, SetDuration(
+      base::TimeDelta::FromMilliseconds(kDefaultSecondClusterEndTimestamp)));
   demuxer_->EndOfStream(PIPELINE_OK);
 
   demuxer_->Seek(base::TimeDelta::FromSeconds(0),
@@ -2133,20 +2143,36 @@
 
 TEST_F(ChunkDemuxerTest, TestDurationChange) {
   ASSERT_TRUE(InitDemuxer(true, true, false));
+  static const int kStreamDuration = kDefaultDuration().InMilliseconds();
 
   // Add data leading up to the currently set duration.
   scoped_ptr<Cluster> first_cluster = GenerateCluster(
-      kDefaultDuration().InMilliseconds() - kAudioBlockDuration,
-      kDefaultDuration().InMilliseconds() - kVideoBlockDuration, 2);
+      kStreamDuration - kAudioBlockDuration,
+      kStreamDuration - kVideoBlockDuration, 2);
   ASSERT_TRUE(AppendData(first_cluster->data(), first_cluster->size()));
 
-  // Now add data past the duration and expect a new duration to be signalled.
+  CheckExpectedRanges(kSourceId, "{ [201191,201224) }");
+
+  // Add data at the currently set duration. The duration should not increase.
   scoped_ptr<Cluster> second_cluster = GenerateCluster(
-      kDefaultDuration().InMilliseconds(), 4);
-  EXPECT_CALL(host_, SetDuration(
-      kDefaultDuration() + base::TimeDelta::FromMilliseconds(
-          kAudioBlockDuration * 2)));
+      kDefaultDuration().InMilliseconds(), 2);
   ASSERT_TRUE(AppendData(second_cluster->data(), second_cluster->size()));
+
+  // Range should not be affected.
+  CheckExpectedRanges(kSourceId, "{ [201191,201224) }");
+
+  // Now add data past the duration and expect a new duration to be signalled.
+  static const int kNewStreamDuration =
+      kStreamDuration + kAudioBlockDuration * 2;
+  scoped_ptr<Cluster> third_cluster = GenerateCluster(
+      kStreamDuration + kAudioBlockDuration,
+      kStreamDuration + kVideoBlockDuration, 2);
+  EXPECT_CALL(host_, SetDuration(
+      base::TimeDelta::FromMilliseconds(kNewStreamDuration)));
+  ASSERT_TRUE(AppendData(third_cluster->data(), third_cluster->size()));
+
+  // See that the range has increased appropriately.
+  CheckExpectedRanges(kSourceId, "{ [201191,201270) }");
 }
 
 TEST_F(ChunkDemuxerTest, TestDurationChangeTimestampOffset) {
@@ -2162,4 +2188,15 @@
   ASSERT_TRUE(AppendData(cluster->data(), cluster->size()));
 }
 
+TEST_F(ChunkDemuxerTest, TestEndOfStreamTruncateDuration) {
+  ASSERT_TRUE(InitDemuxer(true, true, false));
+
+  scoped_ptr<Cluster> cluster_a(kDefaultFirstCluster());
+  ASSERT_TRUE(AppendData(cluster_a->data(), cluster_a->size()));
+
+  EXPECT_CALL(host_, SetDuration(
+      base::TimeDelta::FromMilliseconds(kDefaultFirstClusterEndTimestamp)));
+  demuxer_->EndOfStream(PIPELINE_OK);
+}
+
 }  // namespace media
diff --git a/media/filters/dummy_demuxer.cc b/media/filters/dummy_demuxer.cc
index 317b543a..bbfcff2 100644
--- a/media/filters/dummy_demuxer.cc
+++ b/media/filters/dummy_demuxer.cc
@@ -32,10 +32,6 @@
 
 void DummyDemuxerStream::EnableBitstreamConverter() {}
 
-Ranges<base::TimeDelta> DummyDemuxerStream::GetBufferedRanges() {
-  return Ranges<base::TimeDelta>();
-}
-
 DummyDemuxer::DummyDemuxer(bool has_video, bool has_audio) {
   streams_.resize(DemuxerStream::NUM_TYPES);
   if (has_audio)
diff --git a/media/filters/dummy_demuxer.h b/media/filters/dummy_demuxer.h
index c8c1b5e..700cd84 100644
--- a/media/filters/dummy_demuxer.h
+++ b/media/filters/dummy_demuxer.h
@@ -28,7 +28,6 @@
   virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE;
   virtual const VideoDecoderConfig& video_decoder_config() OVERRIDE;
   virtual void EnableBitstreamConverter() OVERRIDE;
-  virtual Ranges<base::TimeDelta> GetBufferedRanges() OVERRIDE;
  protected:
   virtual ~DummyDemuxerStream();
 
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index cb6494a..35c2d38 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -237,7 +237,7 @@
   return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts);
 }
 
-Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() {
+Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const {
   base::AutoLock auto_lock(lock_);
   return buffered_ranges_;
 }
@@ -323,7 +323,12 @@
 
 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream(
     DemuxerStream::Type type) {
-  StreamVector::iterator iter;
+  return GetFFmpegStream(type);
+}
+
+scoped_refptr<FFmpegDemuxerStream> FFmpegDemuxer::GetFFmpegStream(
+    DemuxerStream::Type type) const {
+  StreamVector::const_iterator iter;
   for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
     if (*iter && (*iter)->type() == type) {
       return *iter;
@@ -717,9 +722,10 @@
 void FFmpegDemuxer::NotifyBufferingChanged() {
   DCHECK_EQ(MessageLoop::current(), message_loop_);
   Ranges<base::TimeDelta> buffered;
-  scoped_refptr<DemuxerStream> audio =
-      audio_disabled_ ? NULL : GetStream(DemuxerStream::AUDIO);
-  scoped_refptr<DemuxerStream> video = GetStream(DemuxerStream::VIDEO);
+  scoped_refptr<FFmpegDemuxerStream> audio =
+      audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO);
+  scoped_refptr<FFmpegDemuxerStream> video =
+      GetFFmpegStream(DemuxerStream::VIDEO);
   if (audio && video) {
     buffered = audio->GetBufferedRanges().IntersectionWith(
         video->GetBufferedRanges());
diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h
index b90cbbb..f395a84 100644
--- a/media/filters/ffmpeg_demuxer.h
+++ b/media/filters/ffmpeg_demuxer.h
@@ -84,7 +84,9 @@
   virtual void EnableBitstreamConverter() OVERRIDE;
   virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE;
   virtual const VideoDecoderConfig& video_decoder_config() OVERRIDE;
-  virtual Ranges<base::TimeDelta> GetBufferedRanges() OVERRIDE;
+
+  // Returns the range of buffered data in this stream.
+  Ranges<base::TimeDelta> GetBufferedRanges() const;
 
   // Returns elapsed time based on the already queued packets.
   // Used to determine stream duration when it's not known ahead of time.
@@ -210,6 +212,11 @@
   // read or kReadError in case of error.
   virtual void SignalReadCompleted(int size);
 
+  // Returns the stream from |streams_| that matches |type| as an
+  // FFmpegDemuxerStream.
+  scoped_refptr<FFmpegDemuxerStream> GetFFmpegStream(
+      DemuxerStream::Type type) const;
+
   DemuxerHost* host_;
 
   MessageLoop* message_loop_;