diff options
author | Torne (Richard Coles) <torne@google.com> | 2012-11-14 11:43:16 +0000 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2012-11-14 11:43:16 +0000 |
commit | 5821806d5e7f356e8fa4b058a389a808ea183019 (patch) | |
tree | e19f4793aac92e2c0d9a01087019a60d6657d838 /media/filters/ffmpeg_demuxer_unittest.cc | |
parent | 8e79a8efe247f109aafd917a69e8a392961b3687 (diff) | |
download | chromium_org-5821806d5e7f356e8fa4b058a389a808ea183019.tar.gz |
Merge from Chromium at DEPS revision r167172
This commit was generated by merge_to_master.py.
Change-Id: Ib8d56fd5ae39a2d7e8c91dcd76cc6d13f25f2aab
Diffstat (limited to 'media/filters/ffmpeg_demuxer_unittest.cc')
-rw-r--r-- | media/filters/ffmpeg_demuxer_unittest.cc | 700 |
1 files changed, 700 insertions, 0 deletions
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc new file mode 100644 index 0000000000..b86008b1b3 --- /dev/null +++ b/media/filters/ffmpeg_demuxer_unittest.cc @@ -0,0 +1,700 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <algorithm> +#include <deque> +#include <string> + +#include "base/bind.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/threading/thread.h" +#include "media/base/mock_callback.h" +#include "media/base/mock_demuxer_host.h" +#include "media/ffmpeg/ffmpeg_common.h" +#include "media/filters/ffmpeg_demuxer.h" +#include "media/filters/file_data_source.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::AnyNumber; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SaveArg; +using ::testing::SetArgPointee; +using ::testing::StrictMock; +using ::testing::WithArgs; +using ::testing::_; + +namespace media { + +MATCHER(IsEndOfStreamBuffer, + std::string(negation ? "isn't" : "is") + " end of stream") { + return arg->IsEndOfStream(); +} + +static void EosOnReadDone(bool* got_eos_buffer, + DemuxerStream::Status status, + const scoped_refptr<DecoderBuffer>& buffer) { + EXPECT_EQ(status, DemuxerStream::kOk); + if (buffer->IsEndOfStream()) { + *got_eos_buffer = true; + EXPECT_TRUE(!buffer->GetData()); + EXPECT_EQ(buffer->GetDataSize(), 0); + return; + } + + EXPECT_TRUE(buffer->GetData()); + EXPECT_GT(buffer->GetDataSize(), 0); + *got_eos_buffer = false; +}; + + +// Fixture class to facilitate writing tests. Takes care of setting up the +// FFmpeg, pipeline and filter host mocks. +class FFmpegDemuxerTest : public testing::Test { + protected: + FFmpegDemuxerTest() {} + + virtual ~FFmpegDemuxerTest() { + if (demuxer_) { + // Call Stop() to shut down internal threads. + demuxer_->Stop(NewExpectedClosure()); + } + + // Finish up any remaining tasks. + message_loop_.RunAllPending(); + // Release the reference to the demuxer. + demuxer_ = NULL; + } + + void CreateDemuxer(const std::string& name) { + CreateDemuxer(name, false); + } + + void CreateDemuxer(const std::string& name, bool disable_file_size) { + CHECK(!demuxer_); + + EXPECT_CALL(host_, SetTotalBytes(_)).Times(AnyNumber()); + EXPECT_CALL(host_, AddBufferedByteRange(_, _)).Times(AnyNumber()); + EXPECT_CALL(host_, AddBufferedTimeRange(_, _)).Times(AnyNumber()); + + CreateDataSource(name, disable_file_size); + demuxer_ = new FFmpegDemuxer(message_loop_.message_loop_proxy(), + data_source_); + } + + MOCK_METHOD1(CheckPoint, void(int v)); + + void InitializeDemuxer() { + EXPECT_CALL(host_, SetDuration(_)); + demuxer_->Initialize(&host_, NewExpectedStatusCB(PIPELINE_OK)); + message_loop_.RunAllPending(); + } + + MOCK_METHOD2(OnReadDoneCalled, void(int, int64)); + + // Verifies that |buffer| has a specific |size| and |timestamp|. + // |location| simply indicates where the call to this function was made. + // This makes it easier to track down where test failures occur. + void OnReadDone(const tracked_objects::Location& location, + int size, int64 timestampInMicroseconds, + DemuxerStream::Status status, + const scoped_refptr<DecoderBuffer>& buffer) { + std::string location_str; + location.Write(true, false, &location_str); + location_str += "\n"; + SCOPED_TRACE(location_str); + EXPECT_EQ(status, DemuxerStream::kOk); + OnReadDoneCalled(size, timestampInMicroseconds); + EXPECT_TRUE(buffer.get() != NULL); + EXPECT_EQ(size, buffer->GetDataSize()); + EXPECT_EQ(base::TimeDelta::FromMicroseconds(timestampInMicroseconds), + buffer->GetTimestamp()); + } + + DemuxerStream::ReadCB NewReadCB(const tracked_objects::Location& location, + int size, int64 timestampInMicroseconds) { + EXPECT_CALL(*this, OnReadDoneCalled(size, timestampInMicroseconds)); + return base::Bind(&FFmpegDemuxerTest::OnReadDone, base::Unretained(this), + location, size, timestampInMicroseconds); + } + + // Accessor to demuxer internals. + void set_duration_known(bool duration_known) { + demuxer_->duration_known_ = duration_known; + } + + bool IsStreamStopped(DemuxerStream::Type type) { + DemuxerStream* stream = demuxer_->GetStream(type); + CHECK(stream); + return static_cast<FFmpegDemuxerStream*>(stream)->stopped_; + } + + // Fixture members. + scoped_refptr<FileDataSource> data_source_; + scoped_refptr<FFmpegDemuxer> demuxer_; + StrictMock<MockDemuxerHost> host_; + MessageLoop message_loop_; + + AVFormatContext* format_context() { + return demuxer_->glue_->format_context(); + } + + void ReadUntilEndOfStream() { + // We should expect an end of stream buffer. + scoped_refptr<DemuxerStream> audio = + demuxer_->GetStream(DemuxerStream::AUDIO); + + bool got_eos_buffer = false; + const int kMaxBuffers = 170; + for (int i = 0; !got_eos_buffer && i < kMaxBuffers; i++) { + audio->Read(base::Bind(&EosOnReadDone, &got_eos_buffer)); + message_loop_.RunAllPending(); + } + + EXPECT_TRUE(got_eos_buffer); + } + + private: + void CreateDataSource(const std::string& name, bool disable_file_size) { + CHECK(!data_source_); + + FilePath file_path; + EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &file_path)); + + file_path = file_path.Append(FILE_PATH_LITERAL("media")) + .Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("data")) + .AppendASCII(name); + + data_source_ = new FileDataSource(disable_file_size); + EXPECT_TRUE(data_source_->Initialize(file_path.MaybeAsASCII())); + } + + DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxerTest); +}; + +TEST_F(FFmpegDemuxerTest, Initialize_OpenFails) { + // Simulate avformat_open_input() failing. + CreateDemuxer("ten_byte_file"), + demuxer_->Initialize( + &host_, NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_OPEN)); + + message_loop_.RunAllPending(); +} + +// TODO(acolwell): Uncomment this test when we discover a file that passes +// avformat_open_input(), but has avformat_find_stream_info() fail. +// +//TEST_F(FFmpegDemuxerTest, Initialize_ParseFails) { +// CreateDemuxer("find_stream_info_fail.webm"); +// demuxer_->Initialize( +// &host_, NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_PARSE)); +// message_loop_.RunAllPending(); +//} + +TEST_F(FFmpegDemuxerTest, Initialize_NoStreams) { + // Open a file with no streams whatsoever. + CreateDemuxer("no_streams.webm"); + demuxer_->Initialize( + &host_, NewExpectedStatusCB(DEMUXER_ERROR_NO_SUPPORTED_STREAMS)); + message_loop_.RunAllPending(); +} + +TEST_F(FFmpegDemuxerTest, Initialize_NoAudioVideo) { + // Open a file containing streams but none of which are audio/video streams. + CreateDemuxer("no_audio_video.webm"); + demuxer_->Initialize( + &host_, NewExpectedStatusCB(DEMUXER_ERROR_NO_SUPPORTED_STREAMS)); + message_loop_.RunAllPending(); +} + +TEST_F(FFmpegDemuxerTest, Initialize_Successful) { + CreateDemuxer("bear-320x240.webm"); + InitializeDemuxer(); + + // Video stream should be present. + scoped_refptr<DemuxerStream> stream = + demuxer_->GetStream(DemuxerStream::VIDEO); + ASSERT_TRUE(stream); + EXPECT_EQ(DemuxerStream::VIDEO, stream->type()); + + const VideoDecoderConfig& video_config = stream->video_decoder_config(); + EXPECT_EQ(kCodecVP8, video_config.codec()); + EXPECT_EQ(VideoFrame::YV12, video_config.format()); + EXPECT_EQ(320, video_config.coded_size().width()); + EXPECT_EQ(240, video_config.coded_size().height()); + EXPECT_EQ(0, video_config.visible_rect().x()); + EXPECT_EQ(0, video_config.visible_rect().y()); + EXPECT_EQ(320, video_config.visible_rect().width()); + EXPECT_EQ(240, video_config.visible_rect().height()); + EXPECT_EQ(320, video_config.natural_size().width()); + EXPECT_EQ(240, video_config.natural_size().height()); + EXPECT_FALSE(video_config.extra_data()); + EXPECT_EQ(0u, video_config.extra_data_size()); + + // Audio stream should be present. + stream = demuxer_->GetStream(DemuxerStream::AUDIO); + ASSERT_TRUE(stream); + EXPECT_EQ(DemuxerStream::AUDIO, stream->type()); + + const AudioDecoderConfig& audio_config = stream->audio_decoder_config(); + EXPECT_EQ(kCodecVorbis, audio_config.codec()); + EXPECT_EQ(16, audio_config.bits_per_channel()); + EXPECT_EQ(CHANNEL_LAYOUT_STEREO, audio_config.channel_layout()); + EXPECT_EQ(44100, audio_config.samples_per_second()); + EXPECT_TRUE(audio_config.extra_data()); + EXPECT_GT(audio_config.extra_data_size(), 0u); + + // Unknown stream should never be present. + EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::UNKNOWN)); +} + +TEST_F(FFmpegDemuxerTest, Initialize_Multitrack) { + // Open a file containing the following streams: + // Stream #0: Video (VP8) + // Stream #1: Audio (Vorbis) + // Stream #2: Subtitles (SRT) + // Stream #3: Video (Theora) + // Stream #4: Audio (16-bit signed little endian PCM) + // + // We should only pick the first audio/video streams we come across. + CreateDemuxer("bear-320x240-multitrack.webm"); + InitializeDemuxer(); + + // Video stream should be VP8. + scoped_refptr<DemuxerStream> stream = + demuxer_->GetStream(DemuxerStream::VIDEO); + ASSERT_TRUE(stream); + EXPECT_EQ(DemuxerStream::VIDEO, stream->type()); + EXPECT_EQ(kCodecVP8, stream->video_decoder_config().codec()); + + // Audio stream should be Vorbis. + stream = demuxer_->GetStream(DemuxerStream::AUDIO); + ASSERT_TRUE(stream); + EXPECT_EQ(DemuxerStream::AUDIO, stream->type()); + EXPECT_EQ(kCodecVorbis, stream->audio_decoder_config().codec()); + + // Unknown stream should never be present. + EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::UNKNOWN)); +} + +TEST_F(FFmpegDemuxerTest, Read_Audio) { + // We test that on a successful audio packet read. + CreateDemuxer("bear-320x240.webm"); + InitializeDemuxer(); + + // Attempt a read from the audio stream and run the message loop until done. + scoped_refptr<DemuxerStream> audio = + demuxer_->GetStream(DemuxerStream::AUDIO); + + audio->Read(NewReadCB(FROM_HERE, 29, 0)); + message_loop_.RunAllPending(); + + audio->Read(NewReadCB(FROM_HERE, 27, 3000)); + message_loop_.RunAllPending(); +} + +TEST_F(FFmpegDemuxerTest, Read_Video) { + // We test that on a successful video packet read. + CreateDemuxer("bear-320x240.webm"); + InitializeDemuxer(); + + // Attempt a read from the video stream and run the message loop until done. + scoped_refptr<DemuxerStream> video = + demuxer_->GetStream(DemuxerStream::VIDEO); + + video->Read(NewReadCB(FROM_HERE, 22084, 0)); + message_loop_.RunAllPending(); + + video->Read(NewReadCB(FROM_HERE, 1057, 33000)); + message_loop_.RunAllPending(); +} + +TEST_F(FFmpegDemuxerTest, Read_VideoNonZeroStart) { + // Test the start time is the first timestamp of the video and audio stream. + CreateDemuxer("nonzero-start-time.webm"); + InitializeDemuxer(); + + // Attempt a read from the video stream and run the message loop until done. + scoped_refptr<DemuxerStream> video = + demuxer_->GetStream(DemuxerStream::VIDEO); + scoped_refptr<DemuxerStream> audio = + demuxer_->GetStream(DemuxerStream::AUDIO); + + // Check first buffer in video stream. + video->Read(NewReadCB(FROM_HERE, 5636, 400000)); + message_loop_.RunAllPending(); + + // Check first buffer in audio stream. + audio->Read(NewReadCB(FROM_HERE, 165, 396000)); + message_loop_.RunAllPending(); + + // Verify that the start time is equal to the lowest timestamp (ie the audio). + EXPECT_EQ(demuxer_->GetStartTime().InMicroseconds(), 396000); +} + +TEST_F(FFmpegDemuxerTest, Read_EndOfStream) { + // Verify that end of stream buffers are created. + CreateDemuxer("bear-320x240.webm"); + InitializeDemuxer(); + ReadUntilEndOfStream(); +} + +TEST_F(FFmpegDemuxerTest, Read_EndOfStream_NoDuration) { + // Verify that end of stream buffers are created. + CreateDemuxer("bear-320x240.webm", false); + InitializeDemuxer(); + set_duration_known(false); + EXPECT_CALL(host_, SetDuration(_)); + ReadUntilEndOfStream(); +} + +TEST_F(FFmpegDemuxerTest, Seek) { + // We're testing that the demuxer frees all queued packets when it receives + // a Seek(). + CreateDemuxer("bear-320x240.webm"); + InitializeDemuxer(); + + // Get our streams. + scoped_refptr<DemuxerStream> video = + demuxer_->GetStream(DemuxerStream::VIDEO); + scoped_refptr<DemuxerStream> audio = + demuxer_->GetStream(DemuxerStream::AUDIO); + ASSERT_TRUE(video); + ASSERT_TRUE(audio); + + // Read a video packet and release it. + video->Read(NewReadCB(FROM_HERE, 22084, 0)); + message_loop_.RunAllPending(); + + // Issue a simple forward seek, which should discard queued packets. + demuxer_->Seek(base::TimeDelta::FromMicroseconds(1000000), + NewExpectedStatusCB(PIPELINE_OK)); + message_loop_.RunAllPending(); + + // Audio read #1. + audio->Read(NewReadCB(FROM_HERE, 145, 803000)); + message_loop_.RunAllPending(); + + // Audio read #2. + audio->Read(NewReadCB(FROM_HERE, 148, 826000)); + message_loop_.RunAllPending(); + + // Video read #1. + video->Read(NewReadCB(FROM_HERE, 5425, 801000)); + message_loop_.RunAllPending(); + + // Video read #2. + video->Read(NewReadCB(FROM_HERE, 1906, 834000)); + message_loop_.RunAllPending(); +} + +// A mocked callback specialization for calling Read(). Since RunWithParams() +// is mocked we don't need to pass in object or method pointers. +class MockReadCB : public base::RefCountedThreadSafe<MockReadCB> { + public: + MockReadCB() {} + + MOCK_METHOD0(OnDelete, void()); + MOCK_METHOD2(Run, void(DemuxerStream::Status status, + const scoped_refptr<DecoderBuffer>& buffer)); + + protected: + virtual ~MockReadCB() { + OnDelete(); + } + + private: + friend class base::RefCountedThreadSafe<MockReadCB>; + + DISALLOW_COPY_AND_ASSIGN(MockReadCB); +}; + +TEST_F(FFmpegDemuxerTest, Stop) { + // Tests that calling Read() on a stopped demuxer stream immediately deletes + // the callback. + CreateDemuxer("bear-320x240.webm"); + InitializeDemuxer(); + + // Get our stream. + scoped_refptr<DemuxerStream> audio = + demuxer_->GetStream(DemuxerStream::AUDIO); + ASSERT_TRUE(audio); + + demuxer_->Stop(NewExpectedClosure()); + + // Expect all calls in sequence. + InSequence s; + + // Create our mocked callback. The Callback created by base::Bind() will take + // ownership of this pointer. + StrictMock<MockReadCB>* callback = new StrictMock<MockReadCB>(); + + // The callback should be immediately deleted. We'll use a checkpoint to + // verify that it has indeed been deleted. + EXPECT_CALL(*callback, Run(DemuxerStream::kOk, IsEndOfStreamBuffer())); + EXPECT_CALL(*callback, OnDelete()); + EXPECT_CALL(*this, CheckPoint(1)); + + // Attempt the read... + audio->Read(base::Bind(&MockReadCB::Run, callback)); + + message_loop_.RunAllPending(); + + // ...and verify that |callback| was deleted. + CheckPoint(1); +} + +// The streams can outlive the demuxer because the streams may still be in use +// by the decoder when the demuxer is destroyed. +// This test verifies that DemuxerStream::Read() does not use an invalid demuxer +// pointer (no crash occurs) and calls the callback with an EndOfStream buffer. +TEST_F(FFmpegDemuxerTest, StreamReadAfterStopAndDemuxerDestruction) { + CreateDemuxer("bear-320x240.webm"); + InitializeDemuxer(); + + // Get our stream. + scoped_refptr<DemuxerStream> audio = + demuxer_->GetStream(DemuxerStream::AUDIO); + ASSERT_TRUE(audio); + + demuxer_->Stop(NewExpectedClosure()); + + // Finish up any remaining tasks. + message_loop_.RunAllPending(); + + // Expect all calls in sequence. + InSequence s; + + // Create our mocked callback. The Callback created by base::Bind() will take + // ownership of this pointer. + StrictMock<MockReadCB>* callback = new StrictMock<MockReadCB>(); + + // The callback should be immediately deleted. We'll use a checkpoint to + // verify that it has indeed been deleted. + EXPECT_CALL(*callback, Run(DemuxerStream::kOk, IsEndOfStreamBuffer())); + EXPECT_CALL(*callback, OnDelete()); + EXPECT_CALL(*this, CheckPoint(1)); + + // Release the reference to the demuxer. This should also destroy it. + demuxer_ = NULL; + // |audio| now has a demuxer_ pointer to invalid memory. + + // Attempt the read... + audio->Read(base::Bind(&MockReadCB::Run, callback)); + + message_loop_.RunAllPending(); + + // ...and verify that |callback| was deleted. + CheckPoint(1); +} + +TEST_F(FFmpegDemuxerTest, DisableAudioStream) { + // We are doing the following things here: + // 1. Initialize the demuxer with audio and video stream. + // 2. Send a "disable audio stream" message to the demuxer. + // 3. Demuxer will free audio packets even if audio stream was initialized. + CreateDemuxer("bear-320x240.webm"); + InitializeDemuxer(); + + // Submit a "disable audio stream" message to the demuxer. + demuxer_->OnAudioRendererDisabled(); + message_loop_.RunAllPending(); + + // Get our streams. + scoped_refptr<DemuxerStream> video = + demuxer_->GetStream(DemuxerStream::VIDEO); + scoped_refptr<DemuxerStream> audio = + demuxer_->GetStream(DemuxerStream::AUDIO); + ASSERT_TRUE(video); + ASSERT_TRUE(audio); + + // The audio stream should have been prematurely stopped. + EXPECT_FALSE(IsStreamStopped(DemuxerStream::VIDEO)); + EXPECT_TRUE(IsStreamStopped(DemuxerStream::AUDIO)); + + // Attempt a read from the video stream: it should return valid data. + video->Read(NewReadCB(FROM_HERE, 22084, 0)); + message_loop_.RunAllPending(); + + // Attempt a read from the audio stream: it should immediately return end of + // stream without requiring the message loop to read data. + bool got_eos_buffer = false; + audio->Read(base::Bind(&EosOnReadDone, &got_eos_buffer)); + EXPECT_TRUE(got_eos_buffer); +} + +TEST_F(FFmpegDemuxerTest, ProtocolRead) { + CreateDemuxer("bear-320x240.webm"); + InitializeDemuxer(); + + // Set read head to zero as Initialize() will have parsed a bit of the file. + int64 position = 0; + EXPECT_TRUE(demuxer_->SetPosition(0)); + EXPECT_TRUE(demuxer_->GetPosition(&position)); + EXPECT_EQ(0, position); + + // Read 32 bytes from offset zero and verify position. + uint8 buffer[32]; + EXPECT_EQ(32, demuxer_->Read(32, buffer)); + EXPECT_TRUE(demuxer_->GetPosition(&position)); + EXPECT_EQ(32, position); + + // Read an additional 32 bytes and verify position. + EXPECT_EQ(32, demuxer_->Read(32, buffer)); + EXPECT_TRUE(demuxer_->GetPosition(&position)); + EXPECT_EQ(64, position); + + // Seek to end and read until EOF. + int64 size = 0; + EXPECT_TRUE(demuxer_->GetSize(&size)); + EXPECT_TRUE(demuxer_->SetPosition(size - 48)); + EXPECT_EQ(32, demuxer_->Read(32, buffer)); + EXPECT_TRUE(demuxer_->GetPosition(&position)); + EXPECT_EQ(size - 16, position); + + EXPECT_EQ(16, demuxer_->Read(32, buffer)); + EXPECT_TRUE(demuxer_->GetPosition(&position)); + EXPECT_EQ(size, position); + + EXPECT_EQ(0, demuxer_->Read(32, buffer)); + EXPECT_TRUE(demuxer_->GetPosition(&position)); + EXPECT_EQ(size, position); + + demuxer_->Stop(NewExpectedClosure()); + message_loop_.RunAllPending(); +} + +TEST_F(FFmpegDemuxerTest, ProtocolGetSetPosition) { + CreateDemuxer("bear-320x240.webm"); + InitializeDemuxer(); + + InSequence s; + + int64 size; + int64 position; + EXPECT_TRUE(demuxer_->GetSize(&size)); + EXPECT_TRUE(demuxer_->GetPosition(&position)); + + EXPECT_TRUE(demuxer_->SetPosition(512)); + EXPECT_FALSE(demuxer_->SetPosition(size)); + EXPECT_FALSE(demuxer_->SetPosition(size + 1)); + EXPECT_FALSE(demuxer_->SetPosition(-1)); + EXPECT_TRUE(demuxer_->GetPosition(&position)); + EXPECT_EQ(512, position); +} + +TEST_F(FFmpegDemuxerTest, ProtocolGetSize) { + CreateDemuxer("bear-320x240.webm"); + InitializeDemuxer(); + + int64 data_source_size = 0; + int64 demuxer_size = 0; + EXPECT_TRUE(data_source_->GetSize(&data_source_size)); + EXPECT_TRUE(demuxer_->GetSize(&demuxer_size)); + EXPECT_NE(0, data_source_size); + EXPECT_EQ(data_source_size, demuxer_size); +} + +TEST_F(FFmpegDemuxerTest, ProtocolIsStreaming) { + CreateDemuxer("bear-320x240.webm"); + InitializeDemuxer(); + + EXPECT_FALSE(data_source_->IsStreaming()); + EXPECT_FALSE(demuxer_->IsStreaming()); +} + +// Verify that seek works properly when the WebM cues data is at the start of +// the file instead of at the end. +TEST_F(FFmpegDemuxerTest, SeekWithCuesBeforeFirstCluster) { + CreateDemuxer("bear-320x240-cues-in-front.webm"); + InitializeDemuxer(); + + // Get our streams. + scoped_refptr<DemuxerStream> video = + demuxer_->GetStream(DemuxerStream::VIDEO); + scoped_refptr<DemuxerStream> audio = + demuxer_->GetStream(DemuxerStream::AUDIO); + ASSERT_TRUE(video); + ASSERT_TRUE(audio); + + // Read a video packet and release it. + video->Read(NewReadCB(FROM_HERE, 22084, 0)); + message_loop_.RunAllPending(); + + // Issue a simple forward seek, which should discard queued packets. + demuxer_->Seek(base::TimeDelta::FromMicroseconds(2500000), + NewExpectedStatusCB(PIPELINE_OK)); + message_loop_.RunAllPending(); + + // Audio read #1. + audio->Read(NewReadCB(FROM_HERE, 40, 2403000)); + message_loop_.RunAllPending(); + + // Audio read #2. + audio->Read(NewReadCB(FROM_HERE, 42, 2406000)); + message_loop_.RunAllPending(); + + // Video read #1. + video->Read(NewReadCB(FROM_HERE, 5276, 2402000)); + message_loop_.RunAllPending(); + + // Video read #2. + video->Read(NewReadCB(FROM_HERE, 1740, 2436000)); + message_loop_.RunAllPending(); +} + +// Ensure ID3v1 tag reading is disabled. id3_test.mp3 has an ID3v1 tag with the +// field "title" set to "sample for id3 test". +TEST_F(FFmpegDemuxerTest, NoID3TagData) { +#if !defined(USE_PROPRIETARY_CODECS) + return; +#endif + CreateDemuxer("id3_test.mp3"); + InitializeDemuxer(); + EXPECT_FALSE(av_dict_get(format_context()->metadata, "title", NULL, 0)); +} + +// Ensure MP3 files with large image/video based ID3 tags demux okay. FFmpeg +// will hand us a video stream to the data which will likely be in a format we +// don't accept as video; e.g. PNG. +TEST_F(FFmpegDemuxerTest, Mp3WithVideoStreamID3TagData) { +#if !defined(USE_PROPRIETARY_CODECS) + return; +#endif + CreateDemuxer("id3_png_test.mp3"); + InitializeDemuxer(); + + // Ensure the expected streams are present. + EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::VIDEO)); + EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::AUDIO)); +} + +// Ensure a video with an unsupported audio track still results in the video +// stream being demuxed. +TEST_F(FFmpegDemuxerTest, UnsupportedAudioSupportedVideoDemux) { + CreateDemuxer("speex_audio_vorbis_video.ogv"); + InitializeDemuxer(); + + // Ensure the expected streams are present. + EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::VIDEO)); + EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::AUDIO)); +} + +// Ensure a video with an unsupported video track still results in the audio +// stream being demuxed. +TEST_F(FFmpegDemuxerTest, UnsupportedVideoSupportedAudioDemux) { + CreateDemuxer("vorbis_audio_wmv_video.mkv"); + InitializeDemuxer(); + + // Ensure the expected streams are present. + EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::VIDEO)); + EXPECT_TRUE(demuxer_->GetStream(DemuxerStream::AUDIO)); +} + +} // namespace media |