1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
9 #include "base/message_loop/message_loop.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_split.h"
12 #include "base/strings/string_util.h"
13 #include "base/time/time.h"
14 #include "media/base/mock_filters.h"
15 #include "media/base/test_helpers.h"
16 #include "media/filters/chunk_demuxer.h"
17 #include "media/filters/frame_processor.h"
18 #include "testing/gtest/include/gtest/gtest.h"
20 using ::testing::InSequence;
21 using ::testing::StrictMock;
22 using ::testing::Values;
26 typedef StreamParser::BufferQueue BufferQueue;
27 typedef StreamParser::TextBufferQueueMap TextBufferQueueMap;
28 typedef StreamParser::TrackId TrackId;
30 static void LogFunc(const std::string& str) { DVLOG(1) << str; }
32 // Used for setting expectations on callbacks. Using a StrictMock also lets us
33 // test for missing or extra callbacks.
34 class FrameProcessorTestCallbackHelper {
36 FrameProcessorTestCallbackHelper() {}
37 virtual ~FrameProcessorTestCallbackHelper() {}
39 MOCK_METHOD1(PossibleDurationIncrease, void(base::TimeDelta new_duration));
41 // Helper that calls the mock method as well as does basic sanity checks on
43 void OnPossibleDurationIncrease(base::TimeDelta new_duration) {
44 PossibleDurationIncrease(new_duration);
45 ASSERT_NE(kNoTimestamp(), new_duration);
46 ASSERT_NE(kInfiniteDuration(), new_duration);
50 DISALLOW_COPY_AND_ASSIGN(FrameProcessorTestCallbackHelper);
53 // Test parameter determines indicates if the TEST_P instance is targeted for
54 // sequence mode (if true), or segments mode (if false).
55 class FrameProcessorTest : public testing::TestWithParam<bool> {
58 : frame_processor_(new FrameProcessor(base::Bind(
59 &FrameProcessorTestCallbackHelper::OnPossibleDurationIncrease,
60 base::Unretained(&callbacks_)))),
61 append_window_end_(kInfiniteDuration()),
62 new_media_segment_(false),
63 audio_id_(FrameProcessor::kAudioTrackId),
64 video_id_(FrameProcessor::kVideoTrackId),
65 frame_duration_(base::TimeDelta::FromMilliseconds(10)) {
73 void AddTestTracks(int stream_flags) {
74 const bool has_audio = (stream_flags & HAS_AUDIO) != 0;
75 const bool has_video = (stream_flags & HAS_VIDEO) != 0;
76 ASSERT_TRUE(has_audio || has_video);
79 CreateAndConfigureStream(DemuxerStream::AUDIO);
81 EXPECT_TRUE(frame_processor_->AddTrack(audio_id_, audio_.get()));
82 audio_->Seek(base::TimeDelta());
83 audio_->StartReturningData();
86 CreateAndConfigureStream(DemuxerStream::VIDEO);
88 EXPECT_TRUE(frame_processor_->AddTrack(video_id_, video_.get()));
89 video_->Seek(base::TimeDelta());
90 video_->StartReturningData();
94 void SetTimestampOffset(base::TimeDelta new_offset) {
95 timestamp_offset_ = new_offset;
96 frame_processor_->SetGroupStartTimestampIfInSequenceMode(timestamp_offset_);
99 BufferQueue StringToBufferQueue(const std::string& buffers_to_append,
100 const TrackId track_id,
101 const DemuxerStream::Type type) {
102 std::vector<std::string> timestamps;
103 base::SplitString(buffers_to_append, ' ', ×tamps);
106 for (size_t i = 0; i < timestamps.size(); i++) {
107 bool is_keyframe = false;
108 if (EndsWith(timestamps[i], "K", true)) {
110 // Remove the "K" off of the token.
111 timestamps[i] = timestamps[i].substr(0, timestamps[i].length() - 1);
115 CHECK(base::StringToDouble(timestamps[i], &time_in_ms));
117 // Create buffer. Encode the original time_in_ms as the buffer's data to
118 // enable later verification of possible buffer relocation in presentation
119 // timeline due to coded frame processing.
120 const uint8* timestamp_as_data = reinterpret_cast<uint8*>(&time_in_ms);
121 scoped_refptr<StreamParserBuffer> buffer =
122 StreamParserBuffer::CopyFrom(timestamp_as_data, sizeof(time_in_ms),
123 is_keyframe, type, track_id);
124 base::TimeDelta timestamp = base::TimeDelta::FromSecondsD(
125 time_in_ms / base::Time::kMillisecondsPerSecond);
126 buffer->set_timestamp(timestamp);
127 buffer->set_duration(frame_duration_);
128 buffers.push_back(buffer);
133 void ProcessFrames(const std::string& audio_timestamps,
134 const std::string& video_timestamps) {
135 ASSERT_TRUE(frame_processor_->ProcessFrames(
136 StringToBufferQueue(audio_timestamps, audio_id_, DemuxerStream::AUDIO),
137 StringToBufferQueue(video_timestamps, video_id_, DemuxerStream::VIDEO),
139 append_window_start_, append_window_end_,
140 &new_media_segment_, ×tamp_offset_));
143 void CheckExpectedRangesByTimestamp(ChunkDemuxerStream* stream,
144 const std::string& expected) {
145 // Note, DemuxerStream::TEXT streams return [0,duration (==infinity here))
146 Ranges<base::TimeDelta> r = stream->GetBufferedRanges(kInfiniteDuration());
148 std::stringstream ss;
150 for (size_t i = 0; i < r.size(); ++i) {
151 int64 start = r.start(i).InMilliseconds();
152 int64 end = r.end(i).InMilliseconds();
153 ss << "[" << start << "," << end << ") ";
156 EXPECT_EQ(expected, ss.str());
159 void CheckReadStalls(ChunkDemuxerStream* stream) {
163 read_callback_called_ = false;
164 stream->Read(base::Bind(&FrameProcessorTest::StoreStatusAndBuffer,
165 base::Unretained(this)));
166 message_loop_.RunUntilIdle();
167 } while (++loop_count < 2 && read_callback_called_ &&
168 last_read_status_ == DemuxerStream::kAborted);
170 ASSERT_FALSE(read_callback_called_ &&
171 last_read_status_ == DemuxerStream::kAborted)
172 << "2 kAborted reads in a row. Giving up.";
173 EXPECT_FALSE(read_callback_called_);
176 // Format of |expected| is a space-delimited sequence of
177 // timestamp_in_ms:original_timestamp_in_ms
178 // original_timestamp_in_ms (and the colon) must be omitted if it is the same
179 // as timestamp_in_ms.
180 void CheckReadsThenReadStalls(ChunkDemuxerStream* stream,
181 const std::string& expected) {
182 std::vector<std::string> timestamps;
183 base::SplitString(expected, ' ', ×tamps);
184 std::stringstream ss;
185 for (size_t i = 0; i < timestamps.size(); ++i) {
189 read_callback_called_ = false;
190 stream->Read(base::Bind(&FrameProcessorTest::StoreStatusAndBuffer,
191 base::Unretained(this)));
192 message_loop_.RunUntilIdle();
193 EXPECT_TRUE(read_callback_called_);
194 } while (++loop_count < 2 &&
195 last_read_status_ == DemuxerStream::kAborted);
197 ASSERT_FALSE(last_read_status_ == DemuxerStream::kAborted)
198 << "2 kAborted reads in a row. Giving up.";
199 EXPECT_EQ(DemuxerStream::kOk, last_read_status_);
200 EXPECT_FALSE(last_read_buffer_->end_of_stream());
205 int time_in_ms = last_read_buffer_->timestamp().InMilliseconds();
208 // Decode the original_time_in_ms from the buffer's data.
209 double original_time_in_ms;
210 ASSERT_EQ(static_cast<int>(sizeof(original_time_in_ms)),
211 last_read_buffer_->data_size());
212 original_time_in_ms = *(reinterpret_cast<const double*>(
213 last_read_buffer_->data()));
214 if (original_time_in_ms != time_in_ms)
215 ss << ":" << original_time_in_ms;
217 // Detect full-discard preroll buffer.
218 if (last_read_buffer_->discard_padding().first == kInfiniteDuration() &&
219 last_read_buffer_->discard_padding().second == base::TimeDelta()) {
224 EXPECT_EQ(expected, ss.str());
225 CheckReadStalls(stream);
228 base::MessageLoop message_loop_;
229 StrictMock<FrameProcessorTestCallbackHelper> callbacks_;
231 scoped_ptr<FrameProcessor> frame_processor_;
232 base::TimeDelta append_window_start_;
233 base::TimeDelta append_window_end_;
234 bool new_media_segment_;
235 base::TimeDelta timestamp_offset_;
236 scoped_ptr<ChunkDemuxerStream> audio_;
237 scoped_ptr<ChunkDemuxerStream> video_;
238 const TrackId audio_id_;
239 const TrackId video_id_;
240 const base::TimeDelta frame_duration_; // Currently the same for all streams.
241 const BufferQueue empty_queue_;
242 const TextBufferQueueMap empty_text_buffers_;
244 // StoreStatusAndBuffer's most recent result.
245 DemuxerStream::Status last_read_status_;
246 scoped_refptr<DecoderBuffer> last_read_buffer_;
247 bool read_callback_called_;
250 void StoreStatusAndBuffer(DemuxerStream::Status status,
251 const scoped_refptr<DecoderBuffer>& buffer) {
252 if (status == DemuxerStream::kOk && buffer) {
253 DVLOG(3) << __FUNCTION__ << "status: " << status << " ts: "
254 << buffer->timestamp().InSecondsF();
256 DVLOG(3) << __FUNCTION__ << "status: " << status << " ts: n/a";
259 read_callback_called_ = true;
260 last_read_status_ = status;
261 last_read_buffer_ = buffer;
264 void CreateAndConfigureStream(DemuxerStream::Type type) {
265 // TODO(wolenetz/dalecurtis): Also test with splicing disabled?
267 case DemuxerStream::AUDIO: {
268 ASSERT_FALSE(audio_);
269 audio_.reset(new ChunkDemuxerStream(DemuxerStream::AUDIO, true));
270 AudioDecoderConfig decoder_config(kCodecVorbis,
271 kSampleFormatPlanarF32,
272 CHANNEL_LAYOUT_STEREO,
277 frame_processor_->OnPossibleAudioConfigUpdate(decoder_config);
279 audio_->UpdateAudioConfig(decoder_config, base::Bind(&LogFunc)));
282 case DemuxerStream::VIDEO: {
283 ASSERT_FALSE(video_);
284 video_.reset(new ChunkDemuxerStream(DemuxerStream::VIDEO, true));
285 ASSERT_TRUE(video_->UpdateVideoConfig(TestVideoConfig::Normal(),
286 base::Bind(&LogFunc)));
289 // TODO(wolenetz): Test text coded frame processing.
290 case DemuxerStream::TEXT:
291 case DemuxerStream::UNKNOWN:
292 case DemuxerStream::NUM_TYPES: {
298 DISALLOW_COPY_AND_ASSIGN(FrameProcessorTest);
301 TEST_F(FrameProcessorTest, WrongTypeInAppendedBuffer) {
302 AddTestTracks(HAS_AUDIO);
303 new_media_segment_ = true;
305 ASSERT_FALSE(frame_processor_->ProcessFrames(
306 StringToBufferQueue("0K", audio_id_, DemuxerStream::VIDEO),
309 append_window_start_, append_window_end_,
310 &new_media_segment_, ×tamp_offset_));
311 EXPECT_TRUE(new_media_segment_);
312 EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
313 CheckExpectedRangesByTimestamp(audio_.get(), "{ }");
314 CheckReadStalls(audio_.get());
317 TEST_F(FrameProcessorTest, NonMonotonicallyIncreasingTimestampInOneCall) {
318 AddTestTracks(HAS_AUDIO);
319 new_media_segment_ = true;
321 ASSERT_FALSE(frame_processor_->ProcessFrames(
322 StringToBufferQueue("10K 0K", audio_id_, DemuxerStream::AUDIO),
325 append_window_start_, append_window_end_,
326 &new_media_segment_, ×tamp_offset_));
327 EXPECT_TRUE(new_media_segment_);
328 EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
329 CheckExpectedRangesByTimestamp(audio_.get(), "{ }");
330 CheckReadStalls(audio_.get());
333 TEST_P(FrameProcessorTest, AudioOnly_SingleFrame) {
334 // Tests A: P(A) -> (a)
336 AddTestTracks(HAS_AUDIO);
337 new_media_segment_ = true;
339 frame_processor_->SetSequenceMode(true);
341 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_));
342 ProcessFrames("0K", "");
343 EXPECT_FALSE(new_media_segment_);
344 EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
345 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,10) }");
346 CheckReadsThenReadStalls(audio_.get(), "0");
349 TEST_P(FrameProcessorTest, VideoOnly_SingleFrame) {
350 // Tests V: P(V) -> (v)
352 AddTestTracks(HAS_VIDEO);
353 new_media_segment_ = true;
355 frame_processor_->SetSequenceMode(true);
357 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_));
358 ProcessFrames("", "0K");
359 EXPECT_FALSE(new_media_segment_);
360 EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
361 CheckExpectedRangesByTimestamp(video_.get(), "{ [0,10) }");
362 CheckReadsThenReadStalls(video_.get(), "0");
365 TEST_P(FrameProcessorTest, AudioOnly_TwoFrames) {
366 // Tests A: P(A0, A10) -> (a0, a10)
368 AddTestTracks(HAS_AUDIO);
369 new_media_segment_ = true;
371 frame_processor_->SetSequenceMode(true);
373 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2));
374 ProcessFrames("0K 10K", "");
375 EXPECT_FALSE(new_media_segment_);
376 EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
377 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }");
378 CheckReadsThenReadStalls(audio_.get(), "0 10");
381 TEST_P(FrameProcessorTest, AudioOnly_SetOffsetThenSingleFrame) {
382 // Tests A: STSO(50)+P(A0) -> TSO==50,(a0@50)
384 AddTestTracks(HAS_AUDIO);
385 new_media_segment_ = true;
387 frame_processor_->SetSequenceMode(true);
389 const base::TimeDelta fifty_ms = base::TimeDelta::FromMilliseconds(50);
390 SetTimestampOffset(fifty_ms);
391 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ + fifty_ms));
392 ProcessFrames("0K", "");
393 EXPECT_FALSE(new_media_segment_);
394 EXPECT_EQ(fifty_ms, timestamp_offset_);
395 CheckExpectedRangesByTimestamp(audio_.get(), "{ [50,60) }");
397 // We do not stall on reading without seeking to 50ms due to
398 // SourceBufferStream::kSeekToStartFudgeRoom().
399 CheckReadsThenReadStalls(audio_.get(), "50:0");
402 TEST_P(FrameProcessorTest, AudioOnly_SetOffsetThenFrameTimestampBelowOffset) {
403 // Tests A: STSO(50)+P(A20) ->
404 // if sequence mode: TSO==30,(a20@50)
405 // if segments mode: TSO==50,(a20@70)
407 AddTestTracks(HAS_AUDIO);
408 new_media_segment_ = true;
409 bool using_sequence_mode = GetParam();
410 if (using_sequence_mode)
411 frame_processor_->SetSequenceMode(true);
413 const base::TimeDelta fifty_ms = base::TimeDelta::FromMilliseconds(50);
414 const base::TimeDelta twenty_ms = base::TimeDelta::FromMilliseconds(20);
415 SetTimestampOffset(fifty_ms);
417 if (using_sequence_mode) {
418 EXPECT_CALL(callbacks_, PossibleDurationIncrease(
419 fifty_ms + frame_duration_));
421 EXPECT_CALL(callbacks_, PossibleDurationIncrease(
422 fifty_ms + twenty_ms + frame_duration_));
425 ProcessFrames("20K", "");
426 EXPECT_FALSE(new_media_segment_);
428 // We do not stall on reading without seeking to 50ms / 70ms due to
429 // SourceBufferStream::kSeekToStartFudgeRoom().
430 if (using_sequence_mode) {
431 EXPECT_EQ(fifty_ms - twenty_ms, timestamp_offset_);
432 CheckExpectedRangesByTimestamp(audio_.get(), "{ [50,60) }");
433 CheckReadsThenReadStalls(audio_.get(), "50:20");
435 EXPECT_EQ(fifty_ms, timestamp_offset_);
436 CheckExpectedRangesByTimestamp(audio_.get(), "{ [70,80) }");
437 CheckReadsThenReadStalls(audio_.get(), "70:20");
441 TEST_P(FrameProcessorTest, AudioOnly_SequentialProcessFrames) {
442 // Tests A: P(A0,A10)+P(A20,A30) -> (a0,a10,a20,a30)
444 AddTestTracks(HAS_AUDIO);
445 new_media_segment_ = true;
447 frame_processor_->SetSequenceMode(true);
449 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2));
450 ProcessFrames("0K 10K", "");
451 EXPECT_FALSE(new_media_segment_);
452 EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
453 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }");
455 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 4));
456 ProcessFrames("20K 30K", "");
457 EXPECT_FALSE(new_media_segment_);
458 EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
459 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,40) }");
461 CheckReadsThenReadStalls(audio_.get(), "0 10 20 30");
464 TEST_P(FrameProcessorTest, AudioOnly_NonSequentialProcessFrames) {
465 // Tests A: P(A20,A30)+P(A0,A10) ->
466 // if sequence mode: TSO==-20 after first P(), 20 after second P(), and
467 // a(20@0,a30@10,a0@20,a10@30)
468 // if segments mode: TSO==0,(a0,a10,a20,a30)
470 AddTestTracks(HAS_AUDIO);
471 new_media_segment_ = true;
472 bool using_sequence_mode = GetParam();
473 if (using_sequence_mode) {
474 frame_processor_->SetSequenceMode(true);
475 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2));
477 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 4));
480 ProcessFrames("20K 30K", "");
481 EXPECT_FALSE(new_media_segment_);
483 if (using_sequence_mode) {
484 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }");
485 EXPECT_EQ(frame_duration_ * -2, timestamp_offset_);
486 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 4));
488 CheckExpectedRangesByTimestamp(audio_.get(), "{ [20,40) }");
489 EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
490 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2));
493 ProcessFrames("0K 10K", "");
494 EXPECT_FALSE(new_media_segment_);
496 if (using_sequence_mode) {
497 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,40) }");
498 EXPECT_EQ(frame_duration_ * 2, timestamp_offset_);
499 CheckReadsThenReadStalls(audio_.get(), "0:20 10:30 20:0 30:10");
501 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,40) }");
502 EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
503 // TODO(wolenetz): Fix this need to seek to 0ms, possibly by having
504 // SourceBufferStream defer initial seek until next read. See
505 // http://crbug.com/371493.
506 audio_->AbortReads();
507 audio_->Seek(base::TimeDelta());
508 audio_->StartReturningData();
509 CheckReadsThenReadStalls(audio_.get(), "0 10 20 30");
513 TEST_P(FrameProcessorTest, AudioVideo_SequentialProcessFrames) {
514 // Tests AV: P(A0,A10;V0k,V10,V20)+P(A20,A30,A40,V30) ->
515 // (a0,a10,a20,a30,a40);(v0,v10,v20,v30)
517 AddTestTracks(HAS_AUDIO | HAS_VIDEO);
518 new_media_segment_ = true;
520 frame_processor_->SetSequenceMode(true);
522 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 3));
523 ProcessFrames("0K 10K", "0K 10 20");
524 EXPECT_FALSE(new_media_segment_);
525 EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
526 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }");
527 CheckExpectedRangesByTimestamp(video_.get(), "{ [0,30) }");
529 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 5));
530 ProcessFrames("20K 30K 40K", "30");
531 EXPECT_FALSE(new_media_segment_);
532 EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
533 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,50) }");
534 CheckExpectedRangesByTimestamp(video_.get(), "{ [0,40) }");
536 CheckReadsThenReadStalls(audio_.get(), "0 10 20 30 40");
537 CheckReadsThenReadStalls(video_.get(), "0 10 20 30");
540 TEST_P(FrameProcessorTest, AudioVideo_Discontinuity) {
541 // Tests AV: P(A0,A10,A30,A40,A50;V0k,V10,V40,V50key) ->
542 // if sequence mode: TSO==10,(a0,a10,a30,a40,a50@60);(v0,v10,v50@60)
543 // if segments mode: TSO==0,(a0,a10,a30,a40,a50);(v0,v10,v50)
544 // This assumes A40K is processed before V40, which depends currently on
545 // MergeBufferQueues() behavior.
547 AddTestTracks(HAS_AUDIO | HAS_VIDEO);
548 new_media_segment_ = true;
549 bool using_sequence_mode = GetParam();
550 if (using_sequence_mode) {
551 frame_processor_->SetSequenceMode(true);
552 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 7));
554 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 6));
557 ProcessFrames("0K 10K 30K 40K 50K", "0K 10 40 50K");
558 EXPECT_FALSE(new_media_segment_);
560 if (using_sequence_mode) {
561 EXPECT_EQ(frame_duration_, timestamp_offset_);
562 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,70) }");
563 CheckExpectedRangesByTimestamp(video_.get(), "{ [0,70) }");
564 CheckReadsThenReadStalls(audio_.get(), "0 10 30 40 60:50");
565 CheckReadsThenReadStalls(video_.get(), "0 10 60:50");
567 EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
568 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,60) }");
569 CheckExpectedRangesByTimestamp(video_.get(), "{ [0,20) [50,60) }");
570 CheckReadsThenReadStalls(audio_.get(), "0 10 30 40 50");
571 CheckReadsThenReadStalls(video_.get(), "0 10");
572 video_->AbortReads();
573 video_->Seek(frame_duration_ * 5);
574 video_->StartReturningData();
575 CheckReadsThenReadStalls(video_.get(), "50");
579 TEST_P(FrameProcessorTest,
580 AppendWindowFilterOfNegativeBufferTimestampsWithPrerollDiscard) {
582 AddTestTracks(HAS_AUDIO);
583 new_media_segment_ = true;
585 frame_processor_->SetSequenceMode(true);
587 SetTimestampOffset(frame_duration_ * -2);
588 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_));
589 ProcessFrames("0K 10K 20K", "");
590 EXPECT_FALSE(new_media_segment_);
591 EXPECT_EQ(frame_duration_ * -2, timestamp_offset_);
592 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,10) }");
593 CheckReadsThenReadStalls(audio_.get(), "0:10P 0:20");
596 TEST_P(FrameProcessorTest, AppendWindowFilterWithInexactPreroll) {
598 AddTestTracks(HAS_AUDIO);
599 new_media_segment_ = true;
601 frame_processor_->SetSequenceMode(true);
602 SetTimestampOffset(-frame_duration_);
603 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2));
604 ProcessFrames("0K 9.75K 20K", "");
605 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }");
606 CheckReadsThenReadStalls(audio_.get(), "0P 0:9.75 10:20");
609 TEST_P(FrameProcessorTest, AppendWindowFilterWithInexactPreroll_2) {
611 AddTestTracks(HAS_AUDIO);
612 new_media_segment_ = true;
614 frame_processor_->SetSequenceMode(true);
615 SetTimestampOffset(-frame_duration_);
616 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2));
617 ProcessFrames("0K 10.25K 20K", "");
618 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }");
619 CheckReadsThenReadStalls(audio_.get(), "0P 0:10.25 10:20");
622 TEST_P(FrameProcessorTest, AllowNegativeFramePTSAndDTSBeforeOffsetAdjustment) {
624 AddTestTracks(HAS_AUDIO);
625 new_media_segment_ = true;
626 bool using_sequence_mode = GetParam();
627 if (using_sequence_mode) {
628 frame_processor_->SetSequenceMode(true);
629 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 3));
631 EXPECT_CALL(callbacks_,
632 PossibleDurationIncrease((frame_duration_ * 5) / 2));
635 ProcessFrames("-5K 5K 15K", "");
637 if (using_sequence_mode) {
638 EXPECT_EQ(frame_duration_ / 2, timestamp_offset_);
639 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,30) }");
640 CheckReadsThenReadStalls(audio_.get(), "0:-5 10:5 20:15");
642 EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
643 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,25) }");
644 CheckReadsThenReadStalls(audio_.get(), "0:-5 5 15");
648 TEST_P(FrameProcessorTest, PartialAppendWindowFilterNoDiscontinuity) {
649 // Tests that spurious discontinuity is not introduced by a partially
652 AddTestTracks(HAS_AUDIO);
653 new_media_segment_ = true;
655 frame_processor_->SetSequenceMode(true);
656 EXPECT_CALL(callbacks_,
657 PossibleDurationIncrease(base::TimeDelta::FromMilliseconds(29)));
659 append_window_start_ = base::TimeDelta::FromMilliseconds(7);
660 ProcessFrames("0K 19K", "");
662 EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
663 CheckExpectedRangesByTimestamp(audio_.get(), "{ [7,29) }");
664 CheckReadsThenReadStalls(audio_.get(), "7:0 19");
667 INSTANTIATE_TEST_CASE_P(SequenceMode, FrameProcessorTest, Values(true));
668 INSTANTIATE_TEST_CASE_P(SegmentsMode, FrameProcessorTest, Values(false));