summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorvrk@chromium.org <vrk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-11 05:27:13 +0000
committervrk@chromium.org <vrk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-11 05:27:13 +0000
commit791c3beb045e5f207d652366f943e9b92d6183ec (patch)
tree50f1d99445d0cd6478baab8dca1b111f0aeaaf70 /media
parent6c75081a1ac9c0f4eb8e430b3d9877b3bcb3d13f (diff)
downloadchromium_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.cc8
-rw-r--r--media/base/stream_parser_buffer.h4
-rw-r--r--media/filters/source_buffer_stream.cc262
-rw-r--r--media/filters/source_buffer_stream.h30
-rw-r--r--media/filters/source_buffer_stream_unittest.cc226
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