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.
10 #include "base/files/file_path.h"
11 #include "base/path_service.h"
12 #include "base/threading/thread.h"
13 #include "media/base/decrypt_config.h"
14 #include "media/base/media_log.h"
15 #include "media/base/mock_demuxer_host.h"
16 #include "media/base/test_helpers.h"
17 #include "media/ffmpeg/ffmpeg_common.h"
18 #include "media/filters/ffmpeg_demuxer.h"
19 #include "media/filters/file_data_source.h"
20 #include "media/webm/webm_crypto_helpers.h"
21 #include "testing/gtest/include/gtest/gtest.h"
23 using ::testing::AnyNumber;
24 using ::testing::DoAll;
25 using ::testing::Exactly;
26 using ::testing::InSequence;
27 using ::testing::Invoke;
28 using ::testing::NotNull;
29 using ::testing::Return;
30 using ::testing::SaveArg;
31 using ::testing::SetArgPointee;
32 using ::testing::StrictMock;
33 using ::testing::WithArgs;
38 MATCHER(IsEndOfStreamBuffer,
39 std::string(negation ? "isn't" : "is") + " end of stream") {
40 return arg->end_of_stream();
43 static void EosOnReadDone(bool* got_eos_buffer,
44 DemuxerStream::Status status,
45 const scoped_refptr<DecoderBuffer>& buffer) {
46 base::MessageLoop::current()->PostTask(
47 FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
49 EXPECT_EQ(status, DemuxerStream::kOk);
50 if (buffer->end_of_stream()) {
51 *got_eos_buffer = true;
55 EXPECT_TRUE(buffer->data());
56 EXPECT_GT(buffer->data_size(), 0);
57 *got_eos_buffer = false;
61 // Fixture class to facilitate writing tests. Takes care of setting up the
62 // FFmpeg, pipeline and filter host mocks.
63 class FFmpegDemuxerTest : public testing::Test {
65 FFmpegDemuxerTest() {}
67 virtual ~FFmpegDemuxerTest() {
69 WaitableMessageLoopEvent event;
70 demuxer_->Stop(event.GetClosure());
75 void CreateDemuxer(const std::string& name) {
78 EXPECT_CALL(host_, SetTotalBytes(_)).Times(AnyNumber());
79 EXPECT_CALL(host_, AddBufferedByteRange(_, _)).Times(AnyNumber());
80 EXPECT_CALL(host_, AddBufferedTimeRange(_, _)).Times(AnyNumber());
82 CreateDataSource(name);
84 Demuxer::NeedKeyCB need_key_cb =
85 base::Bind(&FFmpegDemuxerTest::NeedKeyCB, base::Unretained(this));
86 demuxer_.reset(new FFmpegDemuxer(message_loop_.message_loop_proxy(),
92 MOCK_METHOD1(CheckPoint, void(int v));
94 void InitializeDemuxer() {
95 EXPECT_CALL(host_, SetDuration(_));
96 WaitableMessageLoopEvent event;
97 demuxer_->Initialize(&host_, event.GetPipelineStatusCB());
98 event.RunAndWaitForStatus(PIPELINE_OK);
101 MOCK_METHOD2(OnReadDoneCalled, void(int, int64));
103 // Verifies that |buffer| has a specific |size| and |timestamp|.
104 // |location| simply indicates where the call to this function was made.
105 // This makes it easier to track down where test failures occur.
106 void OnReadDone(const tracked_objects::Location& location,
107 int size, int64 timestampInMicroseconds,
108 DemuxerStream::Status status,
109 const scoped_refptr<DecoderBuffer>& buffer) {
110 std::string location_str;
111 location.Write(true, false, &location_str);
112 location_str += "\n";
113 SCOPED_TRACE(location_str);
114 EXPECT_EQ(status, DemuxerStream::kOk);
115 OnReadDoneCalled(size, timestampInMicroseconds);
116 EXPECT_TRUE(buffer.get() != NULL);
117 EXPECT_EQ(size, buffer->data_size());
118 EXPECT_EQ(base::TimeDelta::FromMicroseconds(timestampInMicroseconds),
119 buffer->timestamp());
121 DCHECK_EQ(&message_loop_, base::MessageLoop::current());
122 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
125 DemuxerStream::ReadCB NewReadCB(const tracked_objects::Location& location,
126 int size, int64 timestampInMicroseconds) {
127 EXPECT_CALL(*this, OnReadDoneCalled(size, timestampInMicroseconds));
128 return base::Bind(&FFmpegDemuxerTest::OnReadDone, base::Unretained(this),
129 location, size, timestampInMicroseconds);
132 // TODO(xhwang): This is a workaround of the issue that move-only parameters
133 // are not supported in mocked methods. Remove this when the issue is fixed
134 // (http://code.google.com/p/googletest/issues/detail?id=395) or when we use
135 // std::string instead of scoped_ptr<uint8[]> (http://crbug.com/130689).
136 MOCK_METHOD3(NeedKeyCBMock, void(const std::string& type,
137 const uint8* init_data, int init_data_size));
138 void NeedKeyCB(const std::string& type,
139 const std::vector<uint8>& init_data) {
140 const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
141 NeedKeyCBMock(type, init_data_ptr, init_data.size());
144 // Accessor to demuxer internals.
145 void set_duration_known(bool duration_known) {
146 demuxer_->duration_known_ = duration_known;
149 bool IsStreamStopped(DemuxerStream::Type type) {
150 DemuxerStream* stream = demuxer_->GetStream(type);
152 return !static_cast<FFmpegDemuxerStream*>(stream)->demuxer_;
156 scoped_ptr<FileDataSource> data_source_;
157 scoped_ptr<FFmpegDemuxer> demuxer_;
158 StrictMock<MockDemuxerHost> host_;
159 base::MessageLoop message_loop_;
161 AVFormatContext* format_context() {
162 return demuxer_->glue_->format_context();
165 void ReadUntilEndOfStream() {
166 // We should expect an end of stream buffer.
167 DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
169 bool got_eos_buffer = false;
170 const int kMaxBuffers = 170;
171 for (int i = 0; !got_eos_buffer && i < kMaxBuffers; i++) {
172 audio->Read(base::Bind(&EosOnReadDone, &got_eos_buffer));
176 EXPECT_TRUE(got_eos_buffer);
180 void CreateDataSource(const std::string& name) {
181 CHECK(!data_source_);
183 base::FilePath file_path;
184 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
186 file_path = file_path.Append(FILE_PATH_LITERAL("media"))
187 .Append(FILE_PATH_LITERAL("test"))
188 .Append(FILE_PATH_LITERAL("data"))
191 data_source_.reset(new FileDataSource());
192 EXPECT_TRUE(data_source_->Initialize(file_path));
195 DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxerTest);
198 TEST_F(FFmpegDemuxerTest, Initialize_OpenFails) {
199 // Simulate avformat_open_input() failing.
200 CreateDemuxer("ten_byte_file");
201 WaitableMessageLoopEvent event;
202 demuxer_->Initialize(&host_, event.GetPipelineStatusCB());
203 event.RunAndWaitForStatus(DEMUXER_ERROR_COULD_NOT_OPEN);
206 // TODO(acolwell): Uncomment this test when we discover a file that passes
207 // avformat_open_input(), but has avformat_find_stream_info() fail.
209 //TEST_F(FFmpegDemuxerTest, Initialize_ParseFails) {
210 // ("find_stream_info_fail.webm");
211 // demuxer_->Initialize(
212 // &host_, NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_PARSE));
213 // message_loop_.RunUntilIdle();
216 TEST_F(FFmpegDemuxerTest, Initialize_NoStreams) {
217 // Open a file with no streams whatsoever.
218 CreateDemuxer("no_streams.webm");
219 WaitableMessageLoopEvent event;
220 demuxer_->Initialize(&host_, event.GetPipelineStatusCB());
221 event.RunAndWaitForStatus(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
224 TEST_F(FFmpegDemuxerTest, Initialize_NoAudioVideo) {
225 // Open a file containing streams but none of which are audio/video streams.
226 CreateDemuxer("no_audio_video.webm");
227 WaitableMessageLoopEvent event;
228 demuxer_->Initialize(&host_, event.GetPipelineStatusCB());
229 event.RunAndWaitForStatus(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
232 TEST_F(FFmpegDemuxerTest, Initialize_Successful) {
233 CreateDemuxer("bear-320x240.webm");
236 // Video stream should be present.
237 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
239 EXPECT_EQ(DemuxerStream::VIDEO, stream->type());
241 const VideoDecoderConfig& video_config = stream->video_decoder_config();
242 EXPECT_EQ(kCodecVP8, video_config.codec());
243 EXPECT_EQ(VideoFrame::YV12, video_config.format());
244 EXPECT_EQ(320, video_config.coded_size().width());
245 EXPECT_EQ(240, video_config.coded_size().height());
246 EXPECT_EQ(0, video_config.visible_rect().x());
247 EXPECT_EQ(0, video_config.visible_rect().y());
248 EXPECT_EQ(320, video_config.visible_rect().width());
249 EXPECT_EQ(240, video_config.visible_rect().height());
250 EXPECT_EQ(320, video_config.natural_size().width());
251 EXPECT_EQ(240, video_config.natural_size().height());
252 EXPECT_FALSE(video_config.extra_data());
253 EXPECT_EQ(0u, video_config.extra_data_size());
255 // Audio stream should be present.
256 stream = demuxer_->GetStream(DemuxerStream::AUDIO);
258 EXPECT_EQ(DemuxerStream::AUDIO, stream->type());
260 const AudioDecoderConfig& audio_config = stream->audio_decoder_config();
261 EXPECT_EQ(kCodecVorbis, audio_config.codec());
262 EXPECT_EQ(32, audio_config.bits_per_channel());
263 EXPECT_EQ(CHANNEL_LAYOUT_STEREO, audio_config.channel_layout());
264 EXPECT_EQ(44100, audio_config.samples_per_second());
265 EXPECT_EQ(kSampleFormatPlanarF32, audio_config.sample_format());
266 EXPECT_TRUE(audio_config.extra_data());
267 EXPECT_GT(audio_config.extra_data_size(), 0u);
269 // Unknown stream should never be present.
270 EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::UNKNOWN));
273 TEST_F(FFmpegDemuxerTest, Initialize_Multitrack) {
274 // Open a file containing the following streams:
275 // Stream #0: Video (VP8)
276 // Stream #1: Audio (Vorbis)
277 // Stream #2: Subtitles (SRT)
278 // Stream #3: Video (Theora)
279 // Stream #4: Audio (16-bit signed little endian PCM)
281 // We should only pick the first audio/video streams we come across.
282 CreateDemuxer("bear-320x240-multitrack.webm");
285 // Video stream should be VP8.
286 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
288 EXPECT_EQ(DemuxerStream::VIDEO, stream->type());
289 EXPECT_EQ(kCodecVP8, stream->video_decoder_config().codec());
291 // Audio stream should be Vorbis.
292 stream = demuxer_->GetStream(DemuxerStream::AUDIO);
294 EXPECT_EQ(DemuxerStream::AUDIO, stream->type());
295 EXPECT_EQ(kCodecVorbis, stream->audio_decoder_config().codec());
297 // Unknown stream should never be present.
298 EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::UNKNOWN));
301 TEST_F(FFmpegDemuxerTest, Initialize_Encrypted) {
302 EXPECT_CALL(*this, NeedKeyCBMock(kWebMEncryptInitDataType, NotNull(),
303 DecryptConfig::kDecryptionKeySize))
306 CreateDemuxer("bear-320x240-av_enc-av.webm");
310 TEST_F(FFmpegDemuxerTest, Read_Audio) {
311 // We test that on a successful audio packet read.
312 CreateDemuxer("bear-320x240.webm");
315 // Attempt a read from the audio stream and run the message loop until done.
316 DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
318 audio->Read(NewReadCB(FROM_HERE, 29, 0));
321 audio->Read(NewReadCB(FROM_HERE, 27, 3000));
325 TEST_F(FFmpegDemuxerTest, Read_Video) {
326 // We test that on a successful video packet read.
327 CreateDemuxer("bear-320x240.webm");
330 // Attempt a read from the video stream and run the message loop until done.
331 DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
333 video->Read(NewReadCB(FROM_HERE, 22084, 0));
336 video->Read(NewReadCB(FROM_HERE, 1057, 33000));
340 TEST_F(FFmpegDemuxerTest, Read_VideoNonZeroStart) {
341 // Test the start time is the first timestamp of the video and audio stream.
342 CreateDemuxer("nonzero-start-time.webm");
345 // Attempt a read from the video stream and run the message loop until done.
346 DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
347 DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
349 // Check first buffer in video stream.
350 video->Read(NewReadCB(FROM_HERE, 5636, 400000));
353 // Check first buffer in audio stream.
354 audio->Read(NewReadCB(FROM_HERE, 165, 396000));
357 // Verify that the start time is equal to the lowest timestamp (ie the audio).
358 EXPECT_EQ(demuxer_->GetStartTime().InMicroseconds(), 396000);
361 TEST_F(FFmpegDemuxerTest, Read_EndOfStream) {
362 // Verify that end of stream buffers are created.
363 CreateDemuxer("bear-320x240.webm");
365 ReadUntilEndOfStream();
368 TEST_F(FFmpegDemuxerTest, Read_EndOfStream_NoDuration) {
369 // Verify that end of stream buffers are created.
370 CreateDemuxer("bear-320x240.webm");
372 set_duration_known(false);
373 EXPECT_CALL(host_, SetDuration(_));
374 ReadUntilEndOfStream();
377 TEST_F(FFmpegDemuxerTest, Seek) {
378 // We're testing that the demuxer frees all queued packets when it receives
380 CreateDemuxer("bear-320x240.webm");
384 DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
385 DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
389 // Read a video packet and release it.
390 video->Read(NewReadCB(FROM_HERE, 22084, 0));
393 // Issue a simple forward seek, which should discard queued packets.
394 WaitableMessageLoopEvent event;
395 demuxer_->Seek(base::TimeDelta::FromMicroseconds(1000000),
396 event.GetPipelineStatusCB());
397 event.RunAndWaitForStatus(PIPELINE_OK);
400 audio->Read(NewReadCB(FROM_HERE, 145, 803000));
404 audio->Read(NewReadCB(FROM_HERE, 148, 826000));
408 video->Read(NewReadCB(FROM_HERE, 5425, 801000));
412 video->Read(NewReadCB(FROM_HERE, 1906, 834000));
421 MOCK_METHOD2(Run, void(DemuxerStream::Status status,
422 const scoped_refptr<DecoderBuffer>& buffer));
424 DISALLOW_COPY_AND_ASSIGN(MockReadCB);
427 TEST_F(FFmpegDemuxerTest, Stop) {
428 // Tests that calling Read() on a stopped demuxer stream immediately deletes
430 CreateDemuxer("bear-320x240.webm");
434 DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
437 WaitableMessageLoopEvent event;
438 demuxer_->Stop(event.GetClosure());
441 // Reads after being stopped are all EOS buffers.
442 StrictMock<MockReadCB> callback;
443 EXPECT_CALL(callback, Run(DemuxerStream::kOk, IsEndOfStreamBuffer()));
445 // Attempt the read...
446 audio->Read(base::Bind(&MockReadCB::Run, base::Unretained(&callback)));
447 message_loop_.RunUntilIdle();
449 // Don't let the test call Stop() again.
453 TEST_F(FFmpegDemuxerTest, DisableAudioStream) {
454 // We are doing the following things here:
455 // 1. Initialize the demuxer with audio and video stream.
456 // 2. Send a "disable audio stream" message to the demuxer.
457 // 3. Demuxer will free audio packets even if audio stream was initialized.
458 CreateDemuxer("bear-320x240.webm");
461 // Submit a "disable audio stream" message to the demuxer.
462 demuxer_->OnAudioRendererDisabled();
463 message_loop_.RunUntilIdle();
466 DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
467 DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
471 // The audio stream should have been prematurely stopped.
472 EXPECT_FALSE(IsStreamStopped(DemuxerStream::VIDEO));
473 EXPECT_TRUE(IsStreamStopped(DemuxerStream::AUDIO));
475 // Attempt a read from the video stream: it should return valid data.
476 video->Read(NewReadCB(FROM_HERE, 22084, 0));
479 // Attempt a read from the audio stream: it should immediately return end of
480 // stream without requiring the message loop to read data.
481 bool got_eos_buffer = false;
482 audio->Read(base::Bind(&EosOnReadDone, &got_eos_buffer));
483 message_loop_.RunUntilIdle();
484 EXPECT_TRUE(got_eos_buffer);
487 // Verify that seek works properly when the WebM cues data is at the start of
488 // the file instead of at the end.
489 TEST_F(FFmpegDemuxerTest, SeekWithCuesBeforeFirstCluster) {
490 CreateDemuxer("bear-320x240-cues-in-front.webm");
494 DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO);
495 DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO);
499 // Read a video packet and release it.
500 video->Read(NewReadCB(FROM_HERE, 22084, 0));
503 // Issue a simple forward seek, which should discard queued packets.
504 WaitableMessageLoopEvent event;
505 demuxer_->Seek(base::TimeDelta::FromMicroseconds(2500000),
506 event.GetPipelineStatusCB());
507 event.RunAndWaitForStatus(PIPELINE_OK);
510 audio->Read(NewReadCB(FROM_HERE, 40, 2403000));
514 audio->Read(NewReadCB(FROM_HERE, 42, 2406000));
518 video->Read(NewReadCB(FROM_HERE, 5276, 2402000));
522 video->Read(NewReadCB(FROM_HERE, 1740, 2436000));
526 // Ensure ID3v1 tag reading is disabled. id3_test.mp3 has an ID3v1 tag with the
527 // field "title" set to "sample for id3 test".
528 TEST_F(FFmpegDemuxerTest, NoID3TagData) {
529 #if !defined(USE_PROPRIETARY_CODECS)
532 CreateDemuxer("id3_test.mp3");
534 EXPECT_FALSE(av_dict_get(format_context()->metadata, "title", NULL, 0));
537 // Ensure MP3 files with large image/video based ID3 tags demux okay. FFmpeg
538 // will hand us a video stream to the data which will likely be in a format we
539 // don't accept as video; e.g. PNG.
540 TEST_F(FFmpegDemuxerTest, Mp3WithVideoStreamID3TagData) {
541 #if !defined(USE_PROPRIETARY_CODECS)
544 CreateDemuxer("id3_png_test.mp3");
547 // Ensure the expected streams are present.
548 EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::VIDEO));
549 EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::AUDIO));
552 // Ensure a video with an unsupported audio track still results in the video
553 // stream being demuxed.
554 TEST_F(FFmpegDemuxerTest, UnsupportedAudioSupportedVideoDemux) {
555 CreateDemuxer("speex_audio_vorbis_video.ogv");
558 // Ensure the expected streams are present.
559 EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::VIDEO));
560 EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::AUDIO));
563 // Ensure a video with an unsupported video track still results in the audio
564 // stream being demuxed.
565 TEST_F(FFmpegDemuxerTest, UnsupportedVideoSupportedAudioDemux) {
566 CreateDemuxer("vorbis_audio_wmv_video.mkv");
569 // Ensure the expected streams are present.
570 EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::VIDEO));
571 EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::AUDIO));
574 // FFmpeg returns null data pointers when samples have zero size, leading to
575 // mistakenly creating end of stream buffers http://crbug.com/169133
576 TEST_F(FFmpegDemuxerTest, MP4_ZeroStszEntry) {
577 #if !defined(USE_PROPRIETARY_CODECS)
580 CreateDemuxer("bear-1280x720-zero-stsz-entry.mp4");
582 ReadUntilEndOfStream();