class SourceBufferStreamTest : public testing::Test {
protected:
- SourceBufferStreamTest()
- : accurate_durations_(false) {
+ SourceBufferStreamTest() {
video_config_ = TestVideoConfig::Normal();
SetStreamInfo(kDefaultFramesPerSecond, kDefaultKeyframesPerSecond);
stream_.reset(new SourceBufferStream(video_config_, log_cb(), true));
void SetAudioStream() {
video_config_ = TestVideoConfig::Invalid();
- accurate_durations_ = true;
audio_config_.Initialize(kCodecVorbis,
kSampleFormatPlanarF32,
CHANNEL_LAYOUT_STEREO,
int GetRemovalRangeInMs(int start, int end, int bytes_to_free,
int* removal_end) {
- base::TimeDelta removal_end_timestamp =
- base::TimeDelta::FromMilliseconds(*removal_end);
+ DecodeTimestamp removal_end_timestamp =
+ DecodeTimestamp::FromMilliseconds(*removal_end);
int bytes_removed = stream_->GetRemovalRange(
- base::TimeDelta::FromMilliseconds(start),
- base::TimeDelta::FromMilliseconds(end), bytes_to_free,
+ DecodeTimestamp::FromMilliseconds(start),
+ DecodeTimestamp::FromMilliseconds(end), bytes_to_free,
&removal_end_timestamp);
*removal_end = removal_end_timestamp.InMilliseconds();
return bytes_removed;
break;
}
- if (timestamps[i] == "C")
- EXPECT_EQ(SourceBufferStream::kConfigChange, status);
+ EXPECT_EQ("C", timestamps[i]);
ss << "C";
continue;
if (status != SourceBufferStream::kSuccess)
break;
- ss << buffer->GetDecodeTimestamp().InMilliseconds();
- if (buffer->IsKeyframe())
+ ss << buffer->timestamp().InMilliseconds();
+
+ if (buffer->GetDecodeTimestamp() !=
+ DecodeTimestamp::FromPresentationTime(buffer->timestamp())) {
+ ss << "|" << buffer->GetDecodeTimestamp().InMilliseconds();
+ }
+
+ // Handle preroll buffers.
+ if (EndsWith(timestamps[i], "P", true)) {
+ ASSERT_TRUE(buffer->IsKeyframe());
+ scoped_refptr<StreamParserBuffer> preroll_buffer;
+ preroll_buffer.swap(buffer);
+
+ // When a preroll buffer is encountered we should be able to request one
+ // more buffer. The first buffer should match the timestamp and config
+ // of the second buffer, except that its discard_padding() should be its
+ // duration.
+ ASSERT_EQ(SourceBufferStream::kSuccess,
+ stream_->GetNextBuffer(&buffer));
+ ASSERT_EQ(buffer->GetConfigId(), preroll_buffer->GetConfigId());
+ ASSERT_EQ(buffer->track_id(), preroll_buffer->track_id());
+ ASSERT_EQ(buffer->timestamp(), preroll_buffer->timestamp());
+ ASSERT_EQ(buffer->GetDecodeTimestamp(),
+ preroll_buffer->GetDecodeTimestamp());
+ ASSERT_EQ(kInfiniteDuration(), preroll_buffer->discard_padding().first);
+ ASSERT_EQ(base::TimeDelta(), preroll_buffer->discard_padding().second);
+ ASSERT_TRUE(buffer->IsKeyframe());
+
+ ss << "P";
+ } else if (buffer->IsKeyframe()) {
ss << "K";
+ }
// Until the last splice frame is seen, indicated by a matching timestamp,
// all buffers must have the same splice_timestamp().
const uint8* data,
int size) {
if (begin_media_segment)
- stream_->OnNewMediaSegment(starting_position * frame_duration_);
+ stream_->OnNewMediaSegment(DecodeTimestamp::FromPresentationTime(
+ starting_position * frame_duration_));
int keyframe_interval = frames_per_second_ / keyframes_per_second_;
if (i == 0)
timestamp += first_buffer_offset;
- buffer->SetDecodeTimestamp(timestamp);
+ buffer->SetDecodeTimestamp(
+ DecodeTimestamp::FromPresentationTime(timestamp));
// Simulate an IBB...BBP pattern in which all B-frames reference both
// the I- and P-frames. For a GOP with playback order 12345, this would
presentation_timestamp = timestamp - frame_duration_;
}
buffer->set_timestamp(presentation_timestamp);
- if (accurate_durations_)
- buffer->set_duration(frame_duration_);
+ buffer->set_duration(frame_duration_);
queue.push_back(buffer);
}
EXPECT_EQ(expect_success, stream_->Append(queue));
}
+ void UpdateLastBufferDuration(DecodeTimestamp current_dts,
+ BufferQueue* buffers) {
+ if (buffers->empty() || buffers->back()->duration() > base::TimeDelta())
+ return;
+
+ DecodeTimestamp last_dts = buffers->back()->GetDecodeTimestamp();
+ DCHECK(current_dts >= last_dts);
+ buffers->back()->set_duration(current_dts - last_dts);
+ }
+
// StringToBufferQueue() allows for the generation of StreamParserBuffers from
// coded strings of timestamps separated by spaces. Supported syntax:
//
- // ##:
- // Generates a StreamParserBuffer with decode timestamp ##. E.g., "0 1 2 3".
+ // xx:
+ // Generates a StreamParserBuffer with decode and presentation timestamp xx.
+ // E.g., "0 1 2 3".
+ //
+ // pp|dd:
+ // Generates a StreamParserBuffer with presentation timestamp pp and decode
+ // timestamp dd. E.g., "0|0 3|1 1|2 2|3".
+ //
+ // ##Dzz
+ // Specifies the duration for a buffer. ## represents one of the 2 timestamp
+ // formats above. zz specifies the duration of the buffer in milliseconds.
+ // If the duration isn't specified with this syntax then the buffer duration
+ // is determined by the difference between the decode timestamp in ## and
+ // the decode timestamp of the previous buffer in the string. If the string
+ // only contains 1 buffer then the duration must be explicitly specified with
+ // this format.
//
// ##K:
- // Indicates the buffer with timestamp ## reflects a keyframe. E.g., "0K 1".
+ // Indicates the buffer with timestamp ## reflects a keyframe. ##
+ // can be any of the 3 timestamp formats above.
+ // E.g., "0K 1|2K 2|4D2K".
//
// S(a# ... y# z#)
// Indicates a splice frame buffer should be created with timestamp z#. The
BufferQueue buffers;
for (size_t i = 0; i < timestamps.size(); i++) {
bool is_keyframe = false;
+ bool has_preroll = false;
bool last_splice_frame = false;
// Handle splice frame starts.
if (StartsWithASCII(timestamps[i], "S(", true)) {
// Remove the "K" off of the token.
timestamps[i] = timestamps[i].substr(0, timestamps[i].length() - 1);
}
+ // Handle preroll buffers.
+ if (EndsWith(timestamps[i], "P", true)) {
+ is_keyframe = true;
+ has_preroll = true;
+ // Remove the "P" off of the token.
+ timestamps[i] = timestamps[i].substr(0, timestamps[i].length() - 1);
+ }
+
+ int duration_in_ms = 0;
+ size_t duration_pos = timestamps[i].find('D');
+ if (duration_pos != std::string::npos) {
+ CHECK(base::StringToInt(timestamps[i].substr(duration_pos + 1),
+ &duration_in_ms));
+ timestamps[i] = timestamps[i].substr(0, duration_pos);
+ }
- int time_in_ms;
- CHECK(base::StringToInt(timestamps[i], &time_in_ms));
+ std::vector<std::string> buffer_timestamps;
+ base::SplitString(timestamps[i], '|', &buffer_timestamps);
+
+ if (buffer_timestamps.size() == 1)
+ buffer_timestamps.push_back(buffer_timestamps[0]);
+
+ CHECK_EQ(2u, buffer_timestamps.size());
+
+ int pts_in_ms = 0;
+ int dts_in_ms = 0;
+ CHECK(base::StringToInt(buffer_timestamps[0], &pts_in_ms));
+ CHECK(base::StringToInt(buffer_timestamps[1], &dts_in_ms));
// Create buffer. Buffer type and track ID are meaningless to these tests.
scoped_refptr<StreamParserBuffer> buffer =
StreamParserBuffer::CopyFrom(&kDataA, kDataSize, is_keyframe,
DemuxerStream::AUDIO, 0);
- base::TimeDelta timestamp =
- base::TimeDelta::FromMilliseconds(time_in_ms);
- buffer->set_timestamp(timestamp);
- if (accurate_durations_)
- buffer->set_duration(frame_duration_);
- buffer->SetDecodeTimestamp(timestamp);
+ buffer->set_timestamp(base::TimeDelta::FromMilliseconds(pts_in_ms));
+
+ if (dts_in_ms != pts_in_ms) {
+ buffer->SetDecodeTimestamp(
+ DecodeTimestamp::FromMilliseconds(dts_in_ms));
+ }
+
+ if (duration_in_ms)
+ buffer->set_duration(base::TimeDelta::FromMilliseconds(duration_in_ms));
+
+ // Simulate preroll buffers by just generating another buffer and sticking
+ // it as the preroll.
+ if (has_preroll) {
+ scoped_refptr<StreamParserBuffer> preroll_buffer =
+ StreamParserBuffer::CopyFrom(
+ &kDataA, kDataSize, is_keyframe, DemuxerStream::AUDIO, 0);
+ preroll_buffer->set_duration(frame_duration_);
+ buffer->SetPrerollBuffer(preroll_buffer);
+ }
if (splice_frame) {
+ // Make sure that splice frames aren't used with content where decode
+ // and presentation timestamps can differ. (i.e., B-frames)
+ CHECK_EQ(buffer->GetDecodeTimestamp().InMicroseconds(),
+ buffer->timestamp().InMicroseconds());
if (!pre_splice_buffers.empty()) {
// Enforce strictly monotonically increasing timestamps.
CHECK_GT(
- timestamp.InMicroseconds(),
+ buffer->timestamp().InMicroseconds(),
+ pre_splice_buffers.back()->timestamp().InMicroseconds());
+ CHECK_GT(
+ buffer->GetDecodeTimestamp().InMicroseconds(),
pre_splice_buffers.back()->GetDecodeTimestamp().InMicroseconds());
}
buffer->SetConfigId(splice_config_id);
+ UpdateLastBufferDuration(buffer->GetDecodeTimestamp(),
+ &pre_splice_buffers);
pre_splice_buffers.push_back(buffer);
continue;
}
pre_splice_buffers.clear();
}
+ UpdateLastBufferDuration(buffer->GetDecodeTimestamp(), &buffers);
buffers.push_back(buffer);
}
+
+ // If the last buffer doesn't have a duration, assume it is the
+ // same as the second to last buffer.
+ if (buffers.size() >= 2 &&
+ buffers.back()->duration() <= base::TimeDelta()) {
+ buffers.back()->set_duration(
+ buffers[buffers.size() - 2]->duration());
+ }
+
return buffers;
}
if (start_new_segment) {
base::TimeDelta start_timestamp = segment_start_timestamp;
if (start_timestamp == kNoTimestamp())
- start_timestamp = buffers[0]->GetDecodeTimestamp();
+ start_timestamp = buffers[0]->timestamp();
- ASSERT_TRUE(start_timestamp <= buffers[0]->GetDecodeTimestamp());
+ ASSERT_TRUE(start_timestamp <= buffers[0]->timestamp());
- stream_->OnNewMediaSegment(start_timestamp);
+ stream_->OnNewMediaSegment(
+ DecodeTimestamp::FromPresentationTime(start_timestamp));
}
if (!one_by_one) {
int frames_per_second_;
int keyframes_per_second_;
base::TimeDelta frame_duration_;
- // TODO(dalecurtis): It's silly to have this, all tests should use accurate
- // durations instead. However, currently all tests are written with an
- // expectation of 0 duration, so it's an involved change.
- bool accurate_durations_;
DISALLOW_COPY_AND_ASSIGN(SourceBufferStreamTest);
};
// Completely overlap the old buffers, with a segment that starts
// after the old segment start timestamp, but before the timestamp
// of the first buffer in the segment.
- NewSegmentAppend("20K 50K 80K 110K");
+ NewSegmentAppend("20K 50K 80K 110D10K");
// Verify that the buffered ranges are updated properly and we don't crash.
CheckExpectedRangesByTimestamp("{ [20,150) }");
NewSegmentAppend("0K 30 60 90 120K 150");
CheckExpectedRangesByTimestamp("{ [0,180) }");
- NewSegmentAppend("0K");
+ NewSegmentAppend("0D30K");
CheckExpectedRangesByTimestamp("{ [0,30) [120,180) }");
CheckExpectedBuffers("0K");
// Seek to 130ms.
SeekToTimestamp(base::TimeDelta::FromMilliseconds(130));
- // Overlap with a new segment from 0 to 120ms.
- NewSegmentAppendOneByOne("0K 120");
+ // Overlap with a new segment from 0 to 130ms.
+ NewSegmentAppendOneByOne("0K 120D10");
// Next buffer should still be 130ms.
CheckExpectedBuffers("130K");
// new : 0K 30 60 90 120K
// after: 0K 30 60 90 *120K* 130K
TEST_F(SourceBufferStreamTest, Overlap_OneByOne_TrackBuffer) {
- NewSegmentAppendOneByOne("10K 40 70 100K 125 130K");
+ NewSegmentAppendOneByOne("10K 40 70 100K 125 130D30K");
CheckExpectedRangesByTimestamp("{ [10,160) }");
// Seek to 70ms.
SeekToTimestamp(base::TimeDelta::FromMilliseconds(70));
CheckExpectedBuffers("10K 40");
- // Overlap with a new segment from 0 to 120ms.
- NewSegmentAppendOneByOne("0K 30 60 90 120K");
+ // Overlap with a new segment from 0 to 130ms.
+ NewSegmentAppendOneByOne("0K 30 60 90 120D10K");
CheckExpectedRangesByTimestamp("{ [0,160) }");
- // Should return frames 70ms and 100ms from the track buffer, then switch
+ // Should return frame 70ms from the track buffer, then switch
// to the new data at 120K, then switch back to the old data at 130K. The
// frame at 125ms that depended on keyframe 100ms should have been deleted.
- CheckExpectedBuffers("70 100K 120K 130K");
+ CheckExpectedBuffers("70 120K 130K");
// Check the final result: should not include data from the track buffer.
SeekToTimestamp(base::TimeDelta::FromMilliseconds(0));
// old : 10K 40 *70* 100K 125 130K
// new : 0K 30 60 90 120K
// after: 0K 30 60 90 *120K* 130K
-// track: 70 100K
+// track: 70
// new : 110K 130
// after: 0K 30 60 90 *110K* 130
TEST_F(SourceBufferStreamTest, Overlap_OneByOne_TrackBuffer2) {
- NewSegmentAppendOneByOne("10K 40 70 100K 125 130K");
+ NewSegmentAppendOneByOne("10K 40 70 100K 125 130D30K");
CheckExpectedRangesByTimestamp("{ [10,160) }");
// Seek to 70ms.
// Overlap with a new segment from 0 to 120ms; 70ms and 100ms go in track
// buffer.
- NewSegmentAppendOneByOne("0K 30 60 90 120K");
+ NewSegmentAppendOneByOne("0K 30 60 90 120D10K");
CheckExpectedRangesByTimestamp("{ [0,160) }");
// Now overlap the keyframe at 120ms.
NewSegmentAppendOneByOne("110K 130");
- // Should expect buffers 70ms and 100ms from the track buffer. Then it should
+ // Should return frame 70ms from the track buffer. Then it should
// return the keyframe after the track buffer, which is at 110ms.
- CheckExpectedBuffers("70 100K 110K 130");
+ CheckExpectedBuffers("70 110K 130");
}
// Overlap the next keyframe after the end of the track buffer without a
// old : 10K 40 *70* 100K 125 130K
// new : 0K 30 60 90 120K
// after: 0K 30 60 90 *120K* 130K
-// track: 70 100K
+// track: 70
// new : 50K 80 110 140
// after: 0K 30 50K 80 110 140 * (waiting for keyframe)
-// track: 70 100K 120K 130K
+// track: 70
TEST_F(SourceBufferStreamTest, Overlap_OneByOne_TrackBuffer3) {
- NewSegmentAppendOneByOne("10K 40 70 100K 125 130K");
+ NewSegmentAppendOneByOne("10K 40 70 100K 125 130D30K");
CheckExpectedRangesByTimestamp("{ [10,160) }");
// Seek to 70ms.
SeekToTimestamp(base::TimeDelta::FromMilliseconds(70));
CheckExpectedBuffers("10K 40");
- // Overlap with a new segment from 0 to 120ms; 70ms and 100ms go in track
- // buffer.
- NewSegmentAppendOneByOne("0K 30 60 90 120K");
+ // Overlap with a new segment from 0 to 120ms; 70ms goes in track buffer.
+ NewSegmentAppendOneByOne("0K 30 60 90 120D10K");
CheckExpectedRangesByTimestamp("{ [0,160) }");
- // Now overlap the keyframe at 120ms. There's no keyframe after 70ms, so 120ms
- // and 130ms go into the track buffer.
+ // Now overlap the keyframe at 120ms and 130ms.
NewSegmentAppendOneByOne("50K 80 110 140");
+ CheckExpectedRangesByTimestamp("{ [0,170) }");
// Should have all the buffers from the track buffer, then stall.
- CheckExpectedBuffers("70 100K 120K 130K");
+ CheckExpectedBuffers("70");
CheckNoNextBuffer();
// Appending a keyframe should fulfill the read.
- AppendBuffersOneByOne("150K");
+ AppendBuffersOneByOne("150D30K");
CheckExpectedBuffers("150K");
CheckNoNextBuffer();
}
// old : 10K 40 *70* 100K 125 130K
// new : 0K 30 60 90 120K
// after: 0K 30 60 90 *120K* 130K
-// track: 70 100K
+// track: 70
// new : 80K 110 140
// after: 0K 30 60 *80K* 110 140
// track: 70
TEST_F(SourceBufferStreamTest, Overlap_OneByOne_TrackBuffer4) {
- NewSegmentAppendOneByOne("10K 40 70 100K 125 130K");
+ NewSegmentAppendOneByOne("10K 40 70 100K 125 130D30K");
CheckExpectedRangesByTimestamp("{ [10,160) }");
// Seek to 70ms.
// Overlap with a new segment from 0 to 120ms; 70ms and 100ms go in track
// buffer.
- NewSegmentAppendOneByOne("0K 30 60 90 120K");
+ NewSegmentAppendOneByOne("0K 30 60 90 120D10K");
CheckExpectedRangesByTimestamp("{ [0,160) }");
// Now append a keyframe at 80ms.
// old : 10K 40 *70* 100K
// new : 0K 30 60 90 120
// after: 0K 30 60 90 120 * (waiting for keyframe)
-// track: 70 100K
+// track: 70
// new : 80K 110 140
// after: 0K 30 60 *80K* 110 140
// track: 70
SeekToTimestamp(base::TimeDelta::FromMilliseconds(70));
CheckExpectedBuffers("10K 40");
- // Overlap with a new segment from 0 to 120ms; 70ms and 100ms go in track
+ // Overlap with a new segment from 0 to 120ms; 70ms goes in track
// buffer.
NewSegmentAppendOneByOne("0K 30 60 90 120");
CheckExpectedRangesByTimestamp("{ [0,150) }");
- // Now append a keyframe at 80ms. The buffer at 100ms should be deleted from
- // the track buffer.
+ // Now append a keyframe at 80ms.
NewSegmentAppendOneByOne("80K 110 140");
CheckExpectedBuffers("70 80K 110 140");
// old : 10K 40 *70* 100K 125 130K ... 200K 230
// new : 0K 30 60 90 120K
// after: 0K 30 60 90 *120K* 130K ... 200K 230
-// track: 70 100K
+// track: 70
// old : 0K 30 60 90 *120K* 130K ... 200K 230
// new : 260K 290
// after: 0K 30 60 90 *120K* 130K ... 200K 230 260K 290
-// track: 70 100K
+// track: 70
TEST_F(SourceBufferStreamTest, Overlap_OneByOne_TrackBuffer6) {
- NewSegmentAppendOneByOne("10K 40 70 100K 125 130K");
+ NewSegmentAppendOneByOne("10K 40 70 100K 125 130D30K");
NewSegmentAppendOneByOne("200K 230");
CheckExpectedRangesByTimestamp("{ [10,160) [200,260) }");
CheckExpectedBuffers("10K 40");
// Overlap with a new segment from 0 to 120ms.
- NewSegmentAppendOneByOne("0K 30 60 90 120K");
+ NewSegmentAppendOneByOne("0K 30 60 90 120D10K");
CheckExpectedRangesByTimestamp("{ [0,160) [200,260) }");
// Verify that 70 gets read out of the track buffer.
NewSegmentAppendOneByOne("260K 290");
CheckExpectedRangesByTimestamp("{ [0,160) [200,320) }");
- CheckExpectedBuffers("100K 120K 130K");
+ CheckExpectedBuffers("120K 130K");
CheckNoNextBuffer();
// Check the final result: should not include data from the track buffer.
// GetNextBuffer() should return the next buffer at position (5 + |bump|).
EXPECT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kSuccess);
- EXPECT_EQ(buffer->GetDecodeTimestamp(), 5 * frame_duration() + bump);
+ EXPECT_EQ(buffer->GetDecodeTimestamp(),
+ DecodeTimestamp::FromPresentationTime(5 * frame_duration() + bump));
// Check rest of buffers.
CheckExpectedBuffers(6, 9);
// GetNextBuffer() should return the next buffer at position (15 + |bump|).
EXPECT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kSuccess);
- EXPECT_EQ(buffer->GetDecodeTimestamp(), 15 * frame_duration() + bump);
+ EXPECT_EQ(buffer->GetDecodeTimestamp(), DecodeTimestamp::FromPresentationTime(
+ 15 * frame_duration() + bump));
// Check rest of buffers.
CheckExpectedBuffers(16, 19);
CheckNoNextBuffer();
// Append a keyframe with the same timestamp as the last buffer output.
- NewSegmentAppend("120K");
+ NewSegmentAppend("120D30K");
CheckNoNextBuffer();
// Append the rest of the segment and make sure that buffers are returned
ASSERT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kSuccess);
if (buffer->IsKeyframe()) {
- EXPECT_EQ(buffer->timestamp(), buffer->GetDecodeTimestamp());
+ EXPECT_EQ(DecodeTimestamp::FromPresentationTime(buffer->timestamp()),
+ buffer->GetDecodeTimestamp());
last_keyframe_idx = i;
last_keyframe_presentation_timestamp = buffer->timestamp();
} else if (i == last_keyframe_idx + 1) {
} else {
EXPECT_GT(buffer->timestamp(), last_keyframe_presentation_timestamp);
EXPECT_LT(buffer->timestamp(), last_p_frame_presentation_timestamp);
- EXPECT_LT(buffer->timestamp(), buffer->GetDecodeTimestamp());
+ EXPECT_LT(DecodeTimestamp::FromPresentationTime(buffer->timestamp()),
+ buffer->GetDecodeTimestamp());
}
}
}
// Make sure you can continue appending data to this GOP; again, GC should not
// wipe out anything.
- AppendBuffers("120");
+ AppendBuffers("120D30");
CheckExpectedRangesByTimestamp("{ [0,150) }");
// Set memory limit to 100 and append a 2nd range after this without
CheckExpectedRangesByTimestamp("{ [290,380) [500,620) }");
// Continue appending to this GOP after GC.
- AppendBuffers("620");
+ AppendBuffers("620D30");
CheckExpectedRangesByTimestamp("{ [290,380) [500,650) }");
}
// This whole GOP should be saved, and should be able to continue appending
// data to it.
CheckExpectedRangesByTimestamp("{ [80,170) }");
- AppendBuffers("170");
+ AppendBuffers("170D30");
CheckExpectedRangesByTimestamp("{ [80,200) }");
// Set memory limit to 100 and append a 2nd range after this without
CheckExpectedRangesByTimestamp("{ [80,200) [500,620) }");
// Continue appending to this GOP after GC.
- AppendBuffers("620");
+ AppendBuffers("620D30");
CheckExpectedRangesByTimestamp("{ [80,200) [500,650) }");
}
CheckExpectedBuffers("240K 270 300");
}
+TEST_F(SourceBufferStreamTest, Remove_WholeGOPBeingAppended) {
+ Seek(0);
+ NewSegmentAppend("0K 30 60 90");
+ CheckExpectedRangesByTimestamp("{ [0,120) }");
+
+ // Remove the keyframe of the current GOP being appended.
+ RemoveInMs(0, 30, 120);
+ CheckExpectedRangesByTimestamp("{ }");
+
+ // Continue appending the current GOP.
+ AppendBuffers("210 240");
+
+ CheckExpectedRangesByTimestamp("{ }");
+
+ // Append the beginning of the next GOP.
+ AppendBuffers("270K 300");
+
+ // Verify that the new range is started at the
+ // beginning of the next GOP.
+ CheckExpectedRangesByTimestamp("{ [270,330) }");
+
+ // Verify the buffers in the ranges.
+ CheckNoNextBuffer();
+ SeekToTimestamp(base::TimeDelta::FromMilliseconds(270));
+ CheckExpectedBuffers("270K 300");
+}
+
TEST_F(SourceBufferStreamTest,
Remove_PreviousAppendDestroyedAndOverwriteExistingRange) {
SeekToTimestamp(base::TimeDelta::FromMilliseconds(90));
CheckExpectedBuffers("90K 121 151");
}
+TEST_F(SourceBufferStreamTest, Remove_GapAtBeginningOfMediaSegment) {
+ Seek(0);
+
+ // Append a media segment that has a gap at the beginning of it.
+ NewSegmentAppend(base::TimeDelta::FromMilliseconds(0),
+ "30K 60 90 120K 150");
+ CheckExpectedRangesByTimestamp("{ [0,180) }");
+
+ // Remove the gap that doesn't contain any buffers.
+ RemoveInMs(0, 10, 180);
+ CheckExpectedRangesByTimestamp("{ [10,180) }");
+
+ // Verify we still get the first buffer still since only part of
+ // the gap was removed.
+ // TODO(acolwell/wolenetz): Consider not returning a buffer at this
+ // point since the current seek position has been explicitly
+ // removed but didn't happen to remove any buffers.
+ // http://crbug.com/384016
+ CheckExpectedBuffers("30K");
+
+ // Remove a range that includes the first GOP.
+ RemoveInMs(0, 60, 180);
+
+ // Verify that no buffer is returned because the current buffer
+ // position has been removed.
+ CheckNoNextBuffer();
+
+ CheckExpectedRangesByTimestamp("{ [120,180) }");
+}
+
TEST_F(SourceBufferStreamTest, Text_Append_SingleRange) {
SetTextStream();
NewSegmentAppend("0K 500K 1000K");
CheckExpectedRangesByTimestamp("{ [3000,4500) }");
NewSegmentAppend("0K 501K 1001K 1501K 2001K 2501K "
"3001K 3501K 4001K 4501K 5001K");
- CheckExpectedRangesByTimestamp("{ [0,5502) }");
+ CheckExpectedRangesByTimestamp("{ [0,5501) }");
Seek(0);
CheckExpectedBuffers("0K 501K 1001K 1501K 2001K 2501K "
NewSegmentAppend("0K 500K 1000K 1500K 2000K");
CheckExpectedRangesByTimestamp("{ [0,2500) }");
NewSegmentAppend("1499K 2001K 2501K 3001K");
- CheckExpectedRangesByTimestamp("{ [0,3503) }");
+ CheckExpectedRangesByTimestamp("{ [0,3501) }");
Seek(0);
CheckExpectedBuffers("0K 500K 1000K 1499K 2001K 2501K 3001K");
NewSegmentAppend("1500K 2000K 2500K 3000K 3500K");
CheckExpectedRangesByTimestamp("{ [1500,4000) }");
NewSegmentAppend("0K 501K 1001K 1501K 2001K");
- CheckExpectedRangesByTimestamp("{ [0,4001) }");
+ CheckExpectedRangesByTimestamp("{ [0,4000) }");
Seek(0);
- CheckExpectedBuffers("0K 501K 1001K 1501K 2001K 2500K 3000K 3500K");
+ CheckExpectedBuffers("0K 501K 1001K 1501K 2001K 3000K 3500K");
}
TEST_F(SourceBufferStreamTest, SpliceFrame_Basic) {
Seek(0);
- NewSegmentAppend("0K S(3K 6 9 10) 15 20 S(25K 30 35) 40");
+ NewSegmentAppend("0K S(3K 6 9D3 10D5) 15 20 S(25K 30D5 35D5) 40");
CheckExpectedBuffers("0K 3K 6 9 C 10 15 20 25K 30 C 35 40");
CheckNoNextBuffer();
}
TEST_F(SourceBufferStreamTest, SpliceFrame_SeekClearsSplice) {
Seek(0);
- NewSegmentAppend("0K S(3K 6 9 10) 15K 20");
+ NewSegmentAppend("0K S(3K 6 9D3 10D5) 15K 20");
CheckExpectedBuffers("0K 3K 6");
SeekToTimestamp(base::TimeDelta::FromMilliseconds(15));
TEST_F(SourceBufferStreamTest, SpliceFrame_SeekClearsSpliceFromTrackBuffer) {
Seek(0);
- NewSegmentAppend("0K 2K S(3K 6 9 10) 15K 20");
+ NewSegmentAppend("0K 2K S(3K 6 9D3 10D5) 15K 20");
CheckExpectedBuffers("0K 2K");
// Overlap the existing segment.
Seek(0);
CheckVideoConfig(video_config_);
- NewSegmentAppend("0K S(3K 6C 9 10) 15");
+ NewSegmentAppend("0K S(3K 6C 9D3 10D5) 15");
CheckExpectedBuffers("0K 3K C");
CheckVideoConfig(new_config);
TEST_F(SourceBufferStreamTest, SpliceFrame_BasicFromTrackBuffer) {
Seek(0);
- NewSegmentAppend("0K 5K S(8K 9 10) 20");
+ NewSegmentAppend("0K 5K S(8K 9D1 10D10) 20");
CheckExpectedBuffers("0K 5K");
// Overlap the existing segment.
Seek(0);
CheckVideoConfig(video_config_);
- NewSegmentAppend("0K 5K S(7K 8C 9 10) 20");
+ NewSegmentAppend("0K 5K S(7K 8C 9D1 10D10) 20");
CheckExpectedBuffers("0K 5K");
// Overlap the existing segment.
Seek(0);
// Create a splice before the first splice which would include it.
- NewSegmentAppend("9K");
+ NewSegmentAppend("9D2K");
// A splice on top of a splice should result in a discard of the original
// splice and no new splice frame being generated.
CheckExpectedRangesByTimestamp("{ [0,6) }");
NewSegmentAppend("6K 8K 10K");
CheckExpectedRangesByTimestamp("{ [0,12) }");
- NewSegmentAppend("1K 4K");
+ NewSegmentAppend("1K 4D2K");
CheckExpectedRangesByTimestamp("{ [0,12) }");
CheckExpectedBuffers("0K 2K 4K C 1K 4K 6K 8K 10K");
CheckNoNextBuffer();
// 2ms this results in an overlap of 1ms between the ranges. A splice frame
// should not be generated since it requires at least 2 frames, or 2ms in this
// case, of data to crossfade.
- NewSegmentAppend("0K");
+ NewSegmentAppend("0D2K");
CheckExpectedRangesByTimestamp("{ [0,2) }");
- NewSegmentAppend("1K");
+ NewSegmentAppend("1D2K");
CheckExpectedRangesByTimestamp("{ [0,3) }");
CheckExpectedBuffers("0K 1K");
CheckNoNextBuffer();
}
+TEST_F(SourceBufferStreamTest, Audio_SpliceFrame_Preroll) {
+ SetAudioStream();
+ Seek(0);
+ NewSegmentAppend("0K 2K 4K 6K 8K 10K 12K");
+ NewSegmentAppend("11P 13K 15K 17K");
+ CheckExpectedBuffers("0K 2K 4K 6K 8K 10K 12K C 11P 13K 15K 17K");
+ CheckNoNextBuffer();
+}
+
+TEST_F(SourceBufferStreamTest, Audio_PrerollFrame) {
+ Seek(0);
+ NewSegmentAppend("0K 3P 6K");
+ CheckExpectedBuffers("0K 3P 6K");
+ CheckNoNextBuffer();
+}
+
+TEST_F(SourceBufferStreamTest, BFrames) {
+ Seek(0);
+ NewSegmentAppend("0K 120|30 30|60 60|90 90|120");
+ CheckExpectedRangesByTimestamp("{ [0,150) }");
+
+ CheckExpectedBuffers("0K 120|30 30|60 60|90 90|120");
+ CheckNoNextBuffer();
+}
+
// TODO(vrk): Add unit tests where keyframes are unaligned between streams.
// (crbug.com/133557)