ffmpeg_demuxer_unittest.cc revision 0de6073388f4e2780db8536178b129cd8f6ab386
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 InitializeDemuxerText(bool enable_text) {
96    EXPECT_CALL(host_, SetDuration(_));
97    WaitableMessageLoopEvent event;
98    demuxer_->Initialize(&host_, event.GetPipelineStatusCB(), enable_text);
99    event.RunAndWaitForStatus(PIPELINE_OK);
100  }
101
102  void InitializeDemuxer() {
103    InitializeDemuxerText(false);
104  }
105
106  MOCK_METHOD2(OnReadDoneCalled, void(int, int64));
107
108  // Verifies that |buffer| has a specific |size| and |timestamp|.
109  // |location| simply indicates where the call to this function was made.
110  // This makes it easier to track down where test failures occur.
111  void OnReadDone(const tracked_objects::Location& location,
112                  int size, int64 timestampInMicroseconds,
113                  DemuxerStream::Status status,
114                  const scoped_refptr<DecoderBuffer>& buffer) {
115    std::string location_str;
116    location.Write(true, false, &location_str);
117    location_str += "\n";
118    SCOPED_TRACE(location_str);
119    EXPECT_EQ(status, DemuxerStream::kOk);
120    OnReadDoneCalled(size, timestampInMicroseconds);
121    EXPECT_TRUE(buffer.get() != NULL);
122    EXPECT_EQ(size, buffer->data_size());
123    EXPECT_EQ(base::TimeDelta::FromMicroseconds(timestampInMicroseconds),
124              buffer->timestamp());
125
126    DCHECK_EQ(&message_loop_, base::MessageLoop::current());
127    message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
128  }
129
130  DemuxerStream::ReadCB NewReadCB(const tracked_objects::Location& location,
131                                  int size, int64 timestampInMicroseconds) {
132    EXPECT_CALL(*this, OnReadDoneCalled(size, timestampInMicroseconds));
133    return base::Bind(&FFmpegDemuxerTest::OnReadDone, base::Unretained(this),
134                      location, size, timestampInMicroseconds);
135  }
136
137  // TODO(xhwang): This is a workaround of the issue that move-only parameters
138  // are not supported in mocked methods. Remove this when the issue is fixed
139  // (http://code.google.com/p/googletest/issues/detail?id=395) or when we use
140  // std::string instead of scoped_ptr<uint8[]> (http://crbug.com/130689).
141  MOCK_METHOD3(NeedKeyCBMock, void(const std::string& type,
142                                   const uint8* init_data, int init_data_size));
143  void NeedKeyCB(const std::string& type,
144                 const std::vector<uint8>& init_data) {
145    const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
146    NeedKeyCBMock(type, init_data_ptr, init_data.size());
147  }
148
149  // Accessor to demuxer internals.
150  void set_duration_known(bool duration_known) {
151    demuxer_->duration_known_ = duration_known;
152  }
153
154  bool IsStreamStopped(DemuxerStream::Type type) {
155    DemuxerStream* stream = demuxer_->GetStream(type);
156    CHECK(stream);
157    return !static_cast<FFmpegDemuxerStream*>(stream)->demuxer_;
158  }
159
160  // Fixture members.
161  scoped_ptr<FileDataSource> data_source_;
162  scoped_ptr<FFmpegDemuxer> demuxer_;
163  StrictMock<MockDemuxerHost> host_;
164  base::MessageLoop message_loop_;
165
166  AVFormatContext* format_context() {
167    return demuxer_->glue_->format_context();
168  }
169
170  void ReadUntilEndOfStream(DemuxerStream* stream) {
171    bool got_eos_buffer = false;
172    const int kMaxBuffers = 170;
173    for (int i = 0; !got_eos_buffer && i < kMaxBuffers; i++) {
174      stream->Read(base::Bind(&EosOnReadDone, &got_eos_buffer));
175      message_loop_.Run();
176    }
177
178    EXPECT_TRUE(got_eos_buffer);
179  }
180
181 private:
182  void CreateDataSource(const std::string& name) {
183    CHECK(!data_source_);
184
185    base::FilePath file_path;
186    EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
187
188    file_path = file_path.Append(FILE_PATH_LITERAL("media"))
189        .Append(FILE_PATH_LITERAL("test"))
190        .Append(FILE_PATH_LITERAL("data"))
191        .AppendASCII(name);
192
193    data_source_.reset(new FileDataSource());
194    EXPECT_TRUE(data_source_->Initialize(file_path));
195  }
196
197  DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxerTest);
198};
199
200TEST_F(FFmpegDemuxerTest, Initialize_OpenFails) {
201  // Simulate avformat_open_input() failing.
202  CreateDemuxer("ten_byte_file");
203  WaitableMessageLoopEvent event;
204  demuxer_->Initialize(&host_, event.GetPipelineStatusCB(), true);
205  event.RunAndWaitForStatus(DEMUXER_ERROR_COULD_NOT_OPEN);
206}
207
208// TODO(acolwell): Uncomment this test when we discover a file that passes
209// avformat_open_input(), but has avformat_find_stream_info() fail.
210//
211//TEST_F(FFmpegDemuxerTest, Initialize_ParseFails) {
212//  ("find_stream_info_fail.webm");
213//  demuxer_->Initialize(
214//      &host_, NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_PARSE));
215//  message_loop_.RunUntilIdle();
216//}
217
218TEST_F(FFmpegDemuxerTest, Initialize_NoStreams) {
219  // Open a file with no streams whatsoever.
220  CreateDemuxer("no_streams.webm");
221  WaitableMessageLoopEvent event;
222  demuxer_->Initialize(&host_, event.GetPipelineStatusCB(), true);
223  event.RunAndWaitForStatus(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
224}
225
226TEST_F(FFmpegDemuxerTest, Initialize_NoAudioVideo) {
227  // Open a file containing streams but none of which are audio/video streams.
228  CreateDemuxer("no_audio_video.webm");
229  WaitableMessageLoopEvent event;
230  demuxer_->Initialize(&host_, event.GetPipelineStatusCB(), true);
231  event.RunAndWaitForStatus(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
232}
233
234TEST_F(FFmpegDemuxerTest, Initialize_Successful) {
235  CreateDemuxer("bear-320x240.webm");
236  InitializeDemuxer();
237
238  // Video stream should be present.
239  DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
240  ASSERT_TRUE(stream);
241  EXPECT_EQ(DemuxerStream::VIDEO, stream->type());
242
243  const VideoDecoderConfig& video_config = stream->video_decoder_config();
244  EXPECT_EQ(kCodecVP8, video_config.codec());
245  EXPECT_EQ(VideoFrame::YV12, video_config.format());
246  EXPECT_EQ(320, video_config.coded_size().width());
247  EXPECT_EQ(240, video_config.coded_size().height());
248  EXPECT_EQ(0, video_config.visible_rect().x());
249  EXPECT_EQ(0, video_config.visible_rect().y());
250  EXPECT_EQ(320, video_config.visible_rect().width());
251  EXPECT_EQ(240, video_config.visible_rect().height());
252  EXPECT_EQ(320, video_config.natural_size().width());
253  EXPECT_EQ(240, video_config.natural_size().height());
254  EXPECT_FALSE(video_config.extra_data());
255  EXPECT_EQ(0u, video_config.extra_data_size());
256
257  // Audio stream should be present.
258  stream = demuxer_->GetStream(DemuxerStream::AUDIO);
259  ASSERT_TRUE(stream);
260  EXPECT_EQ(DemuxerStream::AUDIO, stream->type());
261
262  const AudioDecoderConfig& audio_config = stream->audio_decoder_config();
263  EXPECT_EQ(kCodecVorbis, audio_config.codec());
264  EXPECT_EQ(32, audio_config.bits_per_channel());
265  EXPECT_EQ(CHANNEL_LAYOUT_STEREO, audio_config.channel_layout());
266  EXPECT_EQ(44100, audio_config.samples_per_second());
267  EXPECT_EQ(kSampleFormatPlanarF32, audio_config.sample_format());
268  EXPECT_TRUE(audio_config.extra_data());
269  EXPECT_GT(audio_config.extra_data_size(), 0u);
270
271  // Unknown stream should never be present.
272  EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::UNKNOWN));
273}
274
275TEST_F(FFmpegDemuxerTest, Initialize_Multitrack) {
276  // Open a file containing the following streams:
277  //   Stream #0: Video (VP8)
278  //   Stream #1: Audio (Vorbis)
279  //   Stream #2: Subtitles (SRT)
280  //   Stream #3: Video (Theora)
281  //   Stream #4: Audio (16-bit signed little endian PCM)
282  //
283  // We should only pick the first audio/video streams we come across.
284  CreateDemuxer("bear-320x240-multitrack.webm");
285  InitializeDemuxer();
286
287  // Video stream should be VP8.
288  DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
289  ASSERT_TRUE(stream);
290  EXPECT_EQ(DemuxerStream::VIDEO, stream->type());
291  EXPECT_EQ(kCodecVP8, stream->video_decoder_config().codec());
292
293  // Audio stream should be Vorbis.
294  stream = demuxer_->GetStream(DemuxerStream::AUDIO);
295  ASSERT_TRUE(stream);
296  EXPECT_EQ(DemuxerStream::AUDIO, stream->type());
297  EXPECT_EQ(kCodecVorbis, stream->audio_decoder_config().codec());
298
299  // Unknown stream should never be present.
300  EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::UNKNOWN));
301}
302
303TEST_F(FFmpegDemuxerTest, Initialize_MultitrackText) {
304  // Open a file containing the following streams:
305  //   Stream #0: Video (VP8)
306  //   Stream #1: Audio (Vorbis)
307  //   Stream #2: Text (WebVTT)
308
309  CreateDemuxer("bear-vp8-webvtt.webm");
310  DemuxerStream* text_stream = NULL;
311  EXPECT_CALL(host_, AddTextStream(_, _))
312      .WillOnce(SaveArg<0>(&text_stream));
313  InitializeDemuxerText(true);
314  ASSERT_TRUE(text_stream);
315  EXPECT_EQ(DemuxerStream::TEXT, text_stream->type());
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_Encrypted) {
334  EXPECT_CALL(*this, NeedKeyCBMock(kWebMEncryptInitDataType, NotNull(),
335                                   DecryptConfig::kDecryptionKeySize))
336      .Times(Exactly(2));
337
338  CreateDemuxer("bear-320x240-av_enc-av.webm");
339  InitializeDemuxer();
340}
341
342TEST_F(FFmpegDemuxerTest, Read_Audio) {
343  // We test that on a successful audio packet read.
344  CreateDemuxer("bear-320x240.webm");
345  InitializeDemuxer();
346
347  // Attempt a read from the audio stream and run the message loop until done.
348  DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
349
350  audio->Read(NewReadCB(FROM_HERE, 29, 0));
351  message_loop_.Run();
352
353  audio->Read(NewReadCB(FROM_HERE, 27, 3000));
354  message_loop_.Run();
355}
356
357TEST_F(FFmpegDemuxerTest, Read_Video) {
358  // We test that on a successful video packet read.
359  CreateDemuxer("bear-320x240.webm");
360  InitializeDemuxer();
361
362  // Attempt a read from the video stream and run the message loop until done.
363  DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
364
365  video->Read(NewReadCB(FROM_HERE, 22084, 0));
366  message_loop_.Run();
367
368  video->Read(NewReadCB(FROM_HERE, 1057, 33000));
369  message_loop_.Run();
370}
371
372TEST_F(FFmpegDemuxerTest, Read_Text) {
373  // We test that on a successful text packet read.
374  CreateDemuxer("bear-vp8-webvtt.webm");
375  DemuxerStream* text_stream = NULL;
376  EXPECT_CALL(host_, AddTextStream(_, _))
377      .WillOnce(SaveArg<0>(&text_stream));
378  InitializeDemuxerText(true);
379  ASSERT_TRUE(text_stream);
380  EXPECT_EQ(DemuxerStream::TEXT, text_stream->type());
381
382  text_stream->Read(NewReadCB(FROM_HERE, 31, 0));
383  message_loop_.Run();
384
385  text_stream->Read(NewReadCB(FROM_HERE, 19, 500000));
386  message_loop_.Run();
387}
388
389TEST_F(FFmpegDemuxerTest, Read_VideoNonZeroStart) {
390  // Test the start time is the first timestamp of the video and audio stream.
391  CreateDemuxer("nonzero-start-time.webm");
392  InitializeDemuxer();
393
394  // Attempt a read from the video stream and run the message loop until done.
395  DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
396  DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
397
398  // Check first buffer in video stream.
399  video->Read(NewReadCB(FROM_HERE, 5636, 400000));
400  message_loop_.Run();
401
402  // Check first buffer in audio stream.
403  audio->Read(NewReadCB(FROM_HERE, 165, 396000));
404  message_loop_.Run();
405
406  // Verify that the start time is equal to the lowest timestamp (ie the audio).
407  EXPECT_EQ(demuxer_->GetStartTime().InMicroseconds(), 396000);
408}
409
410TEST_F(FFmpegDemuxerTest, Read_EndOfStream) {
411  // Verify that end of stream buffers are created.
412  CreateDemuxer("bear-320x240.webm");
413  InitializeDemuxer();
414  ReadUntilEndOfStream(demuxer_->GetStream(DemuxerStream::AUDIO));
415}
416
417TEST_F(FFmpegDemuxerTest, Read_EndOfStreamText) {
418  // Verify that end of stream buffers are created.
419  CreateDemuxer("bear-vp8-webvtt.webm");
420  DemuxerStream* text_stream = NULL;
421  EXPECT_CALL(host_, AddTextStream(_, _))
422      .WillOnce(SaveArg<0>(&text_stream));
423  InitializeDemuxerText(true);
424  ASSERT_TRUE(text_stream);
425  EXPECT_EQ(DemuxerStream::TEXT, text_stream->type());
426
427  bool got_eos_buffer = false;
428  const int kMaxBuffers = 10;
429  for (int i = 0; !got_eos_buffer && i < kMaxBuffers; i++) {
430    text_stream->Read(base::Bind(&EosOnReadDone, &got_eos_buffer));
431    message_loop_.Run();
432  }
433
434  EXPECT_TRUE(got_eos_buffer);
435}
436
437TEST_F(FFmpegDemuxerTest, Read_EndOfStream_NoDuration) {
438  // Verify that end of stream buffers are created.
439  CreateDemuxer("bear-320x240.webm");
440  InitializeDemuxer();
441  set_duration_known(false);
442  EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2767)));
443  ReadUntilEndOfStream(demuxer_->GetStream(DemuxerStream::AUDIO));
444  ReadUntilEndOfStream(demuxer_->GetStream(DemuxerStream::VIDEO));
445}
446
447TEST_F(FFmpegDemuxerTest, Read_EndOfStream_NoDuration_VideoOnly) {
448  // Verify that end of stream buffers are created.
449  CreateDemuxer("bear-320x240-video-only.webm");
450  InitializeDemuxer();
451  set_duration_known(false);
452  EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2703)));
453  ReadUntilEndOfStream(demuxer_->GetStream(DemuxerStream::VIDEO));
454}
455
456TEST_F(FFmpegDemuxerTest, Read_EndOfStream_NoDuration_AudioOnly) {
457  // Verify that end of stream buffers are created.
458  CreateDemuxer("bear-320x240-audio-only.webm");
459  InitializeDemuxer();
460  set_duration_known(false);
461  EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2767)));
462  ReadUntilEndOfStream(demuxer_->GetStream(DemuxerStream::AUDIO));
463}
464
465TEST_F(FFmpegDemuxerTest, Read_EndOfStream_NoDuration_UnsupportedStream) {
466  // Verify that end of stream buffers are created and we don't crash
467  // if there are streams in the file that we don't support.
468  CreateDemuxer("vorbis_audio_wmv_video.mkv");
469  InitializeDemuxer();
470  set_duration_known(false);
471  EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(1014)));
472  ReadUntilEndOfStream(demuxer_->GetStream(DemuxerStream::AUDIO));
473}
474
475TEST_F(FFmpegDemuxerTest, Seek) {
476  // We're testing that the demuxer frees all queued packets when it receives
477  // a Seek().
478  CreateDemuxer("bear-320x240.webm");
479  InitializeDemuxer();
480
481  // Get our streams.
482  DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
483  DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
484  ASSERT_TRUE(video);
485  ASSERT_TRUE(audio);
486
487  // Read a video packet and release it.
488  video->Read(NewReadCB(FROM_HERE, 22084, 0));
489  message_loop_.Run();
490
491  // Issue a simple forward seek, which should discard queued packets.
492  WaitableMessageLoopEvent event;
493  demuxer_->Seek(base::TimeDelta::FromMicroseconds(1000000),
494                 event.GetPipelineStatusCB());
495  event.RunAndWaitForStatus(PIPELINE_OK);
496
497  // Audio read #1.
498  audio->Read(NewReadCB(FROM_HERE, 145, 803000));
499  message_loop_.Run();
500
501  // Audio read #2.
502  audio->Read(NewReadCB(FROM_HERE, 148, 826000));
503  message_loop_.Run();
504
505  // Video read #1.
506  video->Read(NewReadCB(FROM_HERE, 5425, 801000));
507  message_loop_.Run();
508
509  // Video read #2.
510  video->Read(NewReadCB(FROM_HERE, 1906, 834000));
511  message_loop_.Run();
512}
513
514TEST_F(FFmpegDemuxerTest, SeekText) {
515  // We're testing that the demuxer frees all queued packets when it receives
516  // a Seek().
517  CreateDemuxer("bear-vp8-webvtt.webm");
518  DemuxerStream* text_stream = NULL;
519  EXPECT_CALL(host_, AddTextStream(_, _))
520      .WillOnce(SaveArg<0>(&text_stream));
521  InitializeDemuxerText(true);
522  ASSERT_TRUE(text_stream);
523  EXPECT_EQ(DemuxerStream::TEXT, text_stream->type());
524
525  // Get our streams.
526  DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
527  DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
528  ASSERT_TRUE(video);
529  ASSERT_TRUE(audio);
530
531  // Read a text packet and release it.
532  text_stream->Read(NewReadCB(FROM_HERE, 31, 0));
533  message_loop_.Run();
534
535  // Issue a simple forward seek, which should discard queued packets.
536  WaitableMessageLoopEvent event;
537  demuxer_->Seek(base::TimeDelta::FromMicroseconds(1000000),
538                 event.GetPipelineStatusCB());
539  event.RunAndWaitForStatus(PIPELINE_OK);
540
541  // Audio read #1.
542  audio->Read(NewReadCB(FROM_HERE, 145, 803000));
543  message_loop_.Run();
544
545  // Audio read #2.
546  audio->Read(NewReadCB(FROM_HERE, 148, 826000));
547  message_loop_.Run();
548
549  // Video read #1.
550  video->Read(NewReadCB(FROM_HERE, 5425, 801000));
551  message_loop_.Run();
552
553  // Video read #2.
554  video->Read(NewReadCB(FROM_HERE, 1906, 834000));
555  message_loop_.Run();
556
557  // Text read #1.
558  text_stream->Read(NewReadCB(FROM_HERE, 19, 500000));
559  message_loop_.Run();
560
561  // Text read #2.
562  text_stream->Read(NewReadCB(FROM_HERE, 19, 1000000));
563  message_loop_.Run();
564}
565
566class MockReadCB {
567 public:
568  MockReadCB() {}
569  ~MockReadCB() {}
570
571  MOCK_METHOD2(Run, void(DemuxerStream::Status status,
572                         const scoped_refptr<DecoderBuffer>& buffer));
573 private:
574  DISALLOW_COPY_AND_ASSIGN(MockReadCB);
575};
576
577TEST_F(FFmpegDemuxerTest, Stop) {
578  // Tests that calling Read() on a stopped demuxer stream immediately deletes
579  // the callback.
580  CreateDemuxer("bear-320x240.webm");
581  InitializeDemuxer();
582
583  // Get our stream.
584  DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
585  ASSERT_TRUE(audio);
586
587  WaitableMessageLoopEvent event;
588  demuxer_->Stop(event.GetClosure());
589  event.RunAndWait();
590
591  // Reads after being stopped are all EOS buffers.
592  StrictMock<MockReadCB> callback;
593  EXPECT_CALL(callback, Run(DemuxerStream::kOk, IsEndOfStreamBuffer()));
594
595  // Attempt the read...
596  audio->Read(base::Bind(&MockReadCB::Run, base::Unretained(&callback)));
597  message_loop_.RunUntilIdle();
598
599  // Don't let the test call Stop() again.
600  demuxer_.reset();
601}
602
603// Verify that seek works properly when the WebM cues data is at the start of
604// the file instead of at the end.
605TEST_F(FFmpegDemuxerTest, SeekWithCuesBeforeFirstCluster) {
606  CreateDemuxer("bear-320x240-cues-in-front.webm");
607  InitializeDemuxer();
608
609  // Get our streams.
610  DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
611  DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
612  ASSERT_TRUE(video);
613  ASSERT_TRUE(audio);
614
615  // Read a video packet and release it.
616  video->Read(NewReadCB(FROM_HERE, 22084, 0));
617  message_loop_.Run();
618
619  // Issue a simple forward seek, which should discard queued packets.
620  WaitableMessageLoopEvent event;
621  demuxer_->Seek(base::TimeDelta::FromMicroseconds(2500000),
622                 event.GetPipelineStatusCB());
623  event.RunAndWaitForStatus(PIPELINE_OK);
624
625  // Audio read #1.
626  audio->Read(NewReadCB(FROM_HERE, 40, 2403000));
627  message_loop_.Run();
628
629  // Audio read #2.
630  audio->Read(NewReadCB(FROM_HERE, 42, 2406000));
631  message_loop_.Run();
632
633  // Video read #1.
634  video->Read(NewReadCB(FROM_HERE, 5276, 2402000));
635  message_loop_.Run();
636
637  // Video read #2.
638  video->Read(NewReadCB(FROM_HERE, 1740, 2436000));
639  message_loop_.Run();
640}
641
642#if defined(USE_PROPRIETARY_CODECS)
643// Ensure ID3v1 tag reading is disabled.  id3_test.mp3 has an ID3v1 tag with the
644// field "title" set to "sample for id3 test".
645TEST_F(FFmpegDemuxerTest, NoID3TagData) {
646  CreateDemuxer("id3_test.mp3");
647  InitializeDemuxer();
648  EXPECT_FALSE(av_dict_get(format_context()->metadata, "title", NULL, 0));
649}
650#endif
651
652#if defined(USE_PROPRIETARY_CODECS)
653// Ensure MP3 files with large image/video based ID3 tags demux okay.  FFmpeg
654// will hand us a video stream to the data which will likely be in a format we
655// don't accept as video; e.g. PNG.
656TEST_F(FFmpegDemuxerTest, Mp3WithVideoStreamID3TagData) {
657  CreateDemuxer("id3_png_test.mp3");
658  InitializeDemuxer();
659
660  // Ensure the expected streams are present.
661  EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::VIDEO));
662  EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::AUDIO));
663}
664#endif
665
666// Ensure a video with an unsupported audio track still results in the video
667// stream being demuxed.
668TEST_F(FFmpegDemuxerTest, UnsupportedAudioSupportedVideoDemux) {
669  CreateDemuxer("speex_audio_vorbis_video.ogv");
670  InitializeDemuxer();
671
672  // Ensure the expected streams are present.
673  EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::VIDEO));
674  EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::AUDIO));
675}
676
677// Ensure a video with an unsupported video track still results in the audio
678// stream being demuxed.
679TEST_F(FFmpegDemuxerTest, UnsupportedVideoSupportedAudioDemux) {
680  CreateDemuxer("vorbis_audio_wmv_video.mkv");
681  InitializeDemuxer();
682
683  // Ensure the expected streams are present.
684  EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::VIDEO));
685  EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::AUDIO));
686}
687
688#if defined(USE_PROPRIETARY_CODECS)
689// FFmpeg returns null data pointers when samples have zero size, leading to
690// mistakenly creating end of stream buffers http://crbug.com/169133
691TEST_F(FFmpegDemuxerTest, MP4_ZeroStszEntry) {
692  CreateDemuxer("bear-1280x720-zero-stsz-entry.mp4");
693  InitializeDemuxer();
694  ReadUntilEndOfStream(demuxer_->GetStream(DemuxerStream::AUDIO));
695}
696
697
698static void ValidateAnnexB(DemuxerStream* stream,
699                           DemuxerStream::Status status,
700                           const scoped_refptr<DecoderBuffer>& buffer) {
701  EXPECT_EQ(status, DemuxerStream::kOk);
702
703  if (buffer->end_of_stream()) {
704    base::MessageLoop::current()->PostTask(
705        FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
706    return;
707  }
708
709  bool is_valid =
710      mp4::AVC::IsValidAnnexB(buffer->data(), buffer->data_size());
711  EXPECT_TRUE(is_valid);
712
713  if (!is_valid) {
714    LOG(ERROR) << "Buffer contains invalid Annex B data.";
715    base::MessageLoop::current()->PostTask(
716        FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
717    return;
718  }
719
720  stream->Read(base::Bind(&ValidateAnnexB, stream));
721};
722
723TEST_F(FFmpegDemuxerTest, IsValidAnnexB) {
724  const char* files[] = {
725    "bear-1280x720-av_frag.mp4",
726    "bear-1280x720-av_with-aud-nalus_frag.mp4"
727  };
728
729  for (size_t i = 0; i < arraysize(files); ++i) {
730    DVLOG(1) << "Testing " << files[i];
731    CreateDemuxer(files[i]);
732    InitializeDemuxer();
733
734    // Ensure the expected streams are present.
735    DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
736    ASSERT_TRUE(stream);
737    stream->EnableBitstreamConverter();
738
739    stream->Read(base::Bind(&ValidateAnnexB, stream));
740    message_loop_.Run();
741
742    WaitableMessageLoopEvent event;
743    demuxer_->Stop(event.GetClosure());
744    event.RunAndWait();
745    demuxer_.reset();
746    data_source_.reset();
747  }
748}
749
750#endif
751
752}  // namespace media
753