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.
6 #include "base/logging.h"
7 #include "base/timer/timer.h"
8 #include "media/base/android/demuxer_android.h"
9 #include "media/base/android/media_codec_bridge.h"
10 #include "media/base/android/media_codec_player.h"
11 #include "media/base/android/media_player_manager.h"
12 #include "media/base/android/test_data_factory.h"
13 #include "media/base/android/test_statistics.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/gl/android/surface_texture.h"
19 // Helper macro to skip the test if MediaCodecBridge isn't available.
20 #define SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE() \
22 if (!MediaCodecBridge::IsAvailable()) { \
23 VLOG(0) << "Could not run test - not supported on device."; \
28 #define RUN_ON_MEDIA_THREAD(CLASS, METHOD, ...) \
30 if (!GetMediaTaskRunner()->BelongsToCurrentThread()) { \
31 GetMediaTaskRunner()->PostTask( \
33 base::Bind(&CLASS::METHOD, base::Unretained(this), ##__VA_ARGS__)); \
40 const base::TimeDelta kDefaultTimeout
= base::TimeDelta::FromMilliseconds(200);
41 const base::TimeDelta kAudioFramePeriod
=
42 base::TimeDelta::FromSecondsD(1024.0 / 44100); // 1024 samples @ 44100 Hz
43 const base::TimeDelta kVideoFramePeriod
= base::TimeDelta::FromMilliseconds(20);
45 // Mock of MediaPlayerManager for testing purpose.
47 class MockMediaPlayerManager
: public MediaPlayerManager
{
49 MockMediaPlayerManager()
50 : playback_completed_(false), weak_ptr_factory_(this) {}
51 ~MockMediaPlayerManager() override
{}
53 MediaResourceGetter
* GetMediaResourceGetter() override
{ return nullptr; }
54 MediaUrlInterceptor
* GetMediaUrlInterceptor() override
{ return nullptr; }
56 void OnTimeUpdate(int player_id
,
57 base::TimeDelta current_timestamp
,
58 base::TimeTicks current_time_ticks
) override
{
59 pts_stat_
.AddValue(current_timestamp
);
62 void OnMediaMetadataChanged(int player_id
,
63 base::TimeDelta duration
,
66 bool success
) override
{
67 media_metadata_
.duration
= duration
;
68 media_metadata_
.width
= width
;
69 media_metadata_
.height
= height
;
70 media_metadata_
.modified
= true;
73 void OnPlaybackComplete(int player_id
) override
{
74 playback_completed_
= true;
77 void OnMediaInterrupted(int player_id
) override
{}
78 void OnBufferingUpdate(int player_id
, int percentage
) override
{}
79 void OnSeekComplete(int player_id
,
80 const base::TimeDelta
& current_time
) override
{}
81 void OnError(int player_id
, int error
) override
{}
82 void OnVideoSizeChanged(int player_id
, int width
, int height
) override
{}
83 void OnAudibleStateChanged(int player_id
, bool is_audible_now
) override
{}
84 void OnWaitingForDecryptionKey(int player_id
) override
{}
85 MediaPlayerAndroid
* GetFullscreenPlayer() override
{ return nullptr; }
86 MediaPlayerAndroid
* GetPlayer(int player_id
) override
{ return nullptr; }
87 bool RequestPlay(int player_id
) override
{ return true; }
89 void OnMediaResourcesRequested(int player_id
) {}
91 base::WeakPtr
<MockMediaPlayerManager
> GetWeakPtr() {
92 return weak_ptr_factory_
.GetWeakPtr();
95 // Conditions to wait for.
96 bool IsMetadataChanged() const { return media_metadata_
.modified
; }
97 bool IsPlaybackCompleted() const { return playback_completed_
; }
99 struct MediaMetadata
{
100 base::TimeDelta duration
;
104 MediaMetadata() : width(0), height(0), modified(false) {}
106 MediaMetadata media_metadata_
;
108 Minimax
<base::TimeDelta
> pts_stat_
;
111 bool playback_completed_
;
113 base::WeakPtrFactory
<MockMediaPlayerManager
> weak_ptr_factory_
;
115 DISALLOW_COPY_AND_ASSIGN(MockMediaPlayerManager
);
118 // Helper method that creates demuxer configuration.
120 DemuxerConfigs
CreateAudioVideoConfigs(const base::TimeDelta
& duration
,
121 const gfx::Size
& video_size
) {
122 DemuxerConfigs configs
=
123 TestDataFactory::CreateAudioConfigs(kCodecAAC
, duration
);
124 configs
.video_codec
= kCodecVP8
;
125 configs
.video_size
= video_size
;
126 configs
.is_video_encrypted
= false;
130 DemuxerConfigs
CreateAudioVideoConfigs(const TestDataFactory
* audio
,
131 const TestDataFactory
* video
) {
132 DemuxerConfigs result
= audio
->GetConfigs();
133 DemuxerConfigs vconf
= video
->GetConfigs();
135 result
.video_codec
= vconf
.video_codec
;
136 result
.video_size
= vconf
.video_size
;
137 result
.is_video_encrypted
= vconf
.is_video_encrypted
;
141 // AudioFactory creates data chunks that simulate audio stream from demuxer.
143 class AudioFactory
: public TestDataFactory
{
145 AudioFactory(base::TimeDelta duration
)
146 : TestDataFactory("aac-44100-packet-%d", duration
, kAudioFramePeriod
) {}
148 DemuxerConfigs
GetConfigs() const override
{
149 return TestDataFactory::CreateAudioConfigs(kCodecAAC
, duration_
);
153 void ModifyAccessUnit(int index_in_chunk
, AccessUnit
* unit
) override
{
154 unit
->is_key_frame
= true;
158 // VideoFactory creates a video stream from demuxer.
160 class VideoFactory
: public TestDataFactory
{
162 VideoFactory(base::TimeDelta duration
)
163 : TestDataFactory("h264-320x180-frame-%d", duration
, kVideoFramePeriod
) {}
165 DemuxerConfigs
GetConfigs() const override
{
166 return TestDataFactory::CreateVideoConfigs(kCodecH264
, duration_
,
167 gfx::Size(320, 180));
171 void ModifyAccessUnit(int index_in_chunk
, AccessUnit
* unit
) override
{
172 // The frames are taken from High profile and some are B-frames.
173 // The first 4 frames appear in the file in the following order:
176 // Decoding order: 0 1 2 3
177 // Presentation order: 0 2 1 4(3)
179 // I keep the last PTS to be 3 for simplicity.
181 // Swap pts for second and third frames. Make first frame a key frame.
182 switch (index_in_chunk
) {
183 case 0: // first frame
184 unit
->is_key_frame
= true;
186 case 1: // second frame
187 unit
->timestamp
+= frame_period_
;
189 case 2: // third frame
190 unit
->timestamp
-= frame_period_
;
192 case 3: // fourth frame, do not modify
201 // Mock of DemuxerAndroid for testing purpose.
203 class MockDemuxerAndroid
: public DemuxerAndroid
{
205 MockDemuxerAndroid() : client_(nullptr) {}
206 ~MockDemuxerAndroid() override
{}
208 // DemuxerAndroid implementation
209 void Initialize(DemuxerAndroidClient
* client
) override
;
210 void RequestDemuxerData(DemuxerStream::Type type
) override
;
211 void RequestDemuxerSeek(const base::TimeDelta
& time_to_seek
,
212 bool is_browser_seek
) override
{}
214 // Sets the audio data factory.
215 void SetAudioFactory(scoped_ptr
<TestDataFactory
> factory
) {
216 audio_factory_
= factory
.Pass();
219 // Sets the video data factory.
220 void SetVideoFactory(scoped_ptr
<TestDataFactory
> factory
) {
221 video_factory_
= factory
.Pass();
224 // Post DemuxerConfigs to the client (i.e. the player) on correct thread.
225 void PostConfigs(const DemuxerConfigs
& configs
);
227 // Post DemuxerConfigs derived from data factories that has been set.
228 void PostInternalConfigs();
230 // Conditions to wait for.
231 bool IsInitialized() const { return client_
; }
232 bool HasPendingConfigs() const { return pending_configs_
; }
235 DemuxerAndroidClient
* client_
;
236 scoped_ptr
<DemuxerConfigs
> pending_configs_
;
237 scoped_ptr
<TestDataFactory
> audio_factory_
;
238 scoped_ptr
<TestDataFactory
> video_factory_
;
240 DISALLOW_COPY_AND_ASSIGN(MockDemuxerAndroid
);
243 void MockDemuxerAndroid::Initialize(DemuxerAndroidClient
* client
) {
244 DVLOG(1) << "MockDemuxerAndroid::" << __FUNCTION__
;
245 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
248 if (pending_configs_
)
249 client_
->OnDemuxerConfigsAvailable(*pending_configs_
);
252 void MockDemuxerAndroid::RequestDemuxerData(DemuxerStream::Type type
) {
254 base::TimeDelta delay
;
256 bool created
= false;
257 if (type
== DemuxerStream::AUDIO
&& audio_factory_
)
258 created
= audio_factory_
->CreateChunk(&chunk
, &delay
);
259 else if (type
== DemuxerStream::VIDEO
&& video_factory_
)
260 created
= video_factory_
->CreateChunk(&chunk
, &delay
);
267 // Post to Media thread.
269 GetMediaTaskRunner()->PostDelayedTask(
270 FROM_HERE
, base::Bind(&DemuxerAndroidClient::OnDemuxerDataAvailable
,
271 base::Unretained(client_
), chunk
),
275 void MockDemuxerAndroid::PostConfigs(const DemuxerConfigs
& configs
) {
276 RUN_ON_MEDIA_THREAD(MockDemuxerAndroid
, PostConfigs
, configs
);
278 DVLOG(1) << "MockDemuxerAndroid::" << __FUNCTION__
;
280 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
283 client_
->OnDemuxerConfigsAvailable(configs
);
285 pending_configs_
= scoped_ptr
<DemuxerConfigs
>(new DemuxerConfigs(configs
));
288 void MockDemuxerAndroid::PostInternalConfigs() {
289 ASSERT_TRUE(audio_factory_
|| video_factory_
);
291 if (audio_factory_
&& video_factory_
) {
293 CreateAudioVideoConfigs(audio_factory_
.get(), video_factory_
.get()));
294 } else if (audio_factory_
) {
295 PostConfigs(audio_factory_
->GetConfigs());
296 } else if (video_factory_
) {
297 PostConfigs(video_factory_
->GetConfigs());
301 } // namespace (anonymous)
303 // The test fixture for MediaCodecPlayer
305 class MediaCodecPlayerTest
: public testing::Test
{
307 MediaCodecPlayerTest();
308 ~MediaCodecPlayerTest() override
;
311 typedef base::Callback
<bool()> Predicate
;
314 void SetVideoSurface();
316 // Waits for condition to become true or for timeout to expire.
317 // Returns true if the condition becomes true.
318 bool WaitForCondition(const Predicate
& condition
,
319 const base::TimeDelta
& timeout
= kDefaultTimeout
);
321 base::MessageLoop message_loop_
;
322 MockMediaPlayerManager manager_
;
323 MockDemuxerAndroid
* demuxer_
; // owned by player_
324 scoped_refptr
<gfx::SurfaceTexture
> surface_texture_
;
325 MediaCodecPlayer
* player_
; // raw pointer due to DeleteOnCorrectThread()
328 bool is_timeout_expired() const { return is_timeout_expired_
; }
329 void SetTimeoutExpired(bool value
) { is_timeout_expired_
= value
; }
331 bool is_timeout_expired_
;
333 DISALLOW_COPY_AND_ASSIGN(MediaCodecPlayerTest
);
336 MediaCodecPlayerTest::MediaCodecPlayerTest()
337 : demuxer_(new MockDemuxerAndroid()), player_(nullptr) {
340 MediaCodecPlayerTest::~MediaCodecPlayerTest() {
342 player_
->DeleteOnCorrectThread();
345 void MediaCodecPlayerTest::CreatePlayer() {
347 player_
= new MediaCodecPlayer(
349 manager_
.GetWeakPtr(),
350 base::Bind(&MockMediaPlayerManager::OnMediaResourcesRequested
,
351 base::Unretained(&manager_
)),
352 scoped_ptr
<MockDemuxerAndroid
>(demuxer_
), GURL());
357 void MediaCodecPlayerTest::SetVideoSurface() {
358 surface_texture_
= gfx::SurfaceTexture::Create(0);
359 gfx::ScopedJavaSurface
surface(surface_texture_
.get());
361 ASSERT_NE(nullptr, player_
);
362 player_
->SetVideoSurface(surface
.Pass());
365 bool MediaCodecPlayerTest::WaitForCondition(const Predicate
& condition
,
366 const base::TimeDelta
& timeout
) {
367 // Let the message_loop_ process events.
368 // We start the timer and RunUntilIdle() until it signals.
370 SetTimeoutExpired(false);
372 base::Timer
timer(false, false);
373 timer
.Start(FROM_HERE
, timeout
,
374 base::Bind(&MediaCodecPlayerTest::SetTimeoutExpired
,
375 base::Unretained(this), true));
378 if (condition
.Run()) {
382 message_loop_
.RunUntilIdle();
383 } while (!is_timeout_expired());
385 DCHECK(!timer
.IsRunning());
389 TEST_F(MediaCodecPlayerTest
, SetAudioConfigsBeforePlayerCreation
) {
390 // Post configuration when there is no player yet.
391 EXPECT_EQ(nullptr, player_
);
393 base::TimeDelta duration
= base::TimeDelta::FromSeconds(10);
395 demuxer_
->PostConfigs(
396 TestDataFactory::CreateAudioConfigs(kCodecAAC
, duration
));
398 // Wait until the configuration gets to the media thread.
399 EXPECT_TRUE(WaitForCondition(base::Bind(
400 &MockDemuxerAndroid::HasPendingConfigs
, base::Unretained(demuxer_
))));
402 // Then create the player.
405 // Configuration should propagate through the player and to the manager.
407 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsMetadataChanged
,
408 base::Unretained(&manager_
))));
410 EXPECT_EQ(duration
, manager_
.media_metadata_
.duration
);
411 EXPECT_EQ(0, manager_
.media_metadata_
.width
);
412 EXPECT_EQ(0, manager_
.media_metadata_
.height
);
415 TEST_F(MediaCodecPlayerTest
, SetAudioConfigsAfterPlayerCreation
) {
418 // Wait till the player is initialized on media thread.
419 EXPECT_TRUE(WaitForCondition(base::Bind(&MockDemuxerAndroid::IsInitialized
,
420 base::Unretained(demuxer_
))));
422 // Post configuration after the player has been initialized.
423 base::TimeDelta duration
= base::TimeDelta::FromSeconds(10);
424 demuxer_
->PostConfigs(
425 TestDataFactory::CreateAudioConfigs(kCodecAAC
, duration
));
427 // Configuration should propagate through the player and to the manager.
429 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsMetadataChanged
,
430 base::Unretained(&manager_
))));
432 EXPECT_EQ(duration
, manager_
.media_metadata_
.duration
);
433 EXPECT_EQ(0, manager_
.media_metadata_
.width
);
434 EXPECT_EQ(0, manager_
.media_metadata_
.height
);
437 TEST_F(MediaCodecPlayerTest
, SetAudioVideoConfigsAfterPlayerCreation
) {
440 // Wait till the player is initialized on media thread.
441 EXPECT_TRUE(WaitForCondition(base::Bind(&MockDemuxerAndroid::IsInitialized
,
442 base::Unretained(demuxer_
))));
444 // Post configuration after the player has been initialized.
445 base::TimeDelta duration
= base::TimeDelta::FromSeconds(10);
446 demuxer_
->PostConfigs(CreateAudioVideoConfigs(duration
, gfx::Size(320, 240)));
448 // Configuration should propagate through the player and to the manager.
450 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsMetadataChanged
,
451 base::Unretained(&manager_
))));
453 EXPECT_EQ(duration
, manager_
.media_metadata_
.duration
);
454 EXPECT_EQ(320, manager_
.media_metadata_
.width
);
455 EXPECT_EQ(240, manager_
.media_metadata_
.height
);
458 TEST_F(MediaCodecPlayerTest
, AudioPlayTillCompletion
) {
459 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
461 base::TimeDelta duration
= base::TimeDelta::FromMilliseconds(1000);
462 base::TimeDelta timeout
= base::TimeDelta::FromMilliseconds(2000);
464 demuxer_
->SetAudioFactory(
465 scoped_ptr
<AudioFactory
>(new AudioFactory(duration
)));
469 // Wait till the player is initialized on media thread.
470 EXPECT_TRUE(WaitForCondition(base::Bind(&MockDemuxerAndroid::IsInitialized
,
471 base::Unretained(demuxer_
))));
473 // Post configuration after the player has been initialized.
474 demuxer_
->PostInternalConfigs();
476 EXPECT_FALSE(manager_
.IsPlaybackCompleted());
481 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackCompleted
,
482 base::Unretained(&manager_
)),
485 // Current timestamp reflects "now playing" time. It might come with delay
486 // relative to the frame's PTS. Allow for 100 ms delay here.
487 base::TimeDelta audio_pts_delay
= base::TimeDelta::FromMilliseconds(100);
488 EXPECT_LT(duration
- audio_pts_delay
, manager_
.pts_stat_
.max());
491 TEST_F(MediaCodecPlayerTest
, VideoPlayTillCompletion
) {
492 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
494 base::TimeDelta duration
= base::TimeDelta::FromMilliseconds(500);
495 base::TimeDelta timeout
= base::TimeDelta::FromMilliseconds(1500);
497 demuxer_
->SetVideoFactory(
498 scoped_ptr
<VideoFactory
>(new VideoFactory(duration
)));
503 // Wait till the player is initialized on media thread.
504 EXPECT_TRUE(WaitForCondition(base::Bind(&MockDemuxerAndroid::IsInitialized
,
505 base::Unretained(demuxer_
))));
507 // Post configuration after the player has been initialized.
508 demuxer_
->PostInternalConfigs();
510 EXPECT_FALSE(manager_
.IsPlaybackCompleted());
515 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackCompleted
,
516 base::Unretained(&manager_
)),
519 EXPECT_LE(duration
, manager_
.pts_stat_
.max());