diff options
author | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-20 00:51:52 +0000 |
---|---|---|
committer | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-20 00:51:52 +0000 |
commit | c566cb79b77ee9dfe3f287fb930c2e9e419793d3 (patch) | |
tree | e5dc3c731b7d7ca2399d412bcf96825b4de67bcf | |
parent | 2767ca041af88d18e387d59c12a49fff10443ce8 (diff) | |
download | chromium_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.h | 28 | ||||
-rw-r--r-- | media/base/ranges_unittest.cc | 49 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.cc | 172 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.h | 22 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_unittest.cc | 206 | ||||
-rw-r--r-- | media/filters/source_buffer_stream.cc | 29 | ||||
-rw-r--r-- | media/filters/source_buffer_stream.h | 5 | ||||
-rw-r--r-- | media/filters/source_buffer_stream_unittest.cc | 161 | ||||
-rw-r--r-- | webkit/media/webmediaplayer_impl.cc | 30 | ||||
-rw-r--r-- | webkit/media/webmediaplayer_proxy.cc | 7 | ||||
-rw-r--r-- | webkit/media/webmediaplayer_proxy.h | 3 |
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); |