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. 4 5 #include <map> 6 #include <string> 7 8 #include "base/bind.h" 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" 19 20 using ::testing::InSequence; 21 using ::testing::StrictMock; 22 using ::testing::Values; 23 24 namespace media { 25 26 typedef StreamParser::BufferQueue BufferQueue; 27 typedef StreamParser::TextBufferQueueMap TextBufferQueueMap; 28 typedef StreamParser::TrackId TrackId; 29 30 static void LogFunc(const std::string& str) { DVLOG(1) << str; } 31 32 // Used for setting expectations on callbacks. Using a StrictMock also lets us 33 // test for missing or extra callbacks. 34 class FrameProcessorTestCallbackHelper { 35 public: 36 FrameProcessorTestCallbackHelper() {} 37 virtual ~FrameProcessorTestCallbackHelper() {} 38 39 MOCK_METHOD1(PossibleDurationIncrease, void(base::TimeDelta new_duration)); 40 41 // Helper that calls the mock method as well as does basic sanity checks on 42 // |new_duration|. 43 void OnPossibleDurationIncrease(base::TimeDelta new_duration) { 44 PossibleDurationIncrease(new_duration); 45 ASSERT_NE(kNoTimestamp(), new_duration); 46 ASSERT_NE(kInfiniteDuration(), new_duration); 47 } 48 49 private: 50 DISALLOW_COPY_AND_ASSIGN(FrameProcessorTestCallbackHelper); 51 }; 52 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> { 56 protected: 57 FrameProcessorTest() 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)) { 66 } 67 68 enum StreamFlags { 69 HAS_AUDIO = 1 << 0, 70 HAS_VIDEO = 1 << 1 71 }; 72 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); 77 78 if (has_audio) { 79 CreateAndConfigureStream(DemuxerStream::AUDIO); 80 ASSERT_TRUE(audio_); 81 EXPECT_TRUE(frame_processor_->AddTrack(audio_id_, audio_.get())); 82 audio_->Seek(base::TimeDelta()); 83 audio_->StartReturningData(); 84 } 85 if (has_video) { 86 CreateAndConfigureStream(DemuxerStream::VIDEO); 87 ASSERT_TRUE(video_); 88 EXPECT_TRUE(frame_processor_->AddTrack(video_id_, video_.get())); 89 video_->Seek(base::TimeDelta()); 90 video_->StartReturningData(); 91 } 92 } 93 94 void SetTimestampOffset(base::TimeDelta new_offset) { 95 timestamp_offset_ = new_offset; 96 frame_processor_->SetGroupStartTimestampIfInSequenceMode(timestamp_offset_); 97 } 98 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); 104 105 BufferQueue buffers; 106 for (size_t i = 0; i < timestamps.size(); i++) { 107 bool is_keyframe = false; 108 if (EndsWith(timestamps[i], "K", true)) { 109 is_keyframe = true; 110 // Remove the "K" off of the token. 111 timestamps[i] = timestamps[i].substr(0, timestamps[i].length() - 1); 112 } 113 114 double time_in_ms; 115 CHECK(base::StringToDouble(timestamps[i], &time_in_ms)); 116 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->SetDecodeTimestamp(timestamp); 128 buffer->set_duration(frame_duration_); 129 buffers.push_back(buffer); 130 } 131 return buffers; 132 } 133 134 void ProcessFrames(const std::string& audio_timestamps, 135 const std::string& video_timestamps) { 136 ASSERT_TRUE(frame_processor_->ProcessFrames( 137 StringToBufferQueue(audio_timestamps, audio_id_, DemuxerStream::AUDIO), 138 StringToBufferQueue(video_timestamps, video_id_, DemuxerStream::VIDEO), 139 empty_text_buffers_, 140 append_window_start_, append_window_end_, 141 &new_media_segment_, ×tamp_offset_)); 142 } 143 144 void CheckExpectedRangesByTimestamp(ChunkDemuxerStream* stream, 145 const std::string& expected) { 146 // Note, DemuxerStream::TEXT streams return [0,duration (==infinity here)) 147 Ranges<base::TimeDelta> r = stream->GetBufferedRanges(kInfiniteDuration()); 148 149 std::stringstream ss; 150 ss << "{ "; 151 for (size_t i = 0; i < r.size(); ++i) { 152 int64 start = r.start(i).InMilliseconds(); 153 int64 end = r.end(i).InMilliseconds(); 154 ss << "[" << start << "," << end << ") "; 155 } 156 ss << "}"; 157 EXPECT_EQ(expected, ss.str()); 158 } 159 160 void CheckReadStalls(ChunkDemuxerStream* stream) { 161 int loop_count = 0; 162 163 do { 164 read_callback_called_ = false; 165 stream->Read(base::Bind(&FrameProcessorTest::StoreStatusAndBuffer, 166 base::Unretained(this))); 167 message_loop_.RunUntilIdle(); 168 } while (++loop_count < 2 && read_callback_called_ && 169 last_read_status_ == DemuxerStream::kAborted); 170 171 ASSERT_FALSE(read_callback_called_ && 172 last_read_status_ == DemuxerStream::kAborted) 173 << "2 kAborted reads in a row. Giving up."; 174 EXPECT_FALSE(read_callback_called_); 175 } 176 177 // Format of |expected| is a space-delimited sequence of 178 // timestamp_in_ms:original_timestamp_in_ms 179 // original_timestamp_in_ms (and the colon) must be omitted if it is the same 180 // as timestamp_in_ms. 181 void CheckReadsThenReadStalls(ChunkDemuxerStream* stream, 182 const std::string& expected) { 183 std::vector<std::string> timestamps; 184 base::SplitString(expected, ' ', ×tamps); 185 std::stringstream ss; 186 for (size_t i = 0; i < timestamps.size(); ++i) { 187 int loop_count = 0; 188 189 do { 190 read_callback_called_ = false; 191 stream->Read(base::Bind(&FrameProcessorTest::StoreStatusAndBuffer, 192 base::Unretained(this))); 193 message_loop_.RunUntilIdle(); 194 EXPECT_TRUE(read_callback_called_); 195 } while (++loop_count < 2 && 196 last_read_status_ == DemuxerStream::kAborted); 197 198 ASSERT_FALSE(last_read_status_ == DemuxerStream::kAborted) 199 << "2 kAborted reads in a row. Giving up."; 200 EXPECT_EQ(DemuxerStream::kOk, last_read_status_); 201 EXPECT_FALSE(last_read_buffer_->end_of_stream()); 202 203 if (i > 0) 204 ss << " "; 205 206 int time_in_ms = last_read_buffer_->timestamp().InMilliseconds(); 207 ss << time_in_ms; 208 209 // Decode the original_time_in_ms from the buffer's data. 210 double original_time_in_ms; 211 ASSERT_EQ(static_cast<int>(sizeof(original_time_in_ms)), 212 last_read_buffer_->data_size()); 213 original_time_in_ms = *(reinterpret_cast<const double*>( 214 last_read_buffer_->data())); 215 if (original_time_in_ms != time_in_ms) 216 ss << ":" << original_time_in_ms; 217 218 // Detect full-discard preroll buffer. 219 if (last_read_buffer_->discard_padding().first == kInfiniteDuration() && 220 last_read_buffer_->discard_padding().second == base::TimeDelta()) { 221 ss << "P"; 222 } 223 } 224 225 EXPECT_EQ(expected, ss.str()); 226 CheckReadStalls(stream); 227 } 228 229 base::MessageLoop message_loop_; 230 StrictMock<FrameProcessorTestCallbackHelper> callbacks_; 231 232 scoped_ptr<FrameProcessor> frame_processor_; 233 base::TimeDelta append_window_start_; 234 base::TimeDelta append_window_end_; 235 bool new_media_segment_; 236 base::TimeDelta timestamp_offset_; 237 scoped_ptr<ChunkDemuxerStream> audio_; 238 scoped_ptr<ChunkDemuxerStream> video_; 239 const TrackId audio_id_; 240 const TrackId video_id_; 241 const base::TimeDelta frame_duration_; // Currently the same for all streams. 242 const BufferQueue empty_queue_; 243 const TextBufferQueueMap empty_text_buffers_; 244 245 // StoreStatusAndBuffer's most recent result. 246 DemuxerStream::Status last_read_status_; 247 scoped_refptr<DecoderBuffer> last_read_buffer_; 248 bool read_callback_called_; 249 250 private: 251 void StoreStatusAndBuffer(DemuxerStream::Status status, 252 const scoped_refptr<DecoderBuffer>& buffer) { 253 if (status == DemuxerStream::kOk && buffer) { 254 DVLOG(3) << __FUNCTION__ << "status: " << status << " ts: " 255 << buffer->timestamp().InSecondsF(); 256 } else { 257 DVLOG(3) << __FUNCTION__ << "status: " << status << " ts: n/a"; 258 } 259 260 read_callback_called_ = true; 261 last_read_status_ = status; 262 last_read_buffer_ = buffer; 263 } 264 265 void CreateAndConfigureStream(DemuxerStream::Type type) { 266 // TODO(wolenetz/dalecurtis): Also test with splicing disabled? 267 switch (type) { 268 case DemuxerStream::AUDIO: { 269 ASSERT_FALSE(audio_); 270 audio_.reset(new ChunkDemuxerStream(DemuxerStream::AUDIO, true)); 271 AudioDecoderConfig decoder_config(kCodecVorbis, 272 kSampleFormatPlanarF32, 273 CHANNEL_LAYOUT_STEREO, 274 1000, 275 NULL, 276 0, 277 false); 278 frame_processor_->OnPossibleAudioConfigUpdate(decoder_config); 279 ASSERT_TRUE( 280 audio_->UpdateAudioConfig(decoder_config, base::Bind(&LogFunc))); 281 break; 282 } 283 case DemuxerStream::VIDEO: { 284 ASSERT_FALSE(video_); 285 video_.reset(new ChunkDemuxerStream(DemuxerStream::VIDEO, true)); 286 ASSERT_TRUE(video_->UpdateVideoConfig(TestVideoConfig::Normal(), 287 base::Bind(&LogFunc))); 288 break; 289 } 290 // TODO(wolenetz): Test text coded frame processing. 291 case DemuxerStream::TEXT: 292 case DemuxerStream::UNKNOWN: 293 case DemuxerStream::NUM_TYPES: { 294 ASSERT_FALSE(true); 295 } 296 } 297 } 298 299 DISALLOW_COPY_AND_ASSIGN(FrameProcessorTest); 300 }; 301 302 TEST_F(FrameProcessorTest, WrongTypeInAppendedBuffer) { 303 AddTestTracks(HAS_AUDIO); 304 new_media_segment_ = true; 305 306 ASSERT_FALSE(frame_processor_->ProcessFrames( 307 StringToBufferQueue("0K", audio_id_, DemuxerStream::VIDEO), 308 empty_queue_, 309 empty_text_buffers_, 310 append_window_start_, append_window_end_, 311 &new_media_segment_, ×tamp_offset_)); 312 EXPECT_TRUE(new_media_segment_); 313 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 314 CheckExpectedRangesByTimestamp(audio_.get(), "{ }"); 315 CheckReadStalls(audio_.get()); 316 } 317 318 TEST_F(FrameProcessorTest, NonMonotonicallyIncreasingTimestampInOneCall) { 319 AddTestTracks(HAS_AUDIO); 320 new_media_segment_ = true; 321 322 ASSERT_FALSE(frame_processor_->ProcessFrames( 323 StringToBufferQueue("10K 0K", audio_id_, DemuxerStream::AUDIO), 324 empty_queue_, 325 empty_text_buffers_, 326 append_window_start_, append_window_end_, 327 &new_media_segment_, ×tamp_offset_)); 328 EXPECT_TRUE(new_media_segment_); 329 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 330 CheckExpectedRangesByTimestamp(audio_.get(), "{ }"); 331 CheckReadStalls(audio_.get()); 332 } 333 334 TEST_P(FrameProcessorTest, AudioOnly_SingleFrame) { 335 // Tests A: P(A) -> (a) 336 InSequence s; 337 AddTestTracks(HAS_AUDIO); 338 new_media_segment_ = true; 339 if (GetParam()) 340 frame_processor_->SetSequenceMode(true); 341 342 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_)); 343 ProcessFrames("0K", ""); 344 EXPECT_FALSE(new_media_segment_); 345 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 346 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,10) }"); 347 CheckReadsThenReadStalls(audio_.get(), "0"); 348 } 349 350 TEST_P(FrameProcessorTest, VideoOnly_SingleFrame) { 351 // Tests V: P(V) -> (v) 352 InSequence s; 353 AddTestTracks(HAS_VIDEO); 354 new_media_segment_ = true; 355 if (GetParam()) 356 frame_processor_->SetSequenceMode(true); 357 358 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_)); 359 ProcessFrames("", "0K"); 360 EXPECT_FALSE(new_media_segment_); 361 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 362 CheckExpectedRangesByTimestamp(video_.get(), "{ [0,10) }"); 363 CheckReadsThenReadStalls(video_.get(), "0"); 364 } 365 366 TEST_P(FrameProcessorTest, AudioOnly_TwoFrames) { 367 // Tests A: P(A0, A10) -> (a0, a10) 368 InSequence s; 369 AddTestTracks(HAS_AUDIO); 370 new_media_segment_ = true; 371 if (GetParam()) 372 frame_processor_->SetSequenceMode(true); 373 374 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2)); 375 ProcessFrames("0K 10K", ""); 376 EXPECT_FALSE(new_media_segment_); 377 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 378 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }"); 379 CheckReadsThenReadStalls(audio_.get(), "0 10"); 380 } 381 382 TEST_P(FrameProcessorTest, AudioOnly_SetOffsetThenSingleFrame) { 383 // Tests A: STSO(50)+P(A0) -> TSO==50,(a0@50) 384 InSequence s; 385 AddTestTracks(HAS_AUDIO); 386 new_media_segment_ = true; 387 if (GetParam()) 388 frame_processor_->SetSequenceMode(true); 389 390 const base::TimeDelta fifty_ms = base::TimeDelta::FromMilliseconds(50); 391 SetTimestampOffset(fifty_ms); 392 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ + fifty_ms)); 393 ProcessFrames("0K", ""); 394 EXPECT_FALSE(new_media_segment_); 395 EXPECT_EQ(fifty_ms, timestamp_offset_); 396 CheckExpectedRangesByTimestamp(audio_.get(), "{ [50,60) }"); 397 398 // We do not stall on reading without seeking to 50ms due to 399 // SourceBufferStream::kSeekToStartFudgeRoom(). 400 CheckReadsThenReadStalls(audio_.get(), "50:0"); 401 } 402 403 TEST_P(FrameProcessorTest, AudioOnly_SetOffsetThenFrameTimestampBelowOffset) { 404 // Tests A: STSO(50)+P(A20) -> 405 // if sequence mode: TSO==30,(a20@50) 406 // if segments mode: TSO==50,(a20@70) 407 InSequence s; 408 AddTestTracks(HAS_AUDIO); 409 new_media_segment_ = true; 410 bool using_sequence_mode = GetParam(); 411 if (using_sequence_mode) 412 frame_processor_->SetSequenceMode(true); 413 414 const base::TimeDelta fifty_ms = base::TimeDelta::FromMilliseconds(50); 415 const base::TimeDelta twenty_ms = base::TimeDelta::FromMilliseconds(20); 416 SetTimestampOffset(fifty_ms); 417 418 if (using_sequence_mode) { 419 EXPECT_CALL(callbacks_, PossibleDurationIncrease( 420 fifty_ms + frame_duration_)); 421 } else { 422 EXPECT_CALL(callbacks_, PossibleDurationIncrease( 423 fifty_ms + twenty_ms + frame_duration_)); 424 } 425 426 ProcessFrames("20K", ""); 427 EXPECT_FALSE(new_media_segment_); 428 429 // We do not stall on reading without seeking to 50ms / 70ms due to 430 // SourceBufferStream::kSeekToStartFudgeRoom(). 431 if (using_sequence_mode) { 432 EXPECT_EQ(fifty_ms - twenty_ms, timestamp_offset_); 433 CheckExpectedRangesByTimestamp(audio_.get(), "{ [50,60) }"); 434 CheckReadsThenReadStalls(audio_.get(), "50:20"); 435 } else { 436 EXPECT_EQ(fifty_ms, timestamp_offset_); 437 CheckExpectedRangesByTimestamp(audio_.get(), "{ [70,80) }"); 438 CheckReadsThenReadStalls(audio_.get(), "70:20"); 439 } 440 } 441 442 TEST_P(FrameProcessorTest, AudioOnly_SequentialProcessFrames) { 443 // Tests A: P(A0,A10)+P(A20,A30) -> (a0,a10,a20,a30) 444 InSequence s; 445 AddTestTracks(HAS_AUDIO); 446 new_media_segment_ = true; 447 if (GetParam()) 448 frame_processor_->SetSequenceMode(true); 449 450 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2)); 451 ProcessFrames("0K 10K", ""); 452 EXPECT_FALSE(new_media_segment_); 453 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 454 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }"); 455 456 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 4)); 457 ProcessFrames("20K 30K", ""); 458 EXPECT_FALSE(new_media_segment_); 459 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 460 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,40) }"); 461 462 CheckReadsThenReadStalls(audio_.get(), "0 10 20 30"); 463 } 464 465 TEST_P(FrameProcessorTest, AudioOnly_NonSequentialProcessFrames) { 466 // Tests A: P(A20,A30)+P(A0,A10) -> 467 // if sequence mode: TSO==-20 after first P(), 20 after second P(), and 468 // a(20@0,a30@10,a0@20,a10@30) 469 // if segments mode: TSO==0,(a0,a10,a20,a30) 470 InSequence s; 471 AddTestTracks(HAS_AUDIO); 472 new_media_segment_ = true; 473 bool using_sequence_mode = GetParam(); 474 if (using_sequence_mode) { 475 frame_processor_->SetSequenceMode(true); 476 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2)); 477 } else { 478 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 4)); 479 } 480 481 ProcessFrames("20K 30K", ""); 482 EXPECT_FALSE(new_media_segment_); 483 484 if (using_sequence_mode) { 485 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }"); 486 EXPECT_EQ(frame_duration_ * -2, timestamp_offset_); 487 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 4)); 488 } else { 489 CheckExpectedRangesByTimestamp(audio_.get(), "{ [20,40) }"); 490 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 491 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2)); 492 } 493 494 ProcessFrames("0K 10K", ""); 495 EXPECT_FALSE(new_media_segment_); 496 497 if (using_sequence_mode) { 498 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,40) }"); 499 EXPECT_EQ(frame_duration_ * 2, timestamp_offset_); 500 CheckReadsThenReadStalls(audio_.get(), "0:20 10:30 20:0 30:10"); 501 } else { 502 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,40) }"); 503 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 504 // TODO(wolenetz): Fix this need to seek to 0ms, possibly by having 505 // SourceBufferStream defer initial seek until next read. See 506 // http://crbug.com/371493. 507 audio_->AbortReads(); 508 audio_->Seek(base::TimeDelta()); 509 audio_->StartReturningData(); 510 CheckReadsThenReadStalls(audio_.get(), "0 10 20 30"); 511 } 512 } 513 514 TEST_P(FrameProcessorTest, AudioVideo_SequentialProcessFrames) { 515 // Tests AV: P(A0,A10;V0k,V10,V20)+P(A20,A30,A40,V30) -> 516 // (a0,a10,a20,a30,a40);(v0,v10,v20,v30) 517 InSequence s; 518 AddTestTracks(HAS_AUDIO | HAS_VIDEO); 519 new_media_segment_ = true; 520 if (GetParam()) 521 frame_processor_->SetSequenceMode(true); 522 523 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 3)); 524 ProcessFrames("0K 10K", "0K 10 20"); 525 EXPECT_FALSE(new_media_segment_); 526 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 527 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }"); 528 CheckExpectedRangesByTimestamp(video_.get(), "{ [0,30) }"); 529 530 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 5)); 531 ProcessFrames("20K 30K 40K", "30"); 532 EXPECT_FALSE(new_media_segment_); 533 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 534 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,50) }"); 535 CheckExpectedRangesByTimestamp(video_.get(), "{ [0,40) }"); 536 537 CheckReadsThenReadStalls(audio_.get(), "0 10 20 30 40"); 538 CheckReadsThenReadStalls(video_.get(), "0 10 20 30"); 539 } 540 541 TEST_P(FrameProcessorTest, AudioVideo_Discontinuity) { 542 // Tests AV: P(A0,A10,A30,A40,A50;V0k,V10,V40,V50key) -> 543 // if sequence mode: TSO==10,(a0,a10,a30,a40,a50@60);(v0,v10,v50@60) 544 // if segments mode: TSO==0,(a0,a10,a30,a40,a50);(v0,v10,v50) 545 // This assumes A40K is processed before V40, which depends currently on 546 // MergeBufferQueues() behavior. 547 InSequence s; 548 AddTestTracks(HAS_AUDIO | HAS_VIDEO); 549 new_media_segment_ = true; 550 bool using_sequence_mode = GetParam(); 551 if (using_sequence_mode) { 552 frame_processor_->SetSequenceMode(true); 553 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 7)); 554 } else { 555 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 6)); 556 } 557 558 ProcessFrames("0K 10K 30K 40K 50K", "0K 10 40 50K"); 559 EXPECT_FALSE(new_media_segment_); 560 561 if (using_sequence_mode) { 562 EXPECT_EQ(frame_duration_, timestamp_offset_); 563 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,70) }"); 564 CheckExpectedRangesByTimestamp(video_.get(), "{ [0,70) }"); 565 CheckReadsThenReadStalls(audio_.get(), "0 10 30 40 60:50"); 566 CheckReadsThenReadStalls(video_.get(), "0 10 60:50"); 567 } else { 568 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 569 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,60) }"); 570 CheckExpectedRangesByTimestamp(video_.get(), "{ [0,20) [50,60) }"); 571 CheckReadsThenReadStalls(audio_.get(), "0 10 30 40 50"); 572 CheckReadsThenReadStalls(video_.get(), "0 10"); 573 video_->AbortReads(); 574 video_->Seek(frame_duration_ * 5); 575 video_->StartReturningData(); 576 CheckReadsThenReadStalls(video_.get(), "50"); 577 } 578 } 579 580 TEST_P(FrameProcessorTest, 581 AppendWindowFilterOfNegativeBufferTimestampsWithPrerollDiscard) { 582 InSequence s; 583 AddTestTracks(HAS_AUDIO); 584 new_media_segment_ = true; 585 if (GetParam()) 586 frame_processor_->SetSequenceMode(true); 587 588 SetTimestampOffset(frame_duration_ * -2); 589 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_)); 590 ProcessFrames("0K 10K 20K", ""); 591 EXPECT_FALSE(new_media_segment_); 592 EXPECT_EQ(frame_duration_ * -2, timestamp_offset_); 593 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,10) }"); 594 CheckReadsThenReadStalls(audio_.get(), "0:10P 0:20"); 595 } 596 597 TEST_P(FrameProcessorTest, AppendWindowFilterWithInexactPreroll) { 598 InSequence s; 599 AddTestTracks(HAS_AUDIO); 600 new_media_segment_ = true; 601 if (GetParam()) 602 frame_processor_->SetSequenceMode(true); 603 SetTimestampOffset(-frame_duration_); 604 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2)); 605 ProcessFrames("0K 9.75K 20K", ""); 606 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,20) }"); 607 CheckReadsThenReadStalls(audio_.get(), "0P 0:9.75 10:20"); 608 } 609 610 TEST_P(FrameProcessorTest, AllowNegativeFramePTSAndDTSBeforeOffsetAdjustment) { 611 InSequence s; 612 AddTestTracks(HAS_AUDIO); 613 new_media_segment_ = true; 614 bool using_sequence_mode = GetParam(); 615 if (using_sequence_mode) { 616 frame_processor_->SetSequenceMode(true); 617 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 3)); 618 } else { 619 EXPECT_CALL(callbacks_, 620 PossibleDurationIncrease((frame_duration_ * 5) / 2)); 621 } 622 623 ProcessFrames("-5K 5K 15K", ""); 624 625 if (using_sequence_mode) { 626 EXPECT_EQ(frame_duration_ / 2, timestamp_offset_); 627 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,30) }"); 628 CheckReadsThenReadStalls(audio_.get(), "0:-5 10:5 20:15"); 629 } else { 630 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 631 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,25) }"); 632 CheckReadsThenReadStalls(audio_.get(), "0:-5 5 15"); 633 } 634 } 635 636 TEST_P(FrameProcessorTest, PartialAppendWindowFilterNoDiscontinuity) { 637 // Tests that spurious discontinuity is not introduced by a partially 638 // trimmed frame. 639 InSequence s; 640 AddTestTracks(HAS_AUDIO); 641 new_media_segment_ = true; 642 if (GetParam()) 643 frame_processor_->SetSequenceMode(true); 644 EXPECT_CALL(callbacks_, 645 PossibleDurationIncrease(base::TimeDelta::FromMilliseconds(29))); 646 647 append_window_start_ = base::TimeDelta::FromMilliseconds(7); 648 ProcessFrames("0K 19K", ""); 649 650 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 651 CheckExpectedRangesByTimestamp(audio_.get(), "{ [7,29) }"); 652 CheckReadsThenReadStalls(audio_.get(), "7:0 19"); 653 } 654 655 INSTANTIATE_TEST_CASE_P(SequenceMode, FrameProcessorTest, Values(true)); 656 INSTANTIATE_TEST_CASE_P(SegmentsMode, FrameProcessorTest, Values(false)); 657 658 } // namespace media 659