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

Side by Side 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 unified diff | Download patch
« no previous file with comments | « media/filters/ffmpeg_demuxer.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2009 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 <deque>
6
7 #include "media/base/filter_host.h"
8 #include "media/base/filters.h"
9 #include "media/base/mock_filter_host.h"
10 #include "media/base/mock_media_filters.h"
11 #include "media/filters/ffmpeg_common.h"
12 #include "media/filters/ffmpeg_demuxer.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 // FFmpeg mocks to remove dependency on having the DLLs present.
16 extern "C" {
17 static const size_t kMaxStreams = 3;
18 static AVFormatContext g_format;
19 static AVStream g_streams[kMaxStreams];
20 static AVCodecContext g_audio_codec;
21 static AVCodecContext g_video_codec;
22 static AVCodecContext g_data_codec;
23 struct AVPacket g_packet;
24
25 // FFmpeg return codes for various functions.
26 static int g_av_open_input_file = 0;
27 static int g_av_find_stream_info = 0;
28 static int g_av_read_frame = 0;
29
30 // Counts the number of packets "allocated" by av_read_frame and "released" by
31 // av_free_packet. This should always be zero after everything is cleaned up.
32 static int g_oustanding_packets = 0;
33
34 int av_open_input_file(AVFormatContext** format, const char* filename,
35 AVInputFormat* input_format, int buffer_size,
36 AVFormatParameters* parameters) {
37 EXPECT_FALSE(input_format) << "AVInputFormat should be NULL.";
38 EXPECT_FALSE(buffer_size) << "buffer_size should be 0.";
39 EXPECT_FALSE(parameters) << "AVFormatParameters should be NULL.";
40 if (g_av_open_input_file < 0) {
41 *format = NULL;
42 } else {
43 *format = &g_format;
44 }
45 return g_av_open_input_file;
46 }
47
48 int av_find_stream_info(AVFormatContext* format) {
49 EXPECT_EQ(&g_format, format);
50 return g_av_find_stream_info;
51 }
52
53 void av_free(void* ptr) {
54 EXPECT_EQ(&g_format, ptr);
55 }
56
57 // Our packet destroying function.
58 void DestructPacket(AVPacket* packet) {
59 --g_oustanding_packets;
60 }
61
62 int av_read_frame(AVFormatContext* format, AVPacket* packet) {
63 EXPECT_EQ(&g_format, format);
64 memcpy(packet, &g_packet, sizeof(g_packet));
65 packet->destruct = &DestructPacket;
66 if (g_av_read_frame == 0) {
67 ++g_oustanding_packets;
68 }
69 return g_av_read_frame;
70 }
71
72 } // extern "C"
73
74 using namespace media;
75
76 namespace {
77
78 void InitializeFFmpegMocks() {
79 // Initialize function return codes.
80 g_av_open_input_file = 0;
81 g_av_find_stream_info = 0;
82 g_av_read_frame = 0;
83
84 // Initialize AVFormatContext structure.
85 memset(&g_format, 0, sizeof(g_format));
86
87 // Initialize AVStream structures.
88 for (size_t i = 0; i < kMaxStreams; ++i) {
89 memset(&g_streams[i], 0, sizeof(g_streams[i]));
90 g_streams[i].time_base.den = 1 * base::Time::kMicrosecondsPerSecond;
91 g_streams[i].time_base.num = 1;
92 }
93
94 // Initialize AVCodexContext structures.
95 memset(&g_audio_codec, 0, sizeof(g_audio_codec));
96 g_audio_codec.codec_type = CODEC_TYPE_AUDIO;
97 g_audio_codec.codec_id = CODEC_ID_VORBIS;
98 g_audio_codec.channels = 2;
99 g_audio_codec.sample_rate = 44100;
100
101 memset(&g_video_codec, 0, sizeof(g_video_codec));
102 g_video_codec.codec_type = CODEC_TYPE_VIDEO;
103 g_video_codec.codec_id = CODEC_ID_THEORA;
104 g_video_codec.height = 720;
105 g_video_codec.width = 1280;
106
107 memset(&g_data_codec, 0, sizeof(g_data_codec));
108 g_data_codec.codec_type = CODEC_TYPE_DATA;
109 g_data_codec.codec_id = CODEC_ID_NONE;
110
111 // Initialize AVPacket structure.
112 memset(&g_packet, 0, sizeof(g_packet));
113 }
114
115 // Simple implementation of Assignable<Buffer> that lets us poke at values.
116 class TestBuffer : public Assignable<Buffer> {
117 public:
118 TestBuffer() : assigned_(false) {}
119 virtual ~TestBuffer() {}
120
121 // Assignable<Buffer> implementation.
122 virtual void SetBuffer(Buffer* buffer) {
123 buffer_ = buffer;
124 }
125
126 void OnAssignment() {
127 EXPECT_FALSE(assigned_);
128 assigned_ = true;
129 }
130
131 // Mock getters/setters.
132 Buffer* buffer() { return buffer_; }
133 bool assigned() { return assigned_; }
134
135 private:
136 scoped_refptr<Buffer> buffer_;
137 bool assigned_;
138 };
139
140 } // namespace
141
142 TEST(FFmpegDemuxerTest, InitializeFailure) {
143 InitializeFFmpegMocks();
144
145 // Get FFmpegDemuxer's filter factory.
146 scoped_refptr<FilterFactory> factory = FFmpegDemuxer::CreateFilterFactory();
147
148 // Should only accept application/octet-stream type.
149 MediaFormat media_format;
150 media_format.SetAsString(MediaFormat::kMimeType, "foo/x-bar");
151 scoped_refptr<Demuxer> demuxer(factory->Create<Demuxer>(&media_format));
152 ASSERT_FALSE(demuxer);
153 media_format.Clear();
154 media_format.SetAsString(MediaFormat::kMimeType,
155 mime_type::kApplicationOctetStream);
156 demuxer = factory->Create<Demuxer>(&media_format);
157 ASSERT_TRUE(demuxer);
158
159 // Prepare a filter host and data source for the demuxer.
160 MockPipeline pipeline;
161 scoped_ptr< MockFilterHost<Demuxer> > filter_host;
162 filter_host.reset(new MockFilterHost<Demuxer>(&pipeline, demuxer));
163 MockFilterConfig config;
164 scoped_refptr<MockDataSource> data_source(new MockDataSource(&config));
165
166 // Simulate av_open_input_fail failing.
167 g_av_open_input_file = AVERROR_IO;
168 g_av_find_stream_info = 0;
169 EXPECT_FALSE(demuxer->Initialize(data_source));
170 EXPECT_FALSE(filter_host->IsInitialized());
171 EXPECT_EQ(DEMUXER_ERROR_COULD_NOT_OPEN, pipeline.GetError());
172
173 // Simulate av_find_stream_info failing.
174 g_av_open_input_file = 0;
175 g_av_find_stream_info = AVERROR_IO;
176 demuxer = factory->Create<Demuxer>(&media_format);
177 filter_host.reset(new MockFilterHost<Demuxer>(&pipeline, demuxer));
178 EXPECT_FALSE(demuxer->Initialize(data_source));
179 EXPECT_FALSE(filter_host->IsInitialized());
180 EXPECT_EQ(DEMUXER_ERROR_COULD_NOT_PARSE, pipeline.GetError());
181
182 // Simulate media with no parseable streams.
183 InitializeFFmpegMocks();
184 demuxer = factory->Create<Demuxer>(&media_format);
185 filter_host.reset(new MockFilterHost<Demuxer>(&pipeline, demuxer));
186 EXPECT_FALSE(demuxer->Initialize(data_source));
187 EXPECT_FALSE(filter_host->IsInitialized());
188 EXPECT_EQ(DEMUXER_ERROR_NO_SUPPORTED_STREAMS, pipeline.GetError());
189
190 // Simulate media with a data stream but no audio or video streams.
191 g_format.nb_streams = 1;
192 g_format.streams[0] = &g_streams[0];
193 g_streams[0].codec = &g_data_codec;
194 g_streams[0].duration = 10;
195 demuxer = factory->Create<Demuxer>(&media_format);
196 filter_host.reset(new MockFilterHost<Demuxer>(&pipeline, demuxer));
197 EXPECT_FALSE(demuxer->Initialize(data_source));
198 EXPECT_FALSE(filter_host->IsInitialized());
199 EXPECT_EQ(DEMUXER_ERROR_NO_SUPPORTED_STREAMS, pipeline.GetError());
200 }
201
202 TEST(FFmpegDemuxerTest, InitializeStreams) {
203 // Simulate media with a data stream, a video stream and audio stream.
204 InitializeFFmpegMocks();
205 g_format.nb_streams = 3;
206 g_format.streams[0] = &g_streams[0];
207 g_format.streams[1] = &g_streams[1];
208 g_format.streams[2] = &g_streams[2];
209 g_streams[0].duration = 1000;
210 g_streams[0].codec = &g_data_codec;
211 g_streams[1].duration = 100;
212 g_streams[1].codec = &g_video_codec;
213 g_streams[2].duration = 10;
214 g_streams[2].codec = &g_audio_codec;
215
216 // Create our pipeline.
217 MockPipeline pipeline;
218
219 // Create our data source.
220 MockFilterConfig config;
221 scoped_refptr<MockDataSource> data_source = new MockDataSource(&config);
222 MockFilterHost<DataSource> filter_host_a(&pipeline, data_source);
223 EXPECT_TRUE(data_source->Initialize("foo"));
224 EXPECT_TRUE(filter_host_a.IsInitialized());
225
226 // Create our demuxer.
227 scoped_refptr<FilterFactory> factory = FFmpegDemuxer::CreateFilterFactory();
228 scoped_refptr<Demuxer> demuxer
229 = factory->Create<Demuxer>(data_source->GetMediaFormat());
230 EXPECT_TRUE(demuxer);
231 MockFilterHost<Demuxer> filter_host_b(&pipeline, demuxer);
232 EXPECT_TRUE(demuxer->Initialize(data_source));
233 EXPECT_TRUE(filter_host_b.IsInitialized());
234 EXPECT_EQ(PIPELINE_OK, pipeline.GetError());
235
236 // Since we ignore data streams, the duration should be equal to the video
237 // stream's duration.
238 EXPECT_EQ(g_streams[1].duration, pipeline.GetDuration().InMicroseconds());
239
240 // Verify that 2 out of 3 streams were created.
241 EXPECT_EQ(2, demuxer->GetNumberOfStreams());
242
243 // First stream should be video.
244 DemuxerStream* stream = demuxer->GetStream(0);
245 ASSERT_TRUE(stream);
246 const MediaFormat* stream_format = stream->GetMediaFormat();
247 std::string mime_type;
248 int result;
249 EXPECT_TRUE(stream_format->GetAsString(MediaFormat::kMimeType, &mime_type));
250 EXPECT_STREQ(mime_type::kFFmpegVideo, mime_type.c_str());
251 EXPECT_TRUE(stream_format->GetAsInteger(kFFmpegCodecID, &result));
252 EXPECT_EQ(CODEC_ID_THEORA, static_cast<CodecID>(result));
253 EXPECT_TRUE(stream_format->GetAsInteger(MediaFormat::kHeight, &result));
254 EXPECT_EQ(g_video_codec.height, result);
255 EXPECT_TRUE(stream_format->GetAsInteger(MediaFormat::kWidth, &result));
256 EXPECT_EQ(g_video_codec.width, result);
257
258 // Second stream should be audio.
259 stream = demuxer->GetStream(1);
260 ASSERT_TRUE(stream);
261 stream_format = stream->GetMediaFormat();
262 EXPECT_TRUE(stream_format->GetAsString(MediaFormat::kMimeType, &mime_type));
263 EXPECT_STREQ(mime_type::kFFmpegAudio, mime_type.c_str());
264 EXPECT_TRUE(stream_format->GetAsInteger(kFFmpegCodecID, &result));
265 EXPECT_EQ(CODEC_ID_VORBIS, static_cast<CodecID>(result));
266 EXPECT_TRUE(stream_format->GetAsInteger(MediaFormat::kChannels, &result));
267 EXPECT_EQ(g_audio_codec.channels, result);
268 EXPECT_TRUE(stream_format->GetAsInteger(MediaFormat::kSampleRate, &result));
269 EXPECT_EQ(g_audio_codec.sample_rate, result);
270 }
271
272 TEST(FFmpegDemuxerTest, Read) {
273 // Prepare some test data.
274 const int kAudio = 0;
275 const int kVideo = 1;
276 const size_t kDataSize = 4;
277 uint8 audio_data[kDataSize] = {0, 1, 2, 3};
278 uint8 video_data[kDataSize] = {4, 5, 6, 7};
279
280 // Simulate media with a an audio stream and video stream.
281 InitializeFFmpegMocks();
282 g_format.nb_streams = 2;
283 g_format.streams[kAudio] = &g_streams[kAudio];
284 g_format.streams[kVideo] = &g_streams[kVideo];
285 g_streams[kAudio].duration = 10;
286 g_streams[kAudio].codec = &g_audio_codec;
287 g_streams[kVideo].duration = 10;
288 g_streams[kVideo].codec = &g_video_codec;
289
290 // Create our pipeline.
291 MockPipeline pipeline;
292
293 // Create our data source.
294 MockFilterConfig config;
295 scoped_refptr<MockDataSource> data_source = new MockDataSource(&config);
296 MockFilterHost<DataSource> filter_host_a(&pipeline, data_source);
297 EXPECT_TRUE(data_source->Initialize("foo"));
298 EXPECT_TRUE(filter_host_a.IsInitialized());
299
300 // Create our demuxer.
301 scoped_refptr<FilterFactory> factory = FFmpegDemuxer::CreateFilterFactory();
302 scoped_refptr<Demuxer> demuxer
303 = factory->Create<Demuxer>(data_source->GetMediaFormat());
304 EXPECT_TRUE(demuxer);
305 MockFilterHost<Demuxer> filter_host_b(&pipeline, demuxer);
306 EXPECT_TRUE(demuxer->Initialize(data_source));
307 EXPECT_TRUE(filter_host_b.IsInitialized());
308 EXPECT_EQ(PIPELINE_OK, pipeline.GetError());
309
310 // Verify both streams were created.
311 EXPECT_EQ(2, demuxer->GetNumberOfStreams());
312
313 // Get our streams.
314 DemuxerStream* audio_stream = demuxer->GetStream(kAudio);
315 DemuxerStream* video_stream = demuxer->GetStream(kVideo);
316 ASSERT_TRUE(audio_stream);
317 ASSERT_TRUE(video_stream);
318
319 // Prepare our test audio packet.
320 g_packet.stream_index = kAudio;
321 g_packet.data = audio_data;
322 g_packet.size = kDataSize;
323
324 // Attempt a read from the audio stream and run the message loop until done.
325 scoped_refptr<TestBuffer> buffer(new TestBuffer());
326 audio_stream->Read(buffer);
327 pipeline.RunAllTasks();
328 EXPECT_TRUE(buffer->assigned());
329 EXPECT_TRUE(buffer->buffer());
330 EXPECT_EQ(audio_data, (uint8*)buffer->buffer()->GetData());
331 EXPECT_EQ(kDataSize, buffer->buffer()->GetDataSize());
332
333 // Prepare our test video packet.
334 g_packet.stream_index = kVideo;
335 g_packet.data = video_data;
336 g_packet.size = kDataSize;
337
338 // Attempt a read from the video stream and run the message loop until done.
339 buffer = new TestBuffer();
340 video_stream->Read(buffer);
341 pipeline.RunAllTasks();
342 EXPECT_TRUE(buffer->assigned());
343 EXPECT_TRUE(buffer->buffer());
344 EXPECT_EQ(video_data, (uint8*)buffer->buffer()->GetData());
345 EXPECT_EQ(kDataSize, buffer->buffer()->GetDataSize());
346
347 // Simulate end of stream.
348 g_av_read_frame = AVERROR_IO;
349
350 // Attempt a read from the audio stream and run the message loop until done.
351 buffer = new TestBuffer();
352 audio_stream->Read(buffer);
353 pipeline.RunAllTasks();
354 EXPECT_FALSE(buffer->assigned());
355 EXPECT_FALSE(buffer->buffer());
356
357 // Manually release buffer, which should release any remaining AVPackets.
358 buffer = NULL;
359 EXPECT_EQ(0, g_oustanding_packets);
360 }
OLDNEW
« 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