diff options
author | vrk@chromium.org <vrk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-11 05:27:13 +0000 |
---|---|---|
committer | vrk@chromium.org <vrk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-11 05:27:13 +0000 |
commit | 791c3beb045e5f207d652366f943e9b92d6183ec (patch) | |
tree | 50f1d99445d0cd6478baab8dca1b111f0aeaaf70 /media | |
parent | 6c75081a1ac9c0f4eb8e430b3d9877b3bcb3d13f (diff) | |
download | chromium_src-791c3beb045e5f207d652366f943e9b92d6183ec.zip chromium_src-791c3beb045e5f207d652366f943e9b92d6183ec.tar.gz chromium_src-791c3beb045e5f207d652366f943e9b92d6183ec.tar.bz2 |
Implement support for completely overlapping the selected range in SourceBufferStream
BUG=126560
TEST=media_unittests
Review URL: https://chromiumcodereview.appspot.com/10375042
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@136512 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/base/stream_parser_buffer.cc | 8 | ||||
-rw-r--r-- | media/base/stream_parser_buffer.h | 4 | ||||
-rw-r--r-- | media/filters/source_buffer_stream.cc | 262 | ||||
-rw-r--r-- | media/filters/source_buffer_stream.h | 30 | ||||
-rw-r--r-- | media/filters/source_buffer_stream_unittest.cc | 226 |
5 files changed, 470 insertions, 60 deletions
diff --git a/media/base/stream_parser_buffer.cc b/media/base/stream_parser_buffer.cc index 77aeb40..3c765b1 100644 --- a/media/base/stream_parser_buffer.cc +++ b/media/base/stream_parser_buffer.cc @@ -4,6 +4,8 @@ #include "media/base/stream_parser_buffer.h" +#include "base/logging.h" + namespace media { StreamParserBuffer::StreamParserBuffer(const uint8* data, int data_size, @@ -22,4 +24,10 @@ scoped_refptr<StreamParserBuffer> StreamParserBuffer::CopyFrom( new StreamParserBuffer(data, data_size, is_keyframe)); } +base::TimeDelta StreamParserBuffer::GetEndTimestamp() const { + DCHECK(GetTimestamp() != kNoTimestamp()); + DCHECK(GetDuration() != kNoTimestamp()); + return GetTimestamp() + GetDuration(); +} + } // namespace media diff --git a/media/base/stream_parser_buffer.h b/media/base/stream_parser_buffer.h index a952a83..1122e91 100644 --- a/media/base/stream_parser_buffer.h +++ b/media/base/stream_parser_buffer.h @@ -17,8 +17,12 @@ class MEDIA_EXPORT StreamParserBuffer : public DataBuffer { const uint8* data, int data_size, bool is_keyframe); bool IsKeyframe() const { return is_keyframe_; } + // Returns this buffer's timestamp + duration, assuming both are valid. + base::TimeDelta GetEndTimestamp() const; + private: StreamParserBuffer(const uint8* data, int data_size, bool is_keyframe); + bool is_keyframe_; DISALLOW_COPY_AND_ASSIGN(StreamParserBuffer); }; diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc index fb03a8d..52ce36c 100644 --- a/media/filters/source_buffer_stream.cc +++ b/media/filters/source_buffer_stream.cc @@ -28,12 +28,19 @@ class SourceBufferRange { // Assumes |timestamp| is valid and in this range. void Seek(base::TimeDelta timestamp); + // Updates |next_buffer_index_| to point to next keyframe after or equal to + // |timestamp|. If there is no such keyframe, then this range will seek to + // the end and return kNoTimestamp(). + // Assumes |timestamp| is valid and in this range. + base::TimeDelta SeekAfter(base::TimeDelta timestamp); + // Updates |out_buffer| with the next buffer in presentation order. Seek() // must be called before calls to GetNextBuffer(), and buffers are returned // in order from the last call to Seek(). Returns true if |out_buffer| is // filled with a valid buffer, false if there is not enough data to fulfill // the request. bool GetNextBuffer(scoped_refptr<StreamParserBuffer>* out_buffer); + base::TimeDelta GetNextTimestamp() const; // Returns the Timespan of buffered time in this range. SourceBufferStream::Timespan GetBufferedTime() const; @@ -43,9 +50,10 @@ class SourceBufferRange { // in this range. // If |transfer_current_position| is true, |range|'s |next_buffer_position_| // is transfered to this SourceBufferRange. - void AppendRange(const SourceBufferRange& range, + void AppendToEnd(const SourceBufferRange& range, bool transfer_current_position); - bool CanAppendRange(const SourceBufferRange& range) const; + bool CanAppendToEnd(const SourceBufferRange& range) const; + bool CanAppendToEnd(const BufferQueue& buffers) 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 @@ -58,10 +66,21 @@ class SourceBufferRange { // |timestamp|, false otherwise. bool CanSeekTo(base::TimeDelta timestamp) const; + // Returns true if this range's buffered timespan completely overlaps the + // buffered timespan of |range|. + bool CompletelyOverlaps(const SourceBufferRange& range) const; + // Returns true if the end of this range contains buffers that overlaps with // the beginning of |range|. bool EndOverlaps(const SourceBufferRange& range) const; + // Functions that tell how |buffers| intersects with this range. + // TODO(vrk): These functions should be unnecessary when overlapping the + // selected range is implemented properly. (crbug.com/126560) + bool IsStartOverlappedBy(const BufferQueue& buffers) const; + bool IsEndOverlappedBy(const BufferQueue& buffers) const; + bool IsCompletelyOverlappedBy(const BufferQueue& buffers) const; + private: // Appends |buffers| to the end of the range and updates |keyframe_map_| as // it encounters new keyframes. Assumes |buffers| belongs at the end of the @@ -77,11 +96,6 @@ class SourceBufferRange { // range is empty. base::TimeDelta BufferedEnd() const; - // Returns the upper bound for the starting timestamp for the next buffer to - // be appended at the end of the range. Should only be called if |buffers_| is - // nonempty. - base::TimeDelta MaxNextTimestamp() const; - // An ordered list of buffers in this range. BufferQueue buffers_; @@ -119,12 +133,33 @@ static bool BufferComparator(scoped_refptr<media::Buffer> first, return first->GetTimestamp() < second->GetTimestamp(); } +// Returns the upper bound for the starting timestamp for the next buffer +// in sequence after |buffer|. Assumes |buffer|'s timestamp and +// duration are valid. +static base::TimeDelta MaxNextTimestamp( + const scoped_refptr<media::StreamParserBuffer>& buffer) { + // Because we do not know exactly when is the next timestamp, any buffer + // that starts within 1/3 of the duration past the end of this buffer + // is considered the next buffer in the sequence. + return buffer->GetEndTimestamp() + buffer->GetDuration() / 3; +} + +// Returns true if |timestamp| is the timestamp of the next buffer in +// sequence after |buffer|, false otherwise. +static bool IsNextInSequence( + const scoped_refptr<media::StreamParserBuffer>& buffer, + base::TimeDelta timestamp) { + return timestamp >= buffer->GetEndTimestamp() && + timestamp <= MaxNextTimestamp(buffer); +} + namespace media { SourceBufferStream::SourceBufferStream() : seek_pending_(false), seek_buffer_timestamp_(kNoTimestamp()), - selected_range_(NULL) { + selected_range_(NULL), + waiting_for_keyframe_(false) { } SourceBufferStream::~SourceBufferStream() { @@ -137,34 +172,43 @@ SourceBufferStream::~SourceBufferStream() { bool SourceBufferStream::Append( const SourceBufferStream::BufferQueue& buffers) { DCHECK(!buffers.empty()); - base::TimeDelta start_timestamp = buffers.front()->GetTimestamp(); - base::TimeDelta end_timestamp = buffers.back()->GetTimestamp(); // Check to see if |buffers| will overlap the currently |selected_range_|, // and if so, ignore this Append() request. - // TODO(vrk): Support end overlap properly. (crbug.com/125072) - if (selected_range_) { - Timespan selected_range_span = selected_range_->GetBufferedTime(); - if (selected_range_span.second > start_timestamp && - selected_range_span.first <= end_timestamp) { - return false; - } + // TODO(vrk): Support overlapping selected range properly. (crbug.com/126560) + if (selected_range_ && + (selected_range_->IsEndOverlappedBy(buffers) || + selected_range_->IsStartOverlappedBy(buffers))) { + return false; } SourceBufferRange* range = NULL; RangeList::iterator itr = ranges_.end(); + base::TimeDelta start_timestamp = buffers.front()->GetTimestamp(); for (itr = ranges_.begin(); itr != ranges_.end(); itr++) { int range_value = (*itr)->BelongsToRange(start_timestamp); // |start_timestamp| is before the current range in this loop. Because // |ranges_| is sorted, this means that we need to create a new range and it // should be placed before |itr|. - if (range_value < 0) + // TODO(vrk): We also break out of the loop if |buffers| completely overlaps + // the current range. This is to cover the case when |buffers| belongs to + // the current range, but also completely overlaps it. This should be + // removed when start overlap is handled properly. + if (range_value < 0 || (*itr)->IsCompletelyOverlappedBy(buffers)) break; if (range_value == 0) { // Found an existing range into which we can append buffers. range = *itr; + + if (range->CanAppendToEnd(buffers) && waiting_for_keyframe_) { + // Currently we do not support the case where the next buffer after the + // buffers in the track buffer is not a keyframe. + if (!buffers.front()->IsKeyframe()) + return false; + waiting_for_keyframe_ = false; + } break; } } @@ -184,37 +228,87 @@ bool SourceBufferStream::Append( // Increment |itr| to be the range after |range|. itr++; - // Handle overlaps if they were created. - while (itr != ranges_.end() && range->EndOverlaps(**itr)) { + // Resolve overlaps. + itr = ResolveCompleteOverlaps(itr, range); + itr = ResolveEndOverlaps(itr, range); + MergeWithAdjacentRangeIfNecessary(itr, range); + + // Finally, try to complete pending seek if one exists. + if (seek_pending_) + Seek(seek_buffer_timestamp_); + + DCHECK(IsRangeListSorted(ranges_)); + return true; +} + +SourceBufferStream::RangeList::iterator +SourceBufferStream::ResolveCompleteOverlaps( + const RangeList::iterator& range_itr, SourceBufferRange* new_range) { + RangeList::iterator itr = range_itr; + while (itr != ranges_.end() && new_range->CompletelyOverlaps(**itr)) { + if (*itr == selected_range_) { + // Get the timestamp for the next buffer in the sequence. + base::TimeDelta next_timestamp = selected_range_->GetNextTimestamp(); + // Then seek to the next keyframe after (or equal to) |next_timestamp|. + // This will allow us to transition from the old buffers to the new + // buffers seamlessly. + base::TimeDelta next_keyframe_timestamp = + new_range->SeekAfter(next_timestamp); + + // If there's no keyframe after |next_timestamp|, then set flag to wait + // for the next keyframe in this range to be appended. + if (next_keyframe_timestamp == kNoTimestamp()) + waiting_for_keyframe_ = true; + + // Add all the old buffers up until |next_keyframe_timestamp| into + // |track_buffer_|. If there was no keyframe, then we add all buffers into + // |track_buffer_|. + scoped_refptr<StreamParserBuffer> next_buffer; + while (selected_range_->GetNextBuffer(&next_buffer) && + (waiting_for_keyframe_ || + next_buffer->GetTimestamp() < next_keyframe_timestamp)) { + track_buffer_.push_back(next_buffer); + } + + selected_range_ = new_range; + } + delete *itr; + itr = ranges_.erase(itr); + } + return itr; +} + +SourceBufferStream::RangeList::iterator +SourceBufferStream::ResolveEndOverlaps( + const RangeList::iterator& range_itr, SourceBufferRange* new_range) { + RangeList::iterator itr = range_itr; + while (itr != ranges_.end() && new_range->EndOverlaps(**itr)) { DCHECK_NE(*itr, selected_range_); delete *itr; itr = ranges_.erase(itr); } + return itr; +} - // Merge with neighbor if necessary. - if (itr != ranges_.end() && range->CanAppendRange(**itr)) { +void SourceBufferStream::MergeWithAdjacentRangeIfNecessary( + const RangeList::iterator& itr, SourceBufferRange* new_range) { + if (itr != ranges_.end() && new_range->CanAppendToEnd(**itr)) { bool transfer_current_position = selected_range_ == *itr; - range->AppendRange(**itr, transfer_current_position); + new_range->AppendToEnd(**itr, transfer_current_position); // Update |selected_range_| pointer if |range| has become selected after // merges. if (transfer_current_position) - selected_range_ = range; + selected_range_ = new_range; delete *itr; ranges_.erase(itr); } - - // Finally, try to complete pending seek if one exists. - if (seek_pending_) - Seek(seek_buffer_timestamp_); - - DCHECK(IsRangeListSorted(ranges_)); - return true; } void SourceBufferStream::Seek(base::TimeDelta timestamp) { - if (selected_range_) - selected_range_ = NULL; + selected_range_ = NULL; + track_buffer_.clear(); + waiting_for_keyframe_ = false; seek_buffer_timestamp_ = timestamp; seek_pending_ = true; @@ -235,6 +329,11 @@ void SourceBufferStream::Seek(base::TimeDelta timestamp) { bool SourceBufferStream::GetNextBuffer( scoped_refptr<StreamParserBuffer>* out_buffer) { + if (!track_buffer_.empty()) { + *out_buffer = track_buffer_.front(); + track_buffer_.pop_front(); + return true; + } return selected_range_ && selected_range_->GetNextBuffer(out_buffer); } @@ -300,6 +399,27 @@ void SourceBufferRange::Seek(base::TimeDelta timestamp) { DCHECK_LT(next_buffer_index_, static_cast<int>(buffers_.size())); } +base::TimeDelta SourceBufferRange::SeekAfter(base::TimeDelta timestamp) { + DCHECK_EQ(BelongsToRange(timestamp), 0); + DCHECK(!keyframe_map_.empty()); + + // lower_bound() returns the first element >= |timestamp|, so |result| is the + // value that we want. + KeyframeMap::iterator result = keyframe_map_.lower_bound(timestamp); + + // If there isn't a keyframe after |timestamp|, then seek to end and return + // kNoTimestamp to signal such. + if (result == keyframe_map_.end()) { + next_buffer_index_ = buffers_.size(); + return kNoTimestamp(); + } + + next_buffer_index_ = result->second; + DCHECK_LT(next_buffer_index_, static_cast<int>(buffers_.size())); + return result->first; +} + + bool SourceBufferRange::GetNextBuffer( scoped_refptr<StreamParserBuffer>* out_buffer) { DCHECK_GE(next_buffer_index_, 0); @@ -311,14 +431,24 @@ bool SourceBufferRange::GetNextBuffer( return true; } +base::TimeDelta SourceBufferRange::GetNextTimestamp() const { + DCHECK_GE(next_buffer_index_, 0); + DCHECK(!buffers_.empty()); + + if (next_buffer_index_ >= static_cast<int>(buffers_.size())) + return buffers_.back()->GetEndTimestamp(); + + return buffers_.at(next_buffer_index_)->GetTimestamp(); +} + SourceBufferStream::Timespan SourceBufferRange::GetBufferedTime() const { return std::make_pair(BufferedStart(), BufferedEnd()); } -void SourceBufferRange::AppendRange(const SourceBufferRange& range, +void SourceBufferRange::AppendToEnd(const SourceBufferRange& range, bool transfer_current_position) { - DCHECK(CanAppendRange(range)); + DCHECK(CanAppendToEnd(range)); DCHECK(!buffers_.empty()); if (transfer_current_position) @@ -327,18 +457,29 @@ void SourceBufferRange::AppendRange(const SourceBufferRange& range, AppendToEnd(range.buffers_); } -bool SourceBufferRange::CanAppendRange(const SourceBufferRange& range) const { - return range.BufferedStart() >= BufferedEnd() && - range.BufferedStart() <= MaxNextTimestamp(); +bool SourceBufferRange::CanAppendToEnd(const SourceBufferRange& range) const { + return CanAppendToEnd(range.buffers_); +} + +bool SourceBufferRange::CanAppendToEnd(const BufferQueue& buffers) const { + return buffers_.empty() || + IsNextInSequence(buffers_.back(), buffers.front()->GetTimestamp()); } int SourceBufferRange::BelongsToRange(base::TimeDelta timestamp) const { - if (buffers_.empty() || MaxNextTimestamp() < timestamp) + if (buffers_.empty()) return 1; - else if (BufferedStart() > timestamp) - return -1; - else + + if (IsNextInSequence(buffers_.back(), timestamp) || + (BufferedEnd() >= timestamp && BufferedStart() <= timestamp)) { return 0; + } + + if (BufferedStart() > timestamp) + return -1; + + // |timestamp| must be after this range. + return 1; } bool SourceBufferRange::CanSeekTo(base::TimeDelta timestamp) const { @@ -346,8 +487,32 @@ bool SourceBufferRange::CanSeekTo(base::TimeDelta timestamp) const { BufferedEnd() > timestamp; } +bool SourceBufferRange::CompletelyOverlaps( + const SourceBufferRange& range) const { + return BufferedStart() <= range.BufferedStart() && + BufferedEnd() >= range.BufferedEnd(); +} + bool SourceBufferRange::EndOverlaps(const SourceBufferRange& range) const { - return range.BufferedStart() < BufferedEnd(); + return range.BufferedStart() < BufferedEnd() && + BufferedEnd() < range.BufferedEnd(); +} + +bool SourceBufferRange::IsStartOverlappedBy(const BufferQueue& buffers) const { + base::TimeDelta start_timestamp = buffers.front()->GetTimestamp(); + return BufferedStart() < start_timestamp && start_timestamp < BufferedEnd(); +} + +bool SourceBufferRange::IsEndOverlappedBy(const BufferQueue& buffers) const { + base::TimeDelta end_timestamp = buffers.back()->GetEndTimestamp(); + return BufferedStart() < end_timestamp && end_timestamp < BufferedEnd(); +} + +bool SourceBufferRange::IsCompletelyOverlappedBy( + const BufferQueue& buffers) const { + base::TimeDelta start_timestamp = buffers.front()->GetTimestamp(); + base::TimeDelta end_timestamp = buffers.back()->GetEndTimestamp(); + return start_timestamp <= BufferedStart() && BufferedEnd() <= end_timestamp; } base::TimeDelta SourceBufferRange::BufferedStart() const { @@ -361,16 +526,7 @@ base::TimeDelta SourceBufferRange::BufferedEnd() const { if (buffers_.empty()) return kNoTimestamp(); - return buffers_.back()->GetTimestamp() + buffers_.back()->GetDuration(); -} - -base::TimeDelta SourceBufferRange::MaxNextTimestamp() const { - DCHECK(!buffers_.empty()); - - // Because we do not know exactly when is the next timestamp, any buffer - // that starts within 1/3 of the duration past the end of the last buffer - // in the queue is considered the next buffer in this range. - return BufferedEnd() + buffers_.back()->GetDuration() / 3; + return buffers_.back()->GetEndTimestamp(); } } // namespace media diff --git a/media/filters/source_buffer_stream.h b/media/filters/source_buffer_stream.h index b094ce6..8e80414 100644 --- a/media/filters/source_buffer_stream.h +++ b/media/filters/source_buffer_stream.h @@ -57,6 +57,22 @@ class MEDIA_EXPORT SourceBufferStream { private: typedef std::list<SourceBufferRange*> RangeList; + + // Resolve overlapping ranges such that no ranges overlap anymore. + // |range_itr| points to the iterator in |ranges_| immediately after + // |new_range|. Returns the iterator in |ranges_| immediately after + // |new_range|, which may be different from the original |range_itr|. + RangeList::iterator ResolveCompleteOverlaps( + const RangeList::iterator& range_itr, SourceBufferRange* new_range); + RangeList::iterator ResolveEndOverlaps( + const RangeList::iterator& range_itr, SourceBufferRange* new_range); + + // Checks to see if the range pointed to by |range_itr| can be appended to the + // end of |new_range|, and if so, appends the range and updates |ranges_| to + // reflect this. + void MergeWithAdjacentRangeIfNecessary( + const RangeList::iterator& range_itr, SourceBufferRange* new_range); + // List of disjoint buffered ranges, ordered by start time. RangeList ranges_; @@ -67,10 +83,20 @@ class MEDIA_EXPORT SourceBufferStream { // Timestamp of the last request to Seek(). base::TimeDelta seek_buffer_timestamp_; - // Pointer to the seeked-to Range. This is the Range from which - // GetNextBuffer() calls are fulfilled. + // Pointer to the seeked-to Range. This is the range from which + // GetNextBuffer() calls are fulfilled after the |track_buffer_| has been + // emptied. SourceBufferRange* selected_range_; + // Queue of the next buffers to be returned from calls to GetNextBuffer(). If + // |track_buffer_| is empty, return buffers from |selected_range_|. + BufferQueue track_buffer_; + + // True if the next buffer after the end of the |track_buffer_| is not + // buffered yet and we need to wait for the next keyframe after + // |track_buffer_| to be appended. + bool waiting_for_keyframe_; + DISALLOW_COPY_AND_ASSIGN(SourceBufferStream); }; diff --git a/media/filters/source_buffer_stream_unittest.cc b/media/filters/source_buffer_stream_unittest.cc index 6e9dd7a..7e043d51 100644 --- a/media/filters/source_buffer_stream_unittest.cc +++ b/media/filters/source_buffer_stream_unittest.cc @@ -12,6 +12,9 @@ namespace media { static const int kDefaultFramesPerSecond = 30; static const int kDefaultKeyframesPerSecond = 6; +static const uint8 kDataA = 0x11; +static const uint8 kDataB = 0x33; +static const int kDataSize = 1; class SourceBufferStreamTest : public testing::Test { protected: @@ -26,12 +29,17 @@ class SourceBufferStreamTest : public testing::Test { } void AppendBuffers(int starting_position, int number_of_buffers) { - AppendBuffers(starting_position, number_of_buffers, true); + AppendBuffers(starting_position, number_of_buffers, true, NULL, 0); + } + + void AppendBuffers(int starting_position, int number_of_buffers, + const uint8* data) { + AppendBuffers(starting_position, number_of_buffers, true, data, kDataSize); } void AppendBuffers_ExpectFailure( int starting_position, int number_of_buffers) { - AppendBuffers(starting_position, number_of_buffers, false); + AppendBuffers(starting_position, number_of_buffers, false, NULL, 0); } void Seek(int position) { @@ -63,11 +71,31 @@ class SourceBufferStreamTest : public testing::Test { void CheckExpectedBuffers( int starting_position, int ending_position) { - CheckExpectedBuffers(starting_position, ending_position, false); + CheckExpectedBuffers(starting_position, ending_position, false, NULL, 0); } void CheckExpectedBuffers( int starting_position, int ending_position, bool expect_keyframe) { + CheckExpectedBuffers(starting_position, ending_position, expect_keyframe, + NULL, 0); + } + + void CheckExpectedBuffers( + int starting_position, int ending_position, const uint8* data) { + CheckExpectedBuffers(starting_position, ending_position, false, data, + kDataSize); + } + + void CheckExpectedBuffers( + int starting_position, int ending_position, const uint8* data, + bool expect_keyframe) { + CheckExpectedBuffers(starting_position, ending_position, expect_keyframe, + data, kDataSize); + } + + void CheckExpectedBuffers( + int starting_position, int ending_position, bool expect_keyframe, + const uint8* expected_data, int expected_size) { int current_position = starting_position; for (; current_position <= ending_position; current_position++) { scoped_refptr<StreamParserBuffer> buffer; @@ -77,6 +105,15 @@ class SourceBufferStreamTest : public testing::Test { if (expect_keyframe && current_position == starting_position) EXPECT_TRUE(buffer->IsKeyframe()); + if (expected_data) { + const uint8* actual_data = buffer->GetData(); + const int actual_size = buffer->GetDataSize(); + EXPECT_EQ(actual_size, expected_size); + for (int i = 0; i < std::min(actual_size, expected_size); i++) { + EXPECT_EQ(actual_data[i], expected_data[i]); + } + } + EXPECT_EQ(buffer->GetTimestamp() / frame_duration_, current_position); } @@ -94,7 +131,7 @@ class SourceBufferStreamTest : public testing::Test { } void AppendBuffers(int starting_position, int number_of_buffers, - bool expect_success) { + bool expect_success, const uint8* data, int size) { int keyframe_interval = frames_per_second_ / keyframes_per_second_; SourceBufferStream::BufferQueue queue; @@ -102,7 +139,7 @@ class SourceBufferStreamTest : public testing::Test { int position = starting_position + i; bool is_keyframe = position % keyframe_interval == 0; scoped_refptr<StreamParserBuffer> buffer = - StreamParserBuffer::CopyFrom(NULL, 0, is_keyframe); + StreamParserBuffer::CopyFrom(data, size, is_keyframe); buffer->SetDuration(frame_duration_); buffer->SetTimestamp(frame_duration_ * position); queue.push_back(buffer); @@ -325,6 +362,128 @@ TEST_F(SourceBufferStreamTest, Overlap_SeveralThenMerge) { CheckExpectedBuffers(0, 21); } +TEST_F(SourceBufferStreamTest, Overlap_Selected_Complete) { + // Append 10 buffers at positions 5 through 14. + AppendBuffers(5, 10, &kDataA); + + // Seek to buffer at position 5. + Seek(5); + + // Replace old data with new data. + AppendBuffers(5, 10, &kDataB); + + // Check timespans are correct. + SourceBufferStream::TimespanList expected; + expected.push_back(CreateTimespan(5, 14)); + CheckExpectedTimespans(expected); + + // Check that data has been replaced with new data. + CheckExpectedBuffers(5, 14, &kDataB); +} + +// This test is testing that a client can append data to SourceBufferStream that +// overlaps the range from which the client is currently grabbing buffers. We +// would expect that the SourceBufferStream would return old data until it hits +// the keyframe of the new data, after which it will return the new data. +TEST_F(SourceBufferStreamTest, Overlap_Selected_Complete_TrackBuffer) { + // Append 10 buffers at positions 5 through 14. + AppendBuffers(5, 10, &kDataA); + + // Seek to buffer at position 5 and get next buffer. + Seek(5); + CheckExpectedBuffers(5, 5, &kDataA); + + // Do a complete overlap by appending 20 buffers at positions 0 through 19. + AppendBuffers(0, 20, &kDataB); + + // Check timespans are correct. + SourceBufferStream::TimespanList expected; + expected.push_back(CreateTimespan(0, 19)); + CheckExpectedTimespans(expected); + + // Expect old data up until next keyframe in new data. + CheckExpectedBuffers(6, 9, &kDataA); + CheckExpectedBuffers(10, 10, &kDataB, true); + + // Expect rest of data to be new. + CheckExpectedBuffers(11, 19, &kDataB); + + // Seek back to beginning; all data should be new. + Seek(0); + CheckExpectedBuffers(0, 19, &kDataB); + + // Check timespan continues to be correct. + CheckExpectedTimespans(expected); +} + +TEST_F(SourceBufferStreamTest, Overlap_Selected_Complete_EdgeCase) { + // Append 10 buffers at positions 5 through 14. + AppendBuffers(5, 10, &kDataA); + + // Seek to buffer at position 5 and get next buffer. + Seek(5); + CheckExpectedBuffers(5, 5, &kDataA); + + // Replace existing data with new data. + AppendBuffers(5, 10, &kDataB); + + // Check timespans are correct. + SourceBufferStream::TimespanList expected; + expected.push_back(CreateTimespan(5, 14)); + CheckExpectedTimespans(expected); + + // Expect old data up until next keyframe in new data. + CheckExpectedBuffers(6, 9, &kDataA); + CheckExpectedBuffers(10, 10, &kDataB, true); + + // Expect rest of data to be new. + CheckExpectedBuffers(11, 14, &kDataB); + + // Seek back to beginning; all data should be new. + Seek(5); + CheckExpectedBuffers(5, 14, &kDataB); + + // Check timespan continues to be correct. + CheckExpectedTimespans(expected); +} + +TEST_F(SourceBufferStreamTest, Overlap_Selected_Complete_Multiple) { + static const uint8 kDataC = 0x55; + static const uint8 kDataD = 0x77; + + // Append 5 buffers at positions 5 through 9. + AppendBuffers(5, 5, &kDataA); + + // Seek to buffer at position 5 and get next buffer. + Seek(5); + CheckExpectedBuffers(5, 5, &kDataA); + + // Replace existing data with new data. + AppendBuffers(5, 5, &kDataB); + + // Then replace it again with different data. + AppendBuffers(5, 5, &kDataC); + + // Now append 5 new buffers at positions 10 through 14. + AppendBuffers(10, 5, &kDataC); + + // Now replace all the data entirely. + AppendBuffers(5, 10, &kDataD); + + // Expect buffers 6 through 9 to be DataA, and the remaining + // buffers to be kDataD. + CheckExpectedBuffers(6, 9, &kDataA); + CheckExpectedBuffers(10, 14, &kDataD); + + // At this point we cannot fulfill request. + scoped_refptr<StreamParserBuffer> buffer; + EXPECT_FALSE(stream_.GetNextBuffer(&buffer)); + + // Seek back to beginning; all data should be new. + Seek(5); + CheckExpectedBuffers(5, 14, &kDataD); +} + TEST_F(SourceBufferStreamTest, Seek_Keyframe) { // Append 6 buffers at positions 0 through 5. AppendBuffers(0, 6); @@ -385,6 +544,34 @@ TEST_F(SourceBufferStreamTest, Seek_InBetweenTimestamps) { CheckExpectedBuffers(0, 0, true); } +// This test will do a complete overlap of an existing range in order to add +// buffers to the track buffers. Then the test does a seek to another part of +// the stream. The SourceBufferStream should clear its internal track buffer in +// response to the Seek(). +TEST_F(SourceBufferStreamTest, Seek_After_TrackBuffer_Filled) { + // Append 10 buffers at positions 5 through 14. + AppendBuffers(5, 10, &kDataA); + + // Seek to buffer at position 5 and get next buffer. + Seek(5); + CheckExpectedBuffers(5, 5, &kDataA); + + // Do a complete overlap by appending 20 buffers at positions 0 through 19. + AppendBuffers(0, 20, &kDataB); + + // Check timespans are correct. + SourceBufferStream::TimespanList expected; + expected.push_back(CreateTimespan(0, 19)); + CheckExpectedTimespans(expected); + + // Seek to beginning; all data should be new. + Seek(0); + CheckExpectedBuffers(0, 19, &kDataB); + + // Check timespan continues to be correct. + CheckExpectedTimespans(expected); +} + // TODO(vrk): When overlaps are handled more elegantly, this test should be // rewritten to test for more meaningful outcomes. Right now we are just // testing to make sure nothing crazy happens in this scenario (like losing @@ -451,4 +638,33 @@ TEST_F(SourceBufferStreamTest, GetNextBuffer_ExhaustThenAppend) { CheckExpectedBuffers(4, 5); } +// This test is testing the "next buffer" logic after a complete overlap. In +// this scenario, when the track buffer is exhausted, there is no buffered data +// to fulfill the request. The SourceBufferStream should be able to fulfill the +// request when the data is later appended, and should not lose track of the +// "next buffer" position. +TEST_F(SourceBufferStreamTest, GetNextBuffer_Overlap_Selected_Complete) { + // Append 5 buffers at positions 5 through 9. + AppendBuffers(5, 5, &kDataA); + + // Seek to buffer at position 5 and get next buffer. + Seek(5); + CheckExpectedBuffers(5, 5, &kDataA); + + // Replace existing data with new data. + AppendBuffers(5, 5, &kDataB); + + // Expect old data up until next keyframe in new data. + CheckExpectedBuffers(6, 9, &kDataA); + + // Next buffer is at position 10, so should not be able to fulfill the + // request. + scoped_refptr<StreamParserBuffer> buffer; + EXPECT_FALSE(stream_.GetNextBuffer(&buffer)); + + // Now add 5 new buffers at positions 10 through 14. + AppendBuffers(10, 5, &kDataB); + CheckExpectedBuffers(10, 14, &kDataB); +} + } // namespace media |