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

Side by Side Diff: media/filters/chunk_demuxer_unittest.cc

Issue 7203002: Adding ChunkDemuxer implementation. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Unit tests and bug fixes Created 9 years, 6 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 "base/base_paths.h"
6 #include "base/bind.h"
7 #include "base/file_util.h"
8 #include "base/path_service.h"
9 #include "media/base/media.h"
10 #include "media/base/mock_callback.h"
11 #include "media/base/mock_ffmpeg.h"
12 #include "media/filters/chunk_demuxer.h"
13 #include "media/webm/cluster_builder.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 using ::testing::InSequence;
17 using ::testing::Return;
18 using ::testing::SetArgumentPointee;
19 using ::testing::_;
20
21 namespace media {
22
23 static const uint8 kTracksHeader[] = {
24 0x16, 0x54, 0xAE, 0x6B, // Tracks ID
25 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // tracks(size = 0)
26 };
27
28 static const int kTracksHeaderSize = sizeof(kTracksHeader);
29 static const int kTracksSizeOffset = 4;
30
31 static const int kVideoTrackNum = 1;
32 static const int kAudioTrackNum = 2;
33
34 class ChunkDemuxerTest : public testing::Test{
35 protected:
36 enum CodecsIndex {
37 AUDIO,
38 VIDEO,
39 MAX_CODECS_INDEX
40 };
41
42 ChunkDemuxerTest()
43 : demuxer_(new ChunkDemuxer()) {
44 memset(&format_context_, 0, sizeof(format_context_));
45 memset(&streams_, 0, sizeof(streams_));
46 memset(&codecs_, 0, sizeof(codecs_));
47
48 codecs_[VIDEO].codec_type = CODEC_TYPE_VIDEO;
49 codecs_[VIDEO].codec_id = CODEC_ID_VP8;
50 codecs_[VIDEO].width = 320;
51 codecs_[VIDEO].height = 240;
52
53 codecs_[AUDIO].codec_type = CODEC_TYPE_AUDIO;
54 codecs_[AUDIO].codec_id = CODEC_ID_VORBIS;
55 codecs_[AUDIO].channels = 2;
56 codecs_[AUDIO].sample_rate = 44100;
57 }
58
59 ~ChunkDemuxerTest() {
scherkus (not reviewing) 2011/06/28 20:03:29 virtual
acolwell GONE FROM CHROMIUM 2011/06/29 16:38:43 Done.
60 if (demuxer_.get())
61 demuxer_->Shutdown();
62 }
63
64 void ReadFile(const std::string& name, scoped_array<uint8>* buffer,
65 int* size) {
66 FilePath file_path;
67 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
68 file_path = file_path.Append(FILE_PATH_LITERAL("media"))
69 .Append(FILE_PATH_LITERAL("test"))
70 .Append(FILE_PATH_LITERAL("data"))
71 .Append(name);
72
73 int64 tmp = 0;
74 EXPECT_TRUE(file_util::GetFileSize(file_path, &tmp));
75 EXPECT_LT(tmp, 32768);
76 int file_size = static_cast<int>(tmp);
77
78 buffer->reset(new uint8[file_size]);
79 EXPECT_EQ(file_size,
80 file_util::ReadFile(file_path,
81 reinterpret_cast<char*>(buffer->get()),
82 file_size));
83 *size = file_size;
84 }
85
86 void CreateInfoTracks(bool has_audio, bool has_video,
87 scoped_array<uint8>* buffer, int* size) {
88 scoped_array<uint8> info;
89 int info_size = 0;
90 scoped_array<uint8> audio_track_entry;
91 int audio_track_entry_size = 0;
92 scoped_array<uint8> video_track_entry;
93 int video_track_entry_size = 0;
94
95 ReadFile("webm_info_element", &info, &info_size);
96 ReadFile("webm_vorbis_track_entry", &audio_track_entry,
97 &audio_track_entry_size);
98 ReadFile("webm_vp8_track_entry", &video_track_entry,
99 &video_track_entry_size);
100
101 int tracks_element_size = 0;
102
103 if (has_audio)
104 tracks_element_size += audio_track_entry_size;
105
106 if (has_video)
107 tracks_element_size += video_track_entry_size;
108
109 *size = info_size + kTracksHeaderSize + tracks_element_size;
110
111 buffer->reset(new uint8[*size]);
112
113 uint8* buf = buffer->get();
114 memcpy(buf, info.get(), info_size);
115 buf += info_size;
116
117 memcpy(buf, kTracksHeader, kTracksHeaderSize);
118
119 int tmp = tracks_element_size;
120 for (int i = 7; i > 0; i--) {
121 buf[kTracksSizeOffset + i] = tmp & 0xff;
122 tmp >>= 8;
123 }
124
125 buf += kTracksHeaderSize;
126
127 if (has_audio) {
128 memcpy(buf, audio_track_entry.get(), audio_track_entry_size);
129 buf += audio_track_entry_size;
130 }
131
132 if (has_video) {
133 memcpy(buf, video_track_entry.get(), video_track_entry_size);
134 buf += video_track_entry_size;
135 }
136 }
137
138 void SetupAVFormatContext(bool has_audio, bool has_video) {
scherkus (not reviewing) 2011/06/28 20:03:29 sadness
139 int i = 0;
140 if (has_audio) {
141 format_context_.streams[i] = &streams_[i];
142 streams_[i].codec = &codecs_[AUDIO];
143 streams_[i].duration = 100;
144 streams_[i].time_base.den = base::Time::kMicrosecondsPerSecond;
145 streams_[i].time_base.num = 1;
146 i++;
147 }
148
149 if (has_video) {
150 format_context_.streams[i] = &streams_[i];
151 streams_[i].codec = &codecs_[VIDEO];
152 streams_[i].duration = 100;
153 streams_[i].time_base.den = base::Time::kMicrosecondsPerSecond;
154 streams_[i].time_base.num = 1;
155 i++;
156 }
157
158 format_context_.nb_streams = i;
159 }
160
161 bool InitDemuxer(bool has_audio, bool has_video) {
scherkus (not reviewing) 2011/06/28 20:03:29 all of your InitDemuxer EXPECT_EQs are true, so wh
acolwell GONE FROM CHROMIUM 2011/06/29 16:38:43 Done.
162 EXPECT_CALL(mock_ffmpeg_, AVOpenInputFile(_, _, NULL, 0, NULL))
163 .WillOnce(DoAll(SetArgumentPointee<0>(&format_context_),
164 Return(0)));
165
166 EXPECT_CALL(mock_ffmpeg_, AVFindStreamInfo(&format_context_))
167 .WillOnce(Return(0));
168
169 EXPECT_CALL(mock_ffmpeg_, AVCloseInputFile(&format_context_));
170
171 EXPECT_CALL(mock_ffmpeg_, AVRegisterLockManager(_))
172 .WillRepeatedly(Return(0));
scherkus (not reviewing) 2011/06/28 20:03:29 indent
acolwell GONE FROM CHROMIUM 2011/06/29 16:38:43 Done.
173
174 scoped_array<uint8> info_tracks;
175 int info_tracks_size = 0;
176
177 CreateInfoTracks(has_audio, has_video, &info_tracks, &info_tracks_size);
178 SetupAVFormatContext(has_audio, has_video);
179 return demuxer_->Init(info_tracks.get(), info_tracks_size);
180 }
181
182 void AddSimpleBlock(ClusterBuilder* cb, int track_num, int64 timecode) {
183 uint8 data[] = { 0x00 };
184 cb->AddSimpleBlock(track_num, timecode, 0, data, sizeof(data));
185 }
186
187 MOCK_METHOD1(Checkpoint, void(int id));
188
189 MockFFmpeg mock_ffmpeg_;
190
191 AVFormatContext format_context_;
192 AVCodecContext codecs_[MAX_CODECS_INDEX];
193 AVStream streams_[MAX_CODECS_INDEX];
194
195 scoped_refptr<ChunkDemuxer> demuxer_;
196
197 private:
198 DISALLOW_COPY_AND_ASSIGN(ChunkDemuxerTest);
199 };
200
201 TEST_F(ChunkDemuxerTest, TestInit) {
202 // Test no streams, audio-only, video-only, and audio & video scenarios.
203 for (int i = 0; i < 4; i++) {
204 bool has_audio = (i & 0x1) != 0;
205 bool has_video = (i & 0x2) != 0;
206 bool has_a_stream = has_audio | has_video;
207
208 demuxer_ = new ChunkDemuxer();
209 EXPECT_EQ(InitDemuxer(has_audio, has_video), has_a_stream);
210 EXPECT_EQ(demuxer_->GetStream(DemuxerStream::AUDIO).get() != NULL,
211 has_audio);
212 EXPECT_EQ(demuxer_->GetStream(DemuxerStream::VIDEO).get() != NULL,
213 has_video);
214 demuxer_->Shutdown();
215 demuxer_ = NULL;
216 }
217 }
218
219 // Makes sure that Seek() reports an error if Shutdown()
220 // is called before the first cluster is passed to the demuxer.
221 TEST_F(ChunkDemuxerTest, TestShutdownBeforeFirstSeekCompletes) {
222 EXPECT_TRUE(InitDemuxer(true, true));
223
224 demuxer_->Seek(base::TimeDelta::FromSeconds(0),
225 NewExpectedStatusCB(PIPELINE_ERROR_ABORT));
226 }
227
228 // Test that Seek() completes successfully when the first cluster
229 // arrives.
230 TEST_F(ChunkDemuxerTest, TestAddDataAfterSeek) {
231 EXPECT_TRUE(InitDemuxer(true, true));
232
233 InSequence s;
234
235 EXPECT_CALL(*this, Checkpoint(1));
236
237 demuxer_->Seek(base::TimeDelta::FromSeconds(0),
238 NewExpectedStatusCB(PIPELINE_OK));
239
240 EXPECT_CALL(*this, Checkpoint(2));
241
242 ClusterBuilder cb(0);
243 AddSimpleBlock(&cb, kVideoTrackNum, 0);
244 cb.Finish();
245
246 Checkpoint(1);
247
248 EXPECT_TRUE(demuxer_->AddData(cb.GetClusterData(), cb.GetClusterDataSize()));
249
250 Checkpoint(2);
251 }
252
253 // Test the case where AddData() is called before Init(). This can happen
254 // when JavaScript starts sending data before the pipeline is completely
255 // initialized.
256 TEST_F(ChunkDemuxerTest, TestAddDataBeforeInit) {
257 ClusterBuilder cb(0);
258 AddSimpleBlock(&cb, kVideoTrackNum, 0);
259 cb.Finish();
260
261 EXPECT_TRUE(demuxer_->AddData(cb.GetClusterData(), cb.GetClusterDataSize()));
262
263 EXPECT_TRUE(InitDemuxer(true, true));
264
265 demuxer_->Seek(base::TimeDelta::FromSeconds(0),
266 NewExpectedStatusCB(PIPELINE_OK));
267 }
268
269 static void OnReadDone(const base::TimeDelta& expected_time,
270 bool* called,
271 Buffer* buffer) {
272 EXPECT_EQ(expected_time, buffer->GetTimestamp());
273 *called = true;
274 }
275
276 // Make sure Read() callbacks are dispatched with the proper data.
277 TEST_F(ChunkDemuxerTest, TestRead) {
278 EXPECT_TRUE(InitDemuxer(true, true));
279
280 scoped_refptr<DemuxerStream> audio =
281 demuxer_->GetStream(DemuxerStream::AUDIO);
282 scoped_refptr<DemuxerStream> video =
283 demuxer_->GetStream(DemuxerStream::VIDEO);
284
285 bool audio_read_done = false;
286 bool video_read_done = false;
287 audio->Read(base::Bind(&OnReadDone,
288 base::TimeDelta::FromMilliseconds(32),
289 &audio_read_done));
290
291 video->Read(base::Bind(&OnReadDone,
292 base::TimeDelta::FromMilliseconds(123),
293 &video_read_done));
294
295 ClusterBuilder cb(0);
296 AddSimpleBlock(&cb, kAudioTrackNum, 32);
297 AddSimpleBlock(&cb, kVideoTrackNum, 123);
298 cb.Finish();
299
300 EXPECT_TRUE(demuxer_->AddData(cb.GetClusterData(), cb.GetClusterDataSize()));
301
302 EXPECT_TRUE(audio_read_done);
303 EXPECT_TRUE(video_read_done);
304 }
305
306 TEST_F(ChunkDemuxerTest, TestOutOfOrderClusters) {
307 EXPECT_TRUE(InitDemuxer(true, true));
308
309 ClusterBuilder clusterA(10);
310 AddSimpleBlock(&clusterA, kAudioTrackNum, 10);
311 AddSimpleBlock(&clusterA, kVideoTrackNum, 10);
312 AddSimpleBlock(&clusterA, kAudioTrackNum, 33);
313 AddSimpleBlock(&clusterA, kVideoTrackNum, 43);
314 clusterA.Finish();
315
316 EXPECT_TRUE(demuxer_->AddData(clusterA.GetClusterData(),
317 clusterA.GetClusterDataSize()));
318
319 // Cluster B starts before clusterA and has data
320 // that overlaps.
321 ClusterBuilder clusterB(5);
322 AddSimpleBlock(&clusterB, kAudioTrackNum, 5);
323 AddSimpleBlock(&clusterB, kVideoTrackNum, 7);
324 AddSimpleBlock(&clusterB, kAudioTrackNum, 28);
325 AddSimpleBlock(&clusterB, kVideoTrackNum, 40);
326 clusterB.Finish();
327
328 // Make sure that AddData() fails because this cluster data
329 // is before previous data.
330 EXPECT_FALSE(demuxer_->AddData(clusterB.GetClusterData(),
331 clusterB.GetClusterDataSize()));
332
333 // Cluster C starts after clusterA.
334 ClusterBuilder clusterC(56);
335 AddSimpleBlock(&clusterC, kAudioTrackNum, 56);
336 AddSimpleBlock(&clusterC, kVideoTrackNum, 76);
337 AddSimpleBlock(&clusterC, kAudioTrackNum, 79);
338 AddSimpleBlock(&clusterC, kVideoTrackNum, 109);
339 clusterC.Finish();
340
341 // Verify that clusterC is accepted.
342 EXPECT_TRUE(demuxer_->AddData(clusterC.GetClusterData(),
343 clusterC.GetClusterDataSize()));
344
345 // Flush and try clusterB again.
346 demuxer_->FlushData();
347 EXPECT_TRUE(demuxer_->AddData(clusterB.GetClusterData(),
348 clusterB.GetClusterDataSize()));
349
350 // Following that with clusterC should work too since it doesn't
351 // overlap with clusterB.
352 EXPECT_TRUE(demuxer_->AddData(clusterC.GetClusterData(),
353 clusterC.GetClusterDataSize()));
354 }
355
356 TEST_F(ChunkDemuxerTest, TestInvalidBlockSequences) {
357 EXPECT_TRUE(InitDemuxer(true, true));
358
359 // Test the case where timecode is not monotonically
360 // increasing but stays above the cluster timecode.
361 ClusterBuilder clusterA(5);
362 AddSimpleBlock(&clusterA, kAudioTrackNum, 5);
363 AddSimpleBlock(&clusterA, kVideoTrackNum, 10);
364 AddSimpleBlock(&clusterA, kAudioTrackNum, 7);
365 AddSimpleBlock(&clusterA, kVideoTrackNum, 15);
366 clusterA.Finish();
367
368 EXPECT_FALSE(demuxer_->AddData(clusterA.GetClusterData(),
369 clusterA.GetClusterDataSize()));
370
371 // Test timecodes going backwards before cluster timecode.
372 ClusterBuilder clusterB(5);
373 AddSimpleBlock(&clusterB, kAudioTrackNum, 5);
374 AddSimpleBlock(&clusterB, kVideoTrackNum, 5);
375 AddSimpleBlock(&clusterB, kAudioTrackNum, 3);
376 AddSimpleBlock(&clusterB, kVideoTrackNum, 3);
377 clusterB.Finish();
378
379 EXPECT_FALSE(demuxer_->AddData(clusterB.GetClusterData(),
380 clusterB.GetClusterDataSize()));
381
382 // Test strict monotonic increasing timestamps on a per stream
383 // basis.
384 ClusterBuilder clusterC(5);
385 AddSimpleBlock(&clusterC, kAudioTrackNum, 5);
386 AddSimpleBlock(&clusterC, kVideoTrackNum, 5);
387 AddSimpleBlock(&clusterC, kAudioTrackNum, 5);
388 AddSimpleBlock(&clusterC, kVideoTrackNum, 7);
389 clusterC.Finish();
390
391 EXPECT_FALSE(demuxer_->AddData(clusterC.GetClusterData(),
392 clusterC.GetClusterDataSize()));
393
394 // Test strict monotonic increasing timestamps on a per stream
395 // basis across clusters.
396 ClusterBuilder clusterD(5);
397 AddSimpleBlock(&clusterD, kAudioTrackNum, 5);
398 AddSimpleBlock(&clusterD, kVideoTrackNum, 5);
399 clusterD.Finish();
400
401 EXPECT_TRUE(demuxer_->AddData(clusterD.GetClusterData(),
402 clusterD.GetClusterDataSize()));
403
404 ClusterBuilder clusterE(5);
405 AddSimpleBlock(&clusterE, kAudioTrackNum, 5);
406 AddSimpleBlock(&clusterE, kVideoTrackNum, 7);
407 clusterE.Finish();
408
409 EXPECT_FALSE(demuxer_->AddData(clusterE.GetClusterData(),
410 clusterE.GetClusterDataSize()));
411 }
412
413 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698