Extension syncing: Introduce a NeedsSync pref
[chromium-blink-merge.git] / media / base / android / media_codec_player_unittest.cc
blobc917c7fe8943d02c9574fa188d2b510582c1ae09
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.
5 #include "base/bind.h"
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"
17 namespace media {
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)
28 #define RUN_ON_MEDIA_THREAD(CLASS, METHOD, ...) \
29 do { \
30 if (!GetMediaTaskRunner()->BelongsToCurrentThread()) { \
31 GetMediaTaskRunner()->PostTask( \
32 FROM_HERE, \
33 base::Bind(&CLASS::METHOD, base::Unretained(this), ##__VA_ARGS__)); \
34 return; \
35 } \
36 } while (0)
38 namespace {
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 {
48 public:
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,
64 int width,
65 int height,
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;
101 int width;
102 int height;
103 bool modified;
104 MediaMetadata() : width(0), height(0), modified(false) {}
106 MediaMetadata media_metadata_;
108 Minimax<base::TimeDelta> pts_stat_;
110 private:
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;
127 return configs;
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;
138 return result;
141 // AudioFactory creates data chunks that simulate audio stream from demuxer.
143 class AudioFactory : public TestDataFactory {
144 public:
145 AudioFactory(base::TimeDelta duration)
146 : TestDataFactory("aac-44100-packet-%d", duration, kAudioFramePeriod) {}
148 DemuxerConfigs GetConfigs() const override {
149 return TestDataFactory::CreateAudioConfigs(kCodecAAC, duration_);
152 protected:
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 {
161 public:
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));
170 protected:
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:
175 // Frames: I P B P
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;
185 break;
186 case 1: // second frame
187 unit->timestamp += frame_period_;
188 break;
189 case 2: // third frame
190 unit->timestamp -= frame_period_;
191 break;
192 case 3: // fourth frame, do not modify
193 break;
194 default:
195 NOTREACHED();
196 break;
201 // Mock of DemuxerAndroid for testing purpose.
203 class MockDemuxerAndroid : public DemuxerAndroid {
204 public:
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_; }
234 private:
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());
247 client_ = client;
248 if (pending_configs_)
249 client_->OnDemuxerConfigsAvailable(*pending_configs_);
252 void MockDemuxerAndroid::RequestDemuxerData(DemuxerStream::Type type) {
253 DemuxerData chunk;
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);
262 if (!created)
263 return;
265 chunk.type = type;
267 // Post to Media thread.
268 DCHECK(client_);
269 GetMediaTaskRunner()->PostDelayedTask(
270 FROM_HERE, base::Bind(&DemuxerAndroidClient::OnDemuxerDataAvailable,
271 base::Unretained(client_), chunk),
272 delay);
275 void MockDemuxerAndroid::PostConfigs(const DemuxerConfigs& configs) {
276 RUN_ON_MEDIA_THREAD(MockDemuxerAndroid, PostConfigs, configs);
278 DVLOG(1) << "MockDemuxerAndroid::" << __FUNCTION__;
280 DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
282 if (client_)
283 client_->OnDemuxerConfigsAvailable(configs);
284 else
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_) {
292 PostConfigs(
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 {
306 public:
307 MediaCodecPlayerTest();
308 ~MediaCodecPlayerTest() override;
310 protected:
311 typedef base::Callback<bool()> Predicate;
313 void CreatePlayer();
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()
327 private:
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() {
341 if (player_)
342 player_->DeleteOnCorrectThread();
345 void MediaCodecPlayerTest::CreatePlayer() {
346 DCHECK(demuxer_);
347 player_ = new MediaCodecPlayer(
348 0, // player_id
349 manager_.GetWeakPtr(),
350 base::Bind(&MockMediaPlayerManager::OnMediaResourcesRequested,
351 base::Unretained(&manager_)),
352 scoped_ptr<MockDemuxerAndroid>(demuxer_), GURL());
354 DCHECK(player_);
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));
377 do {
378 if (condition.Run()) {
379 timer.Stop();
380 return true;
382 message_loop_.RunUntilIdle();
383 } while (!is_timeout_expired());
385 DCHECK(!timer.IsRunning());
386 return false;
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.
403 CreatePlayer();
405 // Configuration should propagate through the player and to the manager.
406 EXPECT_TRUE(
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) {
416 CreatePlayer();
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.
428 EXPECT_TRUE(
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) {
438 CreatePlayer();
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.
449 EXPECT_TRUE(
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)));
467 CreatePlayer();
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());
478 player_->Start();
480 EXPECT_TRUE(
481 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackCompleted,
482 base::Unretained(&manager_)),
483 timeout));
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)));
500 CreatePlayer();
501 SetVideoSurface();
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());
512 player_->Start();
514 EXPECT_TRUE(
515 WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackCompleted,
516 base::Unretained(&manager_)),
517 timeout));
519 EXPECT_LE(duration, manager_.pts_stat_.max());
522 } // namespace media