Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(999)

Unified Diff: media/filters/frame_processor_unittest.cc

Issue 180153003: Implement core of compliant MediaSource coded frame processing (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Aligned with April 1 spec editor's draft, tests added Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/filters/frame_processor_unittest.cc
diff --git a/media/filters/frame_processor_unittest.cc b/media/filters/frame_processor_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c55584718fbd455288c9559b402165b08edaf60e
--- /dev/null
+++ b/media/filters/frame_processor_unittest.cc
@@ -0,0 +1,624 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
acolwell GONE FROM CHROMIUM 2014/05/08 00:06:43 nit: s/(c) //
wolenetz 2014/05/08 02:35:28 Done.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <map>
+#include <string>
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/time/time.h"
+#include "media/base/mock_filters.h"
+#include "media/base/test_helpers.h"
+#include "media/filters/chunk_demuxer.h"
+#include "media/filters/frame_processor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::InSequence;
+using ::testing::StrictMock;
+using ::testing::Values;
+
+namespace media {
+
+typedef StreamParser::BufferQueue BufferQueue;
+typedef StreamParser::TextBufferQueueMap TextBufferQueueMap;
+typedef StreamParser::TrackId TrackId;
+
+static void LogFunc(const std::string& str) { DVLOG(1) << str; }
+
+// Used for setting expectations on callbacks. Using a StrictMock also lets us
+// test for missing or extra callbacks.
+class FrameProcessorTestCallbackHelper {
+ public:
+ FrameProcessorTestCallbackHelper() {}
+ virtual ~FrameProcessorTestCallbackHelper() {}
+
+ MOCK_METHOD1(PossibleDurationIncrease, void(base::TimeDelta new_duration));
+
+ // Helper that calls the mock method as well as does basic sanity checks on
+ // |new_duration|.
+ void OnPossibleDurationIncrease(base::TimeDelta new_duration) {
+ PossibleDurationIncrease(new_duration);
+ ASSERT_NE(kNoTimestamp(), new_duration);
+ ASSERT_NE(kInfiniteDuration(), new_duration);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FrameProcessorTestCallbackHelper);
+};
+
+// Test parameter determines indicates if the TEST_P instance is targeted for
+// sequence mode (if true), or segments mode (if false).
+class FrameProcessorTest : public testing::TestWithParam<bool> {
+ protected:
+ FrameProcessorTest()
+ : frame_processor_(new FrameProcessor(base::Bind(
+ &FrameProcessorTestCallbackHelper::OnPossibleDurationIncrease,
+ base::Unretained(&callbacks_)))),
+ append_window_end_(kInfiniteDuration()),
+ new_media_segment_(false),
+ audio_id_(FrameProcessor::kAudioTrackId),
+ video_id_(FrameProcessor::kVideoTrackId),
+ frame_duration_(base::TimeDelta::FromMilliseconds(10)) {
+ }
+
+ void AddTestTracks(const bool has_audio, const bool has_video) {
acolwell GONE FROM CHROMIUM 2014/05/08 00:06:43 nit: Use HAS_AUDIO & HAS_VIDEO flags like ChunkDem
wolenetz 2014/05/08 02:35:28 Done.
+ ASSERT_TRUE(has_audio || has_video);
+
+ if (has_audio) {
+ CreateAndConfigureStream(DemuxerStream::AUDIO);
+ ASSERT_TRUE(audio_);
+ EXPECT_TRUE(frame_processor_->AddTrack(audio_id_, audio_.get()));
+ audio_->Seek(base::TimeDelta());
+ audio_->StartReturningData();
+ }
+ if (has_video) {
+ CreateAndConfigureStream(DemuxerStream::VIDEO);
+ ASSERT_TRUE(video_);
+ EXPECT_TRUE(frame_processor_->AddTrack(video_id_, video_.get()));
+ video_->Seek(base::TimeDelta());
+ video_->StartReturningData();
+ }
+ }
+
+ void SetTimestampOffset(base::TimeDelta new_offset) {
+ timestamp_offset_ = new_offset;
+ frame_processor_->SetGroupStartTimestampIfInSequenceMode(timestamp_offset_);
+ }
+
+ BufferQueue StringToBufferQueue(const std::string& buffers_to_append,
+ const TrackId track_id,
+ const DemuxerStream::Type type) {
+ std::vector<std::string> timestamps;
+ base::SplitString(buffers_to_append, ' ', &timestamps);
+
+ CHECK_GT(timestamps.size(), 0u);
+
+ BufferQueue pre_splice_buffers;
+ BufferQueue buffers;
+ for (size_t i = 0; i < timestamps.size(); i++) {
+ bool is_keyframe = false;
+ 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));
+
+ // Create buffer. Encode the original time_in_ms as the buffer's data to
+ // enable later verification of possible buffer relocation in presentation
+ // timeline due to coded frame processing.
+ const uint8* timestamp_as_data = reinterpret_cast<uint8*>(&time_in_ms);
+ scoped_refptr<StreamParserBuffer> buffer =
+ StreamParserBuffer::CopyFrom(timestamp_as_data, sizeof(time_in_ms),
+ is_keyframe, type, track_id);
+ base::TimeDelta timestamp =
+ base::TimeDelta::FromMilliseconds(time_in_ms);
+ buffer->set_timestamp(timestamp);
+ buffer->SetDecodeTimestamp(timestamp);
+ buffer->set_duration(frame_duration_);
+ buffers.push_back(buffer);
+ }
+ return buffers;
+ }
+
+ void CheckExpectedRangesByTimestamp(ChunkDemuxerStream* stream,
+ const std::string& expected) {
+ // Note, DemuxerStream::TEXT streams return [0,duration (==infinity here))
+ Ranges<base::TimeDelta> r = stream->GetBufferedRanges(kInfiniteDuration());
+
+ std::stringstream ss;
+ ss << "{ ";
+ for (size_t i = 0; i < r.size(); ++i) {
+ int64 start = r.start(i).InMilliseconds();
+ int64 end = r.end(i).InMilliseconds();
+ ss << "[" << start << "," << end << ") ";
+ }
+ ss << "}";
+ EXPECT_EQ(expected, ss.str());
+ }
+
+ void CheckReadStalls(ChunkDemuxerStream* stream) {
+ int loop_count = 0;
+
+ do {
+ read_callback_called_ = false;
+ stream->Read(base::Bind(&FrameProcessorTest::StoreStatusAndBuffer,
+ base::Unretained(this)));
+ message_loop_.RunUntilIdle();
+ } while (++loop_count < 10 && read_callback_called_ &&
acolwell GONE FROM CHROMIUM 2014/05/08 00:06:43 Do you really need 10 here? Why would you expect m
wolenetz 2014/05/08 02:35:28 Done. While debugging and getting strict mocks and
+ last_read_status_ == DemuxerStream::kAborted);
+
+ ASSERT_FALSE(read_callback_called_ &&
+ last_read_status_ == DemuxerStream::kAborted)
+ << "10 kAborted reads in a row. Giving up.";
+ EXPECT_EQ(false, read_callback_called_);
acolwell GONE FROM CHROMIUM 2014/05/08 00:06:43 nit: EXPECT_FALSE ?
wolenetz 2014/05/08 02:35:28 yep. linux_chromium*_rel try bots also failed to c
+ }
+
+ // Format of |expected| is a space-delimited sequence of
+ // timestamp_in_ms:original_timestamp_in_ms
+ // original_timestamp_in_ms (and the colon) must be omitted if it is the same
+ // as timestamp_in_ms.
+ void CheckReadsThenReadStalls(ChunkDemuxerStream* stream,
+ const std::string& expected) {
+ std::vector<std::string> timestamps;
+ base::SplitString(expected, ' ', &timestamps);
+ std::stringstream ss;
+ for (size_t i = 0; i < timestamps.size(); ++i) {
+ int loop_count = 0;
+
+ do {
+ read_callback_called_ = false;
+ stream->Read(base::Bind(&FrameProcessorTest::StoreStatusAndBuffer,
+ base::Unretained(this)));
+ message_loop_.RunUntilIdle();
+ EXPECT_TRUE(read_callback_called_);
+ } while (++loop_count < 10 &&
acolwell GONE FROM CHROMIUM 2014/05/08 00:06:43 ditto re: 10
wolenetz 2014/05/08 02:35:28 Done.
+ last_read_status_ == DemuxerStream::kAborted);
+
+ ASSERT_FALSE(last_read_status_ == DemuxerStream::kAborted)
+ << "10 kAborted reads in a row. Giving up.";
+ EXPECT_EQ(DemuxerStream::kOk, last_read_status_);
+ EXPECT_FALSE(last_read_buffer_->end_of_stream());
+
+ if (i > 0)
+ ss << " ";
+
+ int time_in_ms = last_read_buffer_->timestamp().InMilliseconds();
+ ss << time_in_ms;
+
+ // Decode the original_time_in_ms from the buffer's data.
+ int original_time_in_ms;
+ ASSERT_EQ(static_cast<int>(sizeof(original_time_in_ms)),
+ last_read_buffer_->data_size());
+ original_time_in_ms = *(reinterpret_cast<const int*>(
+ last_read_buffer_->data()));
+ if (original_time_in_ms != time_in_ms)
+ ss << ":" << original_time_in_ms;
+ }
+
+ EXPECT_EQ(expected, ss.str());
+ CheckReadStalls(stream);
+ }
+
+ base::MessageLoop message_loop_;
+ StrictMock<FrameProcessorTestCallbackHelper> callbacks_;
+
+ scoped_ptr<FrameProcessor> frame_processor_;
+ base::TimeDelta append_window_start_;
+ base::TimeDelta append_window_end_;
+ bool new_media_segment_;
+ base::TimeDelta timestamp_offset_;
+ scoped_ptr<ChunkDemuxerStream> audio_;
+ scoped_ptr<ChunkDemuxerStream> video_;
+ const TrackId audio_id_;
+ const TrackId video_id_;
+ const base::TimeDelta frame_duration_; // Currently the same for all streams.
+ const BufferQueue empty_queue_;
+ const TextBufferQueueMap empty_text_buffers_;
+
+ // StoreStatusAndBuffer's most recent result.
+ DemuxerStream::Status last_read_status_;
+ scoped_refptr<DecoderBuffer> last_read_buffer_;
+ bool read_callback_called_;
+
+ private:
+ void StoreStatusAndBuffer(DemuxerStream::Status status,
+ const scoped_refptr<DecoderBuffer>& buffer) {
+ if (status == DemuxerStream::kOk && buffer) {
+ DVLOG(3) << __FUNCTION__ << "status: " << status << " ts: "
+ << buffer->timestamp().InSecondsF();
+ } else {
+ DVLOG(3) << __FUNCTION__ << "status: " << status << " ts: n/a";
+ }
+
+ read_callback_called_ = true;
+ last_read_status_ = status;
+ last_read_buffer_ = buffer;
+ }
+
+ void CreateAndConfigureStream(DemuxerStream::Type type) {
+ // TODO(wolenetz/dalecurtis): Also test with splicing disabled?
+ switch (type) {
+ case DemuxerStream::AUDIO:
+ ASSERT_FALSE(audio_);
+ audio_.reset(new ChunkDemuxerStream(DemuxerStream::AUDIO, true));
+ ASSERT_TRUE(audio_->UpdateAudioConfig(
+ AudioDecoderConfig(kCodecVorbis,
+ kSampleFormatPlanarF32,
+ CHANNEL_LAYOUT_STEREO,
+ 1000,
+ NULL,
+ 0,
+ false),
+ base::Bind(&LogFunc)));
+ break;
+ case DemuxerStream::VIDEO:
+ ASSERT_FALSE(video_);
+ video_.reset(new ChunkDemuxerStream(DemuxerStream::VIDEO, true));
+ ASSERT_TRUE(video_->UpdateVideoConfig(TestVideoConfig::Normal(),
+ base::Bind(&LogFunc)));
+ break;
+ // TODO(wolenetz): Test text coded frame processing.
+ case DemuxerStream::TEXT:
+ case DemuxerStream::UNKNOWN:
+ case DemuxerStream::NUM_TYPES:
+ ASSERT_FALSE(true);
+ }
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(FrameProcessorTest);
+};
+
+TEST_F(FrameProcessorTest, WrongTypeInAppendedBuffer) {
+ AddTestTracks(true, false);
+ new_media_segment_ = true;
+
+ ASSERT_FALSE(frame_processor_->ProcessFrames(
+ StringToBufferQueue("0K", audio_id_, DemuxerStream::VIDEO),
+ empty_queue_,
+ empty_text_buffers_,
+ append_window_start_, append_window_end_,
+ &new_media_segment_, &timestamp_offset_));
+ EXPECT_TRUE(new_media_segment_);
+ EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ }");
+ CheckReadStalls(audio_.get());
+}
+
+TEST_F(FrameProcessorTest, NonMonotonicallyIncreasingTimestampInOneCall) {
+ AddTestTracks(true, false);
+ new_media_segment_ = true;
+
+ ASSERT_FALSE(frame_processor_->ProcessFrames(
+ StringToBufferQueue("10K 0K", audio_id_, DemuxerStream::AUDIO),
+ empty_queue_,
+ empty_text_buffers_,
+ append_window_start_, append_window_end_,
+ &new_media_segment_, &timestamp_offset_));
+ EXPECT_TRUE(new_media_segment_);
+ EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ }");
+ CheckReadStalls(audio_.get());
+}
+
+TEST_P(FrameProcessorTest, AudioOnly_SingleFrame) {
+ // Tests A: P(A) -> (a)
+ InSequence s;
+ AddTestTracks(true, false);
+ new_media_segment_ = true;
+ if (GetParam())
+ frame_processor_->SetSequenceMode(true);
+
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_));
+ ASSERT_TRUE(frame_processor_->ProcessFrames(
+ StringToBufferQueue("0K", audio_id_, DemuxerStream::AUDIO),
+ empty_queue_,
+ empty_text_buffers_,
acolwell GONE FROM CHROMIUM 2014/05/08 00:06:43 nit: Looks like a helper method could clean this u
wolenetz 2014/05/08 02:35:28 Done.
+ append_window_start_, append_window_end_,
+ &new_media_segment_, &timestamp_offset_));
+ EXPECT_FALSE(new_media_segment_);
+ EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,10) }");
+ CheckReadsThenReadStalls(audio_.get(), "0");
+}
+
+TEST_P(FrameProcessorTest, VideoOnly_SingleFrame) {
+ // Tests V: P(V) -> (v)
+ InSequence s;
+ AddTestTracks(false, true);
+ new_media_segment_ = true;
+ if (GetParam())
+ frame_processor_->SetSequenceMode(true);
+
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_));
+ ASSERT_TRUE(frame_processor_->ProcessFrames(
+ empty_queue_,
+ StringToBufferQueue("0K", video_id_, DemuxerStream::VIDEO),
+ empty_text_buffers_,
+ append_window_start_, append_window_end_,
+ &new_media_segment_, &timestamp_offset_));
+ EXPECT_FALSE(new_media_segment_);
+ EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+ CheckExpectedRangesByTimestamp(video_.get(), "{ [0,10) }");
+ CheckReadsThenReadStalls(video_.get(), "0");
+}
+
+TEST_P(FrameProcessorTest, AudioOnly_TwoFrames) {
+ // Tests A: P(A0, A10) -> (a0, a10)
+ InSequence s;
+ AddTestTracks(true, false);
+ new_media_segment_ = true;
+ if (GetParam())
+ frame_processor_->SetSequenceMode(true);
+
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2));
+ ASSERT_TRUE(frame_processor_->ProcessFrames(
+ StringToBufferQueue("0K 10K", audio_id_, DemuxerStream::AUDIO),
+ empty_queue_,
+ empty_text_buffers_,
+ append_window_start_, append_window_end_,
+ &new_media_segment_, &timestamp_offset_));
+ EXPECT_FALSE(new_media_segment_);
+ EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }");
+ CheckReadsThenReadStalls(audio_.get(), "0 10");
+}
+
+TEST_P(FrameProcessorTest, AudioOnly_SetOffsetThenSingleFrame) {
+ // Tests A: STSO(50)+P(A0) -> TSO==50,(a0@50)
+ InSequence s;
+ AddTestTracks(true, false);
+ new_media_segment_ = true;
+ if (GetParam())
+ frame_processor_->SetSequenceMode(true);
+
+ const base::TimeDelta fifty_ms = base::TimeDelta::FromMilliseconds(50);
+ SetTimestampOffset(fifty_ms);
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ + fifty_ms));
+ ASSERT_TRUE(frame_processor_->ProcessFrames(
+ StringToBufferQueue("0K", audio_id_, DemuxerStream::AUDIO),
+ empty_queue_,
+ empty_text_buffers_,
+ append_window_start_, append_window_end_,
+ &new_media_segment_, &timestamp_offset_));
+ EXPECT_FALSE(new_media_segment_);
+ EXPECT_EQ(fifty_ms, timestamp_offset_);
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [50,60) }");
+
+ // TODO(wolenetz): Determine why we do not need to seek to 50ms, or if this is
acolwell GONE FROM CHROMIUM 2014/05/08 00:06:43 This is to deal with content that doesn't always s
wolenetz 2014/05/08 02:35:28 Makes sense. Thanks!
+ // a bug in SourceBufferStream.
+ CheckReadsThenReadStalls(audio_.get(), "50:0");
+}
+
+TEST_P(FrameProcessorTest, AudioOnly_SetOffsetThenFrameTimestampBelowOffset) {
+ // Tests A: STSO(50)+P(A20) ->
+ // if sequence mode: TSO==30,(a20@50)
+ // if segments mode: TSO==50,(a20@70)
+ InSequence s;
+ AddTestTracks(true, false);
+ new_media_segment_ = true;
+ bool using_sequence_mode = GetParam();
+ if (using_sequence_mode)
+ frame_processor_->SetSequenceMode(true);
+
+ const base::TimeDelta fifty_ms = base::TimeDelta::FromMilliseconds(50);
+ const base::TimeDelta twenty_ms = base::TimeDelta::FromMilliseconds(20);
+ SetTimestampOffset(fifty_ms);
+
+ if (using_sequence_mode) {
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(
+ fifty_ms + frame_duration_));
+ } else {
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(
+ fifty_ms + twenty_ms + frame_duration_));
+ }
+
+ ASSERT_TRUE(frame_processor_->ProcessFrames(
+ StringToBufferQueue("20K", audio_id_, DemuxerStream::AUDIO),
+ empty_queue_,
+ empty_text_buffers_,
+ append_window_start_, append_window_end_,
+ &new_media_segment_, &timestamp_offset_));
+ EXPECT_FALSE(new_media_segment_);
+
+ // TODO(wolenetz): Determine why we do not need to seek to 50ms / 70ms, or if
acolwell GONE FROM CHROMIUM 2014/05/08 00:06:43 ditto
wolenetz 2014/05/08 02:35:28 Done.
+ // this is a bug in SourceBufferStream.
+ if (using_sequence_mode) {
+ EXPECT_EQ(fifty_ms - twenty_ms, timestamp_offset_);
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [50,60) }");
+ CheckReadsThenReadStalls(audio_.get(), "50:20");
+ } else {
+ EXPECT_EQ(fifty_ms, timestamp_offset_);
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [70,80) }");
+ CheckReadsThenReadStalls(audio_.get(), "70:20");
+ }
+}
+
+TEST_P(FrameProcessorTest, AudioOnly_SequentialProcessFrames) {
+ // Tests A: P(A0,A10)+P(A20,A30) -> (a0,a10,a20,a30)
+ InSequence s;
+ AddTestTracks(true, false);
+ new_media_segment_ = true;
+ if (GetParam())
+ frame_processor_->SetSequenceMode(true);
+
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2));
+ ASSERT_TRUE(frame_processor_->ProcessFrames(
+ StringToBufferQueue("0K 10K", audio_id_, DemuxerStream::AUDIO),
+ empty_queue_,
+ empty_text_buffers_,
+ append_window_start_, append_window_end_,
+ &new_media_segment_, &timestamp_offset_));
+ EXPECT_FALSE(new_media_segment_);
+ EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }");
+
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 4));
+ ASSERT_TRUE(frame_processor_->ProcessFrames(
+ StringToBufferQueue("20K 30K", audio_id_, DemuxerStream::AUDIO),
+ empty_queue_,
+ empty_text_buffers_,
+ append_window_start_, append_window_end_,
+ &new_media_segment_, &timestamp_offset_));
+ EXPECT_FALSE(new_media_segment_);
+ EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,40) }");
+
+ CheckReadsThenReadStalls(audio_.get(), "0 10 20 30");
+}
+
+TEST_P(FrameProcessorTest, AudioOnly_NonSequentialProcessFrames) {
+ // Tests A: P(A20,A30)+P(A0,A10) ->
+ // if sequence mode: TSO==-20 after first P(), 20 after second P(), and
+ // a(20@0,a30@10,a0@20,a10@30)
+ // if segments mode: TSO==0,(a0,a10,a20,a30)
+ InSequence s;
+ AddTestTracks(true, false);
+ new_media_segment_ = true;
+ bool using_sequence_mode = GetParam();
+ if (using_sequence_mode) {
+ frame_processor_->SetSequenceMode(true);
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2));
+ } else {
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 4));
+ }
+
+ ASSERT_TRUE(frame_processor_->ProcessFrames(
+ StringToBufferQueue("20K 30K", audio_id_, DemuxerStream::AUDIO),
+ empty_queue_,
+ empty_text_buffers_,
+ append_window_start_, append_window_end_,
+ &new_media_segment_, &timestamp_offset_));
+ EXPECT_FALSE(new_media_segment_);
+
+ if (using_sequence_mode) {
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }");
+ EXPECT_EQ(frame_duration_ * -2, timestamp_offset_);
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 4));
+ } else {
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [20,40) }");
+ EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2));
+ }
+
+ ASSERT_TRUE(frame_processor_->ProcessFrames(
+ StringToBufferQueue("0K 10K", audio_id_, DemuxerStream::AUDIO),
+ empty_queue_,
+ empty_text_buffers_,
+ append_window_start_, append_window_end_,
+ &new_media_segment_, &timestamp_offset_));
+ EXPECT_FALSE(new_media_segment_);
+
+ if (using_sequence_mode) {
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,40) }");
+ EXPECT_EQ(frame_duration_ * 2, timestamp_offset_);
+ CheckReadsThenReadStalls(audio_.get(), "0:20 10:30 20:0 30:10");
+ } else {
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,40) }");
+ EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+ // TODO(wolenetz): Determine why we need to seek to 0ms, or if this is a bug
+ // in SourceBufferStream.
acolwell GONE FROM CHROMIUM 2014/05/08 00:06:43 Not sure what is going on here. What happens if yo
wolenetz 2014/05/08 02:35:28 Correct, only 20 then 30 are read, then unexpected
acolwell GONE FROM CHROMIUM 2014/05/08 17:32:22 Ok. This is what I'd expect because I believe the
wolenetz 2014/05/08 19:59:01 Ok. I filed and referenced crbug 371493 here.
+ audio_->AbortReads();
+ audio_->Seek(base::TimeDelta());
+ audio_->StartReturningData();
+ CheckReadsThenReadStalls(audio_.get(), "0 10 20 30");
+ }
+}
+
+TEST_P(FrameProcessorTest, AudioVideo_SequentialProcessFrames) {
+ // Tests AV: P(A0,A10;V0k,V10,V20)+P(A20,A30,A40,V30) ->
+ // (a0,a10,a20,a30,a40);(v0,v10,v20,v30)
+ InSequence s;
+ AddTestTracks(true, true);
+ new_media_segment_ = true;
+ if (GetParam())
+ frame_processor_->SetSequenceMode(true);
+
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 3));
+ ASSERT_TRUE(frame_processor_->ProcessFrames(
+ StringToBufferQueue("0K 10K", audio_id_, DemuxerStream::AUDIO),
+ StringToBufferQueue("0K 10 20", video_id_, DemuxerStream::VIDEO),
+ empty_text_buffers_,
+ append_window_start_, append_window_end_,
+ &new_media_segment_, &timestamp_offset_));
+ EXPECT_FALSE(new_media_segment_);
+ EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }");
+ CheckExpectedRangesByTimestamp(video_.get(), "{ [0,30) }");
+
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 5));
+ ASSERT_TRUE(frame_processor_->ProcessFrames(
+ StringToBufferQueue("20K 30K 40K", audio_id_, DemuxerStream::AUDIO),
+ StringToBufferQueue("30", video_id_, DemuxerStream::VIDEO),
+ empty_text_buffers_,
+ append_window_start_, append_window_end_,
+ &new_media_segment_, &timestamp_offset_));
+ EXPECT_FALSE(new_media_segment_);
+ EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,50) }");
+ CheckExpectedRangesByTimestamp(video_.get(), "{ [0,40) }");
+
+ CheckReadsThenReadStalls(audio_.get(), "0 10 20 30 40");
+ CheckReadsThenReadStalls(video_.get(), "0 10 20 30");
+}
+
+TEST_P(FrameProcessorTest, AudioVideo_Discontinuity) {
+ // Tests AV: P(A0,A10,A30,A40,A50;V0k,V10,V40,V50key) ->
+ // if sequence mode: TSO==10,(a0,a10,a30,a40,a50@60);(v0,v10,v50@60)
+ // if segments mode: TSO==0,(a0,a10,a30,a40,a50);(v0,v10,v50)
+ // This assumes A40K is processed before V40, which depends currently on
+ // MergeBufferQueues() behavior.
+ InSequence s;
+ AddTestTracks(true, true);
+ new_media_segment_ = true;
+ bool using_sequence_mode = GetParam();
+ if (using_sequence_mode) {
+ frame_processor_->SetSequenceMode(true);
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 7));
+ } else {
+ EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 6));
+ }
+
+ ASSERT_TRUE(frame_processor_->ProcessFrames(
+ StringToBufferQueue("0K 10K 30K 40K 50K", audio_id_,
+ DemuxerStream::AUDIO),
+ StringToBufferQueue("0K 10 40 50K", video_id_,
+ DemuxerStream::VIDEO),
+ empty_text_buffers_,
+ append_window_start_, append_window_end_,
+ &new_media_segment_, &timestamp_offset_));
+ EXPECT_FALSE(new_media_segment_);
+
+ if (using_sequence_mode) {
+ EXPECT_EQ(frame_duration_, timestamp_offset_);
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,70) }");
+ CheckExpectedRangesByTimestamp(video_.get(), "{ [0,20) [60,70) }");
+ CheckReadsThenReadStalls(audio_.get(), "0 10 30 40 60:50");
+ CheckReadsThenReadStalls(video_.get(), "0 10");
+ video_->AbortReads();
+ video_->Seek(frame_duration_ * 6);
+ video_->StartReturningData();
+ CheckReadsThenReadStalls(video_.get(), "60:50");
+ } else {
+ EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
+ CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,60) }");
+ CheckExpectedRangesByTimestamp(video_.get(), "{ [0,20) [50,60) }");
+ CheckReadsThenReadStalls(audio_.get(), "0 10 30 40 50");
+ CheckReadsThenReadStalls(video_.get(), "0 10");
+ video_->AbortReads();
+ video_->Seek(frame_duration_ * 5);
+ video_->StartReturningData();
+ CheckReadsThenReadStalls(video_.get(), "50");
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(SequenceMode, FrameProcessorTest, Values(true));
+INSTANTIATE_TEST_CASE_P(SegmentsMode, FrameProcessorTest, Values(false));
+
+} // namespace media

Powered by Google App Engine
This is Rietveld 408576698