blob: 7e043d51706c163599f01844ec77df97c010db71 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/filters/source_buffer_stream.h"
#include "base/logging.h"
#include "media/base/data_buffer.h"
#include "testing/gtest/include/gtest/gtest.h"
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:
SourceBufferStreamTest() {
SetStreamInfo(kDefaultFramesPerSecond, kDefaultKeyframesPerSecond);
}
void SetStreamInfo(int frames_per_second, int keyframes_per_second) {
frames_per_second_ = frames_per_second;
keyframes_per_second_ = keyframes_per_second;
frame_duration_ = ConvertToFrameDuration(frames_per_second);
}
void AppendBuffers(int starting_position, int number_of_buffers) {
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, NULL, 0);
}
void Seek(int position) {
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 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(actual_itr->first / frame_duration_,
expected_itr->first / frame_duration_);
EXPECT_EQ(actual_itr->second / frame_duration_,
expected_itr->second / frame_duration_);
}
}
void CheckExpectedBuffers(
int starting_position, int ending_position) {
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;
if (!stream_.GetNextBuffer(&buffer))
break;
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);
}
EXPECT_EQ(current_position, ending_position + 1);
}
base::TimeDelta frame_duration() const { return frame_duration_; }
SourceBufferStream stream_;
private:
base::TimeDelta ConvertToFrameDuration(int frames_per_second) {
return base::TimeDelta::FromMicroseconds(
base::Time::kMicrosecondsPerSecond / frames_per_second);
}
void AppendBuffers(int starting_position, int number_of_buffers,
bool expect_success, const uint8* data, int size) {
int keyframe_interval = frames_per_second_ / keyframes_per_second_;
SourceBufferStream::BufferQueue queue;
for (int i = 0; i < number_of_buffers; i++) {
int position = starting_position + i;
bool is_keyframe = position % keyframe_interval == 0;
scoped_refptr<StreamParserBuffer> buffer =
StreamParserBuffer::CopyFrom(data, size, is_keyframe);
buffer->SetDuration(frame_duration_);
buffer->SetTimestamp(frame_duration_ * position);
queue.push_back(buffer);
}
EXPECT_EQ(stream_.Append(queue), expect_success);
}
int frames_per_second_;
int keyframes_per_second_;
base::TimeDelta frame_duration_;
DISALLOW_COPY_AND_ASSIGN(SourceBufferStreamTest);
};
TEST_F(SourceBufferStreamTest, Append_SingleRange) {
// Append 15 buffers at positions 0 through 14.
AppendBuffers(0, 15);
// Check expected range.
SourceBufferStream::TimespanList expected;
expected.push_back(CreateTimespan(0, 14));
CheckExpectedTimespans(expected);
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 14);
}
TEST_F(SourceBufferStreamTest, Append_SingleRange_OneBufferAtATime) {
// Append 15 buffers starting at position 0, one buffer at a time.
for (int i = 0; i < 15; i++)
AppendBuffers(i, 1);
// Check expected range.
SourceBufferStream::TimespanList expected;
expected.push_back(CreateTimespan(0, 14));
CheckExpectedTimespans(expected);
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 14);
}
TEST_F(SourceBufferStreamTest, Append_DisjointRanges) {
// Append 5 buffers at positions 0 through 4.
AppendBuffers(0, 5);
// Append 10 buffers at positions 15 through 24.
AppendBuffers(15, 10);
// Check expected ranges.
SourceBufferStream::TimespanList expected;
expected.push_back(CreateTimespan(0, 4));
expected.push_back(CreateTimespan(15, 24));
CheckExpectedTimespans(expected);
// Check buffers in ranges.
Seek(0);
CheckExpectedBuffers(0, 4);
Seek(15);
CheckExpectedBuffers(15, 24);
}
TEST_F(SourceBufferStreamTest, Append_AdjacentRanges) {
// Append 12 buffers at positions 0 through 11.
AppendBuffers(0, 12);
// Append 11 buffers at positions 15 through 25.
AppendBuffers(15, 11);
// Append 3 buffers at positions 12 through 14 to bridge the gap.
AppendBuffers(12, 3);
// Check expected range.
SourceBufferStream::TimespanList expected;
expected.push_back(CreateTimespan(0, 25));
CheckExpectedTimespans(expected);
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 25);
}
TEST_F(SourceBufferStreamTest, Append_DoesNotBeginWithKeyframe) {
// Append fails because the range doesn't begin with a keyframe.
AppendBuffers_ExpectFailure(3, 5);
// Append 10 buffers at positions 5 through 14.
AppendBuffers(5, 10);
// Check expected range.
SourceBufferStream::TimespanList expected;
expected.push_back(CreateTimespan(5, 14));
CheckExpectedTimespans(expected);
// Check buffers in range.
Seek(5);
CheckExpectedBuffers(5, 14);
// Append fails because the range doesn't begin with a keyframe.
AppendBuffers_ExpectFailure(17, 10);
CheckExpectedTimespans(expected);
Seek(5);
CheckExpectedBuffers(5, 14);
}
TEST_F(SourceBufferStreamTest, Overlap_Complete) {
// Append 5 buffers at positions 5 through 9.
AppendBuffers(5, 5);
// Append 15 buffers at positions 0 through 14.
AppendBuffers(0, 15);
// Check expected range.
SourceBufferStream::TimespanList expected;
expected.push_back(CreateTimespan(0, 14));
CheckExpectedTimespans(expected);
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 14);
}
TEST_F(SourceBufferStreamTest, Overlap_Complete_EdgeCase) {
// Make each frame a keyframe so that it's okay to overlap frames at any point
// (instead of needing to respect keyframe boundaries).
SetStreamInfo(30, 30);
// Append 6 buffers at positions 6 through 11.
AppendBuffers(6, 6);
// Append 8 buffers at positions 5 through 12.
AppendBuffers(5, 8);
// Check expected range.
SourceBufferStream::TimespanList expected;
expected.push_back(CreateTimespan(5, 12));
CheckExpectedTimespans(expected);
// Check buffers in range.
Seek(5);
CheckExpectedBuffers(5, 12);
}
TEST_F(SourceBufferStreamTest, Overlap_Start) {
// Append 5 buffers at positions 5 through 9.
AppendBuffers(5, 5);
// Append 6 buffers at positions 8 through 13.
AppendBuffers(8, 6);
// Check expected range.
SourceBufferStream::TimespanList expected;
expected.push_back(CreateTimespan(5, 13));
CheckExpectedTimespans(expected);
// Check buffers in range.
Seek(5);
CheckExpectedBuffers(5, 13);
}
TEST_F(SourceBufferStreamTest, Overlap_End) {
// Append 6 buffers at positions 10 through 15.
AppendBuffers(10, 6);
// Append 8 buffers at positions 5 through 12.
AppendBuffers(5, 8);
// Check expected range.
SourceBufferStream::TimespanList expected;
expected.push_back(CreateTimespan(5, 12));
CheckExpectedTimespans(expected);
// Check buffers in range.
Seek(5);
CheckExpectedBuffers(5, 12);
}
TEST_F(SourceBufferStreamTest, Overlap_Several) {
// Append 2 buffers at positions 5 through 6.
AppendBuffers(5, 2);
// Append 2 buffers at positions 10 through 11.
AppendBuffers(10, 2);
// Append 2 buffers at positions 15 through 16.
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);
// Append buffers at positions 0 through 19.
AppendBuffers(0, 20);
// Check expected range.
expected.clear();
expected.push_back(CreateTimespan(0, 19));
CheckExpectedTimespans(expected);
// Check buffers in range.
Seek(0);
CheckExpectedBuffers(0, 19);
}
TEST_F(SourceBufferStreamTest, Overlap_SeveralThenMerge) {
// Append 2 buffers at positions 5 through 6.
AppendBuffers(5, 2);
// Append 2 buffers at positions 10 through 11.
AppendBuffers(10, 2);
// Append 2 buffers at positions 15 through 16.
AppendBuffers(15, 2);
// Append 2 buffers at positions 20 through 21.
AppendBuffers(20, 2);
// Append buffers at positions 0 through 19.
AppendBuffers(0, 20);
// Check expected ranges.
SourceBufferStream::TimespanList expected;
expected.push_back(CreateTimespan(0, 21));
CheckExpectedTimespans(expected);
// Check buffers in range.
Seek(0);
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);
// Seek to beginning.
Seek(0);
CheckExpectedBuffers(0, 5, true);
}
TEST_F(SourceBufferStreamTest, Seek_NonKeyframe) {
// Append 15 buffers at positions 0 through 14.
AppendBuffers(0, 15);
// Seek to buffer at position 13.
Seek(13);
// Expect seeking back to the nearest keyframe.
CheckExpectedBuffers(10, 14, true);
// Seek to buffer at position 3.
Seek(3);
// Expect seeking back to the nearest keyframe.
CheckExpectedBuffers(0, 3, true);
}
TEST_F(SourceBufferStreamTest, Seek_NotBuffered) {
// Seek to beginning.
Seek(0);
// Try to get buffer; nothing's appended.
scoped_refptr<StreamParserBuffer> buffer;
EXPECT_FALSE(stream_.GetNextBuffer(&buffer));
// Append 2 buffers at positions 0.
AppendBuffers(0, 2);
Seek(0);
CheckExpectedBuffers(0, 1);
// Try to get buffer out of range.
Seek(2);
EXPECT_FALSE(stream_.GetNextBuffer(&buffer));
}
TEST_F(SourceBufferStreamTest, Seek_InBetweenTimestamps) {
// Append 10 buffers at positions 0 through 9.
AppendBuffers(0, 10);
base::TimeDelta bump = frame_duration() / 4;
CHECK(bump > base::TimeDelta());
// Seek to buffer a little after position 5.
stream_.Seek(5 * frame_duration() + bump);
CheckExpectedBuffers(5, 5, true);
// Seek to buffer a little before position 5.
stream_.Seek(5 * frame_duration() - bump);
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
// the seek position or garbage collecting the data at position 13).
// Bug for overlaps is crbug.com/125072.
TEST_F(SourceBufferStreamTest, GetNextBuffer_AfterOverlap) {
// Append 15 buffers at positions 0 through 14.
AppendBuffers(0, 15);
// Seek to buffer at position 13.
Seek(13);
// Append 5 buffers at positions 10 through 14.
// The current implementation expects a failure, though fixing
// crbug.com/125072 should change this expectation.
AppendBuffers_ExpectFailure(10, 5);
// Make sure we can still get the buffer at 13.
CheckExpectedBuffers(10, 13);
}
TEST_F(SourceBufferStreamTest, GetNextBuffer_AfterMerges) {
// Append 5 buffers at positions 10 through 14.
AppendBuffers(10, 5);
// Seek to buffer at position 12.
Seek(12);
// Append 5 buffers at positions 5 through 9.
AppendBuffers(5, 5);
// Make sure ranges are merged.
SourceBufferStream::TimespanList expected;
expected.push_back(CreateTimespan(5, 14));
CheckExpectedTimespans(expected);
// Make sure the next buffer is correct.
CheckExpectedBuffers(10, 10);
// Append 5 buffers at positions 15 through 19.
AppendBuffers(15, 5);
expected.clear();
expected.push_back(CreateTimespan(5, 19));
CheckExpectedTimespans(expected);
// Make sure the remaining next buffers are correct.
CheckExpectedBuffers(11, 14);
}
TEST_F(SourceBufferStreamTest, GetNextBuffer_ExhaustThenAppend) {
// Append 4 buffers at positions 0 through 3.
AppendBuffers(0, 4);
// Seek to buffer at position 0 and get all buffers.
Seek(0);
CheckExpectedBuffers(0, 3);
// Next buffer is at position 4, so should not be able to fulfill request.
scoped_refptr<StreamParserBuffer> buffer;
EXPECT_FALSE(stream_.GetNextBuffer(&buffer));
// Append 2 buffers at positions 4 through 5.
AppendBuffers(4, 2);
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