summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-20 00:51:52 +0000
committeracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-20 00:51:52 +0000
commitc566cb79b77ee9dfe3f287fb930c2e9e419793d3 (patch)
treee5dc3c731b7d7ca2399d412bcf96825b4de67bcf
parent2767ca041af88d18e387d59c12a49fff10443ce8 (diff)
downloadchromium_src-c566cb79b77ee9dfe3f287fb930c2e9e419793d3.zip
chromium_src-c566cb79b77ee9dfe3f287fb930c2e9e419793d3.tar.gz
chromium_src-c566cb79b77ee9dfe3f287fb930c2e9e419793d3.tar.bz2
Fix ChunkDemuxer so it properly outputs buffered ranges.
BUG=133042 TEST=None. Verified manually by a simple test page that appends some data and then checks the buffered attribute. I will convert this to a LayoutTest soon. Review URL: https://chromiumcodereview.appspot.com/10558011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@143106 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--media/base/ranges.h28
-rw-r--r--media/base/ranges_unittest.cc49
-rw-r--r--media/filters/chunk_demuxer.cc172
-rw-r--r--media/filters/chunk_demuxer.h22
-rw-r--r--media/filters/chunk_demuxer_unittest.cc206
-rw-r--r--media/filters/source_buffer_stream.cc29
-rw-r--r--media/filters/source_buffer_stream.h5
-rw-r--r--media/filters/source_buffer_stream_unittest.cc161
-rw-r--r--webkit/media/webmediaplayer_impl.cc30
-rw-r--r--webkit/media/webmediaplayer_proxy.cc7
-rw-r--r--webkit/media/webmediaplayer_proxy.h3
11 files changed, 370 insertions, 342 deletions
diff --git a/media/base/ranges.h b/media/base/ranges.h
index c412b25..1117ae5 100644
--- a/media/base/ranges.h
+++ b/media/base/ranges.h
@@ -5,6 +5,7 @@
#ifndef MEDIA_BASE_RANGES_H_
#define MEDIA_BASE_RANGES_H_
+#include <algorithm>
#include <ostream>
#include <vector>
@@ -36,6 +37,9 @@ class Ranges {
// Clear all ranges.
void clear();
+ // Computes the intersection between this range and |other|.
+ Ranges<T> IntersectionWith(const Ranges<T>& other);
+
private:
// Disjoint, in increasing order of start.
std::vector<std::pair<T, T> > ranges_;
@@ -115,6 +119,30 @@ void Ranges<T>::clear() {
ranges_.clear();
}
+template<class T>
+Ranges<T> Ranges<T>::IntersectionWith(const Ranges<T>& other) {
+ Ranges<T> result;
+
+ size_t i = 0;
+ size_t j = 0;
+
+ while (i < size() && j < other.size()) {
+ T max_start = std::max(start(i), other.start(j));
+ T min_end = std::min(end(i), other.end(j));
+
+ // Add an intersection range to the result if the ranges overlap.
+ if (max_start < min_end)
+ result.Add(max_start, min_end);
+
+ if (end(i) < other.end(j))
+ ++i;
+ else
+ ++j;
+ }
+
+ return result;
+}
+
} // namespace media
#endif // MEDIA_BASE_RANGES_H_
diff --git a/media/base/ranges_unittest.cc b/media/base/ranges_unittest.cc
index 30c00b0..967d138 100644
--- a/media/base/ranges_unittest.cc
+++ b/media/base/ranges_unittest.cc
@@ -99,4 +99,53 @@ TEST(RangesTest, CoalesceRanges) {
ASSERT_RANGES(r, "{ [0,2) }");
}
+TEST(RangesTest, IntersectionWith) {
+ Ranges<int> a;
+ Ranges<int> b;
+
+ ASSERT_EQ(a.Add(0, 1), 1u) << a;
+ ASSERT_EQ(a.Add(4, 7), 2u) << a;
+ ASSERT_EQ(a.Add(10, 12), 3u) << a;
+
+ // Test intersections with an empty range.
+ ASSERT_RANGES(a, "{ [0,1) [4,7) [10,12) }");
+ ASSERT_RANGES(b, "{ }");
+ ASSERT_RANGES(a.IntersectionWith(b), "{ }");
+ ASSERT_RANGES(b.IntersectionWith(a), "{ }");
+
+ // Test intersections with a completely overlaping range.
+ ASSERT_EQ(b.Add(-1, 13), 1u) << b;
+ ASSERT_RANGES(a, "{ [0,1) [4,7) [10,12) }");
+ ASSERT_RANGES(b, "{ [-1,13) }");
+ ASSERT_RANGES(a.IntersectionWith(b), "{ [0,1) [4,7) [10,12) }");
+ ASSERT_RANGES(b.IntersectionWith(a), "{ [0,1) [4,7) [10,12) }");
+
+ // Test intersections with a disjoint ranges.
+ b.clear();
+ ASSERT_EQ(b.Add(1, 4), 1u) << b;
+ ASSERT_EQ(b.Add(8, 9), 2u) << b;
+ ASSERT_RANGES(a, "{ [0,1) [4,7) [10,12) }");
+ ASSERT_RANGES(b, "{ [1,4) [8,9) }");
+ ASSERT_RANGES(a.IntersectionWith(b), "{ }");
+ ASSERT_RANGES(b.IntersectionWith(a), "{ }");
+
+ // Test intersections with partially overlapping ranges.
+ b.clear();
+ ASSERT_EQ(b.Add(0, 3), 1u) << b;
+ ASSERT_EQ(b.Add(5, 11), 2u) << b;
+ ASSERT_RANGES(a, "{ [0,1) [4,7) [10,12) }");
+ ASSERT_RANGES(b, "{ [0,3) [5,11) }");
+ ASSERT_RANGES(a.IntersectionWith(b), "{ [0,1) [5,7) [10,11) }");
+ ASSERT_RANGES(b.IntersectionWith(a), "{ [0,1) [5,7) [10,11) }");
+
+ // Test intersection with a range that starts at the beginning of the
+ // first range and ends at the end of the last range.
+ b.clear();
+ ASSERT_EQ(b.Add(0, 12), 1u) << b;
+ ASSERT_RANGES(a, "{ [0,1) [4,7) [10,12) }");
+ ASSERT_RANGES(b, "{ [0,12) }");
+ ASSERT_RANGES(a.IntersectionWith(b), "{ [0,1) [4,7) [10,12) }");
+ ASSERT_RANGES(b.IntersectionWith(a), "{ [0,1) [4,7) [10,12) }");
+}
+
} // namespace media
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 1c87590..32d029a 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -15,6 +15,8 @@
#include "media/mp4/mp4_stream_parser.h"
#include "media/webm/webm_stream_parser.h"
+using base::TimeDelta;
+
namespace media {
struct CodecInfo {
@@ -73,6 +75,13 @@ static const SupportedTypeInfo kSupportedTypeInfo[] = {
{ "audio/mp4", &BuildMP4Parser, kAudioMP4Codecs },
};
+
+// The fake total size we use for converting times to bytes
+// for AddBufferedByteRange() calls.
+// TODO(acolwell): Remove this once Pipeline accepts buffered times
+// instead of only buffered bytes.
+enum { kFakeTotalBytes = 1000000 };
+
// Checks to see if the specified |type| and |codecs| list are supported.
// Returns true if |type| and all codecs listed in |codecs| are supported.
// |factory_function| contains a function that can build a StreamParser
@@ -147,7 +156,7 @@ class ChunkDemuxerStream : public DemuxerStream {
explicit ChunkDemuxerStream(const VideoDecoderConfig& video_config);
void StartWaitingForSeek();
- void Seek(base::TimeDelta time);
+ void Seek(TimeDelta time);
bool IsSeekPending() const;
void Flush();
@@ -157,11 +166,11 @@ class ChunkDemuxerStream : public DemuxerStream {
bool Append(const StreamParser::BufferQueue& buffers);
// Returns a list of the buffered time ranges.
- SourceBufferStream::TimespanList GetBufferedTime() const;
+ Ranges<TimeDelta> GetBufferedTime() 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(base::TimeDelta start_timestamp);
+ void OnNewMediaSegment(TimeDelta start_timestamp);
// Called when mid-stream config updates occur.
// Returns true if the new config is accepted.
@@ -212,7 +221,7 @@ class ChunkDemuxerStream : public DemuxerStream {
// The timestamp of the current media segment being parsed by
// |stream_parser_|.
- base::TimeDelta media_segment_start_time_;
+ TimeDelta media_segment_start_time_;
DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream);
};
@@ -244,7 +253,7 @@ void ChunkDemuxerStream::StartWaitingForSeek() {
it->Run(NULL);
}
-void ChunkDemuxerStream::Seek(base::TimeDelta time) {
+void ChunkDemuxerStream::Seek(TimeDelta time) {
base::AutoLock auto_lock(lock_);
DCHECK(read_cbs_.empty());
@@ -264,7 +273,7 @@ void ChunkDemuxerStream::Flush() {
media_segment_start_time_ = kNoTimestamp();
}
-void ChunkDemuxerStream::OnNewMediaSegment(base::TimeDelta start_timestamp) {
+void ChunkDemuxerStream::OnNewMediaSegment(TimeDelta start_timestamp) {
media_segment_start_time_ = start_timestamp;
}
@@ -290,7 +299,7 @@ bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue& buffers) {
return true;
}
-SourceBufferStream::TimespanList ChunkDemuxerStream::GetBufferedTime() const {
+Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedTime() const {
base::AutoLock auto_lock(lock_);
return stream_->GetBufferedTime();
}
@@ -463,8 +472,7 @@ void ChunkDemuxerStream::CreateReadDoneClosures_Locked(ClosureQueue* closures) {
ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client)
: state_(WAITING_FOR_INIT),
host_(NULL),
- client_(client),
- buffered_bytes_(0) {
+ client_(client) {
DCHECK(client);
}
@@ -489,7 +497,7 @@ void ChunkDemuxer::Stop(const base::Closure& callback) {
callback.Run();
}
-void ChunkDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
+void ChunkDemuxer::Seek(TimeDelta time, const PipelineStatusCB& cb) {
DVLOG(1) << "Seek(" << time.InSecondsF() << ")";
PipelineStatus status = PIPELINE_ERROR_INVALID_STATE;
@@ -538,11 +546,11 @@ scoped_refptr<DemuxerStream> ChunkDemuxer::GetStream(
return NULL;
}
-base::TimeDelta ChunkDemuxer::GetStartTime() const {
+TimeDelta ChunkDemuxer::GetStartTime() const {
DVLOG(1) << "GetStartTime()";
// TODO(acolwell) : Fix this so it uses the time on the first packet.
// (crbug.com/132815)
- return base::TimeDelta();
+ return TimeDelta();
}
void ChunkDemuxer::StartWaitingForSeek() {
@@ -627,99 +635,55 @@ void ChunkDemuxer::RemoveId(const std::string& id) {
video_->Shutdown();
}
-bool ChunkDemuxer::GetBufferedRanges(const std::string& id,
- Ranges* ranges_out) const {
+Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const {
DCHECK(!id.empty());
DCHECK_GT(stream_parser_map_.count(id), 0u);
DCHECK(id == source_id_audio_ || id == source_id_video_);
- DCHECK(ranges_out);
base::AutoLock auto_lock(lock_);
if (id == source_id_audio_ && id != source_id_video_) {
// Only include ranges that have been buffered in |audio_|
- return CopyIntoRanges(audio_->GetBufferedTime(), ranges_out);
+ return audio_->GetBufferedTime();
}
if (id != source_id_audio_ && id == source_id_video_) {
// Only include ranges that have been buffered in |video_|
- return CopyIntoRanges(video_->GetBufferedTime(), ranges_out);
+ return video_->GetBufferedTime();
}
- // Include ranges that have been buffered in both |audio_| and |video_|.
- SourceBufferStream::TimespanList audio_ranges = audio_->GetBufferedTime();
- SourceBufferStream::TimespanList video_ranges = video_->GetBufferedTime();
- SourceBufferStream::TimespanList::const_iterator video_ranges_itr =
- video_ranges.begin();
- SourceBufferStream::TimespanList::const_iterator audio_ranges_itr =
- audio_ranges.begin();
- bool success = false;
-
- while (audio_ranges_itr != audio_ranges.end() &&
- video_ranges_itr != video_ranges.end()) {
- // If this is the last range and EndOfStream() was called (i.e. all data
- // has been appended), choose the max end point of the ranges.
- bool last_range_after_ended =
- state_ == ENDED &&
- (audio_ranges_itr + 1) == audio_ranges.end() &&
- (video_ranges_itr + 1) == video_ranges.end();
-
- // Audio range start time is within the video range.
- if ((*audio_ranges_itr).first >= (*video_ranges_itr).first &&
- (*audio_ranges_itr).first <= (*video_ranges_itr).second) {
- AddIntersectionRange(*audio_ranges_itr, *video_ranges_itr,
- last_range_after_ended, ranges_out);
- audio_ranges_itr++;
- success = true;
- continue;
- }
+ return ComputeIntersection();
+}
- // Video range start time is within the audio range.
- if ((*video_ranges_itr).first >= (*audio_ranges_itr).first &&
- (*video_ranges_itr).first <= (*audio_ranges_itr).second) {
- AddIntersectionRange(*video_ranges_itr, *audio_ranges_itr,
- last_range_after_ended, ranges_out);
- video_ranges_itr++;
- success = true;
- continue;
- }
+Ranges<TimeDelta> ChunkDemuxer::ComputeIntersection() const {
+ lock_.AssertAcquired();
- // No overlap was found. Increment the earliest one and keep looking.
- if ((*audio_ranges_itr).first < (*video_ranges_itr).first)
- audio_ranges_itr++;
- else
- video_ranges_itr++;
- }
+ if (!audio_ || !video_)
+ return Ranges<TimeDelta>();
- return success;
-}
-
-bool ChunkDemuxer::CopyIntoRanges(
- const SourceBufferStream::TimespanList& timespans,
- Ranges* ranges_out) const {
- for (SourceBufferStream::TimespanList::const_iterator itr = timespans.begin();
- itr != timespans.end(); ++itr) {
- ranges_out->push_back(*itr);
+ // Include ranges that have been buffered in both |audio_| and |video_|.
+ Ranges<TimeDelta> audio_ranges = audio_->GetBufferedTime();
+ Ranges<TimeDelta> video_ranges = video_->GetBufferedTime();
+ Ranges<TimeDelta> result = audio_ranges.IntersectionWith(video_ranges);
+
+ if (state_ == ENDED && result.size() > 0) {
+ // If appending has ended, extend the last intersection range to include the
+ // max end time of the last audio/video range. This allows the buffered
+ // information to match the actual time range that will get played out if
+ // the streams have slightly different lengths.
+ TimeDelta audio_start = audio_ranges.start(audio_ranges.size() - 1);
+ TimeDelta audio_end = audio_ranges.end(audio_ranges.size() - 1);
+ TimeDelta video_start = video_ranges.start(video_ranges.size() - 1);
+ TimeDelta video_end = video_ranges.end(video_ranges.size() - 1);
+
+ // Verify the last audio range overlaps with the last video range.
+ // This is enforced by the logic that controls the transition to ENDED.
+ DCHECK((audio_start <= video_start && video_start <= audio_end) ||
+ (video_start <= audio_start && audio_start <= video_end));
+ result.Add(result.end(result.size()-1), std::max(audio_end, video_end));
}
- return !timespans.empty();
-}
-
-void ChunkDemuxer::AddIntersectionRange(
- SourceBufferStream::Timespan timespan_a,
- SourceBufferStream::Timespan timespan_b,
- bool last_range_after_ended,
- Ranges* ranges_out) const {
- base::TimeDelta start = timespan_a.first;
-
- // If this is the last range after EndOfStream() was called, choose the later
- // end point of the ranges, otherwise choose the earlier.
- base::TimeDelta end;
- if (last_range_after_ended)
- end = std::max(timespan_a.second, timespan_b.second);
- else
- end = std::min(timespan_a.second, timespan_b.second);
- ranges_out->push_back(std::make_pair(start, end));
+ return result;
}
bool ChunkDemuxer::AppendData(const std::string& id,
@@ -731,7 +695,7 @@ bool ChunkDemuxer::AppendData(const std::string& id,
DCHECK(data);
DCHECK_GT(length, 0u);
- int64 buffered_bytes = 0;
+ Ranges<TimeDelta> ranges;
PipelineStatusCB cb;
{
@@ -772,13 +736,26 @@ bool ChunkDemuxer::AppendData(const std::string& id,
std::swap(cb, seek_cb_);
}
- buffered_bytes_ += length;
- buffered_bytes = buffered_bytes_;
+ if (duration_ > TimeDelta() && duration_ != kInfiniteDuration()) {
+ if (audio_ && !video_) {
+ ranges = audio_->GetBufferedTime();
+ } else if (!audio_ && video_) {
+ ranges = video_->GetBufferedTime();
+ } else {
+ ranges = ComputeIntersection();
+ }
+ }
}
- // Notify the host of 'network activity' because we got data, using a bogus
- // range.
- host_->AddBufferedByteRange(0, buffered_bytes);
+ DCHECK(!ranges.size() || duration_ > TimeDelta());
+ for (size_t i = 0; i < ranges.size(); ++i) {
+ // Notify the host of 'network activity' because we got data.
+ int64 start =
+ kFakeTotalBytes * ranges.start(i).InSecondsF() / duration_.InSecondsF();
+ int64 end =
+ kFakeTotalBytes * ranges.end(i).InSecondsF() / duration_.InSecondsF();
+ host_->AddBufferedByteRange(start, end);
+ }
if (!cb.is_null())
cb.Run(PIPELINE_OK);
@@ -821,10 +798,10 @@ bool ChunkDemuxer::EndOfStream(PipelineStatus status) {
if (video_)
video_->EndOfStream();
- ChangeState_Locked(ENDED);
-
if (status != PIPELINE_OK)
ReportError_Locked(status);
+ else
+ ChangeState_Locked(ENDED);
return true;
}
@@ -928,8 +905,7 @@ bool ChunkDemuxer::CanEndOfStream_Locked() const {
(!video_ || video_->CanEndOfStream());
}
-void ChunkDemuxer::OnStreamParserInitDone(bool success,
- base::TimeDelta duration) {
+void ChunkDemuxer::OnStreamParserInitDone(bool success, TimeDelta duration) {
DVLOG(1) << "OnSourceBufferInitDone(" << success << ", "
<< duration.InSecondsF() << ")";
lock_.AssertAcquired();
@@ -947,6 +923,8 @@ void ChunkDemuxer::OnStreamParserInitDone(bool success,
(!source_id_video_.empty() && !video_))
return;
+ if (duration_ > TimeDelta() && duration_ != kInfiniteDuration())
+ host_->SetTotalBytes(kFakeTotalBytes);
host_->SetDuration(duration_);
ChangeState_Locked(INITIALIZED);
@@ -1024,7 +1002,7 @@ bool ChunkDemuxer::OnNeedKey(scoped_array<uint8> init_data,
}
void ChunkDemuxer::OnNewMediaSegment(const std::string& source_id,
- base::TimeDelta start_timestamp) {
+ TimeDelta start_timestamp) {
// TODO(vrk): There should be a special case for the first appends where all
// streams (for both demuxed and muxed case) begin at the earliest stream
// timestamp. (crbug.com/132815)
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index a03d50f..ecd2d77 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -13,6 +13,7 @@
#include "base/synchronization/lock.h"
#include "media/base/byte_queue.h"
#include "media/base/demuxer.h"
+#include "media/base/ranges.h"
#include "media/base/stream_parser.h"
#include "media/filters/source_buffer_stream.h"
@@ -32,8 +33,6 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
kReachedIdLimit, // Reached ID limit. We can't handle any more IDs.
};
- typedef std::vector<std::pair<base::TimeDelta, base::TimeDelta> > Ranges;
-
explicit ChunkDemuxer(ChunkDemuxerClient* client);
// Demuxer implementation.
@@ -65,10 +64,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
void RemoveId(const std::string& id);
// Gets the currently buffered ranges for the specified ID.
- // Returns true if data is buffered & |ranges_out| is set to the
- // time ranges currently buffered.
- // Returns false if no data is buffered.
- bool GetBufferedRanges(const std::string& id, Ranges* ranges_out) const;
+ Ranges<base::TimeDelta> GetBufferedRanges(const std::string& id) const;
// Appends media data to the source buffer associated with |id|. Returns
// false if this method is called in an invalid state.
@@ -121,15 +117,9 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
void OnNewMediaSegment(const std::string& source_id,
base::TimeDelta start_timestamp);
- // Helper functions for calculating GetBufferedRanges().
- bool CopyIntoRanges(
- const SourceBufferStream::TimespanList& timespans,
- Ranges* ranges_out) const;
- void AddIntersectionRange(
- SourceBufferStream::Timespan timespan_a,
- SourceBufferStream::Timespan timespan_b,
- bool last_range_after_ended,
- Ranges* ranges_out) const;
+ // Computes the intersection between the video & audio
+ // buffered ranges.
+ Ranges<base::TimeDelta> ComputeIntersection() const;
mutable base::Lock lock_;
State state_;
@@ -142,8 +132,6 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
scoped_refptr<ChunkDemuxerStream> audio_;
scoped_refptr<ChunkDemuxerStream> video_;
- int64 buffered_bytes_;
-
base::TimeDelta duration_;
typedef std::map<std::string, StreamParser*> StreamParserMap;
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index d55dff4..afbf979 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -46,6 +46,7 @@ static const int kAudioBlockDuration = 23;
static const int kVideoBlockDuration = 33;
static const char* kSourceId = "SourceId";
+static const char* kDefaultFirstClusterRange = "{ [0,46) }";
base::TimeDelta kDefaultDuration() {
return base::TimeDelta::FromMilliseconds(201224);
@@ -256,7 +257,7 @@ class ChunkDemuxerTest : public testing::Test {
return false;
start += append_size;
- EXPECT_GT(buffered_bytes_, old_buffered_bytes);
+ EXPECT_GE(buffered_bytes_, old_buffered_bytes);
}
return true;
}
@@ -284,8 +285,11 @@ class ChunkDemuxerTest : public testing::Test {
PipelineStatusCB CreateInitDoneCB(const base::TimeDelta& expected_duration,
PipelineStatus expected_status) {
- if (expected_status == PIPELINE_OK)
+ if (expected_status == PIPELINE_OK) {
+ if (expected_duration != kInfiniteDuration())
+ EXPECT_CALL(host_, SetTotalBytes(_));
EXPECT_CALL(host_, SetDuration(expected_duration));
+ }
return base::Bind(&ChunkDemuxerTest::InitDoneCalled,
base::Unretained(this),
@@ -395,10 +399,10 @@ class ChunkDemuxerTest : public testing::Test {
}
scoped_ptr<Cluster> GenerateSingleStreamCluster(int timecode,
- int block_count,
+ int end_timecode,
int track_number,
int block_duration) {
- CHECK_GT(block_count, 0);
+ CHECK_GT(end_timecode, timecode);
int size = 10;
scoped_array<uint8> data(new uint8[size]);
@@ -407,7 +411,7 @@ class ChunkDemuxerTest : public testing::Test {
cb.SetClusterTimecode(timecode);
// Create simple blocks for everything except the last block.
- for (int i = 0; i < block_count - 1; i++) {
+ for (int i = 0; timecode < (end_timecode - block_duration); i++) {
cb.AddSimpleBlock(track_number, timecode, kWebMFlagKeyframe,
data.get(), size);
timecode += block_duration;
@@ -463,26 +467,22 @@ class ChunkDemuxerTest : public testing::Test {
}
}
- std::pair<base::TimeDelta, base::TimeDelta> CreateRange(
- int start_time, int block_count, int block_duration) {
- return std::make_pair(base::TimeDelta::FromMilliseconds(start_time),
- base::TimeDelta::FromMilliseconds(start_time +
- (block_count * block_duration)));
+ void CheckExpectedRanges(const std::string& expected) {
+ CheckExpectedRanges(kSourceId, expected);
}
- void CheckExpectedRanges(const ChunkDemuxer::Ranges& expected_times) {
- ChunkDemuxer::Ranges actual_times;
- demuxer_->GetBufferedRanges(kSourceId, &actual_times);
- EXPECT_EQ(expected_times.size(), actual_times.size());
-
- for (ChunkDemuxer::Ranges::const_iterator actual_itr =
- actual_times.begin(), expected_itr = expected_times.begin();
- actual_itr != actual_times.end() &&
- expected_itr != expected_times.end();
- actual_itr++, expected_itr++) {
- EXPECT_EQ(expected_itr->first, actual_itr->first);
- EXPECT_EQ(expected_itr->second, actual_itr->second);
+ void CheckExpectedRanges(const std::string& id,
+ const std::string& expected) {
+ Ranges<base::TimeDelta> r = demuxer_->GetBufferedRanges(id);
+
+ std::stringstream ss;
+ ss << "{ ";
+ for (size_t i = 0; i < r.size(); ++i) {
+ ss << "[" << r.start(i).InMilliseconds() << ","
+ << r.end(i).InMilliseconds() << ") ";
}
+ ss << "}";
+ EXPECT_EQ(ss.str(), expected);
}
MOCK_METHOD1(ReadDone, void(const scoped_refptr<DecoderBuffer>&));
@@ -873,14 +873,24 @@ TEST_F(ChunkDemuxerTest, TestEOSDuringInit) {
demuxer_->EndOfStream(PIPELINE_OK);
}
+TEST_F(ChunkDemuxerTest, TestEndOfStreamWithNoAppend) {
+ ASSERT_TRUE(InitDemuxer(true, true, false));
+
+ CheckExpectedRanges("{ }");
+ demuxer_->EndOfStream(PIPELINE_OK);
+ CheckExpectedRanges("{ }");
+}
+
TEST_F(ChunkDemuxerTest, TestDecodeErrorEndOfStream) {
ASSERT_TRUE(InitDemuxer(true, true, false));
scoped_ptr<Cluster> cluster(kDefaultFirstCluster());
ASSERT_TRUE(AppendData(cluster->data(), cluster->size()));
+ CheckExpectedRanges(kDefaultFirstClusterRange);
EXPECT_CALL(host_, OnDemuxerError(PIPELINE_ERROR_DECODE));
demuxer_->EndOfStream(PIPELINE_ERROR_DECODE);
+ CheckExpectedRanges(kDefaultFirstClusterRange);
}
TEST_F(ChunkDemuxerTest, TestNetworkErrorEndOfStream) {
@@ -888,6 +898,7 @@ TEST_F(ChunkDemuxerTest, TestNetworkErrorEndOfStream) {
scoped_ptr<Cluster> cluster(kDefaultFirstCluster());
ASSERT_TRUE(AppendData(cluster->data(), cluster->size()));
+ CheckExpectedRanges(kDefaultFirstClusterRange);
EXPECT_CALL(host_, OnDemuxerError(PIPELINE_ERROR_NETWORK));
demuxer_->EndOfStream(PIPELINE_ERROR_NETWORK);
@@ -1282,10 +1293,10 @@ TEST_F(ChunkDemuxerTest, TestAddSeparateSourcesForAudioAndVideo) {
demuxer_->GetStream(DemuxerStream::VIDEO);
scoped_ptr<Cluster> cluster_a(
- GenerateSingleStreamCluster(0, 4, kAudioTrackNum, kAudioBlockDuration));
+ GenerateSingleStreamCluster(0, 92, kAudioTrackNum, kAudioBlockDuration));
scoped_ptr<Cluster> cluster_v(
- GenerateSingleStreamCluster(0, 4, kVideoTrackNum, kVideoBlockDuration));
+ GenerateSingleStreamCluster(0, 132, kVideoTrackNum, kVideoBlockDuration));
// Append audio and video data into separate source ids.
ASSERT_TRUE(AppendData(audio_id, cluster_a->data(), cluster_a->size()));
@@ -1320,10 +1331,10 @@ TEST_F(ChunkDemuxerTest, TestRemoveId) {
ASSERT_TRUE(InitDemuxerAudioAndVideoSources(audio_id, video_id));
scoped_ptr<Cluster> cluster_a(
- GenerateSingleStreamCluster(0, 4, kAudioTrackNum, kAudioBlockDuration));
+ GenerateSingleStreamCluster(0, 92, kAudioTrackNum, kAudioBlockDuration));
scoped_ptr<Cluster> cluster_v(
- GenerateSingleStreamCluster(0, 4, kVideoTrackNum, kVideoBlockDuration));
+ GenerateSingleStreamCluster(0, 132, kVideoTrackNum, kVideoBlockDuration));
// Append audio and video data into separate source ids.
ASSERT_TRUE(AppendData(audio_id, cluster_a->data(), cluster_a->size()));
@@ -1356,10 +1367,10 @@ TEST_F(ChunkDemuxerTest, TestSeekAudioAndVideoSources) {
ASSERT_TRUE(InitDemuxerAudioAndVideoSources(audio_id, video_id));
scoped_ptr<Cluster> cluster_a1(
- GenerateSingleStreamCluster(0, 4, kAudioTrackNum, kAudioBlockDuration));
+ GenerateSingleStreamCluster(0, 92, kAudioTrackNum, kAudioBlockDuration));
scoped_ptr<Cluster> cluster_v1(
- GenerateSingleStreamCluster(0, 4, kVideoTrackNum, kVideoBlockDuration));
+ GenerateSingleStreamCluster(0, 132, kVideoTrackNum, kVideoBlockDuration));
ASSERT_TRUE(AppendData(audio_id, cluster_a1->data(), cluster_a1->size()));
ASSERT_TRUE(AppendData(video_id, cluster_v1->data(), cluster_v1->size()));
@@ -1400,11 +1411,11 @@ TEST_F(ChunkDemuxerTest, TestSeekAudioAndVideoSources) {
EXPECT_FALSE(video_read_done);
scoped_ptr<Cluster> cluster_a2(
- GenerateSingleStreamCluster(3000, 4, kAudioTrackNum,
+ GenerateSingleStreamCluster(3000, 3092, kAudioTrackNum,
kAudioBlockDuration));
scoped_ptr<Cluster> cluster_v2(
- GenerateSingleStreamCluster(3000, 4, kVideoTrackNum,
+ GenerateSingleStreamCluster(3000, 3132, kVideoTrackNum,
kVideoBlockDuration));
ASSERT_TRUE(AppendData(audio_id, cluster_a2->data(), cluster_a2->size()));
@@ -1425,23 +1436,19 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_AudioIdOnly) {
ASSERT_TRUE(AppendInitSegment(true, false, false));
// Test a simple cluster.
- scoped_ptr<Cluster> cluster_1(GenerateSingleStreamCluster(0, 4,
+ scoped_ptr<Cluster> cluster_1(GenerateSingleStreamCluster(0, 92,
kAudioTrackNum, kAudioBlockDuration));
- ChunkDemuxer::Ranges expected;
- expected.push_back(CreateRange(0, 4, kAudioBlockDuration));
-
ASSERT_TRUE(AppendData(cluster_1->data(), cluster_1->size()));
- CheckExpectedRanges(expected);
+ CheckExpectedRanges("{ [0,92) }");
// Append a disjoint cluster to check for two separate ranges.
- scoped_ptr<Cluster> cluster_2(GenerateSingleStreamCluster(150, 3,
+ scoped_ptr<Cluster> cluster_2(GenerateSingleStreamCluster(150, 219,
kAudioTrackNum, kAudioBlockDuration));
- expected.push_back(CreateRange(150, 3, kAudioBlockDuration));
ASSERT_TRUE(AppendData(cluster_2->data(), cluster_2->size()));
- CheckExpectedRanges(expected);
+ CheckExpectedRanges("{ [0,92) [150,219) }");
}
// Test ranges in a video-only stream.
@@ -1454,23 +1461,20 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_VideoIdOnly) {
ASSERT_TRUE(AppendInitSegment(false, true, false));
// Test a simple cluster.
- scoped_ptr<Cluster> cluster_1(GenerateSingleStreamCluster(0, 4,
+ scoped_ptr<Cluster> cluster_1(GenerateSingleStreamCluster(0, 132,
kVideoTrackNum, kVideoBlockDuration));
- ChunkDemuxer::Ranges expected;
- expected.push_back(CreateRange(0, 4, kVideoBlockDuration));
ASSERT_TRUE(AppendData(cluster_1->data(), cluster_1->size()));
- CheckExpectedRanges(expected);
+ CheckExpectedRanges("{ [0,132) }");
// Append a disjoint cluster to check for two separate ranges.
- scoped_ptr<Cluster> cluster_2(GenerateSingleStreamCluster(150, 3,
+ scoped_ptr<Cluster> cluster_2(GenerateSingleStreamCluster(150, 249,
kVideoTrackNum, kVideoBlockDuration));
- expected.push_back(CreateRange(150, 3, kVideoBlockDuration));
ASSERT_TRUE(AppendData(cluster_2->data(), cluster_2->size()));
- CheckExpectedRanges(expected);
+ CheckExpectedRanges("{ [0,132) [150,249) }");
}
TEST_F(ChunkDemuxerTest, GetBufferedRanges_AudioVideo) {
@@ -1482,95 +1486,86 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_AudioVideo) {
// Audio block duration is smaller than video block duration,
// so the buffered ranges should correspond to the audio blocks.
scoped_ptr<Cluster> cluster_a0(
- GenerateSingleStreamCluster(0, 1, kAudioTrackNum, kAudioBlockDuration));
+ GenerateSingleStreamCluster(0, kAudioBlockDuration, kAudioTrackNum,
+ kAudioBlockDuration));
scoped_ptr<Cluster> cluster_v0(
- GenerateSingleStreamCluster(0, 1, kVideoTrackNum, kVideoBlockDuration));
-
- ChunkDemuxer::Ranges expected;
- expected.push_back(CreateRange(0, 1, kAudioBlockDuration));
+ GenerateSingleStreamCluster(0, kVideoBlockDuration, kVideoTrackNum,
+ kVideoBlockDuration));
ASSERT_TRUE(AppendData(cluster_a0->data(), cluster_a0->size()));
ASSERT_TRUE(AppendData(cluster_v0->data(), cluster_v0->size()));
- CheckExpectedRanges(expected);
+ CheckExpectedRanges("{ [0,23) }");
// Audio: 100 -> 150
// Video: 120 -> 170
// Buffered Range: 120 -> 150 (end overlap)
scoped_ptr<Cluster> cluster_a1(
- GenerateSingleStreamCluster(100, 1, kAudioTrackNum, 50));
+ GenerateSingleStreamCluster(100, 150, kAudioTrackNum, 50));
scoped_ptr<Cluster> cluster_v1(
- GenerateSingleStreamCluster(120, 1, kVideoTrackNum, 50));
-
- expected.push_back(CreateRange(120, 1, 30));
+ GenerateSingleStreamCluster(120, 170, kVideoTrackNum, 50));
ASSERT_TRUE(AppendData(cluster_a1->data(), cluster_a1->size()));
ASSERT_TRUE(AppendData(cluster_v1->data(), cluster_v1->size()));
- CheckExpectedRanges(expected);
+ CheckExpectedRanges("{ [0,23) [120,150) }");
// Audio: 220 -> 290
// Video: 200 -> 270
// Buffered Range: 220 -> 270 (front overlap)
scoped_ptr<Cluster> cluster_a2(
- GenerateSingleStreamCluster(220, 1, kAudioTrackNum, 70));
+ GenerateSingleStreamCluster(220, 290, kAudioTrackNum, 70));
scoped_ptr<Cluster> cluster_v2(
- GenerateSingleStreamCluster(200, 1, kVideoTrackNum, 70));
-
- expected.push_back(CreateRange(220, 1, 50));
+ GenerateSingleStreamCluster(200, 270, kVideoTrackNum, 70));
ASSERT_TRUE(AppendData(cluster_a2->data(), cluster_a2->size()));
ASSERT_TRUE(AppendData(cluster_v2->data(), cluster_v2->size()));
- CheckExpectedRanges(expected);
+ CheckExpectedRanges("{ [0,23) [120,150) [220,270) }");
// Audio: 320 -> 350
// Video: 300 -> 370
// Buffered Range: 320 -> 350 (complete overlap, audio)
scoped_ptr<Cluster> cluster_a3(
- GenerateSingleStreamCluster(320, 1, kAudioTrackNum, 30));
+ GenerateSingleStreamCluster(320, 350, kAudioTrackNum, 30));
scoped_ptr<Cluster> cluster_v3(
- GenerateSingleStreamCluster(300, 1, kVideoTrackNum, 70));
-
- expected.push_back(CreateRange(320, 1, 30));
+ GenerateSingleStreamCluster(300, 370, kVideoTrackNum, 70));
ASSERT_TRUE(AppendData(cluster_a3->data(), cluster_a3->size()));
ASSERT_TRUE(AppendData(cluster_v3->data(), cluster_v3->size()));
- CheckExpectedRanges(expected);
+ CheckExpectedRanges("{ [0,23) [120,150) [220,270) [320,350) }");
// Audio: 400 -> 470
// Video: 420 -> 450
// Buffered Range: 420 -> 450 (complete overlap, video)
scoped_ptr<Cluster> cluster_a4(
- GenerateSingleStreamCluster(400, 1, kAudioTrackNum, 70));
+ GenerateSingleStreamCluster(400, 470, kAudioTrackNum, 70));
scoped_ptr<Cluster> cluster_v4(
- GenerateSingleStreamCluster(420, 1, kVideoTrackNum, 30));
-
- expected.push_back(CreateRange(420, 1, 30));
+ GenerateSingleStreamCluster(420, 450, kVideoTrackNum, 30));
ASSERT_TRUE(AppendData(cluster_a4->data(), cluster_a4->size()));
ASSERT_TRUE(AppendData(cluster_v4->data(), cluster_v4->size()));
- CheckExpectedRanges(expected);
+ CheckExpectedRanges("{ [0,23) [120,150) [220,270) [320,350) [420,450) }");
// Appending within buffered range should not affect buffered ranges.
scoped_ptr<Cluster> cluster_a5(
- GenerateSingleStreamCluster(430, 1, kAudioTrackNum, 20));
+ GenerateSingleStreamCluster(430, 450, kAudioTrackNum, 20));
ASSERT_TRUE(AppendData(cluster_a5->data(), cluster_a5->size()));
- CheckExpectedRanges(expected);
+ CheckExpectedRanges("{ [0,23) [120,150) [220,270) [320,350) [420,450) }");
// Appending to single stream outside buffered ranges should not affect
// buffered ranges.
scoped_ptr<Cluster> cluster_v5(
- GenerateSingleStreamCluster(530, 1, kVideoTrackNum, 10));
+ GenerateSingleStreamCluster(530, 540, kVideoTrackNum, 10));
ASSERT_TRUE(AppendData(cluster_v5->data(), cluster_v5->size()));
- CheckExpectedRanges(expected);
+ CheckExpectedRanges("{ [0,23) [120,150) [220,270) [320,350) [420,450) }");
}
// Once EndOfStream() is called, GetBufferedRanges should not cut off any
@@ -1580,17 +1575,18 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_EndOfStream) {
ASSERT_TRUE(InitDemuxer(true, true, false));
scoped_ptr<Cluster> cluster_a(
- GenerateSingleStreamCluster(0, 1, kAudioTrackNum, 90));
+ GenerateSingleStreamCluster(0, 90, kAudioTrackNum, 90));
scoped_ptr<Cluster> cluster_v(
- GenerateSingleStreamCluster(0, 1, kVideoTrackNum, 100));
- ChunkDemuxer::Ranges expected;
- expected.push_back(CreateRange(0, 1, 100));
+ GenerateSingleStreamCluster(0, 100, kVideoTrackNum, 100));
ASSERT_TRUE(AppendData(cluster_a->data(), cluster_a->size()));
ASSERT_TRUE(AppendData(cluster_v->data(), cluster_v->size()));
+ CheckExpectedRanges("{ [0,90) }");
+
demuxer_->EndOfStream(PIPELINE_OK);
- CheckExpectedRanges(expected);
+
+ CheckExpectedRanges("{ [0,100) }");
}
TEST_F(ChunkDemuxerTest, TestDifferentStreamTimecodes) {
@@ -1632,4 +1628,54 @@ TEST_F(ChunkDemuxerTest, TestCodecPrefixMatching) {
demuxer_->AddId("source_id", "video/mp4", codecs));
}
+TEST_F(ChunkDemuxerTest, TestEndOfStreamFailures) {
+ std::string audio_id = "audio";
+ std::string video_id = "video";
+
+ ASSERT_TRUE(InitDemuxerAudioAndVideoSources(audio_id, video_id));
+
+ scoped_ptr<Cluster> cluster_a1(
+ GenerateSingleStreamCluster(0, 15, kAudioTrackNum, 15));
+ scoped_ptr<Cluster> cluster_v1(
+ GenerateSingleStreamCluster(0, 5, kVideoTrackNum, 5));
+ scoped_ptr<Cluster> cluster_v2(
+ GenerateSingleStreamCluster(5, 10, kVideoTrackNum, 5));
+ scoped_ptr<Cluster> cluster_v3(
+ GenerateSingleStreamCluster(10, 20, kVideoTrackNum, 10));
+
+ ASSERT_TRUE(AppendData(audio_id, cluster_a1->data(), cluster_a1->size()));
+ ASSERT_TRUE(AppendData(video_id, cluster_v1->data(), cluster_v1->size()));
+ ASSERT_TRUE(AppendData(video_id, cluster_v3->data(), cluster_v3->size()));
+
+ CheckExpectedRanges(audio_id, "{ [0,15) }");
+ CheckExpectedRanges(video_id, "{ [0,5) [10,20) }");
+
+ // Make sure that end of stream fails because there is a gap between
+ // the current position(0) and the end of the appended data.
+ ASSERT_FALSE(demuxer_->EndOfStream(PIPELINE_OK));
+
+ // Seek to an time that is inside the last ranges for both streams
+ // and verify that the EndOfStream() is successful.
+ demuxer_->StartWaitingForSeek();
+ demuxer_->Seek(base::TimeDelta::FromMilliseconds(10),
+ NewExpectedStatusCB(PIPELINE_OK));
+
+ ASSERT_TRUE(demuxer_->EndOfStream(PIPELINE_OK));
+
+ // Seek back to 0 and verify that EndOfStream() fails again.
+ demuxer_->StartWaitingForSeek();
+ demuxer_->Seek(base::TimeDelta::FromMilliseconds(0),
+ NewExpectedStatusCB(PIPELINE_OK));
+
+ ASSERT_FALSE(demuxer_->EndOfStream(PIPELINE_OK));
+
+ // Append the missing range and verify that EndOfStream() succeeds now.
+ ASSERT_TRUE(AppendData(video_id, cluster_v2->data(), cluster_v2->size()));
+
+ CheckExpectedRanges(audio_id, "{ [0,15) }");
+ CheckExpectedRanges(video_id, "{ [0,20) }");
+
+ ASSERT_TRUE(demuxer_->EndOfStream(PIPELINE_OK));
+}
+
} // namespace media
diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc
index 47899ee..ffe9602 100644
--- a/media/filters/source_buffer_stream.cc
+++ b/media/filters/source_buffer_stream.cc
@@ -86,13 +86,13 @@ class SourceBufferRange {
// if this range does not have the next buffer yet.
base::TimeDelta GetNextTimestamp() const;
+ // Returns the start timestamp of the range.
+ base::TimeDelta GetStartTimestamp() const;
+
// Returns the end timestamp of the buffered data. (Note that this is equal to
// the last buffer's timestamp + its duration.)
base::TimeDelta GetEndTimestamp() const;
- // Returns the Timespan of buffered time in this range.
- SourceBufferStream::Timespan GetBufferedTime() const;
-
// Returns whether a buffer with a starting timestamp of |timestamp| would
// belong in this range. This includes a buffer that would be appended to
// the end of the range.
@@ -119,9 +119,6 @@ class SourceBufferRange {
BufferQueue* deleted_buffers,
BufferQueue::iterator* next_buffer);
- // Returns the start timestamp of the range.
- base::TimeDelta GetStartTimestamp() const;
-
// An ordered list of buffers in this range.
BufferQueue buffers_;
@@ -130,7 +127,7 @@ class SourceBufferRange {
KeyframeMap keyframe_map_;
// Index into |buffers_| for the next buffer to be returned by
- // GetBufferedTime(), set to -1 before Seek().
+ // GetNextBuffer(), set to -1 before Seek().
int next_buffer_index_;
// True if the range needs to wait for the next keyframe to be appended before
@@ -163,10 +160,9 @@ static bool IsRangeListSorted(
base::TimeDelta prev = media::kNoTimestamp();
for (std::list<media::SourceBufferRange*>::const_iterator itr =
ranges.begin(); itr != ranges.end(); itr++) {
- media::SourceBufferStream::Timespan buffered = (*itr)->GetBufferedTime();
- if (prev != media::kNoTimestamp() && prev >= buffered.first)
+ if (prev != media::kNoTimestamp() && prev >= (*itr)->GetStartTimestamp())
return false;
- prev = buffered.second;
+ prev = (*itr)->GetEndTimestamp();
}
return true;
}
@@ -503,13 +499,13 @@ bool SourceBufferStream::GetNextBuffer(
return selected_range_ && selected_range_->GetNextBuffer(out_buffer);
}
-SourceBufferStream::TimespanList SourceBufferStream::GetBufferedTime() const {
- TimespanList timespans;
+Ranges<base::TimeDelta> SourceBufferStream::GetBufferedTime() const {
+ Ranges<base::TimeDelta> ranges;
for (RangeList::const_iterator itr = ranges_.begin();
itr != ranges_.end(); itr++) {
- timespans.push_back((*itr)->GetBufferedTime());
+ ranges.Add((*itr)->GetStartTimestamp(), (*itr)->GetEndTimestamp());
}
- return timespans;
+ return ranges;
}
void SourceBufferStream::EndOfStream() {
@@ -703,11 +699,6 @@ base::TimeDelta SourceBufferRange::GetNextTimestamp() const {
return buffers_.at(next_buffer_index_)->GetTimestamp();
}
-SourceBufferStream::Timespan
-SourceBufferRange::GetBufferedTime() const {
- return std::make_pair(GetStartTimestamp(), GetEndTimestamp());
-}
-
void SourceBufferRange::AppendToEnd(const SourceBufferRange& range,
bool transfer_current_position) {
DCHECK(CanAppendToEnd(range));
diff --git a/media/filters/source_buffer_stream.h b/media/filters/source_buffer_stream.h
index 6d7c179..5bc4ba0 100644
--- a/media/filters/source_buffer_stream.h
+++ b/media/filters/source_buffer_stream.h
@@ -12,6 +12,7 @@
#include "base/memory/ref_counted.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/media_export.h"
+#include "media/base/ranges.h"
#include "media/base/stream_parser_buffer.h"
#include "media/base/video_decoder_config.h"
@@ -26,8 +27,6 @@ class SourceBufferRange;
class MEDIA_EXPORT SourceBufferStream {
public:
typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue;
- typedef std::pair<base::TimeDelta, base::TimeDelta> Timespan;
- typedef std::deque<Timespan> TimespanList;
SourceBufferStream();
explicit SourceBufferStream(const AudioDecoderConfig& audio_config);
@@ -64,7 +63,7 @@ class MEDIA_EXPORT SourceBufferStream {
bool GetNextBuffer(scoped_refptr<StreamParserBuffer>* out_buffer);
// Returns a list of the buffered time ranges.
- TimespanList GetBufferedTime() const;
+ Ranges<base::TimeDelta> GetBufferedTime() const;
// Notifies this SourceBufferStream that EndOfStream has been called and that
// GetNextBuffer() should return EOS buffers after all other buffered data.
diff --git a/media/filters/source_buffer_stream_unittest.cc b/media/filters/source_buffer_stream_unittest.cc
index eaf1ef5..a89eec5 100644
--- a/media/filters/source_buffer_stream_unittest.cc
+++ b/media/filters/source_buffer_stream_unittest.cc
@@ -55,33 +55,18 @@ class SourceBufferStreamTest : public testing::Test {
stream_.Seek(position * frame_duration_);
}
- SourceBufferStream::Timespan CreateTimespan(
- int start_position, int end_position) {
- return std::make_pair(
- start_position * frame_duration_, (end_position + 1) * frame_duration_);
- }
-
- void CheckExpectedTimespan(int start_position, int end_position) {
- SourceBufferStream::TimespanList expected;
- expected.push_back(CreateTimespan(start_position, end_position));
- CheckExpectedTimespans(expected);
- }
-
- void CheckExpectedTimespans(
- SourceBufferStream::TimespanList expected_times) {
- SourceBufferStream::TimespanList actual_times = stream_.GetBufferedTime();
- EXPECT_EQ(expected_times.size(), actual_times.size());
-
- for (SourceBufferStream::TimespanList::iterator actual_itr =
- actual_times.begin(), expected_itr = expected_times.begin();
- actual_itr != actual_times.end() &&
- expected_itr != expected_times.end();
- actual_itr++, expected_itr++) {
- EXPECT_EQ(expected_itr->first / frame_duration_,
- actual_itr->first / frame_duration_);
- EXPECT_EQ(expected_itr->second / frame_duration_,
- actual_itr->second / frame_duration_);
+ void CheckExpectedRanges(const std::string& expected) {
+ Ranges<base::TimeDelta> r = stream_.GetBufferedTime();
+
+ std::stringstream ss;
+ ss << "{ ";
+ for (size_t i = 0; i < r.size(); ++i) {
+ int64 start = (r.start(i) / frame_duration_);
+ int64 end = (r.end(i) / frame_duration_) - 1;
+ ss << "[" << start << "," << end << ") ";
}
+ ss << "}";
+ EXPECT_EQ(ss.str(), expected);
}
void CheckExpectedBuffers(
@@ -187,7 +172,7 @@ TEST_F(SourceBufferStreamTest, Append_SingleRange) {
AppendBuffers(0, 15);
// Check expected range.
- CheckExpectedTimespan(0, 14);
+ CheckExpectedRanges("{ [0,14) }");
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 14);
@@ -199,7 +184,7 @@ TEST_F(SourceBufferStreamTest, Append_SingleRange_OneBufferAtATime) {
AppendBuffers(i, 1);
// Check expected range.
- CheckExpectedTimespan(0, 14);
+ CheckExpectedRanges("{ [0,14) }");
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 14);
@@ -213,10 +198,7 @@ TEST_F(SourceBufferStreamTest, Append_DisjointRanges) {
AppendBuffers(15, 10);
// Check expected ranges.
- SourceBufferStream::TimespanList expected;
- expected.push_back(CreateTimespan(0, 4));
- expected.push_back(CreateTimespan(15, 24));
- CheckExpectedTimespans(expected);
+ CheckExpectedRanges("{ [0,4) [15,24) }");
// Check buffers in ranges.
Seek(0);
CheckExpectedBuffers(0, 4);
@@ -235,7 +217,7 @@ TEST_F(SourceBufferStreamTest, Append_AdjacentRanges) {
AppendBuffers(12, 3);
// Check expected range.
- CheckExpectedTimespan(0, 25);
+ CheckExpectedRanges("{ [0,25) }");
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 25);
@@ -249,14 +231,14 @@ TEST_F(SourceBufferStreamTest, Append_DoesNotBeginWithKeyframe) {
AppendBuffers(5, 10);
// Check expected range.
- CheckExpectedTimespan(5, 14);
+ CheckExpectedRanges("{ [5,14) }");
// Check buffers in range.
Seek(5);
CheckExpectedBuffers(5, 14);
// Append fails because the range doesn't begin with a keyframe.
AppendBuffers_ExpectFailure(17, 10);
- CheckExpectedTimespan(5, 14);
+ CheckExpectedRanges("{ [5,14) }");
Seek(5);
CheckExpectedBuffers(5, 14);
}
@@ -269,7 +251,7 @@ TEST_F(SourceBufferStreamTest, Complete_Overlap) {
AppendBuffers(0, 15);
// Check expected range.
- CheckExpectedTimespan(0, 14);
+ CheckExpectedRanges("{ [0,14) }");
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 14);
@@ -287,7 +269,7 @@ TEST_F(SourceBufferStreamTest, Complete_Overlap_EdgeCase) {
AppendBuffers(5, 8);
// Check expected range.
- CheckExpectedTimespan(5, 12);
+ CheckExpectedRanges("{ [5,12) }");
// Check buffers in range.
Seek(5);
CheckExpectedBuffers(5, 12);
@@ -301,7 +283,7 @@ TEST_F(SourceBufferStreamTest, Start_Overlap) {
AppendBuffers(8, 6);
// Check expected range.
- CheckExpectedTimespan(5, 13);
+ CheckExpectedRanges("{ [5,13) }");
// Check buffers in range.
Seek(5);
CheckExpectedBuffers(5, 13);
@@ -315,7 +297,7 @@ TEST_F(SourceBufferStreamTest, End_Overlap) {
AppendBuffers(5, 10);
// Check expected range.
- CheckExpectedTimespan(5, 19);
+ CheckExpectedRanges("{ [5,19) }");
// Check buffers in range.
Seek(5);
CheckExpectedBuffers(5, 19);
@@ -330,10 +312,7 @@ TEST_F(SourceBufferStreamTest, End_Overlap_Several) {
// Check expected ranges: stream should not have kept buffers 13 and 14
// because the keyframe on which they depended was overwritten.
- SourceBufferStream::TimespanList expected;
- expected.push_back(CreateTimespan(5, 12));
- expected.push_back(CreateTimespan(15, 19));
- CheckExpectedTimespans(expected);
+ CheckExpectedRanges("{ [5,12) [15,19) }");
// Check buffers in range.
Seek(5);
@@ -355,19 +334,13 @@ TEST_F(SourceBufferStreamTest, Complete_Overlap_Several) {
AppendBuffers(15, 2);
// Check expected ranges.
- SourceBufferStream::TimespanList expected;
- expected.push_back(CreateTimespan(5, 6));
- expected.push_back(CreateTimespan(10, 11));
- expected.push_back(CreateTimespan(15, 16));
- CheckExpectedTimespans(expected);
+ CheckExpectedRanges("{ [5,6) [10,11) [15,16) }");
// Append buffers at positions 0 through 19.
AppendBuffers(0, 20);
// Check expected range.
- expected.clear();
- expected.push_back(CreateTimespan(0, 19));
- CheckExpectedTimespans(expected);
+ CheckExpectedRanges("{ [0,19) }");
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 19);
@@ -390,7 +363,7 @@ TEST_F(SourceBufferStreamTest, Complete_Overlap_Several_Then_Merge) {
AppendBuffers(0, 20);
// Check expected ranges.
- CheckExpectedTimespan(0, 21);
+ CheckExpectedRanges("{ [0,21) }");
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 21);
@@ -406,8 +379,8 @@ TEST_F(SourceBufferStreamTest, Complete_Overlap_Selected) {
// Replace old data with new data.
AppendBuffers(5, 10, &kDataB);
- // Check timespans are correct.
- CheckExpectedTimespan(5, 14);
+ // Check ranges are correct.
+ CheckExpectedRanges("{ [5,14) }");
// Check that data has been replaced with new data.
CheckExpectedBuffers(5, 14, &kDataB);
@@ -428,8 +401,8 @@ TEST_F(SourceBufferStreamTest, Complete_Overlap_Selected_TrackBuffer) {
// Do a complete overlap by appending 20 buffers at positions 0 through 19.
AppendBuffers(0, 20, &kDataB);
- // Check timespan is correct.
- CheckExpectedTimespan(0, 19);
+ // Check range is correct.
+ CheckExpectedRanges("{ [0,19) }");
// Expect old data up until next keyframe in new data.
CheckExpectedBuffers(6, 9, &kDataA);
@@ -442,8 +415,8 @@ TEST_F(SourceBufferStreamTest, Complete_Overlap_Selected_TrackBuffer) {
Seek(0);
CheckExpectedBuffers(0, 19, &kDataB);
- // Check timespan continues to be correct.
- CheckExpectedTimespan(0, 19);
+ // Check range continues to be correct.
+ CheckExpectedRanges("{ [0,19) }");
}
TEST_F(SourceBufferStreamTest, Complete_Overlap_Selected_EdgeCase) {
@@ -457,8 +430,8 @@ TEST_F(SourceBufferStreamTest, Complete_Overlap_Selected_EdgeCase) {
// Replace existing data with new data.
AppendBuffers(5, 10, &kDataB);
- // Check timespans are correct.
- CheckExpectedTimespan(5, 14);
+ // Check ranges are correct.
+ CheckExpectedRanges("{ [5,14) }");
// Expect old data up until next keyframe in new data.
CheckExpectedBuffers(6, 9, &kDataA);
@@ -471,8 +444,8 @@ TEST_F(SourceBufferStreamTest, Complete_Overlap_Selected_EdgeCase) {
Seek(5);
CheckExpectedBuffers(5, 14, &kDataB);
- // Check timespan continues to be correct.
- CheckExpectedTimespan(5, 14);
+ // Check range continues to be correct.
+ CheckExpectedRanges("{ [5,14) }");
}
TEST_F(SourceBufferStreamTest, Complete_Overlap_Selected_Multiple) {
@@ -520,7 +493,7 @@ TEST_F(SourceBufferStreamTest, Start_Overlap_Selected) {
AppendBuffers(5, 10, &kDataB);
// Check expected range.
- CheckExpectedTimespan(0, 14);
+ CheckExpectedRanges("{ [0,14) }");
// Because we seeked to a keyframe, the next buffers should all be new data.
CheckExpectedBuffers(5, 14, &kDataB);
@@ -543,7 +516,7 @@ TEST_F(SourceBufferStreamTest, Start_Overlap_Selected_TrackBuffer) {
AppendBuffers(10, 10, &kDataB);
// Check expected range.
- CheckExpectedTimespan(0, 19);
+ CheckExpectedRanges("{ [0,19) }");
// The next 4 buffers should be a from the old buffer, followed by a keyframe
// from the new data.
@@ -559,8 +532,8 @@ TEST_F(SourceBufferStreamTest, Start_Overlap_Selected_TrackBuffer) {
CheckExpectedBuffers(0, 9, &kDataA);
CheckExpectedBuffers(10, 19, &kDataB);
- // Make sure timespan is still correct.
- CheckExpectedTimespan(0, 19);
+ // Make sure range is still correct.
+ CheckExpectedRanges("{ [0,19) }");
}
TEST_F(SourceBufferStreamTest, Start_Overlap_Selected_EdgeCase) {
@@ -590,7 +563,7 @@ TEST_F(SourceBufferStreamTest, Start_Overlap_Selected_EdgeCase) {
CheckExpectedBuffers(10, 19, &kDataB);
// Check expected range.
- CheckExpectedTimespan(5, 19);
+ CheckExpectedRanges("{ [5,19) }");
}
// This test covers the case where new buffers end-overlap an existing, selected
@@ -611,7 +584,7 @@ TEST_F(SourceBufferStreamTest, End_Overlap_Selected) {
AppendBuffers(0, 10, &kDataB);
// Check expected range.
- CheckExpectedTimespan(0, 14);
+ CheckExpectedRanges("{ [0,14) }");
// Because we seeked to a keyframe, the next buffers should be new.
CheckExpectedBuffers(5, 9, &kDataB);
@@ -642,7 +615,7 @@ TEST_F(SourceBufferStreamTest, End_Overlap_Selected_AfterEndOfNew_1) {
AppendBuffers(0, 10, &kDataB);
// Check expected range.
- CheckExpectedTimespan(0, 14);
+ CheckExpectedRanges("{ [0,14) }");
// Make sure rest of data is as expected.
CheckExpectedBuffers(13, 14, &kDataA);
@@ -674,10 +647,7 @@ TEST_F(SourceBufferStreamTest, End_Overlap_Selected_AfterEndOfNew_2) {
AppendBuffers(0, 8, &kDataB);
// Check expected ranges.
- SourceBufferStream::TimespanList expected;
- expected.push_back(CreateTimespan(0, 7));
- expected.push_back(CreateTimespan(10, 14));
- CheckExpectedTimespans(expected);
+ CheckExpectedRanges("{ [0,7) [10,14) }");
// Make sure rest of data is as expected.
CheckExpectedBuffers(13, 14, &kDataA);
@@ -713,10 +683,7 @@ TEST_F(SourceBufferStreamTest, End_Overlap_Selected_AfterEndOfNew_3) {
AppendBuffers(0, 8, &kDataB);
// Check expected ranges.
- SourceBufferStream::TimespanList expected;
- expected.push_back(CreateTimespan(0, 7));
- expected.push_back(CreateTimespan(10, 14));
- CheckExpectedTimespans(expected);
+ CheckExpectedRanges("{ [0,7) [10,14) }");
// Check for data in the track buffer.
CheckExpectedBuffers(8, 9, &kDataA);
@@ -751,7 +718,7 @@ TEST_F(SourceBufferStreamTest, End_Overlap_Selected_OverlappedByNew_1) {
AppendBuffers(0, 10, &kDataB);
// Check expected range.
- CheckExpectedTimespan(0, 14);
+ CheckExpectedRanges("{ [0,14) }");
// Check for data in the track buffer.
CheckExpectedBuffers(8, 9, &kDataA);
@@ -786,10 +753,7 @@ TEST_F(SourceBufferStreamTest, End_Overlap_Selected_OverlappedByNew_2) {
AppendBuffers(0, 7, &kDataB);
// Check expected ranges.
- SourceBufferStream::TimespanList expected;
- expected.push_back(CreateTimespan(0, 6));
- expected.push_back(CreateTimespan(10, 14));
- CheckExpectedTimespans(expected);
+ CheckExpectedRanges("{ [0,6) [10,14) }");
// Check for data in the track buffer.
CheckExpectedBuffers(6, 9, &kDataA);
@@ -827,10 +791,7 @@ TEST_F(SourceBufferStreamTest, End_Overlap_Selected_OverlappedByNew_3) {
AppendBuffers(0, 13, &kDataB);
// Check expected ranges.
- SourceBufferStream::TimespanList expected;
- expected.push_back(CreateTimespan(0, 12));
- expected.push_back(CreateTimespan(15, 19));
- CheckExpectedTimespans(expected);
+ CheckExpectedRanges("{ [0,12) [15,19) }");
// Check for data in the track buffer.
CheckExpectedBuffers(6, 9, &kDataA);
@@ -866,7 +827,7 @@ TEST_F(SourceBufferStreamTest, End_Overlap_Selected_NoKeyframeAfterNew) {
AppendBuffers(0, 6, &kDataB);
// Check expected range.
- CheckExpectedTimespan(0, 5);
+ CheckExpectedRanges("{ [0,5) }");
// Check for data in the track buffer.
CheckExpectedBuffers(6, 9, &kDataA);
@@ -899,7 +860,7 @@ TEST_F(SourceBufferStreamTest, Middle_Overlap_Selected_1) {
AppendBuffers(5, 5, &kDataB);
// Check expected range.
- CheckExpectedTimespan(0, 14);
+ CheckExpectedRanges("{ [0,14) }");
// Check for next data; should be new data.
CheckExpectedBuffers(5, 9, &kDataB);
@@ -930,7 +891,7 @@ TEST_F(SourceBufferStreamTest, Middle_Overlap_Selected_2) {
AppendBuffers(5, 5, &kDataB);
// Check expected range.
- CheckExpectedTimespan(0, 14);
+ CheckExpectedRanges("{ [0,14) }");
// Make sure data is correct.
CheckExpectedBuffers(11, 14, &kDataA);
@@ -959,10 +920,7 @@ TEST_F(SourceBufferStreamTest, Middle_Overlap_Selected_3) {
AppendBuffers(5, 3, &kDataB);
// Check expected range.
- SourceBufferStream::TimespanList expected;
- expected.push_back(CreateTimespan(0, 7));
- expected.push_back(CreateTimespan(10, 14));
- CheckExpectedTimespans(expected);
+ CheckExpectedRanges("{ [0,7) [10,14) }");
// Make sure data is correct.
CheckExpectedBuffers(2, 4, &kDataA);
@@ -991,10 +949,7 @@ TEST_F(SourceBufferStreamTest, Middle_Overlap_Selected_4) {
AppendBuffers(5, 3, &kDataB);
// Check expected range.
- SourceBufferStream::TimespanList expected;
- expected.push_back(CreateTimespan(0, 7));
- expected.push_back(CreateTimespan(10, 14));
- CheckExpectedTimespans(expected);
+ CheckExpectedRanges("{ [0,7) [10,14) }");
// Buffers 8 and 9 should be in the track buffer.
CheckExpectedBuffers(8, 9, &kDataA);
@@ -1083,15 +1038,15 @@ TEST_F(SourceBufferStreamTest, Seek_After_TrackBuffer_Filled) {
// Do a complete overlap by appending 20 buffers at positions 0 through 19.
AppendBuffers(0, 20, &kDataB);
- // Check timespan is correct.
- CheckExpectedTimespan(0, 19);
+ // Check range is correct.
+ CheckExpectedRanges("{ [0,19) }");
// Seek to beginning; all data should be new.
Seek(0);
CheckExpectedBuffers(0, 19, &kDataB);
- // Check timespan continues to be correct.
- CheckExpectedTimespan(0, 19);
+ // Check range continues to be correct.
+ CheckExpectedRanges("{ [0,19) }");
}
TEST_F(SourceBufferStreamTest, Seek_StartOfSegment) {
@@ -1136,14 +1091,14 @@ TEST_F(SourceBufferStreamTest, GetNextBuffer_AfterMerges) {
AppendBuffers(5, 5);
// Make sure ranges are merged.
- CheckExpectedTimespan(5, 14);
+ CheckExpectedRanges("{ [5,14) }");
// Make sure the next buffer is correct.
CheckExpectedBuffers(10, 10);
// Append 5 buffers at positions 15 through 19.
AppendBuffers(15, 5);
- CheckExpectedTimespan(5, 19);
+ CheckExpectedRanges("{ [5,19) }");
// Make sure the remaining next buffers are correct.
CheckExpectedBuffers(11, 14);
diff --git a/webkit/media/webmediaplayer_impl.cc b/webkit/media/webmediaplayer_impl.cc
index 321ca72..5707a7b 100644
--- a/webkit/media/webmediaplayer_impl.cc
+++ b/webkit/media/webmediaplayer_impl.cc
@@ -91,6 +91,16 @@ COMPILE_ASSERT_MATCHING_ENUM(Anonymous);
COMPILE_ASSERT_MATCHING_ENUM(UseCredentials);
#undef COMPILE_ASSERT_MATCHING_ENUM
+static WebKit::WebTimeRanges ConvertToWebTimeRanges(
+ const media::Ranges<base::TimeDelta>& ranges) {
+ WebKit::WebTimeRanges result(ranges.size());
+ for (size_t i = 0; i < ranges.size(); i++) {
+ result[i].start = ranges.start(i).InSecondsF();
+ result[i].end = ranges.end(i).InSecondsF();
+ }
+ return result;
+}
+
WebMediaPlayerImpl::WebMediaPlayerImpl(
WebKit::WebFrame* frame,
WebKit::WebMediaPlayerClient* client,
@@ -471,13 +481,8 @@ WebMediaPlayer::ReadyState WebMediaPlayerImpl::readyState() const {
const WebKit::WebTimeRanges& WebMediaPlayerImpl::buffered() {
DCHECK_EQ(main_loop_, MessageLoop::current());
- media::Ranges<base::TimeDelta> buffered_time_ranges =
- pipeline_->GetBufferedTimeRanges();
- WebKit::WebTimeRanges web_ranges(buffered_time_ranges.size());
- for (size_t i = 0; i < buffered_time_ranges.size(); ++i) {
- web_ranges[i].start = buffered_time_ranges.start(i).InSecondsF();
- web_ranges[i].end = buffered_time_ranges.end(i).InSecondsF();
- }
+ WebKit::WebTimeRanges web_ranges(
+ ConvertToWebTimeRanges(pipeline_->GetBufferedTimeRanges()));
buffered_.swap(web_ranges);
return buffered_;
}
@@ -656,16 +661,7 @@ bool WebMediaPlayerImpl::sourceRemoveId(const WebKit::WebString& id) {
WebKit::WebTimeRanges WebMediaPlayerImpl::sourceBuffered(
const WebKit::WebString& id) {
- media::ChunkDemuxer::Ranges buffered_ranges;
- if (!proxy_->DemuxerBufferedRange(id.utf8().data(), &buffered_ranges))
- return WebKit::WebTimeRanges();
-
- WebKit::WebTimeRanges ranges(buffered_ranges.size());
- for (size_t i = 0; i < buffered_ranges.size(); i++) {
- ranges[i].start = buffered_ranges[i].first.InSecondsF();
- ranges[i].end = buffered_ranges[i].second.InSecondsF();
- }
- return ranges;
+ return ConvertToWebTimeRanges(proxy_->DemuxerBufferedRange(id.utf8().data()));
}
bool WebMediaPlayerImpl::sourceAppend(const unsigned char* data,
diff --git a/webkit/media/webmediaplayer_proxy.cc b/webkit/media/webmediaplayer_proxy.cc
index 8b2adc1..2809e68 100644
--- a/webkit/media/webmediaplayer_proxy.cc
+++ b/webkit/media/webmediaplayer_proxy.cc
@@ -200,10 +200,9 @@ void WebMediaPlayerProxy::DemuxerRemoveId(const std::string& id) {
chunk_demuxer_->RemoveId(id);
}
-bool WebMediaPlayerProxy::DemuxerBufferedRange(
- const std::string& id,
- media::ChunkDemuxer::Ranges* ranges_out) {
- return chunk_demuxer_->GetBufferedRanges(id, ranges_out);
+media::Ranges<base::TimeDelta> WebMediaPlayerProxy::DemuxerBufferedRange(
+ const std::string& id) {
+ return chunk_demuxer_->GetBufferedRanges(id);
}
bool WebMediaPlayerProxy::DemuxerAppend(const std::string& id,
diff --git a/webkit/media/webmediaplayer_proxy.h b/webkit/media/webmediaplayer_proxy.h
index 0fe84f1..dcca88d 100644
--- a/webkit/media/webmediaplayer_proxy.h
+++ b/webkit/media/webmediaplayer_proxy.h
@@ -100,8 +100,7 @@ class WebMediaPlayerProxy
const std::string& type,
std::vector<std::string>& codecs);
void DemuxerRemoveId(const std::string& id);
- bool DemuxerBufferedRange(const std::string& id,
- media::ChunkDemuxer::Ranges* ranges_out);
+ media::Ranges<base::TimeDelta> DemuxerBufferedRange(const std::string& id);
bool DemuxerAppend(const std::string& id, const uint8* data, size_t length);
void DemuxerAbort(const std::string& id);
void DemuxerEndOfStream(media::PipelineStatus status);