frame_processor_unittest.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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 20using ::testing::InSequence; 21using ::testing::StrictMock; 22using ::testing::Values; 23 24namespace media { 25 26typedef StreamParser::BufferQueue BufferQueue; 27typedef StreamParser::TextBufferQueueMap TextBufferQueueMap; 28typedef StreamParser::TrackId TrackId; 29 30static 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. 34class 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). 55class 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->set_duration(frame_duration_); 128 buffers.push_back(buffer); 129 } 130 return buffers; 131 } 132 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), 138 empty_text_buffers_, 139 append_window_start_, append_window_end_, 140 &new_media_segment_, ×tamp_offset_)); 141 } 142 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()); 147 148 std::stringstream ss; 149 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 << ") "; 154 } 155 ss << "}"; 156 EXPECT_EQ(expected, ss.str()); 157 } 158 159 void CheckReadStalls(ChunkDemuxerStream* stream) { 160 int loop_count = 0; 161 162 do { 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); 169 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_); 174 } 175 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) { 186 int loop_count = 0; 187 188 do { 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); 196 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()); 201 202 if (i > 0) 203 ss << " "; 204 205 int time_in_ms = last_read_buffer_->timestamp().InMilliseconds(); 206 ss << time_in_ms; 207 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; 216 217 // Detect full-discard preroll buffer. 218 if (last_read_buffer_->discard_padding().first == kInfiniteDuration() && 219 last_read_buffer_->discard_padding().second == base::TimeDelta()) { 220 ss << "P"; 221 } 222 } 223 224 EXPECT_EQ(expected, ss.str()); 225 CheckReadStalls(stream); 226 } 227 228 base::MessageLoop message_loop_; 229 StrictMock<FrameProcessorTestCallbackHelper> callbacks_; 230 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_; 243 244 // StoreStatusAndBuffer's most recent result. 245 DemuxerStream::Status last_read_status_; 246 scoped_refptr<DecoderBuffer> last_read_buffer_; 247 bool read_callback_called_; 248 249 private: 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(); 255 } else { 256 DVLOG(3) << __FUNCTION__ << "status: " << status << " ts: n/a"; 257 } 258 259 read_callback_called_ = true; 260 last_read_status_ = status; 261 last_read_buffer_ = buffer; 262 } 263 264 void CreateAndConfigureStream(DemuxerStream::Type type) { 265 // TODO(wolenetz/dalecurtis): Also test with splicing disabled? 266 switch (type) { 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, 273 1000, 274 NULL, 275 0, 276 false); 277 frame_processor_->OnPossibleAudioConfigUpdate(decoder_config); 278 ASSERT_TRUE( 279 audio_->UpdateAudioConfig(decoder_config, base::Bind(&LogFunc))); 280 break; 281 } 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))); 287 break; 288 } 289 // TODO(wolenetz): Test text coded frame processing. 290 case DemuxerStream::TEXT: 291 case DemuxerStream::UNKNOWN: 292 case DemuxerStream::NUM_TYPES: { 293 ASSERT_FALSE(true); 294 } 295 } 296 } 297 298 DISALLOW_COPY_AND_ASSIGN(FrameProcessorTest); 299}; 300 301TEST_F(FrameProcessorTest, WrongTypeInAppendedBuffer) { 302 AddTestTracks(HAS_AUDIO); 303 new_media_segment_ = true; 304 305 ASSERT_FALSE(frame_processor_->ProcessFrames( 306 StringToBufferQueue("0K", audio_id_, DemuxerStream::VIDEO), 307 empty_queue_, 308 empty_text_buffers_, 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()); 315} 316 317TEST_F(FrameProcessorTest, NonMonotonicallyIncreasingTimestampInOneCall) { 318 AddTestTracks(HAS_AUDIO); 319 new_media_segment_ = true; 320 321 ASSERT_FALSE(frame_processor_->ProcessFrames( 322 StringToBufferQueue("10K 0K", audio_id_, DemuxerStream::AUDIO), 323 empty_queue_, 324 empty_text_buffers_, 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()); 331} 332 333TEST_P(FrameProcessorTest, AudioOnly_SingleFrame) { 334 // Tests A: P(A) -> (a) 335 InSequence s; 336 AddTestTracks(HAS_AUDIO); 337 new_media_segment_ = true; 338 if (GetParam()) 339 frame_processor_->SetSequenceMode(true); 340 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"); 347} 348 349TEST_P(FrameProcessorTest, VideoOnly_SingleFrame) { 350 // Tests V: P(V) -> (v) 351 InSequence s; 352 AddTestTracks(HAS_VIDEO); 353 new_media_segment_ = true; 354 if (GetParam()) 355 frame_processor_->SetSequenceMode(true); 356 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"); 363} 364 365TEST_P(FrameProcessorTest, AudioOnly_TwoFrames) { 366 // Tests A: P(A0, A10) -> (a0, a10) 367 InSequence s; 368 AddTestTracks(HAS_AUDIO); 369 new_media_segment_ = true; 370 if (GetParam()) 371 frame_processor_->SetSequenceMode(true); 372 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"); 379} 380 381TEST_P(FrameProcessorTest, AudioOnly_SetOffsetThenSingleFrame) { 382 // Tests A: STSO(50)+P(A0) -> TSO==50,(a0@50) 383 InSequence s; 384 AddTestTracks(HAS_AUDIO); 385 new_media_segment_ = true; 386 if (GetParam()) 387 frame_processor_->SetSequenceMode(true); 388 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) }"); 396 397 // We do not stall on reading without seeking to 50ms due to 398 // SourceBufferStream::kSeekToStartFudgeRoom(). 399 CheckReadsThenReadStalls(audio_.get(), "50:0"); 400} 401 402TEST_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) 406 InSequence s; 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); 412 413 const base::TimeDelta fifty_ms = base::TimeDelta::FromMilliseconds(50); 414 const base::TimeDelta twenty_ms = base::TimeDelta::FromMilliseconds(20); 415 SetTimestampOffset(fifty_ms); 416 417 if (using_sequence_mode) { 418 EXPECT_CALL(callbacks_, PossibleDurationIncrease( 419 fifty_ms + frame_duration_)); 420 } else { 421 EXPECT_CALL(callbacks_, PossibleDurationIncrease( 422 fifty_ms + twenty_ms + frame_duration_)); 423 } 424 425 ProcessFrames("20K", ""); 426 EXPECT_FALSE(new_media_segment_); 427 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"); 434 } else { 435 EXPECT_EQ(fifty_ms, timestamp_offset_); 436 CheckExpectedRangesByTimestamp(audio_.get(), "{ [70,80) }"); 437 CheckReadsThenReadStalls(audio_.get(), "70:20"); 438 } 439} 440 441TEST_P(FrameProcessorTest, AudioOnly_SequentialProcessFrames) { 442 // Tests A: P(A0,A10)+P(A20,A30) -> (a0,a10,a20,a30) 443 InSequence s; 444 AddTestTracks(HAS_AUDIO); 445 new_media_segment_ = true; 446 if (GetParam()) 447 frame_processor_->SetSequenceMode(true); 448 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) }"); 454 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) }"); 460 461 CheckReadsThenReadStalls(audio_.get(), "0 10 20 30"); 462} 463 464TEST_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) 469 InSequence s; 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)); 476 } else { 477 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 4)); 478 } 479 480 ProcessFrames("20K 30K", ""); 481 EXPECT_FALSE(new_media_segment_); 482 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)); 487 } else { 488 CheckExpectedRangesByTimestamp(audio_.get(), "{ [20,40) }"); 489 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 490 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 2)); 491 } 492 493 ProcessFrames("0K 10K", ""); 494 EXPECT_FALSE(new_media_segment_); 495 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"); 500 } else { 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"); 510 } 511} 512 513TEST_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) 516 InSequence s; 517 AddTestTracks(HAS_AUDIO | HAS_VIDEO); 518 new_media_segment_ = true; 519 if (GetParam()) 520 frame_processor_->SetSequenceMode(true); 521 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) }"); 528 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) }"); 535 536 CheckReadsThenReadStalls(audio_.get(), "0 10 20 30 40"); 537 CheckReadsThenReadStalls(video_.get(), "0 10 20 30"); 538} 539 540TEST_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. 546 InSequence s; 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)); 553 } else { 554 EXPECT_CALL(callbacks_, PossibleDurationIncrease(frame_duration_ * 6)); 555 } 556 557 ProcessFrames("0K 10K 30K 40K 50K", "0K 10 40 50K"); 558 EXPECT_FALSE(new_media_segment_); 559 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"); 566 } else { 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"); 576 } 577} 578 579TEST_P(FrameProcessorTest, 580 AppendWindowFilterOfNegativeBufferTimestampsWithPrerollDiscard) { 581 InSequence s; 582 AddTestTracks(HAS_AUDIO); 583 new_media_segment_ = true; 584 if (GetParam()) 585 frame_processor_->SetSequenceMode(true); 586 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"); 594} 595 596TEST_P(FrameProcessorTest, AppendWindowFilterWithInexactPreroll) { 597 InSequence s; 598 AddTestTracks(HAS_AUDIO); 599 new_media_segment_ = true; 600 if (GetParam()) 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"); 607} 608 609TEST_P(FrameProcessorTest, AppendWindowFilterWithInexactPreroll_2) { 610 InSequence s; 611 AddTestTracks(HAS_AUDIO); 612 new_media_segment_ = true; 613 if (GetParam()) 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"); 620} 621 622TEST_P(FrameProcessorTest, AllowNegativeFramePTSAndDTSBeforeOffsetAdjustment) { 623 InSequence s; 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)); 630 } else { 631 EXPECT_CALL(callbacks_, 632 PossibleDurationIncrease((frame_duration_ * 5) / 2)); 633 } 634 635 ProcessFrames("-5K 5K 15K", ""); 636 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"); 641 } else { 642 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 643 CheckExpectedRangesByTimestamp(audio_.get(), "{ [0,25) }"); 644 CheckReadsThenReadStalls(audio_.get(), "0:-5 5 15"); 645 } 646} 647 648TEST_P(FrameProcessorTest, PartialAppendWindowFilterNoDiscontinuity) { 649 // Tests that spurious discontinuity is not introduced by a partially 650 // trimmed frame. 651 InSequence s; 652 AddTestTracks(HAS_AUDIO); 653 new_media_segment_ = true; 654 if (GetParam()) 655 frame_processor_->SetSequenceMode(true); 656 EXPECT_CALL(callbacks_, 657 PossibleDurationIncrease(base::TimeDelta::FromMilliseconds(29))); 658 659 append_window_start_ = base::TimeDelta::FromMilliseconds(7); 660 ProcessFrames("0K 19K", ""); 661 662 EXPECT_EQ(base::TimeDelta(), timestamp_offset_); 663 CheckExpectedRangesByTimestamp(audio_.get(), "{ [7,29) }"); 664 CheckReadsThenReadStalls(audio_.get(), "7:0 19"); 665} 666 667INSTANTIATE_TEST_CASE_P(SequenceMode, FrameProcessorTest, Values(true)); 668INSTANTIATE_TEST_CASE_P(SegmentsMode, FrameProcessorTest, Values(false)); 669 670} // namespace media 671