ffmpeg_demuxer_unittest.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright (c) 2012 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 <algorithm>
6#include <deque>
7#include <string>
8
9#include "base/bind.h"
10#include "base/files/file_path.h"
11#include "base/logging.h"
12#include "base/path_service.h"
13#include "base/threading/thread.h"
14#include "media/base/decrypt_config.h"
15#include "media/base/media_log.h"
16#include "media/base/mock_demuxer_host.h"
17#include "media/base/test_helpers.h"
18#include "media/ffmpeg/ffmpeg_common.h"
19#include "media/filters/ffmpeg_demuxer.h"
20#include "media/filters/file_data_source.h"
21#include "media/formats/mp4/avc.h"
22#include "media/formats/webm/webm_crypto_helpers.h"
23#include "testing/gtest/include/gtest/gtest.h"
24
25using ::testing::AnyNumber;
26using ::testing::DoAll;
27using ::testing::Exactly;
28using ::testing::InSequence;
29using ::testing::Invoke;
30using ::testing::NotNull;
31using ::testing::Return;
32using ::testing::SaveArg;
33using ::testing::SetArgPointee;
34using ::testing::StrictMock;
35using ::testing::WithArgs;
36using ::testing::_;
37
38namespace media {
39
40MATCHER(IsEndOfStreamBuffer,
41        std::string(negation ? "isn't" : "is") + " end of stream") {
42  return arg->end_of_stream();
43}
44
45static void EosOnReadDone(bool* got_eos_buffer,
46                          DemuxerStream::Status status,
47                          const scoped_refptr<DecoderBuffer>& buffer) {
48  base::MessageLoop::current()->PostTask(
49      FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
50
51  EXPECT_EQ(status, DemuxerStream::kOk);
52  if (buffer->end_of_stream()) {
53    *got_eos_buffer = true;
54    return;
55  }
56
57  EXPECT_TRUE(buffer->data());
58  EXPECT_GT(buffer->data_size(), 0);
59  *got_eos_buffer = false;
60};
61
62
63// Fixture class to facilitate writing tests.  Takes care of setting up the
64// FFmpeg, pipeline and filter host mocks.
65class FFmpegDemuxerTest : public testing::Test {
66 protected:
67  FFmpegDemuxerTest() {}
68
69  virtual ~FFmpegDemuxerTest() {
70    if (demuxer_) {
71      WaitableMessageLoopEvent event;
72      demuxer_->Stop(event.GetClosure());
73      event.RunAndWait();
74    }
75  }
76
77  void CreateDemuxer(const std::string& name) {
78    CHECK(!demuxer_);
79
80    EXPECT_CALL(host_, AddBufferedTimeRange(_, _)).Times(AnyNumber());
81
82    CreateDataSource(name);
83
84    Demuxer::NeedKeyCB need_key_cb =
85        base::Bind(&FFmpegDemuxerTest::NeedKeyCB, base::Unretained(this));
86
87    demuxer_.reset(new FFmpegDemuxer(message_loop_.message_loop_proxy(),
88                                     data_source_.get(),
89                                     need_key_cb,
90                                     new MediaLog()));
91  }
92
93  MOCK_METHOD1(CheckPoint, void(int v));
94
95  void InitializeDemuxerWithTimelineOffset(bool enable_text,
96                                           base::Time timeline_offset) {
97    EXPECT_CALL(host_, SetDuration(_));
98    WaitableMessageLoopEvent event;
99    demuxer_->Initialize(&host_, event.GetPipelineStatusCB(), enable_text);
100    demuxer_->timeline_offset_ = timeline_offset;
101    event.RunAndWaitForStatus(PIPELINE_OK);
102  }
103
104  void InitializeDemuxerText(bool enable_text) {
105    InitializeDemuxerWithTimelineOffset(enable_text, base::Time());
106  }
107
108  void InitializeDemuxer() {
109    InitializeDemuxerText(false);
110  }
111
112  MOCK_METHOD2(OnReadDoneCalled, void(int, int64));
113
114  // Verifies that |buffer| has a specific |size| and |timestamp|.
115  // |location| simply indicates where the call to this function was made.
116  // This makes it easier to track down where test failures occur.
117  void OnReadDone(const tracked_objects::Location& location,
118                  int size,
119                  int64 timestamp_us,
120                  base::TimeDelta discard_front_padding,
121                  DemuxerStream::Status status,
122                  const scoped_refptr<DecoderBuffer>& buffer) {
123    std::string location_str;
124    location.Write(true, false, &location_str);
125    location_str += "\n";
126    SCOPED_TRACE(location_str);
127    EXPECT_EQ(status, DemuxerStream::kOk);
128    OnReadDoneCalled(size, timestamp_us);
129    EXPECT_TRUE(buffer.get() != NULL);
130    EXPECT_EQ(size, buffer->data_size());
131    EXPECT_EQ(timestamp_us, buffer->timestamp().InMicroseconds());
132    EXPECT_EQ(discard_front_padding, buffer->discard_padding().first);
133    DCHECK_EQ(&message_loop_, base::MessageLoop::current());
134    message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
135  }
136
137  DemuxerStream::ReadCB NewReadCB(const tracked_objects::Location& location,
138                                  int size,
139                                  int64 timestamp_us) {
140    EXPECT_CALL(*this, OnReadDoneCalled(size, timestamp_us));
141    return base::Bind(&FFmpegDemuxerTest::OnReadDone,
142                      base::Unretained(this),
143                      location,
144                      size,
145                      timestamp_us,
146                      base::TimeDelta());
147  }
148
149  DemuxerStream::ReadCB NewReadCBWithCheckedDiscard(
150      const tracked_objects::Location& location,
151      int size,
152      int64 timestamp_us,
153      base::TimeDelta discard_front_padding) {
154    EXPECT_CALL(*this, OnReadDoneCalled(size, timestamp_us));
155    return base::Bind(&FFmpegDemuxerTest::OnReadDone,
156                      base::Unretained(this),
157                      location,
158                      size,
159                      timestamp_us,
160                      discard_front_padding);
161  }
162
163  // TODO(xhwang): This is a workaround of the issue that move-only parameters
164  // are not supported in mocked methods. Remove this when the issue is fixed
165  // (http://code.google.com/p/googletest/issues/detail?id=395) or when we use
166  // std::string instead of scoped_ptr<uint8[]> (http://crbug.com/130689).
167  MOCK_METHOD3(NeedKeyCBMock, void(const std::string& type,
168                                   const uint8* init_data, int init_data_size));
169  void NeedKeyCB(const std::string& type,
170                 const std::vector<uint8>& init_data) {
171    const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
172    NeedKeyCBMock(type, init_data_ptr, init_data.size());
173  }
174
175  // Accessor to demuxer internals.
176  void set_duration_known(bool duration_known) {
177    demuxer_->duration_known_ = duration_known;
178  }
179
180  bool IsStreamStopped(DemuxerStream::Type type) {
181    DemuxerStream* stream = demuxer_->GetStream(type);
182    CHECK(stream);
183    return !static_cast<FFmpegDemuxerStream*>(stream)->demuxer_;
184  }
185
186  // Fixture members.
187  scoped_ptr<FileDataSource> data_source_;
188  scoped_ptr<FFmpegDemuxer> demuxer_;
189  StrictMock<MockDemuxerHost> host_;
190  base::MessageLoop message_loop_;
191
192  AVFormatContext* format_context() {
193    return demuxer_->glue_->format_context();
194  }
195
196  int preferred_seeking_stream_index() const {
197    return demuxer_->preferred_stream_for_seeking_.first;
198  }
199
200  void ReadUntilEndOfStream(DemuxerStream* stream) {
201    bool got_eos_buffer = false;
202    const int kMaxBuffers = 170;
203    for (int i = 0; !got_eos_buffer && i < kMaxBuffers; i++) {
204      stream->Read(base::Bind(&EosOnReadDone, &got_eos_buffer));
205      message_loop_.Run();
206    }
207
208    EXPECT_TRUE(got_eos_buffer);
209  }
210
211 private:
212  void CreateDataSource(const std::string& name) {
213    CHECK(!data_source_);
214
215    base::FilePath file_path;
216    EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
217
218    file_path = file_path.Append(FILE_PATH_LITERAL("media"))
219        .Append(FILE_PATH_LITERAL("test"))
220        .Append(FILE_PATH_LITERAL("data"))
221        .AppendASCII(name);
222
223    data_source_.reset(new FileDataSource());
224    EXPECT_TRUE(data_source_->Initialize(file_path));
225  }
226
227  DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxerTest);
228};
229
230TEST_F(FFmpegDemuxerTest, Initialize_OpenFails) {
231  // Simulate avformat_open_input() failing.
232  CreateDemuxer("ten_byte_file");
233  WaitableMessageLoopEvent event;
234  demuxer_->Initialize(&host_, event.GetPipelineStatusCB(), true);
235  event.RunAndWaitForStatus(DEMUXER_ERROR_COULD_NOT_OPEN);
236}
237
238// TODO(acolwell): Uncomment this test when we discover a file that passes
239// avformat_open_input(), but has avformat_find_stream_info() fail.
240//
241//TEST_F(FFmpegDemuxerTest, Initialize_ParseFails) {
242//  ("find_stream_info_fail.webm");
243//  demuxer_->Initialize(
244//      &host_, NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_PARSE));
245//  message_loop_.RunUntilIdle();
246//}
247
248TEST_F(FFmpegDemuxerTest, Initialize_NoStreams) {
249  // Open a file with no streams whatsoever.
250  CreateDemuxer("no_streams.webm");
251  WaitableMessageLoopEvent event;
252  demuxer_->Initialize(&host_, event.GetPipelineStatusCB(), true);
253  event.RunAndWaitForStatus(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
254}
255
256TEST_F(FFmpegDemuxerTest, Initialize_NoAudioVideo) {
257  // Open a file containing streams but none of which are audio/video streams.
258  CreateDemuxer("no_audio_video.webm");
259  WaitableMessageLoopEvent event;
260  demuxer_->Initialize(&host_, event.GetPipelineStatusCB(), true);
261  event.RunAndWaitForStatus(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
262}
263
264TEST_F(FFmpegDemuxerTest, Initialize_Successful) {
265  CreateDemuxer("bear-320x240.webm");
266  InitializeDemuxer();
267
268  // Video stream should be present.
269  DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
270  ASSERT_TRUE(stream);
271  EXPECT_EQ(DemuxerStream::VIDEO, stream->type());
272
273  const VideoDecoderConfig& video_config = stream->video_decoder_config();
274  EXPECT_EQ(kCodecVP8, video_config.codec());
275  EXPECT_EQ(VideoFrame::YV12, video_config.format());
276  EXPECT_EQ(320, video_config.coded_size().width());
277  EXPECT_EQ(240, video_config.coded_size().height());
278  EXPECT_EQ(0, video_config.visible_rect().x());
279  EXPECT_EQ(0, video_config.visible_rect().y());
280  EXPECT_EQ(320, video_config.visible_rect().width());
281  EXPECT_EQ(240, video_config.visible_rect().height());
282  EXPECT_EQ(320, video_config.natural_size().width());
283  EXPECT_EQ(240, video_config.natural_size().height());
284  EXPECT_FALSE(video_config.extra_data());
285  EXPECT_EQ(0u, video_config.extra_data_size());
286
287  // Audio stream should be present.
288  stream = demuxer_->GetStream(DemuxerStream::AUDIO);
289  ASSERT_TRUE(stream);
290  EXPECT_EQ(DemuxerStream::AUDIO, stream->type());
291
292  const AudioDecoderConfig& audio_config = stream->audio_decoder_config();
293  EXPECT_EQ(kCodecVorbis, audio_config.codec());
294  EXPECT_EQ(32, audio_config.bits_per_channel());
295  EXPECT_EQ(CHANNEL_LAYOUT_STEREO, audio_config.channel_layout());
296  EXPECT_EQ(44100, audio_config.samples_per_second());
297  EXPECT_EQ(kSampleFormatPlanarF32, audio_config.sample_format());
298  EXPECT_TRUE(audio_config.extra_data());
299  EXPECT_GT(audio_config.extra_data_size(), 0u);
300
301  // Unknown stream should never be present.
302  EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::UNKNOWN));
303}
304
305TEST_F(FFmpegDemuxerTest, Initialize_Multitrack) {
306  // Open a file containing the following streams:
307  //   Stream #0: Video (VP8)
308  //   Stream #1: Audio (Vorbis)
309  //   Stream #2: Subtitles (SRT)
310  //   Stream #3: Video (Theora)
311  //   Stream #4: Audio (16-bit signed little endian PCM)
312  //
313  // We should only pick the first audio/video streams we come across.
314  CreateDemuxer("bear-320x240-multitrack.webm");
315  InitializeDemuxer();
316
317  // Video stream should be VP8.
318  DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
319  ASSERT_TRUE(stream);
320  EXPECT_EQ(DemuxerStream::VIDEO, stream->type());
321  EXPECT_EQ(kCodecVP8, stream->video_decoder_config().codec());
322
323  // Audio stream should be Vorbis.
324  stream = demuxer_->GetStream(DemuxerStream::AUDIO);
325  ASSERT_TRUE(stream);
326  EXPECT_EQ(DemuxerStream::AUDIO, stream->type());
327  EXPECT_EQ(kCodecVorbis, stream->audio_decoder_config().codec());
328
329  // Unknown stream should never be present.
330  EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::UNKNOWN));
331}
332
333TEST_F(FFmpegDemuxerTest, Initialize_MultitrackText) {
334  // Open a file containing the following streams:
335  //   Stream #0: Video (VP8)
336  //   Stream #1: Audio (Vorbis)
337  //   Stream #2: Text (WebVTT)
338
339  CreateDemuxer("bear-vp8-webvtt.webm");
340  DemuxerStream* text_stream = NULL;
341  EXPECT_CALL(host_, AddTextStream(_, _))
342      .WillOnce(SaveArg<0>(&text_stream));
343  InitializeDemuxerText(true);
344  ASSERT_TRUE(text_stream);
345  EXPECT_EQ(DemuxerStream::TEXT, text_stream->type());
346
347  // Video stream should be VP8.
348  DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
349  ASSERT_TRUE(stream);
350  EXPECT_EQ(DemuxerStream::VIDEO, stream->type());
351  EXPECT_EQ(kCodecVP8, stream->video_decoder_config().codec());
352
353  // Audio stream should be Vorbis.
354  stream = demuxer_->GetStream(DemuxerStream::AUDIO);
355  ASSERT_TRUE(stream);
356  EXPECT_EQ(DemuxerStream::AUDIO, stream->type());
357  EXPECT_EQ(kCodecVorbis, stream->audio_decoder_config().codec());
358
359  // Unknown stream should never be present.
360  EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::UNKNOWN));
361}
362
363TEST_F(FFmpegDemuxerTest, Initialize_Encrypted) {
364  EXPECT_CALL(*this, NeedKeyCBMock(kWebMEncryptInitDataType, NotNull(),
365                                   DecryptConfig::kDecryptionKeySize))
366      .Times(Exactly(2));
367
368  CreateDemuxer("bear-320x240-av_enc-av.webm");
369  InitializeDemuxer();
370}
371
372TEST_F(FFmpegDemuxerTest, Read_Audio) {
373  // We test that on a successful audio packet read.
374  CreateDemuxer("bear-320x240.webm");
375  InitializeDemuxer();
376
377  // Attempt a read from the audio stream and run the message loop until done.
378  DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
379
380  audio->Read(NewReadCB(FROM_HERE, 29, 0));
381  message_loop_.Run();
382
383  audio->Read(NewReadCB(FROM_HERE, 27, 3000));
384  message_loop_.Run();
385}
386
387TEST_F(FFmpegDemuxerTest, Read_Video) {
388  // We test that on a successful video packet read.
389  CreateDemuxer("bear-320x240.webm");
390  InitializeDemuxer();
391
392  // Attempt a read from the video stream and run the message loop until done.
393  DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
394
395  video->Read(NewReadCB(FROM_HERE, 22084, 0));
396  message_loop_.Run();
397
398  video->Read(NewReadCB(FROM_HERE, 1057, 33000));
399  message_loop_.Run();
400}
401
402TEST_F(FFmpegDemuxerTest, Read_Text) {
403  // We test that on a successful text packet read.
404  CreateDemuxer("bear-vp8-webvtt.webm");
405  DemuxerStream* text_stream = NULL;
406  EXPECT_CALL(host_, AddTextStream(_, _))
407      .WillOnce(SaveArg<0>(&text_stream));
408  InitializeDemuxerText(true);
409  ASSERT_TRUE(text_stream);
410  EXPECT_EQ(DemuxerStream::TEXT, text_stream->type());
411
412  text_stream->Read(NewReadCB(FROM_HERE, 31, 0));
413  message_loop_.Run();
414
415  text_stream->Read(NewReadCB(FROM_HERE, 19, 500000));
416  message_loop_.Run();
417}
418
419TEST_F(FFmpegDemuxerTest, SeekInitialized_NoVideoStartTime) {
420  CreateDemuxer("audio-start-time-only.webm");
421  InitializeDemuxer();
422  EXPECT_EQ(0, preferred_seeking_stream_index());
423}
424
425TEST_F(FFmpegDemuxerTest, Read_VideoPositiveStartTime) {
426  const int64 kTimelineOffsetMs = 1352550896000LL;
427
428  // Test the start time is the first timestamp of the video and audio stream.
429  CreateDemuxer("nonzero-start-time.webm");
430  InitializeDemuxerWithTimelineOffset(
431      false, base::Time::FromJsTime(kTimelineOffsetMs));
432
433  // Attempt a read from the video stream and run the message loop until done.
434  DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
435  DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
436
437  const base::TimeDelta video_start_time =
438      base::TimeDelta::FromMicroseconds(400000);
439  const base::TimeDelta audio_start_time =
440      base::TimeDelta::FromMicroseconds(396000);
441
442  // Run the test twice with a seek in between.
443  for (int i = 0; i < 2; ++i) {
444    // Check first buffer in video stream.  It should have been adjusted such
445    // that it starts 400ms after the first audio buffer.
446    video->Read(
447        NewReadCB(FROM_HERE,
448                  5636,
449                  (video_start_time - audio_start_time).InMicroseconds()));
450    message_loop_.Run();
451
452    // Since the audio buffer has a lower first timestamp, it should become
453    // zero.
454    audio->Read(NewReadCB(FROM_HERE, 165, 0));
455    message_loop_.Run();
456
457    // Verify that the start time is equal to the lowest timestamp (ie the
458    // audio).
459    EXPECT_EQ(audio_start_time, demuxer_->start_time());
460
461    // Verify that the timeline offset has been adjusted by the start time.
462    EXPECT_EQ(kTimelineOffsetMs + audio_start_time.InMilliseconds(),
463              demuxer_->GetTimelineOffset().ToJavaTime());
464
465    // Seek back to the beginning and repeat the test.
466    WaitableMessageLoopEvent event;
467    demuxer_->Seek(base::TimeDelta(), event.GetPipelineStatusCB());
468    event.RunAndWaitForStatus(PIPELINE_OK);
469  }
470}
471
472TEST_F(FFmpegDemuxerTest, Read_AudioNoStartTime) {
473  // FFmpeg does not set timestamps when demuxing wave files.  Ensure that the
474  // demuxer sets a start time of zero in this case.
475  CreateDemuxer("sfx_s24le.wav");
476  InitializeDemuxer();
477
478  // Run the test twice with a seek in between.
479  for (int i = 0; i < 2; ++i) {
480    demuxer_->GetStream(DemuxerStream::AUDIO)
481        ->Read(NewReadCB(FROM_HERE, 4095, 0));
482    message_loop_.Run();
483    EXPECT_EQ(base::TimeDelta(), demuxer_->start_time());
484
485    // Seek back to the beginning and repeat the test.
486    WaitableMessageLoopEvent event;
487    demuxer_->Seek(base::TimeDelta(), event.GetPipelineStatusCB());
488    event.RunAndWaitForStatus(PIPELINE_OK);
489  }
490}
491
492// TODO(dalecurtis): Test is disabled since FFmpeg does not currently guarantee
493// the order of demuxed packets in OGG containers.  Re-enable once we decide to
494// either workaround it or attempt a fix upstream.  See http://crbug.com/387996.
495TEST_F(FFmpegDemuxerTest,
496       DISABLED_Read_AudioNegativeStartTimeAndOggDiscard_Bear) {
497  // Many ogg files have negative starting timestamps, so ensure demuxing and
498  // seeking work correctly with a negative start time.
499  CreateDemuxer("bear.ogv");
500  InitializeDemuxer();
501
502  // Attempt a read from the video stream and run the message loop until done.
503  DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
504  DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
505
506  // Run the test twice with a seek in between.
507  for (int i = 0; i < 2; ++i) {
508    audio->Read(
509        NewReadCBWithCheckedDiscard(FROM_HERE, 40, 0, kInfiniteDuration()));
510    message_loop_.Run();
511    audio->Read(
512        NewReadCBWithCheckedDiscard(FROM_HERE, 41, 2903, kInfiniteDuration()));
513    message_loop_.Run();
514    audio->Read(NewReadCBWithCheckedDiscard(
515        FROM_HERE, 173, 5805, base::TimeDelta::FromMicroseconds(10159)));
516    message_loop_.Run();
517
518    audio->Read(NewReadCB(FROM_HERE, 148, 18866));
519    message_loop_.Run();
520    EXPECT_EQ(base::TimeDelta::FromMicroseconds(-15964),
521              demuxer_->start_time());
522
523    video->Read(NewReadCB(FROM_HERE, 5751, 0));
524    message_loop_.Run();
525
526    video->Read(NewReadCB(FROM_HERE, 846, 33367));
527    message_loop_.Run();
528
529    video->Read(NewReadCB(FROM_HERE, 1255, 66733));
530    message_loop_.Run();
531
532    // Seek back to the beginning and repeat the test.
533    WaitableMessageLoopEvent event;
534    demuxer_->Seek(base::TimeDelta(), event.GetPipelineStatusCB());
535    event.RunAndWaitForStatus(PIPELINE_OK);
536  }
537}
538
539// Same test above, but using sync2.ogv which has video stream muxed before the
540// audio stream, so seeking based only on start time will fail since ffmpeg is
541// essentially just seeking based on file position.
542TEST_F(FFmpegDemuxerTest, Read_AudioNegativeStartTimeAndOggDiscard_Sync) {
543  // Many ogg files have negative starting timestamps, so ensure demuxing and
544  // seeking work correctly with a negative start time.
545  CreateDemuxer("sync2.ogv");
546  InitializeDemuxer();
547
548  // Attempt a read from the video stream and run the message loop until done.
549  DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
550  DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
551
552  // Run the test twice with a seek in between.
553  for (int i = 0; i < 2; ++i) {
554    audio->Read(NewReadCBWithCheckedDiscard(
555        FROM_HERE, 1, 0, base::TimeDelta::FromMicroseconds(2902)));
556    message_loop_.Run();
557
558    audio->Read(NewReadCB(FROM_HERE, 1, 2902));
559    message_loop_.Run();
560    EXPECT_EQ(base::TimeDelta::FromMicroseconds(-2902),
561              demuxer_->start_time());
562
563    video->Read(NewReadCB(FROM_HERE, 9997, 0));
564    message_loop_.Run();
565
566    video->Read(NewReadCB(FROM_HERE, 16, 33241));
567    message_loop_.Run();
568
569    video->Read(NewReadCB(FROM_HERE, 631, 66482));
570    message_loop_.Run();
571
572    // Seek back to the beginning and repeat the test.
573    WaitableMessageLoopEvent event;
574    demuxer_->Seek(base::TimeDelta(), event.GetPipelineStatusCB());
575    event.RunAndWaitForStatus(PIPELINE_OK);
576  }
577}
578
579TEST_F(FFmpegDemuxerTest, Read_EndOfStream) {
580  // Verify that end of stream buffers are created.
581  CreateDemuxer("bear-320x240.webm");
582  InitializeDemuxer();
583  ReadUntilEndOfStream(demuxer_->GetStream(DemuxerStream::AUDIO));
584}
585
586TEST_F(FFmpegDemuxerTest, Read_EndOfStreamText) {
587  // Verify that end of stream buffers are created.
588  CreateDemuxer("bear-vp8-webvtt.webm");
589  DemuxerStream* text_stream = NULL;
590  EXPECT_CALL(host_, AddTextStream(_, _))
591      .WillOnce(SaveArg<0>(&text_stream));
592  InitializeDemuxerText(true);
593  ASSERT_TRUE(text_stream);
594  EXPECT_EQ(DemuxerStream::TEXT, text_stream->type());
595
596  bool got_eos_buffer = false;
597  const int kMaxBuffers = 10;
598  for (int i = 0; !got_eos_buffer && i < kMaxBuffers; i++) {
599    text_stream->Read(base::Bind(&EosOnReadDone, &got_eos_buffer));
600    message_loop_.Run();
601  }
602
603  EXPECT_TRUE(got_eos_buffer);
604}
605
606TEST_F(FFmpegDemuxerTest, Read_EndOfStream_NoDuration) {
607  // Verify that end of stream buffers are created.
608  CreateDemuxer("bear-320x240.webm");
609  InitializeDemuxer();
610  set_duration_known(false);
611  EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2767)));
612  ReadUntilEndOfStream(demuxer_->GetStream(DemuxerStream::AUDIO));
613  ReadUntilEndOfStream(demuxer_->GetStream(DemuxerStream::VIDEO));
614}
615
616TEST_F(FFmpegDemuxerTest, Read_EndOfStream_NoDuration_VideoOnly) {
617  // Verify that end of stream buffers are created.
618  CreateDemuxer("bear-320x240-video-only.webm");
619  InitializeDemuxer();
620  set_duration_known(false);
621  EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2703)));
622  ReadUntilEndOfStream(demuxer_->GetStream(DemuxerStream::VIDEO));
623}
624
625TEST_F(FFmpegDemuxerTest, Read_EndOfStream_NoDuration_AudioOnly) {
626  // Verify that end of stream buffers are created.
627  CreateDemuxer("bear-320x240-audio-only.webm");
628  InitializeDemuxer();
629  set_duration_known(false);
630  EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2767)));
631  ReadUntilEndOfStream(demuxer_->GetStream(DemuxerStream::AUDIO));
632}
633
634TEST_F(FFmpegDemuxerTest, Read_EndOfStream_NoDuration_UnsupportedStream) {
635  // Verify that end of stream buffers are created and we don't crash
636  // if there are streams in the file that we don't support.
637  CreateDemuxer("vorbis_audio_wmv_video.mkv");
638  InitializeDemuxer();
639  set_duration_known(false);
640  EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(1014)));
641  ReadUntilEndOfStream(demuxer_->GetStream(DemuxerStream::AUDIO));
642}
643
644TEST_F(FFmpegDemuxerTest, Seek) {
645  // We're testing that the demuxer frees all queued packets when it receives
646  // a Seek().
647  CreateDemuxer("bear-320x240.webm");
648  InitializeDemuxer();
649
650  // Get our streams.
651  DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
652  DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
653  ASSERT_TRUE(video);
654  ASSERT_TRUE(audio);
655
656  // Read a video packet and release it.
657  video->Read(NewReadCB(FROM_HERE, 22084, 0));
658  message_loop_.Run();
659
660  // Issue a simple forward seek, which should discard queued packets.
661  WaitableMessageLoopEvent event;
662  demuxer_->Seek(base::TimeDelta::FromMicroseconds(1000000),
663                 event.GetPipelineStatusCB());
664  event.RunAndWaitForStatus(PIPELINE_OK);
665
666  // Audio read #1.
667  audio->Read(NewReadCB(FROM_HERE, 145, 803000));
668  message_loop_.Run();
669
670  // Audio read #2.
671  audio->Read(NewReadCB(FROM_HERE, 148, 826000));
672  message_loop_.Run();
673
674  // Video read #1.
675  video->Read(NewReadCB(FROM_HERE, 5425, 801000));
676  message_loop_.Run();
677
678  // Video read #2.
679  video->Read(NewReadCB(FROM_HERE, 1906, 834000));
680  message_loop_.Run();
681}
682
683TEST_F(FFmpegDemuxerTest, SeekText) {
684  // We're testing that the demuxer frees all queued packets when it receives
685  // a Seek().
686  CreateDemuxer("bear-vp8-webvtt.webm");
687  DemuxerStream* text_stream = NULL;
688  EXPECT_CALL(host_, AddTextStream(_, _))
689      .WillOnce(SaveArg<0>(&text_stream));
690  InitializeDemuxerText(true);
691  ASSERT_TRUE(text_stream);
692  EXPECT_EQ(DemuxerStream::TEXT, text_stream->type());
693
694  // Get our streams.
695  DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
696  DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
697  ASSERT_TRUE(video);
698  ASSERT_TRUE(audio);
699
700  // Read a text packet and release it.
701  text_stream->Read(NewReadCB(FROM_HERE, 31, 0));
702  message_loop_.Run();
703
704  // Issue a simple forward seek, which should discard queued packets.
705  WaitableMessageLoopEvent event;
706  demuxer_->Seek(base::TimeDelta::FromMicroseconds(1000000),
707                 event.GetPipelineStatusCB());
708  event.RunAndWaitForStatus(PIPELINE_OK);
709
710  // Audio read #1.
711  audio->Read(NewReadCB(FROM_HERE, 145, 803000));
712  message_loop_.Run();
713
714  // Audio read #2.
715  audio->Read(NewReadCB(FROM_HERE, 148, 826000));
716  message_loop_.Run();
717
718  // Video read #1.
719  video->Read(NewReadCB(FROM_HERE, 5425, 801000));
720  message_loop_.Run();
721
722  // Video read #2.
723  video->Read(NewReadCB(FROM_HERE, 1906, 834000));
724  message_loop_.Run();
725
726  // Text read #1.
727  text_stream->Read(NewReadCB(FROM_HERE, 19, 500000));
728  message_loop_.Run();
729
730  // Text read #2.
731  text_stream->Read(NewReadCB(FROM_HERE, 19, 1000000));
732  message_loop_.Run();
733}
734
735class MockReadCB {
736 public:
737  MockReadCB() {}
738  ~MockReadCB() {}
739
740  MOCK_METHOD2(Run, void(DemuxerStream::Status status,
741                         const scoped_refptr<DecoderBuffer>& buffer));
742 private:
743  DISALLOW_COPY_AND_ASSIGN(MockReadCB);
744};
745
746TEST_F(FFmpegDemuxerTest, Stop) {
747  // Tests that calling Read() on a stopped demuxer stream immediately deletes
748  // the callback.
749  CreateDemuxer("bear-320x240.webm");
750  InitializeDemuxer();
751
752  // Get our stream.
753  DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
754  ASSERT_TRUE(audio);
755
756  WaitableMessageLoopEvent event;
757  demuxer_->Stop(event.GetClosure());
758  event.RunAndWait();
759
760  // Reads after being stopped are all EOS buffers.
761  StrictMock<MockReadCB> callback;
762  EXPECT_CALL(callback, Run(DemuxerStream::kOk, IsEndOfStreamBuffer()));
763
764  // Attempt the read...
765  audio->Read(base::Bind(&MockReadCB::Run, base::Unretained(&callback)));
766  message_loop_.RunUntilIdle();
767
768  // Don't let the test call Stop() again.
769  demuxer_.reset();
770}
771
772// Verify that seek works properly when the WebM cues data is at the start of
773// the file instead of at the end.
774TEST_F(FFmpegDemuxerTest, SeekWithCuesBeforeFirstCluster) {
775  CreateDemuxer("bear-320x240-cues-in-front.webm");
776  InitializeDemuxer();
777
778  // Get our streams.
779  DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
780  DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
781  ASSERT_TRUE(video);
782  ASSERT_TRUE(audio);
783
784  // Read a video packet and release it.
785  video->Read(NewReadCB(FROM_HERE, 22084, 0));
786  message_loop_.Run();
787
788  // Issue a simple forward seek, which should discard queued packets.
789  WaitableMessageLoopEvent event;
790  demuxer_->Seek(base::TimeDelta::FromMicroseconds(2500000),
791                 event.GetPipelineStatusCB());
792  event.RunAndWaitForStatus(PIPELINE_OK);
793
794  // Audio read #1.
795  audio->Read(NewReadCB(FROM_HERE, 40, 2403000));
796  message_loop_.Run();
797
798  // Audio read #2.
799  audio->Read(NewReadCB(FROM_HERE, 42, 2406000));
800  message_loop_.Run();
801
802  // Video read #1.
803  video->Read(NewReadCB(FROM_HERE, 5276, 2402000));
804  message_loop_.Run();
805
806  // Video read #2.
807  video->Read(NewReadCB(FROM_HERE, 1740, 2436000));
808  message_loop_.Run();
809}
810
811#if defined(USE_PROPRIETARY_CODECS)
812// Ensure ID3v1 tag reading is disabled.  id3_test.mp3 has an ID3v1 tag with the
813// field "title" set to "sample for id3 test".
814TEST_F(FFmpegDemuxerTest, NoID3TagData) {
815  CreateDemuxer("id3_test.mp3");
816  InitializeDemuxer();
817  EXPECT_FALSE(av_dict_get(format_context()->metadata, "title", NULL, 0));
818}
819#endif
820
821#if defined(USE_PROPRIETARY_CODECS)
822// Ensure MP3 files with large image/video based ID3 tags demux okay.  FFmpeg
823// will hand us a video stream to the data which will likely be in a format we
824// don't accept as video; e.g. PNG.
825TEST_F(FFmpegDemuxerTest, Mp3WithVideoStreamID3TagData) {
826  CreateDemuxer("id3_png_test.mp3");
827  InitializeDemuxer();
828
829  // Ensure the expected streams are present.
830  EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::VIDEO));
831  EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::AUDIO));
832}
833#endif
834
835// Ensure a video with an unsupported audio track still results in the video
836// stream being demuxed.
837TEST_F(FFmpegDemuxerTest, UnsupportedAudioSupportedVideoDemux) {
838  CreateDemuxer("speex_audio_vorbis_video.ogv");
839  InitializeDemuxer();
840
841  // Ensure the expected streams are present.
842  EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::VIDEO));
843  EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::AUDIO));
844}
845
846// Ensure a video with an unsupported video track still results in the audio
847// stream being demuxed.
848TEST_F(FFmpegDemuxerTest, UnsupportedVideoSupportedAudioDemux) {
849  CreateDemuxer("vorbis_audio_wmv_video.mkv");
850  InitializeDemuxer();
851
852  // Ensure the expected streams are present.
853  EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::VIDEO));
854  EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::AUDIO));
855}
856
857#if defined(USE_PROPRIETARY_CODECS)
858// FFmpeg returns null data pointers when samples have zero size, leading to
859// mistakenly creating end of stream buffers http://crbug.com/169133
860TEST_F(FFmpegDemuxerTest, MP4_ZeroStszEntry) {
861  CreateDemuxer("bear-1280x720-zero-stsz-entry.mp4");
862  InitializeDemuxer();
863  ReadUntilEndOfStream(demuxer_->GetStream(DemuxerStream::AUDIO));
864}
865
866
867static void ValidateAnnexB(DemuxerStream* stream,
868                           DemuxerStream::Status status,
869                           const scoped_refptr<DecoderBuffer>& buffer) {
870  EXPECT_EQ(status, DemuxerStream::kOk);
871
872  if (buffer->end_of_stream()) {
873    base::MessageLoop::current()->PostTask(
874        FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
875    return;
876  }
877
878  std::vector<SubsampleEntry> subsamples;
879
880  if (buffer->decrypt_config())
881    subsamples = buffer->decrypt_config()->subsamples();
882
883  bool is_valid =
884      mp4::AVC::IsValidAnnexB(buffer->data(), buffer->data_size(),
885                              subsamples);
886  EXPECT_TRUE(is_valid);
887
888  if (!is_valid) {
889    LOG(ERROR) << "Buffer contains invalid Annex B data.";
890    base::MessageLoop::current()->PostTask(
891        FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
892    return;
893  }
894
895  stream->Read(base::Bind(&ValidateAnnexB, stream));
896};
897
898TEST_F(FFmpegDemuxerTest, IsValidAnnexB) {
899  const char* files[] = {
900    "bear-1280x720-av_frag.mp4",
901    "bear-1280x720-av_with-aud-nalus_frag.mp4"
902  };
903
904  for (size_t i = 0; i < arraysize(files); ++i) {
905    DVLOG(1) << "Testing " << files[i];
906    CreateDemuxer(files[i]);
907    InitializeDemuxer();
908
909    // Ensure the expected streams are present.
910    DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
911    ASSERT_TRUE(stream);
912    stream->EnableBitstreamConverter();
913
914    stream->Read(base::Bind(&ValidateAnnexB, stream));
915    message_loop_.Run();
916
917    WaitableMessageLoopEvent event;
918    demuxer_->Stop(event.GetClosure());
919    event.RunAndWait();
920    demuxer_.reset();
921    data_source_.reset();
922  }
923}
924
925TEST_F(FFmpegDemuxerTest, Rotate_Metadata_0) {
926  CreateDemuxer("bear_rotate_0.mp4");
927  InitializeDemuxer();
928
929  DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
930  ASSERT_TRUE(stream);
931  ASSERT_EQ(VIDEO_ROTATION_0, stream->video_rotation());
932}
933
934TEST_F(FFmpegDemuxerTest, Rotate_Metadata_90) {
935  CreateDemuxer("bear_rotate_90.mp4");
936  InitializeDemuxer();
937
938  DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
939  ASSERT_TRUE(stream);
940  ASSERT_EQ(VIDEO_ROTATION_90, stream->video_rotation());
941}
942
943TEST_F(FFmpegDemuxerTest, Rotate_Metadata_180) {
944  CreateDemuxer("bear_rotate_180.mp4");
945  InitializeDemuxer();
946
947  DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
948  ASSERT_TRUE(stream);
949  ASSERT_EQ(VIDEO_ROTATION_180, stream->video_rotation());
950}
951
952TEST_F(FFmpegDemuxerTest, Rotate_Metadata_270) {
953  CreateDemuxer("bear_rotate_270.mp4");
954  InitializeDemuxer();
955
956  DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
957  ASSERT_TRUE(stream);
958  ASSERT_EQ(VIDEO_ROTATION_270, stream->video_rotation());
959}
960
961#endif
962
963}  // namespace media
964