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 "testing/gtest/include/gtest/gtest.h"
17 // Helper macro to skip the test if MediaCodecBridge isn't available.
18 #define SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE() \
20 if (!MediaCodecBridge::IsAvailable()) { \
21 VLOG(0) << "Could not run test - not supported on device."; \
26 #define RUN_ON_MEDIA_THREAD(CLASS, METHOD, ...) \
28 if (!GetMediaTaskRunner()->BelongsToCurrentThread()) { \
29 GetMediaTaskRunner()->PostTask( \
31 base::Bind(&CLASS::METHOD, base::Unretained(this), ##__VA_ARGS__)); \
38 const base::TimeDelta kDefaultTimeout
= base::TimeDelta::FromMilliseconds(200);
39 const base::TimeDelta kAudioFramePeriod
= base::TimeDelta::FromMilliseconds(20);
41 // Mock of MediaPlayerManager for testing purpose.
43 class MockMediaPlayerManager
: public MediaPlayerManager
{
45 MockMediaPlayerManager()
46 : playback_completed_(false), weak_ptr_factory_(this) {}
47 ~MockMediaPlayerManager() override
{}
49 MediaResourceGetter
* GetMediaResourceGetter() override
{ return nullptr; }
50 MediaUrlInterceptor
* GetMediaUrlInterceptor() override
{ return nullptr; }
51 void OnTimeUpdate(int player_id
,
52 base::TimeDelta current_timestamp
,
53 base::TimeTicks current_time_ticks
) override
{}
54 void OnMediaMetadataChanged(int player_id
,
55 base::TimeDelta duration
,
58 bool success
) override
{
59 media_metadata_
.duration
= duration
;
60 media_metadata_
.width
= width
;
61 media_metadata_
.height
= height
;
62 media_metadata_
.modified
= true;
65 void OnPlaybackComplete(int player_id
) override
{
66 playback_completed_
= true;
68 void OnMediaInterrupted(int player_id
) override
{}
69 void OnBufferingUpdate(int player_id
, int percentage
) override
{}
70 void OnSeekComplete(int player_id
,
71 const base::TimeDelta
& current_time
) override
{}
72 void OnError(int player_id
, int error
) override
{}
73 void OnVideoSizeChanged(int player_id
, int width
, int height
) override
{}
74 void OnAudibleStateChanged(int player_id
, bool is_audible_now
) override
{}
75 void OnWaitingForDecryptionKey(int player_id
) override
{}
76 MediaPlayerAndroid
* GetFullscreenPlayer() override
{ return nullptr; }
77 MediaPlayerAndroid
* GetPlayer(int player_id
) override
{ return nullptr; }
78 bool RequestPlay(int player_id
) override
{ return true; }
80 void OnMediaResourcesRequested(int player_id
) {}
82 base::WeakPtr
<MockMediaPlayerManager
> GetWeakPtr() {
83 return weak_ptr_factory_
.GetWeakPtr();
86 // Conditions to wait for.
87 bool IsMetadataChanged() const { return media_metadata_
.modified
; }
88 bool IsPlaybackCompleted() const { return playback_completed_
; }
90 struct MediaMetadata
{
91 base::TimeDelta duration
;
95 MediaMetadata() : width(0), height(0), modified(false) {}
97 MediaMetadata media_metadata_
;
100 bool playback_completed_
;
102 base::WeakPtrFactory
<MockMediaPlayerManager
> weak_ptr_factory_
;
104 DISALLOW_COPY_AND_ASSIGN(MockMediaPlayerManager
);
107 // Helper method that creates demuxer configuration.
109 DemuxerConfigs
CreateAudioVideoConfigs(const base::TimeDelta
& duration
,
110 const gfx::Size
& video_size
) {
111 DemuxerConfigs configs
=
112 TestDataFactory::CreateAudioConfigs(kCodecVorbis
, duration
);
113 configs
.video_codec
= kCodecVP8
;
114 configs
.video_size
= video_size
;
115 configs
.is_video_encrypted
= false;
119 DemuxerConfigs
CreateAudioVideoConfigs(const TestDataFactory
* audio
,
120 const TestDataFactory
* video
) {
121 DemuxerConfigs result
= audio
->GetConfigs();
122 DemuxerConfigs vconf
= video
->GetConfigs();
124 result
.video_codec
= vconf
.video_codec
;
125 result
.video_size
= vconf
.video_size
;
126 result
.is_video_encrypted
= vconf
.is_video_encrypted
;
130 // AudioFactory creates data chunks that simulate audio stream from demuxer.
132 class AudioFactory
: public TestDataFactory
{
134 AudioFactory(const base::TimeDelta
& duration
)
135 : TestDataFactory("vorbis-packet-%d", duration
, kAudioFramePeriod
) {}
137 DemuxerConfigs
GetConfigs() const override
{
138 return TestDataFactory::CreateAudioConfigs(kCodecVorbis
, duration_
);
142 void ModifyAccessUnit(int index_in_chunk
, AccessUnit
* unit
) override
{
143 // Vorbis needs 4 extra bytes padding on Android to decode properly.
144 // Check NuMediaExtractor.cpp in Android source code.
145 uint8 padding
[4] = {0xff, 0xff, 0xff, 0xff};
146 unit
->data
.insert(unit
->data
.end(), padding
, padding
+ 4);
150 // Mock of DemuxerAndroid for testing purpose.
152 class MockDemuxerAndroid
: public DemuxerAndroid
{
154 MockDemuxerAndroid() : client_(nullptr) {}
155 ~MockDemuxerAndroid() override
{}
157 // DemuxerAndroid implementation
158 void Initialize(DemuxerAndroidClient
* client
) override
;
159 void RequestDemuxerData(DemuxerStream::Type type
) override
;
160 void RequestDemuxerSeek(const base::TimeDelta
& time_to_seek
,
161 bool is_browser_seek
) override
{}
163 // Sets the audio data factory.
164 void SetAudioFactory(scoped_ptr
<TestDataFactory
> factory
) {
165 audio_factory_
= factory
.Pass();
168 // Sets the video data factory.
169 void SetVideoFactory(scoped_ptr
<TestDataFactory
> factory
) {
170 video_factory_
= factory
.Pass();
173 // Post DemuxerConfigs to the client (i.e. the player) on correct thread.
174 void PostConfigs(const DemuxerConfigs
& configs
);
176 // Post DemuxerConfigs derived from data factories that has been set.
177 void PostInternalConfigs();
179 // Conditions to wait for.
180 bool IsInitialized() const { return client_
; }
181 bool HasPendingConfigs() const { return pending_configs_
; }
184 DemuxerAndroidClient
* client_
;
185 scoped_ptr
<DemuxerConfigs
> pending_configs_
;
186 scoped_ptr
<TestDataFactory
> audio_factory_
;
187 scoped_ptr
<TestDataFactory
> video_factory_
;
189 DISALLOW_COPY_AND_ASSIGN(MockDemuxerAndroid
);
192 void MockDemuxerAndroid::Initialize(DemuxerAndroidClient
* client
) {
193 DVLOG(1) << "MockDemuxerAndroid::" << __FUNCTION__
;
194 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
197 if (pending_configs_
)
198 client_
->OnDemuxerConfigsAvailable(*pending_configs_
);
201 void MockDemuxerAndroid::RequestDemuxerData(DemuxerStream::Type type
) {
203 base::TimeDelta delay
;
205 bool created
= false;
206 if (type
== DemuxerStream::AUDIO
&& audio_factory_
)
207 created
= audio_factory_
->CreateChunk(&chunk
, &delay
);
208 else if (type
== DemuxerStream::VIDEO
&& audio_factory_
)
209 created
= video_factory_
->CreateChunk(&chunk
, &delay
);
216 // Post to Media thread.
218 GetMediaTaskRunner()->PostDelayedTask(
219 FROM_HERE
, base::Bind(&DemuxerAndroidClient::OnDemuxerDataAvailable
,
220 base::Unretained(client_
), chunk
),
224 void MockDemuxerAndroid::PostConfigs(const DemuxerConfigs
& configs
) {
225 DVLOG(1) << "MockDemuxerAndroid::" << __FUNCTION__
;
226 RUN_ON_MEDIA_THREAD(MockDemuxerAndroid
, PostConfigs
, configs
);
228 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
231 client_
->OnDemuxerConfigsAvailable(configs
);
233 pending_configs_
= scoped_ptr
<DemuxerConfigs
>(new DemuxerConfigs(configs
));
236 void MockDemuxerAndroid::PostInternalConfigs() {
237 ASSERT_TRUE(audio_factory_
|| video_factory_
);
239 if (audio_factory_
&& video_factory_
) {
241 CreateAudioVideoConfigs(audio_factory_
.get(), video_factory_
.get()));
242 } else if (audio_factory_
) {
243 PostConfigs(audio_factory_
->GetConfigs());
244 } else if (video_factory_
) {
245 PostConfigs(video_factory_
->GetConfigs());
249 } // namespace (anonymous)
251 // The test fixture for MediaCodecPlayer
253 class MediaCodecPlayerTest
: public testing::Test
{
255 MediaCodecPlayerTest();
256 ~MediaCodecPlayerTest() override
;
259 typedef base::Callback
<bool()> Predicate
;
263 // Waits for condition to become true or for timeout to expire.
264 // Returns true if the condition becomes true.
265 bool WaitForCondition(const Predicate
& condition
,
266 const base::TimeDelta
& timeout
= kDefaultTimeout
);
268 base::MessageLoop message_loop_
;
269 MockMediaPlayerManager manager_
;
270 MockDemuxerAndroid
* demuxer_
; // owned by player_
271 MediaCodecPlayer
* player_
; // raw pointer due to DeleteOnCorrectThread()
274 bool is_timeout_expired() const { return is_timeout_expired_
; }
275 void SetTimeoutExpired(bool value
) { is_timeout_expired_
= value
; }
277 bool is_timeout_expired_
;
279 DISALLOW_COPY_AND_ASSIGN(MediaCodecPlayerTest
);
282 MediaCodecPlayerTest::MediaCodecPlayerTest()
283 : demuxer_(new MockDemuxerAndroid()), player_(nullptr) {
286 void MediaCodecPlayerTest::CreatePlayer() {
288 player_
= new MediaCodecPlayer(
290 manager_
.GetWeakPtr(),
291 base::Bind(&MockMediaPlayerManager::OnMediaResourcesRequested
,
292 base::Unretained(&manager_
)),
293 scoped_ptr
<MockDemuxerAndroid
>(demuxer_
), GURL());
298 MediaCodecPlayerTest::~MediaCodecPlayerTest() {
300 player_
->DeleteOnCorrectThread();
303 bool MediaCodecPlayerTest::WaitForCondition(const Predicate
& condition
,
304 const base::TimeDelta
& timeout
) {
305 // Let the message_loop_ process events.
306 // We start the timer and RunUntilIdle() until it signals.
308 SetTimeoutExpired(false);
310 base::Timer
timer(false, false);
311 timer
.Start(FROM_HERE
, timeout
,
312 base::Bind(&MediaCodecPlayerTest::SetTimeoutExpired
,
313 base::Unretained(this), true));
316 if (condition
.Run()) {
320 message_loop_
.RunUntilIdle();
321 } while (!is_timeout_expired());
323 DCHECK(!timer
.IsRunning());
327 TEST_F(MediaCodecPlayerTest
, SetAudioConfigsBeforePlayerCreation
) {
328 // Post configuration when there is no player yet.
329 EXPECT_EQ(nullptr, player_
);
331 base::TimeDelta duration
= base::TimeDelta::FromSeconds(10);
333 demuxer_
->PostConfigs(
334 TestDataFactory::CreateAudioConfigs(kCodecVorbis
, duration
));
336 // Wait until the configuration gets to the media thread.
337 EXPECT_TRUE(WaitForCondition(base::Bind(
338 &MockDemuxerAndroid::HasPendingConfigs
, base::Unretained(demuxer_
))));
340 // Then create the player.
343 // Configuration should propagate through the player and to the manager.
345 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsMetadataChanged
,
346 base::Unretained(&manager_
))));
348 EXPECT_EQ(duration
, manager_
.media_metadata_
.duration
);
349 EXPECT_EQ(0, manager_
.media_metadata_
.width
);
350 EXPECT_EQ(0, manager_
.media_metadata_
.height
);
353 TEST_F(MediaCodecPlayerTest
, SetAudioConfigsAfterPlayerCreation
) {
356 // Wait till the player is initialized on media thread.
357 EXPECT_TRUE(WaitForCondition(base::Bind(&MockDemuxerAndroid::IsInitialized
,
358 base::Unretained(demuxer_
))));
360 // Post configuration after the player has been initialized.
361 base::TimeDelta duration
= base::TimeDelta::FromSeconds(10);
362 demuxer_
->PostConfigs(
363 TestDataFactory::CreateAudioConfigs(kCodecVorbis
, duration
));
365 // Configuration should propagate through the player and to the manager.
367 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsMetadataChanged
,
368 base::Unretained(&manager_
))));
370 EXPECT_EQ(duration
, manager_
.media_metadata_
.duration
);
371 EXPECT_EQ(0, manager_
.media_metadata_
.width
);
372 EXPECT_EQ(0, manager_
.media_metadata_
.height
);
375 TEST_F(MediaCodecPlayerTest
, SetAudioVideoConfigsAfterPlayerCreation
) {
378 // Wait till the player is initialized on media thread.
379 EXPECT_TRUE(WaitForCondition(base::Bind(&MockDemuxerAndroid::IsInitialized
,
380 base::Unretained(demuxer_
))));
382 // Post configuration after the player has been initialized.
383 base::TimeDelta duration
= base::TimeDelta::FromSeconds(10);
384 demuxer_
->PostConfigs(CreateAudioVideoConfigs(duration
, gfx::Size(320, 240)));
386 // Configuration should propagate through the player and to the manager.
388 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsMetadataChanged
,
389 base::Unretained(&manager_
))));
391 EXPECT_EQ(duration
, manager_
.media_metadata_
.duration
);
392 EXPECT_EQ(320, manager_
.media_metadata_
.width
);
393 EXPECT_EQ(240, manager_
.media_metadata_
.height
);
396 TEST_F(MediaCodecPlayerTest
, PlayAudioTillCompletion
) {
397 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
399 base::TimeDelta duration
= base::TimeDelta::FromMilliseconds(1000);
400 base::TimeDelta timeout
= base::TimeDelta::FromMilliseconds(1100);
402 demuxer_
->SetAudioFactory(
403 scoped_ptr
<AudioFactory
>(new AudioFactory(duration
)));
407 // Wait till the player is initialized on media thread.
408 EXPECT_TRUE(WaitForCondition(base::Bind(&MockDemuxerAndroid::IsInitialized
,
409 base::Unretained(demuxer_
))));
411 // Post configuration after the player has been initialized.
412 demuxer_
->PostInternalConfigs();
414 EXPECT_FALSE(manager_
.IsPlaybackCompleted());
419 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackCompleted
,
420 base::Unretained(&manager_
)),