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

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

Issue 1128383003: Implementation of MediaCodecPlayer stage 1 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: AccessUnitQueue::GetInfo() returns result by value 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/strings/stringprintf.h"
8 #include "base/timer/timer.h"
9 #include "media/base/android/demuxer_android.h"
10 #include "media/base/android/media_codec_bridge.h"
11 #include "media/base/android/media_codec_player.h"
12 #include "media/base/android/media_player_manager.h"
13 #include "media/base/decoder_buffer.h"
14 #include "media/base/test_data_util.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace media {
18
19 // Helper macro to skip the test if MediaCodecBridge isn't available.
20 #define SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE() \
21 do { \
22 if (!MediaCodecBridge::IsAvailable()) { \
23 VLOG(0) << "Could not run test - not supported on device."; \
24 return; \
25 } \
26 } while (0)
27
28
29 #define RUN_ON_MEDIA_THREAD(CLASS, METHOD, ...) \
30 do { \
31 if (!GetMediaTaskRunner()->BelongsToCurrentThread()) { \
32 GetMediaTaskRunner()->PostTask( \
33 FROM_HERE, \
34 base::Bind(&CLASS :: METHOD, base::Unretained(this), ##__VA_ARGS__));\
35 return; \
36 } \
37 } while(0)
38
39 namespace {
40 const base::TimeDelta kDefaultTimeout = base::TimeDelta::FromMilliseconds(200);
41 }
42
43 // Mock of MediaPlayerManager for testing purpose.
44
45 class MockMediaPlayerManager : public MediaPlayerManager {
46 public:
47 MockMediaPlayerManager()
48 : playback_completed_(false),
49 weak_ptr_factory_(this) {}
50 ~MockMediaPlayerManager() override {}
51
52 MediaResourceGetter* GetMediaResourceGetter() override { return nullptr; }
53 MediaUrlInterceptor* GetMediaUrlInterceptor() override { return nullptr; }
54 void OnTimeUpdate(int player_id,
55 base::TimeDelta current_timestamp,
56 base::TimeTicks current_time_ticks) override {}
57 void OnMediaMetadataChanged(
58 int player_id,
59 base::TimeDelta duration,
60 int width,
61 int height,
62 bool success) override {
63 media_metadata_.duration = duration;
64 media_metadata_.width = width;
65 media_metadata_.height = height;
66 media_metadata_.modified = true;
67 }
68
69 void OnPlaybackComplete(int player_id) override {
70 playback_completed_ = true;
71 }
72 void OnMediaInterrupted(int player_id) override {}
73 void OnBufferingUpdate(int player_id, int percentage) override {}
74 void OnSeekComplete(
75 int player_id,
76 const base::TimeDelta& current_time) override {}
77 void OnError(int player_id, int error) override {}
78 void OnVideoSizeChanged(int player_id, int width, int height) override {}
79 void OnAudibleStateChanged(int player_id, bool is_audible_now) override {}
80 void OnWaitingForDecryptionKey(int player_id) override {}
81 MediaPlayerAndroid* GetFullscreenPlayer() override { return nullptr; }
82 MediaPlayerAndroid* GetPlayer(int player_id) override { return nullptr; }
83 void RequestFullScreen(int player_id) override {}
84
85 void OnMediaResourcesRequested(int player_id) {}
86
87 base::WeakPtr<MockMediaPlayerManager> GetWeakPtr() {
88 return weak_ptr_factory_.GetWeakPtr();
89 }
90
91 // Conditions to wait for.
92 bool IsMetadataChanged() const { return media_metadata_.modified; }
93 bool IsPlaybackCompleted() const { return playback_completed_; }
94
95 struct MediaMetadata {
96 base::TimeDelta duration;
97 int width;
98 int height;
99 bool modified;
100 MediaMetadata() : width(0), height(0), modified(false) {}
101 };
102 MediaMetadata media_metadata_;
103
104 private:
105 bool playback_completed_;
106
107 base::WeakPtrFactory<MockMediaPlayerManager> weak_ptr_factory_;
108
109 DISALLOW_COPY_AND_ASSIGN(MockMediaPlayerManager);
110 };
111
112 // ChunkFactory defines how the data will be generated.
113
114 struct ChunkFactory {
115 virtual ~ChunkFactory() {}
116 // Returns true if data is created.
117 virtual bool CreateChunk(DemuxerStream::Type stream_type,
118 DemuxerData* chunk, base::TimeDelta* delay) = 0;
119 };
120
121 class AudioFactory : public ChunkFactory {
122 public:
123 AudioFactory(const base::TimeDelta& duration,
124 const base::TimeDelta& frame_period);
125
126 bool CreateChunk(DemuxerStream::Type stream_type,
127 DemuxerData* chunk, base::TimeDelta* delay) override;
128
129 private:
130 base::TimeDelta duration_;
131 base::TimeDelta frame_period_;
132 std::vector<uint8> packet_[4];
133 base::TimeDelta current_pts_;
134 };
135
136 AudioFactory::AudioFactory(const base::TimeDelta& duration,
137 const base::TimeDelta& frame_period)
138 : duration_(duration),
139 frame_period_(frame_period) {
140 // Load packets
141 for (int i = 0; i < 4; ++i) {
142 scoped_refptr<DecoderBuffer> buffer =
143 ReadTestDataFile(base::StringPrintf("vorbis-packet-%d", i));
144 packet_[i] = std::vector<uint8>(
145 buffer->data(), buffer->data() + buffer->data_size());
146 }
147 }
148
149 bool AudioFactory::CreateChunk(DemuxerStream::Type stream_type,
150 DemuxerData* chunk, base::TimeDelta* delay) {
151 if (stream_type != DemuxerStream::AUDIO)
152 return false;
153
154 DCHECK(chunk);
155 DCHECK(delay);
156
157 *delay = base::TimeDelta();
158
159 chunk->type = stream_type;
160 for (int i = 0; i < 4; ++i) {
161 chunk->access_units.push_back(AccessUnit());
162 AccessUnit& unit = chunk->access_units.back();
163 unit.status = DemuxerStream::kOk;
164
165 unit.timestamp = current_pts_;
166 current_pts_ += frame_period_;
167
168 if (unit.timestamp > duration_) {
169 unit.is_end_of_stream = true;
170 break; // EOS units have no data
171 }
172
173 unit.data = packet_[i];
174 // Vorbis needs 4 extra bytes padding on Android to decode properly. Check
175 // NuMediaExtractor.cpp in Android source code.
176 uint8 padding[4] = { 0xff , 0xff , 0xff , 0xff };
177 unit.data.insert(unit.data.end(), padding, padding + 4);
178 }
179 return true;
180 }
181
182 // Mock of DemuxerAndroid for testing purpose.
183
184 class MockDemuxerAndroid : public DemuxerAndroid {
185 public:
186 MockDemuxerAndroid() : client_(nullptr) {}
187 ~MockDemuxerAndroid() override {}
188
189 // DemuxerAndroid implementation
190 void Initialize(DemuxerAndroidClient* client) override;
191 void RequestDemuxerData(DemuxerStream::Type type) override;
192 void RequestDemuxerSeek(const base::TimeDelta& time_to_seek,
193 bool is_browser_seek) override {}
194
195 // Post DemuxerConfigs to the client (i.e. the player) on correct thread.
196 void PostConfigs(const DemuxerConfigs& configs);
197
198 // Sets the data rule that governs the process of packet generation.
199 void SetChunkFactory(scoped_ptr<ChunkFactory> factory) {
200 chunk_factory_ = factory.Pass();
201 }
202
203 // Conditions to wait for.
204 bool IsInitialized() const { return client_; }
205 bool HasPendingConfigs() const { return pending_configs_; }
206
207 private:
208 DemuxerAndroidClient* client_;
209 scoped_ptr<DemuxerConfigs> pending_configs_;
210 scoped_ptr<ChunkFactory> chunk_factory_;
211
212 DISALLOW_COPY_AND_ASSIGN(MockDemuxerAndroid);
213 };
214
215 void MockDemuxerAndroid::Initialize(DemuxerAndroidClient* client) {
216 DVLOG(1) << "MockDemuxerAndroid::" << __FUNCTION__;
217 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
218
219 client_ = client;
220 if (pending_configs_)
221 client_->OnDemuxerConfigsAvailable(*pending_configs_);
222 }
223
224 void MockDemuxerAndroid::RequestDemuxerData(DemuxerStream::Type type) {
225 DCHECK(chunk_factory_);
226 DCHECK(client_);
227
228 DemuxerData chunk;
229 base::TimeDelta delay;
230 if (chunk_factory_->CreateChunk(type, &chunk, &delay)) {
231 // Post to Media thread.
232 GetMediaTaskRunner()->PostDelayedTask(
233 FROM_HERE,
234 base::Bind(&DemuxerAndroidClient::OnDemuxerDataAvailable,
235 base::Unretained(client_), chunk),
236 delay);
237 }
238 }
239
240 void MockDemuxerAndroid::PostConfigs(const DemuxerConfigs& configs) {
241 DVLOG(1) << "MockDemuxerAndroid::" << __FUNCTION__;
242 RUN_ON_MEDIA_THREAD(MockDemuxerAndroid, PostConfigs, configs);
243
244 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
245
246 if (client_)
247 client_->OnDemuxerConfigsAvailable(configs);
248 else
249 pending_configs_ = scoped_ptr<DemuxerConfigs>(new DemuxerConfigs(configs));
250 }
251
252 // The test fixture for MediaCodecPlayer
253
254 class MediaCodecPlayerTest : public testing::Test {
255 public:
256 MediaCodecPlayerTest();
257 ~MediaCodecPlayerTest() override;
258
259 protected:
260 typedef base::Callback<bool ()> Predicate;
261
262 void CreatePlayer();
263
264 // Waits for condition to become true or for timeout to expire.
265 // Returns true if the condition becomes true.
266 bool WaitForCondition(const Predicate& condition,
267 const base::TimeDelta& timeout = kDefaultTimeout);
268
269 DemuxerConfigs CreateAudioConfigs(
270 AudioCodec audio_codec, const base::TimeDelta& duration);
271 DemuxerConfigs CreateVideoConfigs(const base::TimeDelta& duration);
272 DemuxerConfigs CreateAudioVideoConfigs(const base::TimeDelta& duration);
273
274 base::MessageLoop message_loop_;
275 MockMediaPlayerManager manager_;
276 MockDemuxerAndroid* demuxer_; // owned by player_
277 MediaCodecPlayer* player_; // raw pointer due to DeleteOnCorrectThread()
278
279 private:
280 bool is_timeout_expired() const { return is_timeout_expired_; }
281 void SetTimeoutExpired(bool value) {
282 is_timeout_expired_ = value;
283 }
284
285 bool is_timeout_expired_;
286
287 DISALLOW_COPY_AND_ASSIGN(MediaCodecPlayerTest);
288 };
289
290 MediaCodecPlayerTest::MediaCodecPlayerTest()
291 : demuxer_(new MockDemuxerAndroid()),
292 player_(nullptr) {
293 }
294
295 void MediaCodecPlayerTest::CreatePlayer() {
296 DCHECK(demuxer_);
297 player_ = new MediaCodecPlayer(
298 0, // player_id
299 manager_.GetWeakPtr(),
300 base::Bind(&MockMediaPlayerManager::OnMediaResourcesRequested,
301 base::Unretained(&manager_)),
302 scoped_ptr<MockDemuxerAndroid>(demuxer_),
303 GURL());
304
305 DCHECK(player_);
306 }
307
308 MediaCodecPlayerTest::~MediaCodecPlayerTest() {
309 if (player_)
310 player_->DeleteOnCorrectThread();
311 }
312
313 bool MediaCodecPlayerTest::WaitForCondition(const Predicate& condition,
314 const base::TimeDelta& timeout) {
315 // Let the message_loop_ process events.
316 // We start the timer and RunUntilIdle() until it signals.
317
318 SetTimeoutExpired(false);
319
320 base::Timer timer(false, false);
321 timer.Start(FROM_HERE, timeout,
322 base::Bind(&MediaCodecPlayerTest::SetTimeoutExpired,
323 base::Unretained(this), true));
324
325 do {
326 if (condition.Run()) {
327 timer.Stop();
328 return true;
329 }
330 message_loop_.RunUntilIdle();
331 } while (!is_timeout_expired());
332
333 DCHECK(!timer.IsRunning());
334 return false;
335 }
336
337 DemuxerConfigs MediaCodecPlayerTest::CreateAudioConfigs(
338 AudioCodec audio_codec, const base::TimeDelta& duration) {
339
340 DemuxerConfigs configs;
341 configs.audio_codec = audio_codec;
342 configs.audio_channels = 2;
343 configs.is_audio_encrypted = false;
344 configs.duration = duration;
345
346 // Other codecs are not yet supported by this helper.
347 EXPECT_TRUE(audio_codec == kCodecAAC || audio_codec == kCodecVorbis);
348
349 switch (audio_codec) {
350 case kCodecVorbis:
351 {
352 configs.audio_sampling_rate = 44100;
353 scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(
354 "vorbis-extradata");
355 configs.audio_extra_data = std::vector<uint8>(
356 buffer->data(),
357 buffer->data() + buffer->data_size());
358 }
359 break;
360
361 case kCodecAAC:
362 {
363 configs.audio_sampling_rate = 48000;
364 uint8 aac_extra_data[] = { 0x13, 0x10 };
365 configs.audio_extra_data = std::vector<uint8>(
366 aac_extra_data,
367 aac_extra_data + 2);
368 }
369 break;
370
371 default:
372 NOTREACHED();
373 break;
374 }
375
376 return configs;
377 }
378
379 DemuxerConfigs MediaCodecPlayerTest::CreateVideoConfigs(
380 const base::TimeDelta& duration) {
381 DemuxerConfigs configs;
382 configs.video_codec = kCodecVP8;
383 configs.video_size = gfx::Size(320, 240);
384 configs.is_video_encrypted = false;
385 return configs;
386 }
387
388 DemuxerConfigs MediaCodecPlayerTest::CreateAudioVideoConfigs(
389 const base::TimeDelta& duration) {
390 DemuxerConfigs configs = CreateAudioConfigs(kCodecVorbis, duration);
391 configs.video_codec = kCodecVP8;
392 configs.video_size = gfx::Size(320, 240);
393 configs.is_video_encrypted = false;
394 return configs;
395 }
396
397 TEST_F(MediaCodecPlayerTest, SetAudioConfigsBeforePlayerCreation) {
398 // Post configuration when there is no player yet.
399 EXPECT_EQ(nullptr, player_);
400
401 base::TimeDelta duration = base::TimeDelta::FromSeconds(10);
402 demuxer_->PostConfigs(CreateAudioConfigs(kCodecVorbis, duration));
403
404 // Wait until the configuration gets to the media thread.
405 WaitForCondition(base::Bind(&MockDemuxerAndroid::HasPendingConfigs,
406 base::Unretained(demuxer_)));
407
408 // Then create the player.
409 CreatePlayer();
410
411 // Configuration should propagate through the player and to the manager.
412 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsMetadataChanged,
413 base::Unretained(&manager_)));
414
415 EXPECT_EQ(duration, manager_.media_metadata_.duration);
416 EXPECT_EQ(0, manager_.media_metadata_.width);
417 EXPECT_EQ(0, manager_.media_metadata_.height);
418 }
419
420 TEST_F(MediaCodecPlayerTest, SetAudioConfigsAfterPlayerCreation) {
421 CreatePlayer();
422
423 // Wait till the player is initialized on media thread.
424 WaitForCondition(base::Bind(&MockDemuxerAndroid::IsInitialized,
425 base::Unretained(demuxer_)));
426
427 // Post configuration after the player has been initialized.
428 base::TimeDelta duration = base::TimeDelta::FromSeconds(10);
429 demuxer_->PostConfigs(CreateAudioConfigs(kCodecVorbis, duration));
430
431 // Configuration should propagate through the player and to the manager.
432 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsMetadataChanged,
433 base::Unretained(&manager_)));
434
435 EXPECT_EQ(duration, manager_.media_metadata_.duration);
436 EXPECT_EQ(0, manager_.media_metadata_.width);
437 EXPECT_EQ(0, manager_.media_metadata_.height);
438 }
439
440 TEST_F(MediaCodecPlayerTest, SetAudioVideoConfigsAfterPlayerCreation) {
441 CreatePlayer();
442
443 // Wait till the player is initialized on media thread.
444 WaitForCondition(base::Bind(&MockDemuxerAndroid::IsInitialized,
445 base::Unretained(demuxer_)));
446
447 // Post configuration after the player has been initialized.
448 base::TimeDelta duration = base::TimeDelta::FromSeconds(10);
449 demuxer_->PostConfigs(CreateAudioVideoConfigs(duration));
450
451 // Configuration should propagate through the player and to the manager.
452 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsMetadataChanged,
453 base::Unretained(&manager_)));
454
455 EXPECT_EQ(duration, manager_.media_metadata_.duration);
456 EXPECT_EQ(320, manager_.media_metadata_.width);
457 EXPECT_EQ(240, manager_.media_metadata_.height);
458 }
459
460 TEST_F(MediaCodecPlayerTest, PlayAudioTillCompletion) {
461 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
462
463 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(1000);
464 base::TimeDelta frame_period = base::TimeDelta::FromMilliseconds(20);
465 demuxer_->SetChunkFactory(scoped_ptr<ChunkFactory>(
466 new AudioFactory(duration, frame_period)));
467
468 CreatePlayer();
469
470 // Wait till the player is initialized on media thread.
471 WaitForCondition(base::Bind(&MockDemuxerAndroid::IsInitialized,
472 base::Unretained(demuxer_)));
473
474 // Post configuration after the player has been initialized.
475 demuxer_->PostConfigs(CreateAudioConfigs(kCodecVorbis, duration));
476
477 EXPECT_FALSE(manager_.IsPlaybackCompleted());
478
479 player_->Start();
480
481 bool playback_completed =
482 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackCompleted,
483 base::Unretained(&manager_)),
484 base::TimeDelta::FromMilliseconds(1100));
485 EXPECT_TRUE(playback_completed);
486 }
487
488 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698