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, ' ', &timestamps);
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_, &timestamp_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, ' ', &timestamps);
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
302TEST_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_, &timestamp_offset_));
312  EXPECT_TRUE(new_media_segment_);
313  EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
314  CheckExpectedRangesByTimestamp(audio_.get(), "{ }");
315  CheckReadStalls(audio_.get());
316}
317
318TEST_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_, &timestamp_offset_));
328  EXPECT_TRUE(new_media_segment_);
329  EXPECT_EQ(base::TimeDelta(), timestamp_offset_);
330  CheckExpectedRangesByTimestamp(audio_.get(), "{ }");
331  CheckReadStalls(audio_.get());
332}
333
334TEST_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
350TEST_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
366TEST_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
382TEST_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
403TEST_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
442TEST_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
465TEST_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
514TEST_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
541TEST_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
580TEST_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
597TEST_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
610TEST_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
636TEST_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
655INSTANTIATE_TEST_CASE_P(SequenceMode, FrameProcessorTest, Values(true));
656INSTANTIATE_TEST_CASE_P(SegmentsMode, FrameProcessorTest, Values(false));
657
658}  // namespace media
659