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, ' ', &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->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_, &timestamp_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, ' ', &timestamps);
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_, &timestamp_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_, &timestamp_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