Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1154)

Unified Diff: media/filters/ffmpeg_demuxer_unittest.cc

Issue 39295: Checking in media::FFmpegDemuxer and tests. (Closed)
Patch Set: Fixes Created 11 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/filters/ffmpeg_demuxer.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/filters/ffmpeg_demuxer_unittest.cc
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2c4ecbe8363c8fe97fb28047fb1feceb90a3726d
--- /dev/null
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -0,0 +1,360 @@
+// Copyright (c) 2009 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 <deque>
+
+#include "media/base/filter_host.h"
+#include "media/base/filters.h"
+#include "media/base/mock_filter_host.h"
+#include "media/base/mock_media_filters.h"
+#include "media/filters/ffmpeg_common.h"
+#include "media/filters/ffmpeg_demuxer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// FFmpeg mocks to remove dependency on having the DLLs present.
+extern "C" {
+static const size_t kMaxStreams = 3;
+static AVFormatContext g_format;
+static AVStream g_streams[kMaxStreams];
+static AVCodecContext g_audio_codec;
+static AVCodecContext g_video_codec;
+static AVCodecContext g_data_codec;
+struct AVPacket g_packet;
+
+// FFmpeg return codes for various functions.
+static int g_av_open_input_file = 0;
+static int g_av_find_stream_info = 0;
+static int g_av_read_frame = 0;
+
+// Counts the number of packets "allocated" by av_read_frame and "released" by
+// av_free_packet. This should always be zero after everything is cleaned up.
+static int g_oustanding_packets = 0;
+
+int av_open_input_file(AVFormatContext** format, const char* filename,
+ AVInputFormat* input_format, int buffer_size,
+ AVFormatParameters* parameters) {
+ EXPECT_FALSE(input_format) << "AVInputFormat should be NULL.";
+ EXPECT_FALSE(buffer_size) << "buffer_size should be 0.";
+ EXPECT_FALSE(parameters) << "AVFormatParameters should be NULL.";
+ if (g_av_open_input_file < 0) {
+ *format = NULL;
+ } else {
+ *format = &g_format;
+ }
+ return g_av_open_input_file;
+}
+
+int av_find_stream_info(AVFormatContext* format) {
+ EXPECT_EQ(&g_format, format);
+ return g_av_find_stream_info;
+}
+
+void av_free(void* ptr) {
+ EXPECT_EQ(&g_format, ptr);
+}
+
+// Our packet destroying function.
+void DestructPacket(AVPacket* packet) {
+ --g_oustanding_packets;
+}
+
+int av_read_frame(AVFormatContext* format, AVPacket* packet) {
+ EXPECT_EQ(&g_format, format);
+ memcpy(packet, &g_packet, sizeof(g_packet));
+ packet->destruct = &DestructPacket;
+ if (g_av_read_frame == 0) {
+ ++g_oustanding_packets;
+ }
+ return g_av_read_frame;
+}
+
+} // extern "C"
+
+using namespace media;
+
+namespace {
+
+void InitializeFFmpegMocks() {
+ // Initialize function return codes.
+ g_av_open_input_file = 0;
+ g_av_find_stream_info = 0;
+ g_av_read_frame = 0;
+
+ // Initialize AVFormatContext structure.
+ memset(&g_format, 0, sizeof(g_format));
+
+ // Initialize AVStream structures.
+ for (size_t i = 0; i < kMaxStreams; ++i) {
+ memset(&g_streams[i], 0, sizeof(g_streams[i]));
+ g_streams[i].time_base.den = 1 * base::Time::kMicrosecondsPerSecond;
+ g_streams[i].time_base.num = 1;
+ }
+
+ // Initialize AVCodexContext structures.
+ memset(&g_audio_codec, 0, sizeof(g_audio_codec));
+ g_audio_codec.codec_type = CODEC_TYPE_AUDIO;
+ g_audio_codec.codec_id = CODEC_ID_VORBIS;
+ g_audio_codec.channels = 2;
+ g_audio_codec.sample_rate = 44100;
+
+ memset(&g_video_codec, 0, sizeof(g_video_codec));
+ g_video_codec.codec_type = CODEC_TYPE_VIDEO;
+ g_video_codec.codec_id = CODEC_ID_THEORA;
+ g_video_codec.height = 720;
+ g_video_codec.width = 1280;
+
+ memset(&g_data_codec, 0, sizeof(g_data_codec));
+ g_data_codec.codec_type = CODEC_TYPE_DATA;
+ g_data_codec.codec_id = CODEC_ID_NONE;
+
+ // Initialize AVPacket structure.
+ memset(&g_packet, 0, sizeof(g_packet));
+}
+
+// Simple implementation of Assignable<Buffer> that lets us poke at values.
+class TestBuffer : public Assignable<Buffer> {
+ public:
+ TestBuffer() : assigned_(false) {}
+ virtual ~TestBuffer() {}
+
+ // Assignable<Buffer> implementation.
+ virtual void SetBuffer(Buffer* buffer) {
+ buffer_ = buffer;
+ }
+
+ void OnAssignment() {
+ EXPECT_FALSE(assigned_);
+ assigned_ = true;
+ }
+
+ // Mock getters/setters.
+ Buffer* buffer() { return buffer_; }
+ bool assigned() { return assigned_; }
+
+ private:
+ scoped_refptr<Buffer> buffer_;
+ bool assigned_;
+};
+
+} // namespace
+
+TEST(FFmpegDemuxerTest, InitializeFailure) {
+ InitializeFFmpegMocks();
+
+ // Get FFmpegDemuxer's filter factory.
+ scoped_refptr<FilterFactory> factory = FFmpegDemuxer::CreateFilterFactory();
+
+ // Should only accept application/octet-stream type.
+ MediaFormat media_format;
+ media_format.SetAsString(MediaFormat::kMimeType, "foo/x-bar");
+ scoped_refptr<Demuxer> demuxer(factory->Create<Demuxer>(&media_format));
+ ASSERT_FALSE(demuxer);
+ media_format.Clear();
+ media_format.SetAsString(MediaFormat::kMimeType,
+ mime_type::kApplicationOctetStream);
+ demuxer = factory->Create<Demuxer>(&media_format);
+ ASSERT_TRUE(demuxer);
+
+ // Prepare a filter host and data source for the demuxer.
+ MockPipeline pipeline;
+ scoped_ptr< MockFilterHost<Demuxer> > filter_host;
+ filter_host.reset(new MockFilterHost<Demuxer>(&pipeline, demuxer));
+ MockFilterConfig config;
+ scoped_refptr<MockDataSource> data_source(new MockDataSource(&config));
+
+ // Simulate av_open_input_fail failing.
+ g_av_open_input_file = AVERROR_IO;
+ g_av_find_stream_info = 0;
+ EXPECT_FALSE(demuxer->Initialize(data_source));
+ EXPECT_FALSE(filter_host->IsInitialized());
+ EXPECT_EQ(DEMUXER_ERROR_COULD_NOT_OPEN, pipeline.GetError());
+
+ // Simulate av_find_stream_info failing.
+ g_av_open_input_file = 0;
+ g_av_find_stream_info = AVERROR_IO;
+ demuxer = factory->Create<Demuxer>(&media_format);
+ filter_host.reset(new MockFilterHost<Demuxer>(&pipeline, demuxer));
+ EXPECT_FALSE(demuxer->Initialize(data_source));
+ EXPECT_FALSE(filter_host->IsInitialized());
+ EXPECT_EQ(DEMUXER_ERROR_COULD_NOT_PARSE, pipeline.GetError());
+
+ // Simulate media with no parseable streams.
+ InitializeFFmpegMocks();
+ demuxer = factory->Create<Demuxer>(&media_format);
+ filter_host.reset(new MockFilterHost<Demuxer>(&pipeline, demuxer));
+ EXPECT_FALSE(demuxer->Initialize(data_source));
+ EXPECT_FALSE(filter_host->IsInitialized());
+ EXPECT_EQ(DEMUXER_ERROR_NO_SUPPORTED_STREAMS, pipeline.GetError());
+
+ // Simulate media with a data stream but no audio or video streams.
+ g_format.nb_streams = 1;
+ g_format.streams[0] = &g_streams[0];
+ g_streams[0].codec = &g_data_codec;
+ g_streams[0].duration = 10;
+ demuxer = factory->Create<Demuxer>(&media_format);
+ filter_host.reset(new MockFilterHost<Demuxer>(&pipeline, demuxer));
+ EXPECT_FALSE(demuxer->Initialize(data_source));
+ EXPECT_FALSE(filter_host->IsInitialized());
+ EXPECT_EQ(DEMUXER_ERROR_NO_SUPPORTED_STREAMS, pipeline.GetError());
+}
+
+TEST(FFmpegDemuxerTest, InitializeStreams) {
+ // Simulate media with a data stream, a video stream and audio stream.
+ InitializeFFmpegMocks();
+ g_format.nb_streams = 3;
+ g_format.streams[0] = &g_streams[0];
+ g_format.streams[1] = &g_streams[1];
+ g_format.streams[2] = &g_streams[2];
+ g_streams[0].duration = 1000;
+ g_streams[0].codec = &g_data_codec;
+ g_streams[1].duration = 100;
+ g_streams[1].codec = &g_video_codec;
+ g_streams[2].duration = 10;
+ g_streams[2].codec = &g_audio_codec;
+
+ // Create our pipeline.
+ MockPipeline pipeline;
+
+ // Create our data source.
+ MockFilterConfig config;
+ scoped_refptr<MockDataSource> data_source = new MockDataSource(&config);
+ MockFilterHost<DataSource> filter_host_a(&pipeline, data_source);
+ EXPECT_TRUE(data_source->Initialize("foo"));
+ EXPECT_TRUE(filter_host_a.IsInitialized());
+
+ // Create our demuxer.
+ scoped_refptr<FilterFactory> factory = FFmpegDemuxer::CreateFilterFactory();
+ scoped_refptr<Demuxer> demuxer
+ = factory->Create<Demuxer>(data_source->GetMediaFormat());
+ EXPECT_TRUE(demuxer);
+ MockFilterHost<Demuxer> filter_host_b(&pipeline, demuxer);
+ EXPECT_TRUE(demuxer->Initialize(data_source));
+ EXPECT_TRUE(filter_host_b.IsInitialized());
+ EXPECT_EQ(PIPELINE_OK, pipeline.GetError());
+
+ // Since we ignore data streams, the duration should be equal to the video
+ // stream's duration.
+ EXPECT_EQ(g_streams[1].duration, pipeline.GetDuration().InMicroseconds());
+
+ // Verify that 2 out of 3 streams were created.
+ EXPECT_EQ(2, demuxer->GetNumberOfStreams());
+
+ // First stream should be video.
+ DemuxerStream* stream = demuxer->GetStream(0);
+ ASSERT_TRUE(stream);
+ const MediaFormat* stream_format = stream->GetMediaFormat();
+ std::string mime_type;
+ int result;
+ EXPECT_TRUE(stream_format->GetAsString(MediaFormat::kMimeType, &mime_type));
+ EXPECT_STREQ(mime_type::kFFmpegVideo, mime_type.c_str());
+ EXPECT_TRUE(stream_format->GetAsInteger(kFFmpegCodecID, &result));
+ EXPECT_EQ(CODEC_ID_THEORA, static_cast<CodecID>(result));
+ EXPECT_TRUE(stream_format->GetAsInteger(MediaFormat::kHeight, &result));
+ EXPECT_EQ(g_video_codec.height, result);
+ EXPECT_TRUE(stream_format->GetAsInteger(MediaFormat::kWidth, &result));
+ EXPECT_EQ(g_video_codec.width, result);
+
+ // Second stream should be audio.
+ stream = demuxer->GetStream(1);
+ ASSERT_TRUE(stream);
+ stream_format = stream->GetMediaFormat();
+ EXPECT_TRUE(stream_format->GetAsString(MediaFormat::kMimeType, &mime_type));
+ EXPECT_STREQ(mime_type::kFFmpegAudio, mime_type.c_str());
+ EXPECT_TRUE(stream_format->GetAsInteger(kFFmpegCodecID, &result));
+ EXPECT_EQ(CODEC_ID_VORBIS, static_cast<CodecID>(result));
+ EXPECT_TRUE(stream_format->GetAsInteger(MediaFormat::kChannels, &result));
+ EXPECT_EQ(g_audio_codec.channels, result);
+ EXPECT_TRUE(stream_format->GetAsInteger(MediaFormat::kSampleRate, &result));
+ EXPECT_EQ(g_audio_codec.sample_rate, result);
+}
+
+TEST(FFmpegDemuxerTest, Read) {
+ // Prepare some test data.
+ const int kAudio = 0;
+ const int kVideo = 1;
+ const size_t kDataSize = 4;
+ uint8 audio_data[kDataSize] = {0, 1, 2, 3};
+ uint8 video_data[kDataSize] = {4, 5, 6, 7};
+
+ // Simulate media with a an audio stream and video stream.
+ InitializeFFmpegMocks();
+ g_format.nb_streams = 2;
+ g_format.streams[kAudio] = &g_streams[kAudio];
+ g_format.streams[kVideo] = &g_streams[kVideo];
+ g_streams[kAudio].duration = 10;
+ g_streams[kAudio].codec = &g_audio_codec;
+ g_streams[kVideo].duration = 10;
+ g_streams[kVideo].codec = &g_video_codec;
+
+ // Create our pipeline.
+ MockPipeline pipeline;
+
+ // Create our data source.
+ MockFilterConfig config;
+ scoped_refptr<MockDataSource> data_source = new MockDataSource(&config);
+ MockFilterHost<DataSource> filter_host_a(&pipeline, data_source);
+ EXPECT_TRUE(data_source->Initialize("foo"));
+ EXPECT_TRUE(filter_host_a.IsInitialized());
+
+ // Create our demuxer.
+ scoped_refptr<FilterFactory> factory = FFmpegDemuxer::CreateFilterFactory();
+ scoped_refptr<Demuxer> demuxer
+ = factory->Create<Demuxer>(data_source->GetMediaFormat());
+ EXPECT_TRUE(demuxer);
+ MockFilterHost<Demuxer> filter_host_b(&pipeline, demuxer);
+ EXPECT_TRUE(demuxer->Initialize(data_source));
+ EXPECT_TRUE(filter_host_b.IsInitialized());
+ EXPECT_EQ(PIPELINE_OK, pipeline.GetError());
+
+ // Verify both streams were created.
+ EXPECT_EQ(2, demuxer->GetNumberOfStreams());
+
+ // Get our streams.
+ DemuxerStream* audio_stream = demuxer->GetStream(kAudio);
+ DemuxerStream* video_stream = demuxer->GetStream(kVideo);
+ ASSERT_TRUE(audio_stream);
+ ASSERT_TRUE(video_stream);
+
+ // Prepare our test audio packet.
+ g_packet.stream_index = kAudio;
+ g_packet.data = audio_data;
+ g_packet.size = kDataSize;
+
+ // Attempt a read from the audio stream and run the message loop until done.
+ scoped_refptr<TestBuffer> buffer(new TestBuffer());
+ audio_stream->Read(buffer);
+ pipeline.RunAllTasks();
+ EXPECT_TRUE(buffer->assigned());
+ EXPECT_TRUE(buffer->buffer());
+ EXPECT_EQ(audio_data, (uint8*)buffer->buffer()->GetData());
+ EXPECT_EQ(kDataSize, buffer->buffer()->GetDataSize());
+
+ // Prepare our test video packet.
+ g_packet.stream_index = kVideo;
+ g_packet.data = video_data;
+ g_packet.size = kDataSize;
+
+ // Attempt a read from the video stream and run the message loop until done.
+ buffer = new TestBuffer();
+ video_stream->Read(buffer);
+ pipeline.RunAllTasks();
+ EXPECT_TRUE(buffer->assigned());
+ EXPECT_TRUE(buffer->buffer());
+ EXPECT_EQ(video_data, (uint8*)buffer->buffer()->GetData());
+ EXPECT_EQ(kDataSize, buffer->buffer()->GetDataSize());
+
+ // Simulate end of stream.
+ g_av_read_frame = AVERROR_IO;
+
+ // Attempt a read from the audio stream and run the message loop until done.
+ buffer = new TestBuffer();
+ audio_stream->Read(buffer);
+ pipeline.RunAllTasks();
+ EXPECT_FALSE(buffer->assigned());
+ EXPECT_FALSE(buffer->buffer());
+
+ // Manually release buffer, which should release any remaining AVPackets.
+ buffer = NULL;
+ EXPECT_EQ(0, g_oustanding_packets);
+}
« no previous file with comments | « media/filters/ffmpeg_demuxer.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698