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

Side by Side Diff: media/base/android/media_codec_decoder_unittest.cc

Issue 1176993005: Audio and video decoders for MediaCodecPlayer (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: A better unittest fix, increased timeout for video Created 5 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
OLDNEW
(Empty)
1 // Copyright 2015 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/bind.h"
6 #include "base/logging.h"
7 #include "base/thread_task_runner_handle.h"
8 #include "base/timer/timer.h"
9 #include "media/base/android/media_codec_audio_decoder.h"
10 #include "media/base/android/media_codec_bridge.h"
11 #include "media/base/android/media_codec_video_decoder.h"
12 #include "media/base/android/test_data_factory.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "ui/gl/android/surface_texture.h"
15
16 namespace media {
17
18 // Helper macro to skip the test if MediaCodecBridge isn't available.
19 #define SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE() \
20 do { \
21 if (!MediaCodecBridge::IsAvailable()) { \
22 VLOG(0) << "Could not run test - not supported on device."; \
23 return; \
24 } \
25 } while (0)
26
27 namespace {
28
29 const base::TimeDelta kDefaultTimeout = base::TimeDelta::FromMilliseconds(200);
30 const base::TimeDelta kAudioFramePeriod = base::TimeDelta::FromMilliseconds(20);
31 const base::TimeDelta kVideoFramePeriod = base::TimeDelta::FromMilliseconds(20);
32
33 class AudioFactory : public TestDataFactory {
34 public:
35 AudioFactory(const base::TimeDelta& duration);
36 DemuxerConfigs GetConfigs() override;
37
38 protected:
39 void ModifyAccessUnit(int index_in_chunk, AccessUnit* unit) override;
40 };
41
42 class VideoFactory : public TestDataFactory {
43 public:
44 VideoFactory(const base::TimeDelta& duration);
45 DemuxerConfigs GetConfigs() override;
46
47 protected:
48 void ModifyAccessUnit(int index_in_chunk, AccessUnit* unit) override;
49 };
50
51 AudioFactory::AudioFactory(const base::TimeDelta& duration)
52 : TestDataFactory("vorbis-packet-%d", duration, kAudioFramePeriod) {
53 }
54
55 DemuxerConfigs AudioFactory::GetConfigs() {
56 return TestDataFactory::CreateAudioConfigs(kCodecVorbis, duration_);
57 }
58
59 void AudioFactory::ModifyAccessUnit(int index_in_chunk, AccessUnit* unit) {
60 // Vorbis needs 4 extra bytes padding on Android to decode properly. Check
61 // NuMediaExtractor.cpp in Android source code.
62 uint8 padding[4] = {0xff, 0xff, 0xff, 0xff};
63 unit->data.insert(unit->data.end(), padding, padding + 4);
64 }
65
66 VideoFactory::VideoFactory(const base::TimeDelta& duration)
67 : TestDataFactory("h264-320x180-frame-%d", duration, kVideoFramePeriod) {
68 }
69
70 DemuxerConfigs VideoFactory::GetConfigs() {
71 return TestDataFactory::CreateVideoConfigs(kCodecH264, duration_,
72 gfx::Size(320, 180));
73 }
74
75 void VideoFactory::ModifyAccessUnit(int index_in_chunk, AccessUnit* unit) {
76 // The frames are taken from High profile and some are B-frames.
77 // The first 4 frames appear in the file in the following order:
78 //
79 // Frames: I P B P
80 // Decoding order: 0 1 2 3
81 // Presentation order: 0 2 1 4(3)
82 //
83 // I keep the last PTS to be 3 for simplicity.
84
85 // Swap pts for second and third frames.
86 if (index_in_chunk == 1) // second frame
87 unit->timestamp += frame_period_;
88 if (index_in_chunk == 2) // third frame
89 unit->timestamp -= frame_period_;
90
91 if (index_in_chunk == 0)
92 unit->is_key_frame = true;
93 }
94
95 // Class that computes statistics: number of calls, minimum and maximum values.
96 // It is used for PTS statistics to verify that playback did actually happen.
97
98 template <typename T>
99 class Minimax {
100 public:
101 Minimax() : num_values_(0) {}
102 ~Minimax() {}
103
104 void AddValue(const T& value) {
105 ++num_values_;
106 if (value < min_)
107 min_ = value;
108 else if (max_ < value)
109 max_ = value;
110 }
111
112 const T& min() const { return min_; }
113 const T& max() const { return max_; }
114 int num_values() const { return num_values_; }
115
116 private:
117 T min_;
118 T max_;
119 int num_values_;
120 };
121
122 } // namespace (anonymous)
123
124 // The test fixture for MediaCodecDecoder
125
126 class MediaCodecDecoderTest : public testing::Test {
127 public:
128 MediaCodecDecoderTest();
129 ~MediaCodecDecoderTest() override;
130
131 // Conditions we wait for.
132 bool is_prefetched() const { return is_prefetched_; }
133 bool is_stopped() const { return is_stopped_; }
134 bool is_starved() const { return is_starved_; }
135
136 // Prefetch callback has to be public.
137 void SetPrefetched() { is_prefetched_ = true; }
138
139 protected:
140 typedef base::Callback<bool()> Predicate;
141
142 typedef base::Callback<void(const DemuxerData&)> DataAvailableCallback;
143
144 // Waits for condition to become true or for timeout to expire.
145 // Returns true if the condition becomes true.
146 bool WaitForCondition(const Predicate& condition,
147 const base::TimeDelta& timeout = kDefaultTimeout);
148
149 void SetDataFactory(scoped_ptr<TestDataFactory> factory) {
150 data_factory_ = factory.Pass();
151 }
152
153 DemuxerConfigs GetConfigs() {
154 // ASSERT_NE does not compile here because it expects void return value.
155 EXPECT_NE(nullptr, data_factory_.get());
156 return data_factory_->GetConfigs();
157 }
158
159 void CreateAudioDecoder();
160 void CreateVideoDecoder();
161 void SetVideoSurface();
162
163 // Decoder callbacks.
164 void OnDataRequested();
165 void OnStarvation() { is_starved_ = true; }
166 void OnStopDone() { is_stopped_ = true; }
167 void OnError() {}
168 void OnUpdateCurrentTime(base::TimeDelta now_playing,
169 base::TimeDelta last_buffered) {
170 pts_stat_.AddValue(now_playing);
171 }
172 void OnVideoSizeChanged(const gfx::Size& video_size) {}
173 void OnVideoCodecCreated() {}
174
175 scoped_ptr<MediaCodecDecoder> decoder_;
176 scoped_ptr<TestDataFactory> data_factory_;
177 Minimax<base::TimeDelta> pts_stat_;
178
179 private:
180 bool is_timeout_expired() const { return is_timeout_expired_; }
181 void SetTimeoutExpired(bool value) { is_timeout_expired_ = value; }
182
183 base::MessageLoop message_loop_;
184 bool is_timeout_expired_;
185
186 bool is_prefetched_;
187 bool is_stopped_;
188 bool is_starved_;
189
190 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
191 DataAvailableCallback data_available_cb_;
192 scoped_refptr<gfx::SurfaceTexture> surface_texture_;
193
194 DISALLOW_COPY_AND_ASSIGN(MediaCodecDecoderTest);
195 };
196
197 MediaCodecDecoderTest::MediaCodecDecoderTest()
198 : is_timeout_expired_(false),
199 is_prefetched_(false),
200 is_stopped_(false),
201 is_starved_(false),
202 task_runner_(base::ThreadTaskRunnerHandle::Get()) {
203 }
204
205 MediaCodecDecoderTest::~MediaCodecDecoderTest() {}
206
207 bool MediaCodecDecoderTest::WaitForCondition(const Predicate& condition,
208 const base::TimeDelta& timeout) {
209 // Let the message_loop_ process events.
210 // We start the timer and RunUntilIdle() until it signals.
211
212 SetTimeoutExpired(false);
213
214 base::Timer timer(false, false);
215 timer.Start(FROM_HERE, timeout,
216 base::Bind(&MediaCodecDecoderTest::SetTimeoutExpired,
217 base::Unretained(this), true));
218
219 do {
220 if (condition.Run()) {
221 timer.Stop();
222 return true;
223 }
224 message_loop_.RunUntilIdle();
225 } while (!is_timeout_expired());
226
227 DCHECK(!timer.IsRunning());
228 return false;
229 }
230
231 void MediaCodecDecoderTest::CreateAudioDecoder() {
232 decoder_ = scoped_ptr<MediaCodecDecoder>(new MediaCodecAudioDecoder(
233 task_runner_, base::Bind(&MediaCodecDecoderTest::OnDataRequested,
234 base::Unretained(this)),
235 base::Bind(&MediaCodecDecoderTest::OnStarvation, base::Unretained(this)),
236 base::Bind(&MediaCodecDecoderTest::OnStopDone, base::Unretained(this)),
237 base::Bind(&MediaCodecDecoderTest::OnError, base::Unretained(this)),
238 base::Bind(&MediaCodecDecoderTest::OnUpdateCurrentTime,
239 base::Unretained(this))));
240
241 data_available_cb_ = base::Bind(&MediaCodecDecoder::OnDemuxerDataAvailable,
242 base::Unretained(decoder_.get()));
243 }
244
245 void MediaCodecDecoderTest::CreateVideoDecoder() {
246 decoder_ = scoped_ptr<MediaCodecDecoder>(new MediaCodecVideoDecoder(
247 task_runner_, base::Bind(&MediaCodecDecoderTest::OnDataRequested,
248 base::Unretained(this)),
249 base::Bind(&MediaCodecDecoderTest::OnStarvation, base::Unretained(this)),
250 base::Bind(&MediaCodecDecoderTest::OnStopDone, base::Unretained(this)),
251 base::Bind(&MediaCodecDecoderTest::OnError, base::Unretained(this)),
252 base::Bind(&MediaCodecDecoderTest::OnUpdateCurrentTime,
253 base::Unretained(this)),
254 base::Bind(&MediaCodecDecoderTest::OnVideoSizeChanged,
255 base::Unretained(this)),
256 base::Bind(&MediaCodecDecoderTest::OnVideoCodecCreated,
257 base::Unretained(this))));
258
259 data_available_cb_ = base::Bind(&MediaCodecDecoder::OnDemuxerDataAvailable,
260 base::Unretained(decoder_.get()));
261 }
262
263 void MediaCodecDecoderTest::OnDataRequested() {
264 if (!data_factory_)
265 return;
266
267 DemuxerData data;
268 base::TimeDelta delay;
269 data_factory_->CreateChunk(&data, &delay);
270
271 task_runner_->PostDelayedTask(FROM_HERE, base::Bind(data_available_cb_, data),
272 delay);
273 }
274
275 void MediaCodecDecoderTest::SetVideoSurface() {
276 surface_texture_ = gfx::SurfaceTexture::Create(0);
277 gfx::ScopedJavaSurface surface(surface_texture_.get());
278 ASSERT_NE(nullptr, decoder_.get());
279 MediaCodecVideoDecoder* video_decoder =
280 static_cast<MediaCodecVideoDecoder*>(decoder_.get());
281 video_decoder->SetPendingSurface(surface.Pass());
282 }
283
284 TEST_F(MediaCodecDecoderTest, AudioPrefetch) {
285 CreateAudioDecoder();
286
287 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
288 SetDataFactory(scoped_ptr<TestDataFactory>(new AudioFactory(duration)));
289
290 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
291 base::Unretained(this)));
292
293 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
294 base::Unretained(this))));
295 }
296
297 TEST_F(MediaCodecDecoderTest, VideoPrefetch) {
298 CreateVideoDecoder();
299
300 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
301 SetDataFactory(scoped_ptr<VideoFactory>(new VideoFactory(duration)));
302
303 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
304 base::Unretained(this)));
305
306 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
307 base::Unretained(this))));
308 }
309
310 TEST_F(MediaCodecDecoderTest, AudioConfigureNoParams) {
311 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
312
313 CreateAudioDecoder();
314
315 // Cannot configure without config parameters.
316 EXPECT_EQ(MediaCodecDecoder::CONFIG_FAILURE, decoder_->Configure());
317 }
318
319 TEST_F(MediaCodecDecoderTest, AudioConfigureValidParams) {
320 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
321
322 CreateAudioDecoder();
323
324 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
325 scoped_ptr<AudioFactory> factory(new AudioFactory(duration));
326 decoder_->SetDemuxerConfigs(factory->GetConfigs());
327
328 EXPECT_EQ(MediaCodecDecoder::CONFIG_OK, decoder_->Configure());
329 }
330
331 TEST_F(MediaCodecDecoderTest, VideoConfigureNoParams) {
332 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
333
334 CreateVideoDecoder();
335
336 // Cannot configure without config parameters.
337 EXPECT_EQ(MediaCodecDecoder::CONFIG_FAILURE, decoder_->Configure());
338 }
339
340 TEST_F(MediaCodecDecoderTest, VideoConfigureNoSurface) {
341 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
342
343 CreateVideoDecoder();
344
345 // decoder_->Configure() searches back for the key frame.
346 // We have to prefetch decoder.
347
348 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
349 SetDataFactory(scoped_ptr<VideoFactory>(new VideoFactory(duration)));
350
351 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
352 base::Unretained(this)));
353
354 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
355 base::Unretained(this))));
356
357 decoder_->SetDemuxerConfigs(GetConfigs());
358
359 // Surface is not set, Configure() should fail.
360
361 EXPECT_EQ(MediaCodecDecoder::CONFIG_FAILURE, decoder_->Configure());
362 }
363
364 TEST_F(MediaCodecDecoderTest, VideoConfigureInvalidSurface) {
365 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
366
367 CreateVideoDecoder();
368
369 // decoder_->Configure() searches back for the key frame.
370 // We have to prefetch decoder.
371
372 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
373 SetDataFactory(scoped_ptr<VideoFactory>(new VideoFactory(duration)));
374
375 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
376 base::Unretained(this)));
377
378 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
379 base::Unretained(this))));
380
381 decoder_->SetDemuxerConfigs(GetConfigs());
382
383 // Prepare the surface.
384 scoped_refptr<gfx::SurfaceTexture> surface_texture(
385 gfx::SurfaceTexture::Create(0));
386 gfx::ScopedJavaSurface surface(surface_texture.get());
387
388 // Release the surface texture.
389 surface_texture = NULL;
390
391 MediaCodecVideoDecoder* video_decoder =
392 static_cast<MediaCodecVideoDecoder*>(decoder_.get());
393 video_decoder->SetPendingSurface(surface.Pass());
394
395 EXPECT_EQ(MediaCodecDecoder::CONFIG_FAILURE, decoder_->Configure());
396 }
397
398 TEST_F(MediaCodecDecoderTest, VideoConfigureValidParams) {
399 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
400
401 CreateVideoDecoder();
402
403 // decoder_->Configure() searches back for the key frame.
404 // We have to prefetch decoder.
405
406 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
407 SetDataFactory(scoped_ptr<VideoFactory>(new VideoFactory(duration)));
408
409 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
410 base::Unretained(this)));
411
412 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
413 base::Unretained(this))));
414
415 decoder_->SetDemuxerConfigs(GetConfigs());
416
417 SetVideoSurface();
418
419 // Now we can expect Configure() to succeed.
420
421 EXPECT_EQ(MediaCodecDecoder::CONFIG_OK, decoder_->Configure());
422 }
423
424 TEST_F(MediaCodecDecoderTest, AudioStartWithoutConfigure) {
425 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
426
427 CreateAudioDecoder();
428
429 // Decoder has to be prefetched and configured before the start.
430
431 // Wrong state: not prefetched
432 EXPECT_FALSE(decoder_->Start(base::TimeDelta::FromMilliseconds(0)));
433
434 // Do the prefetch.
435 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
436 SetDataFactory(scoped_ptr<AudioFactory>(new AudioFactory(duration)));
437
438 // Prefetch to avoid starvation at the beginning of playback.
439 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
440 base::Unretained(this)));
441
442 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
443 base::Unretained(this))));
444
445 // Still, decoder is not configured.
446 EXPECT_FALSE(decoder_->Start(base::TimeDelta::FromMilliseconds(0)));
447 }
448
449 TEST_F(MediaCodecDecoderTest, AudioPlayTillCompletion) {
450 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
451
452 CreateAudioDecoder();
453
454 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
455 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(600);
456
457 SetDataFactory(scoped_ptr<AudioFactory>(new AudioFactory(duration)));
458
459 // Prefetch to avoid starvation at the beginning of playback.
460 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
461 base::Unretained(this)));
462
463 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
464 base::Unretained(this))));
465
466 decoder_->SetDemuxerConfigs(GetConfigs());
467
468 EXPECT_EQ(MediaCodecDecoder::CONFIG_OK, decoder_->Configure());
469
470 EXPECT_TRUE(decoder_->Start(base::TimeDelta::FromMilliseconds(0)));
471
472 EXPECT_TRUE(WaitForCondition(
473 base::Bind(&MediaCodecDecoderTest::is_stopped, base::Unretained(this)),
474 timeout));
475
476 EXPECT_TRUE(decoder_->IsStopped());
477 EXPECT_TRUE(decoder_->IsCompleted());
478
479 // It is hard to properly estimate minimum and maximum values because
480 // reported times are different from PTS.
481 EXPECT_EQ(25, pts_stat_.num_values());
482 }
483
484 TEST_F(MediaCodecDecoderTest, VideoPlayTillCompletion) {
485 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
486
487 CreateVideoDecoder();
488
489 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
490 // The first output frame might come out with significant delay. Apparently
491 // the codec does initial configuration at this time. We increase the timeout
492 // to leave a room of 1 second for this initial configuration.
493 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(1500);
494 SetDataFactory(scoped_ptr<VideoFactory>(new VideoFactory(duration)));
495
496 // Prefetch
497 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
498 base::Unretained(this)));
499
500 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
501 base::Unretained(this))));
502
503 decoder_->SetDemuxerConfigs(GetConfigs());
504
505 SetVideoSurface();
506
507 EXPECT_EQ(MediaCodecDecoder::CONFIG_OK, decoder_->Configure());
508
509 EXPECT_TRUE(decoder_->Start(base::TimeDelta::FromMilliseconds(0)));
510
511 EXPECT_TRUE(WaitForCondition(
512 base::Bind(&MediaCodecDecoderTest::is_stopped, base::Unretained(this)),
513 timeout));
514
515 EXPECT_TRUE(decoder_->IsStopped());
516 EXPECT_TRUE(decoder_->IsCompleted());
517
518 EXPECT_EQ(26, pts_stat_.num_values());
519 EXPECT_EQ(data_factory_->last_pts(), pts_stat_.max());
520 }
521
522 } // namespace media
OLDNEW
« no previous file with comments | « media/base/android/media_codec_decoder.cc ('k') | media/base/android/media_codec_video_decoder.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698