OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
watk
2015/06/05 01:00:33
s/2013/2015
Tima Vaisburd
2015/06/05 04:17:47
Done.
| |
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 kDefaultDelay = 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 delay to expire. | |
265 // Returns true if the condition becomes true. | |
266 bool WaitForCondition(const Predicate& condition, | |
267 const base::TimeDelta& delay = kDefaultDelay); | |
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_delay_expired() const { return is_delay_expired_; } | |
281 void SetDelayExpired(bool value) { | |
282 is_delay_expired_ = value; | |
283 } | |
284 | |
285 bool is_delay_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& delay) { | |
315 // Let the message_loop_ process events. | |
316 // We post delayed task and RunUntilIdle() until it signals. | |
317 | |
318 SetDelayExpired(false); | |
319 | |
320 base::Timer timer(false, false); | |
321 timer.Start(FROM_HERE, delay, | |
322 base::Bind(&MediaCodecPlayerTest::SetDelayExpired, | |
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_delay_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 | |
OLD | NEW |