Index: media/filters/source_buffer_stream_unittest.cc |
diff --git a/media/filters/source_buffer_stream_unittest.cc b/media/filters/source_buffer_stream_unittest.cc |
index 03345feb3973a7ae6641b15c87179f3686bac46e..7db62a1b7343b92e1bee11dfb8353ed1f0332216 100644 |
--- a/media/filters/source_buffer_stream_unittest.cc |
+++ b/media/filters/source_buffer_stream_unittest.cc |
@@ -6,6 +6,8 @@ |
#include <string> |
+#include "base/bind.h" |
+#include "base/bind_helpers.h" |
#include "base/logging.h" |
#include "base/strings/string_number_conversions.h" |
#include "base/strings/string_split.h" |
@@ -29,8 +31,8 @@ class SourceBufferStreamTest : public testing::Test { |
protected: |
SourceBufferStreamTest() { |
video_config_ = TestVideoConfig::Normal(); |
- stream_.reset(new SourceBufferStream(video_config_, LogCB())); |
SetStreamInfo(kDefaultFramesPerSecond, kDefaultKeyframesPerSecond); |
+ stream_.reset(new SourceBufferStream(video_config_, log_cb())); |
} |
void SetMemoryLimit(int buffers_of_data) { |
@@ -239,6 +241,13 @@ class SourceBufferStreamTest : public testing::Test { |
if (i > 0) |
ss << " "; |
+ if (timestamps[i] == "C") { |
+ EXPECT_EQ(SourceBufferStream::kConfigChange, status); |
+ stream_->GetCurrentVideoDecoderConfig(); |
+ ss << timestamps[i]; |
+ continue; |
+ } |
+ |
EXPECT_EQ(SourceBufferStream::kSuccess, status); |
if (status != SourceBufferStream::kSuccess) |
break; |
@@ -262,6 +271,11 @@ class SourceBufferStreamTest : public testing::Test { |
<< "\nActual: " << actual.AsHumanReadableString(); |
} |
+ const LogCB log_cb() { |
+ return base::Bind(&SourceBufferStreamTest::DebugMediaLog, |
+ base::Unretained(this)); |
+ } |
+ |
base::TimeDelta frame_duration() const { return frame_duration_; } |
scoped_ptr<SourceBufferStream> stream_; |
@@ -318,6 +332,21 @@ class SourceBufferStreamTest : public testing::Test { |
EXPECT_EQ(expect_success, stream_->Append(queue)); |
} |
+ // 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". |
+ // |
+ // ##K: |
+ // Indicates the buffer with timestamp ## reflects a keyframe. E.g., "0K 1". |
+ // |
+ // S(a# ... y# z#) |
+ // Indicates a splice frame buffer should be created with timestamp z#. The |
+ // preceding timestamps a# ... y# will be treated as the fade out preroll for |
+ // the splice frame. If a timestamp within the preroll ends with C the config |
+ // id to use for that and subsequent preroll appends is incremented by one. |
+ // The config id for non-splice frame appends will not be affected. |
SourceBufferStream::BufferQueue StringToBufferQueue( |
const std::string& buffers_to_append) { |
std::vector<std::string> timestamps; |
@@ -325,14 +354,40 @@ class SourceBufferStreamTest : public testing::Test { |
CHECK_GT(timestamps.size(), 0u); |
+ bool splice_frame = false; |
+ size_t splice_config_id = stream_->append_config_index_; |
+ std::vector<scoped_refptr<StreamParserBuffer> > fade_out_preroll; |
SourceBufferStream::BufferQueue buffers; |
for (size_t i = 0; i < timestamps.size(); i++) { |
bool is_keyframe = false; |
+ bool last_splice_frame = false; |
+ // Handle splice frame starts. |
+ if (StartsWithASCII(timestamps[i], "S(", true)) { |
+ CHECK(!splice_frame); |
+ splice_frame = true; |
+ // Remove the "S(" off of the token. |
+ timestamps[i] = timestamps[i].substr(2, timestamps[i].length()); |
+ } |
+ if (splice_frame && EndsWith(timestamps[i], ")", true)) { |
+ splice_frame = false; |
+ last_splice_frame = true; |
+ // Remove the ")" off of the token. |
+ timestamps[i] = timestamps[i].substr(0, timestamps[i].length() - 1); |
+ } |
+ // Handle config changes within the splice frame. |
+ if (splice_frame && EndsWith(timestamps[i], "C", true)) { |
+ splice_config_id++; |
+ CHECK(splice_config_id < stream_->audio_configs_.size() || |
+ splice_config_id < stream_->video_configs_.size()); |
+ // Remove the "C" off of the token. |
+ timestamps[i] = timestamps[i].substr(0, timestamps[i].length() - 1); |
+ } |
if (EndsWith(timestamps[i], "K", true)) { |
is_keyframe = true; |
// Remove the "K" off of the token. |
timestamps[i] = timestamps[i].substr(0, timestamps[i].length() - 1); |
} |
+ |
int time_in_ms; |
CHECK(base::StringToInt(timestamps[i], &time_in_ms)); |
@@ -344,6 +399,25 @@ class SourceBufferStreamTest : public testing::Test { |
buffer->set_timestamp(timestamp); |
buffer->SetDecodeTimestamp(timestamp); |
+ if (splice_frame) { |
+ if (!fade_out_preroll.empty()) { |
+ // Enforce strictly monotonically increasing timestamps. |
+ CHECK_GT( |
+ timestamp.InMicroseconds(), |
+ fade_out_preroll.back()->GetDecodeTimestamp().InMicroseconds()); |
+ } |
+ buffer->SetConfigId(splice_config_id); |
+ fade_out_preroll.push_back(buffer); |
+ continue; |
+ } |
+ |
+ if (last_splice_frame) { |
+ // Forbid splice frames without preroll. |
+ CHECK(!fade_out_preroll.empty()); |
+ buffer->SetFadeOutPreroll(fade_out_preroll); |
+ fade_out_preroll.clear(); |
+ } |
+ |
buffers.push_back(buffer); |
} |
return buffers; |
@@ -380,6 +454,10 @@ class SourceBufferStreamTest : public testing::Test { |
} |
} |
+ void DebugMediaLog(const std::string& log) { |
+ DVLOG(1) << log; |
+ } |
+ |
int frames_per_second_; |
int keyframes_per_second_; |
base::TimeDelta frame_duration_; |
@@ -1398,7 +1476,7 @@ TEST_F(SourceBufferStreamTest, Overlap_OneByOne_TrackBuffer) { |
CheckExpectedRangesByTimestamp("{ [10,160) }"); |
// Seek to 70ms. |
- SeekToTimestamp(base::TimeDelta::FromMilliseconds(10)); |
+ SeekToTimestamp(base::TimeDelta::FromMilliseconds(70)); |
CheckExpectedBuffers("10K 40"); |
// Overlap with a new segment from 0 to 120ms. |
@@ -1556,7 +1634,7 @@ TEST_F(SourceBufferStreamTest, Overlap_OneByOne_TrackBuffer6) { |
CheckExpectedRangesByTimestamp("{ [10,160) [200,260) }"); |
// Seek to 70ms. |
- SeekToTimestamp(base::TimeDelta::FromMilliseconds(10)); |
+ SeekToTimestamp(base::TimeDelta::FromMilliseconds(70)); |
CheckExpectedBuffers("10K 40"); |
// Overlap with a new segment from 0 to 120ms. |
@@ -3073,7 +3151,7 @@ TEST_F(SourceBufferStreamTest, SameTimestamp_Video_Overlap_3) { |
TEST_F(SourceBufferStreamTest, SameTimestamp_Audio) { |
AudioDecoderConfig config(kCodecMP3, kSampleFormatF32, CHANNEL_LAYOUT_STEREO, |
44100, NULL, 0, false); |
- stream_.reset(new SourceBufferStream(config, LogCB())); |
+ stream_.reset(new SourceBufferStream(config, log_cb())); |
Seek(0); |
NewSegmentAppend("0K 0K 30K 30 60 60"); |
CheckExpectedBuffers("0K 0K 30K 30 60 60"); |
@@ -3082,7 +3160,7 @@ TEST_F(SourceBufferStreamTest, SameTimestamp_Audio) { |
TEST_F(SourceBufferStreamTest, SameTimestamp_Audio_Invalid_1) { |
AudioDecoderConfig config(kCodecMP3, kSampleFormatF32, CHANNEL_LAYOUT_STEREO, |
44100, NULL, 0, false); |
- stream_.reset(new SourceBufferStream(config, LogCB())); |
+ stream_.reset(new SourceBufferStream(config, log_cb())); |
Seek(0); |
NewSegmentAppend_ExpectFailure("0K 30 30K 60"); |
} |
@@ -3323,7 +3401,6 @@ TEST_F(SourceBufferStreamTest, Remove_GOPBeingAppended) { |
CheckExpectedBuffers("240K 270 300"); |
} |
- |
TEST_F(SourceBufferStreamTest, |
Remove_PreviousAppendDestroyedAndOverwriteExistingRange) { |
SeekToTimestamp(base::TimeDelta::FromMilliseconds(90)); |
@@ -3414,6 +3491,92 @@ TEST_F(SourceBufferStreamTest, Text_OverlapBefore) { |
CheckExpectedBuffers("0K 501K 1001K 1501K 2001K 2500K 3000K 3500K"); |
} |
+TEST_F(SourceBufferStreamTest, SpliceFrame_Basic) { |
+ Seek(0); |
+ NewSegmentAppend("0K S(3K 6 9 10) 15 20 S(25K 30 35) 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"); |
+ CheckExpectedBuffers("0K 3K 6"); |
+ |
+ SeekToTimestamp(base::TimeDelta::FromMilliseconds(15)); |
+ CheckExpectedBuffers("15K 20"); |
+ CheckNoNextBuffer(); |
+} |
+ |
+TEST_F(SourceBufferStreamTest, SpliceFrame_SeekClearsSpliceFromTrackBuffer) { |
+ Seek(0); |
+ NewSegmentAppend("0K 2K S(3K 6 9 10) 15K 20"); |
+ CheckExpectedBuffers("0K 2K"); |
+ |
+ // Overlap the existing segment. |
+ NewSegmentAppend("5K 15K 20"); |
+ CheckExpectedBuffers("3K 6"); |
+ |
+ SeekToTimestamp(base::TimeDelta::FromMilliseconds(15)); |
+ CheckExpectedBuffers("15K 20"); |
+ CheckNoNextBuffer(); |
+} |
+ |
+TEST_F(SourceBufferStreamTest, SpliceFrame_ConfigChangeWithinSplice) { |
+ VideoDecoderConfig new_config = TestVideoConfig::Large(); |
+ ASSERT_FALSE(new_config.Matches(video_config_)); |
+ |
+ // Add a new video config, then reset the config index back to the original. |
+ stream_->UpdateVideoConfig(new_config); |
+ stream_->UpdateVideoConfig(video_config_); |
+ |
+ Seek(0); |
+ CheckVideoConfig(video_config_); |
+ NewSegmentAppend("0K S(3K 6C 9 10) 15"); |
+ |
+ CheckExpectedBuffers("0K 3K C"); |
+ CheckVideoConfig(new_config); |
+ CheckExpectedBuffers("6 9 C"); |
+ CheckVideoConfig(video_config_); |
+ CheckExpectedBuffers("10 15"); |
+ CheckNoNextBuffer(); |
+} |
+ |
+TEST_F(SourceBufferStreamTest, SpliceFrame_BasicFromTrackBuffer) { |
+ Seek(0); |
+ NewSegmentAppend("0K 5K S(8K 9 10) 20"); |
+ CheckExpectedBuffers("0K 5K"); |
+ |
+ // Overlap the existing segment. |
+ NewSegmentAppend("5K 20"); |
+ CheckExpectedBuffers("8K 9 C 10 20"); |
+ CheckNoNextBuffer(); |
+} |
+ |
+TEST_F(SourceBufferStreamTest, |
+ SpliceFrame_ConfigChangeWithinSpliceFromTrackBuffer) { |
+ VideoDecoderConfig new_config = TestVideoConfig::Large(); |
+ ASSERT_FALSE(new_config.Matches(video_config_)); |
+ |
+ // Add a new video config, then reset the config index back to the original. |
+ stream_->UpdateVideoConfig(new_config); |
+ stream_->UpdateVideoConfig(video_config_); |
+ |
+ Seek(0); |
+ CheckVideoConfig(video_config_); |
+ NewSegmentAppend("0K 5K S(7K 8C 9 10) 20"); |
+ CheckExpectedBuffers("0K 5K"); |
+ |
+ // Overlap the existing segment. |
+ NewSegmentAppend("5K 20"); |
+ CheckExpectedBuffers("7K C"); |
+ CheckVideoConfig(new_config); |
+ CheckExpectedBuffers("8 9 C"); |
+ CheckVideoConfig(video_config_); |
+ CheckExpectedBuffers("10 20"); |
+ CheckNoNextBuffer(); |
+} |
+ |
// TODO(vrk): Add unit tests where keyframes are unaligned between streams. |
// (crbug.com/133557) |