| Index: media/base/android/media_codec_player_unittest.cc
|
| diff --git a/media/base/android/media_codec_player_unittest.cc b/media/base/android/media_codec_player_unittest.cc
|
| index a93c7dcd93d19be5fe2c0ba004350b9d87268e10..aa2a89c747b90016bb50213a1795e01c0b6ab248 100644
|
| --- a/media/base/android/media_codec_player_unittest.cc
|
| +++ b/media/base/android/media_codec_player_unittest.cc
|
| @@ -175,13 +175,16 @@ class AudioFactory : public TestDataFactory {
|
| class VideoFactory : public TestDataFactory {
|
| public:
|
| VideoFactory(base::TimeDelta duration)
|
| - : TestDataFactory("h264-320x180-frame-%d", duration, kVideoFramePeriod) {}
|
| + : TestDataFactory("h264-320x180-frame-%d", duration, kVideoFramePeriod),
|
| + key_frame_requested_(true) {}
|
|
|
| DemuxerConfigs GetConfigs() const override {
|
| return TestDataFactory::CreateVideoConfigs(kCodecH264, duration_,
|
| gfx::Size(320, 180));
|
| }
|
|
|
| + void RequestKeyFrame() { key_frame_requested_ = true; }
|
| +
|
| protected:
|
| void ModifyAccessUnit(int index_in_chunk, AccessUnit* unit) override {
|
| // The frames are taken from High profile and some are B-frames.
|
| @@ -196,7 +199,8 @@ class VideoFactory : public TestDataFactory {
|
| // Swap pts for second and third frames. Make first frame a key frame.
|
| switch (index_in_chunk) {
|
| case 0: // first frame
|
| - unit->is_key_frame = true;
|
| + unit->is_key_frame = key_frame_requested_;
|
| + key_frame_requested_ = false;
|
| break;
|
| case 1: // second frame
|
| unit->timestamp += frame_period_;
|
| @@ -211,13 +215,16 @@ class VideoFactory : public TestDataFactory {
|
| break;
|
| }
|
| }
|
| +
|
| + private:
|
| + bool key_frame_requested_;
|
| };
|
|
|
| // Mock of DemuxerAndroid for testing purpose.
|
|
|
| class MockDemuxerAndroid : public DemuxerAndroid {
|
| public:
|
| - MockDemuxerAndroid() : client_(nullptr) {}
|
| + MockDemuxerAndroid() : client_(nullptr), num_browser_seeks_(0) {}
|
| ~MockDemuxerAndroid() override {}
|
|
|
| // DemuxerAndroid implementation
|
| @@ -227,12 +234,12 @@ class MockDemuxerAndroid : public DemuxerAndroid {
|
| bool is_browser_seek) override;
|
|
|
| // Sets the audio data factory.
|
| - void SetAudioFactory(scoped_ptr<TestDataFactory> factory) {
|
| + void SetAudioFactory(scoped_ptr<AudioFactory> factory) {
|
| audio_factory_ = factory.Pass();
|
| }
|
|
|
| // Sets the video data factory.
|
| - void SetVideoFactory(scoped_ptr<TestDataFactory> factory) {
|
| + void SetVideoFactory(scoped_ptr<VideoFactory> factory) {
|
| video_factory_ = factory.Pass();
|
| }
|
|
|
| @@ -245,12 +252,14 @@ class MockDemuxerAndroid : public DemuxerAndroid {
|
| // Conditions to wait for.
|
| bool IsInitialized() const { return client_; }
|
| bool HasPendingConfigs() const { return pending_configs_; }
|
| + bool ReceivedBrowserSeekRequest() const { return num_browser_seeks_ > 0; }
|
|
|
| private:
|
| DemuxerAndroidClient* client_;
|
| scoped_ptr<DemuxerConfigs> pending_configs_;
|
| - scoped_ptr<TestDataFactory> audio_factory_;
|
| - scoped_ptr<TestDataFactory> video_factory_;
|
| + scoped_ptr<AudioFactory> audio_factory_;
|
| + scoped_ptr<VideoFactory> video_factory_;
|
| + int num_browser_seeks_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(MockDemuxerAndroid);
|
| };
|
| @@ -292,8 +301,13 @@ void MockDemuxerAndroid::RequestDemuxerSeek(const base::TimeDelta& time_to_seek,
|
| // Tell data factories to start next chunk with the new timestamp.
|
| if (audio_factory_)
|
| audio_factory_->SeekTo(time_to_seek);
|
| - if (video_factory_)
|
| + if (video_factory_) {
|
| video_factory_->SeekTo(time_to_seek);
|
| + video_factory_->RequestKeyFrame();
|
| + }
|
| +
|
| + if (is_browser_seek)
|
| + ++num_browser_seeks_;
|
|
|
| // Post OnDemuxerSeekDone() to the player.
|
| DCHECK(client_);
|
| @@ -347,6 +361,8 @@ class MediaCodecPlayerTest : public testing::Test {
|
|
|
| void CreatePlayer();
|
| void SetVideoSurface();
|
| + void SetVideoSurfaceB();
|
| + void RemoveVideoSurface();
|
|
|
| // Waits for condition to become true or for timeout to expire.
|
| // Returns true if the condition becomes true.
|
| @@ -363,10 +379,15 @@ class MediaCodecPlayerTest : public testing::Test {
|
| const base::TimeDelta& pts,
|
| const base::TimeDelta& timeout = kDefaultTimeout);
|
|
|
| + // Helper method that starts video only stream. Waits till it actually
|
| + // started.
|
| + void StartVideoPlayback(base::TimeDelta duration);
|
| +
|
| base::MessageLoop message_loop_;
|
| MockMediaPlayerManager manager_;
|
| MockDemuxerAndroid* demuxer_; // owned by player_
|
| - scoped_refptr<gfx::SurfaceTexture> surface_texture_;
|
| + scoped_refptr<gfx::SurfaceTexture> surface_texture_a_;
|
| + scoped_refptr<gfx::SurfaceTexture> surface_texture_b_;
|
| MediaCodecPlayer* player_; // raw pointer due to DeleteOnCorrectThread()
|
|
|
| private:
|
| @@ -400,13 +421,26 @@ void MediaCodecPlayerTest::CreatePlayer() {
|
| }
|
|
|
| void MediaCodecPlayerTest::SetVideoSurface() {
|
| - surface_texture_ = gfx::SurfaceTexture::Create(0);
|
| - gfx::ScopedJavaSurface surface(surface_texture_.get());
|
| + surface_texture_a_ = gfx::SurfaceTexture::Create(0);
|
| + gfx::ScopedJavaSurface surface(surface_texture_a_.get());
|
|
|
| ASSERT_NE(nullptr, player_);
|
| player_->SetVideoSurface(surface.Pass());
|
| }
|
|
|
| +void MediaCodecPlayerTest::SetVideoSurfaceB() {
|
| + surface_texture_b_ = gfx::SurfaceTexture::Create(1);
|
| + gfx::ScopedJavaSurface surface(surface_texture_b_.get());
|
| +
|
| + ASSERT_NE(nullptr, player_);
|
| + player_->SetVideoSurface(surface.Pass());
|
| +}
|
| +
|
| +void MediaCodecPlayerTest::RemoveVideoSurface() {
|
| + player_->SetVideoSurface(gfx::ScopedJavaSurface());
|
| + surface_texture_a_ = NULL;
|
| +}
|
| +
|
| bool MediaCodecPlayerTest::WaitForCondition(const Predicate& condition,
|
| const base::TimeDelta& timeout) {
|
| // Let the message_loop_ process events.
|
| @@ -444,6 +478,33 @@ bool MediaCodecPlayerTest::WaitForPlaybackPassedPosition(
|
| timeout);
|
| }
|
|
|
| +void MediaCodecPlayerTest::StartVideoPlayback(base::TimeDelta duration) {
|
| + const base::TimeDelta start_timeout = base::TimeDelta::FromMilliseconds(800);
|
| +
|
| + demuxer_->SetVideoFactory(
|
| + scoped_ptr<VideoFactory>(new VideoFactory(duration)));
|
| +
|
| + CreatePlayer();
|
| + SetVideoSurface();
|
| +
|
| + // Wait till the player is initialized on media thread.
|
| + EXPECT_TRUE(WaitForCondition(base::Bind(&MockDemuxerAndroid::IsInitialized,
|
| + base::Unretained(demuxer_))));
|
| +
|
| + // Post configuration after the player has been initialized.
|
| + demuxer_->PostInternalConfigs();
|
| +
|
| + // Start the player.
|
| + EXPECT_FALSE(manager_.IsPlaybackStarted());
|
| + player_->Start();
|
| +
|
| + // Wait for playback to start.
|
| + EXPECT_TRUE(
|
| + WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackStarted,
|
| + base::Unretained(&manager_)),
|
| + start_timeout));
|
| +}
|
| +
|
| TEST_F(MediaCodecPlayerTest, SetAudioConfigsBeforePlayerCreation) {
|
| // Post configuration when there is no player yet.
|
| EXPECT_EQ(nullptr, player_);
|
| @@ -552,23 +613,9 @@ TEST_F(MediaCodecPlayerTest, VideoPlayTillCompletion) {
|
| base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
|
| base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(1500);
|
|
|
| - demuxer_->SetVideoFactory(
|
| - scoped_ptr<VideoFactory>(new VideoFactory(duration)));
|
| -
|
| - CreatePlayer();
|
| - SetVideoSurface();
|
| -
|
| - // Wait till the player is initialized on media thread.
|
| - EXPECT_TRUE(WaitForCondition(base::Bind(&MockDemuxerAndroid::IsInitialized,
|
| - base::Unretained(demuxer_))));
|
| -
|
| - // Post configuration after the player has been initialized.
|
| - demuxer_->PostInternalConfigs();
|
| -
|
| - EXPECT_FALSE(manager_.IsPlaybackCompleted());
|
| -
|
| - player_->Start();
|
| + StartVideoPlayback(duration);
|
|
|
| + // Wait till completion.
|
| EXPECT_TRUE(
|
| WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackCompleted,
|
| base::Unretained(&manager_)),
|
| @@ -742,4 +789,132 @@ TEST_F(MediaCodecPlayerTest, AudioSeekWhilePlaying) {
|
| EXPECT_TRUE(player_->IsPlaying());
|
| }
|
|
|
| +TEST_F(MediaCodecPlayerTest, VideoReplaceSurface) {
|
| + SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
|
| +
|
| + base::TimeDelta duration = base::TimeDelta::FromMilliseconds(1000);
|
| + base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(1500);
|
| +
|
| + StartVideoPlayback(duration);
|
| +
|
| + // Wait for some time and check statistics.
|
| + WaitForDelay(base::TimeDelta::FromMilliseconds(200));
|
| +
|
| + // Make sure we played at least 100 ms.
|
| + EXPECT_LT(base::TimeDelta::FromMilliseconds(100), manager_.pts_stat_.max());
|
| +
|
| + // Set new video surface without removing the old one.
|
| + SetVideoSurfaceB();
|
| +
|
| + // We should receive a browser seek request.
|
| + EXPECT_TRUE(WaitForCondition(
|
| + base::Bind(&MockDemuxerAndroid::ReceivedBrowserSeekRequest,
|
| + base::Unretained(demuxer_))));
|
| +
|
| + // Playback should continue with a new surface. Wait till completion.
|
| + EXPECT_TRUE(
|
| + WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackCompleted,
|
| + base::Unretained(&manager_)),
|
| + timeout));
|
| + EXPECT_LE(duration, manager_.pts_stat_.max());
|
| +}
|
| +
|
| +TEST_F(MediaCodecPlayerTest, VideoRemoveAndSetSurface) {
|
| + SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
|
| +
|
| + base::TimeDelta duration = base::TimeDelta::FromMilliseconds(1000);
|
| +
|
| + StartVideoPlayback(duration);
|
| +
|
| + // Wait for some time and check statistics.
|
| + WaitForDelay(base::TimeDelta::FromMilliseconds(200));
|
| +
|
| + // Make sure we played at least 100 ms.
|
| + EXPECT_LT(base::TimeDelta::FromMilliseconds(100), manager_.pts_stat_.max());
|
| +
|
| + // Remove video surface.
|
| + RemoveVideoSurface();
|
| +
|
| + // We should be stuck waiting for the new surface.
|
| + WaitForDelay(base::TimeDelta::FromMilliseconds(200));
|
| + EXPECT_FALSE(player_->IsPlaying());
|
| +
|
| + // Save last PTS and clear statistics.
|
| + base::TimeDelta max_pts_before_removal = manager_.pts_stat_.max();
|
| + manager_.pts_stat_.Clear();
|
| +
|
| + // After clearing statistics we are ready to wait for IsPlaybackStarted again.
|
| + EXPECT_FALSE(manager_.IsPlaybackStarted());
|
| +
|
| + // Extra RemoveVideoSurface() should not change anything.
|
| + RemoveVideoSurface();
|
| +
|
| + // Set another video surface.
|
| + SetVideoSurfaceB();
|
| +
|
| + // We should receive a browser seek request.
|
| + EXPECT_TRUE(WaitForCondition(
|
| + base::Bind(&MockDemuxerAndroid::ReceivedBrowserSeekRequest,
|
| + base::Unretained(demuxer_))));
|
| +
|
| + // Playback should continue with a new surface. Wait till it starts again.
|
| + base::TimeDelta reconfigure_timeout = base::TimeDelta::FromMilliseconds(800);
|
| + EXPECT_TRUE(
|
| + WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackStarted,
|
| + base::Unretained(&manager_)),
|
| + reconfigure_timeout));
|
| +
|
| + // Timestamps should not go back.
|
| + EXPECT_LE(max_pts_before_removal, manager_.pts_stat_.max());
|
| +}
|
| +
|
| +TEST_F(MediaCodecPlayerTest, VideoBackgroundForeground) {
|
| + SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
|
| +
|
| + base::TimeDelta duration = base::TimeDelta::FromMilliseconds(1000);
|
| +
|
| + StartVideoPlayback(duration);
|
| +
|
| + // Wait for some time and check statistics.
|
| + WaitForDelay(base::TimeDelta::FromMilliseconds(200));
|
| +
|
| + // Make sure we played at least 100 ms.
|
| + EXPECT_LT(base::TimeDelta::FromMilliseconds(100), manager_.pts_stat_.max());
|
| +
|
| + // When the user presses Tasks button Chrome calls Pause() and Release().
|
| + player_->Pause(true);
|
| + player_->Release();
|
| +
|
| + // Make sure we are not playing any more.
|
| + WaitForDelay(base::TimeDelta::FromMilliseconds(200));
|
| + EXPECT_FALSE(player_->IsPlaying());
|
| +
|
| + // Save last PTS and clear statistics.
|
| + base::TimeDelta max_pts_before_backgrounding = manager_.pts_stat_.max();
|
| + manager_.pts_stat_.Clear();
|
| +
|
| + // After clearing statistics we are ready to wait for IsPlaybackStarted again.
|
| + EXPECT_FALSE(manager_.IsPlaybackStarted());
|
| +
|
| + // Emulate that we returned to the foreground: set video surface and start
|
| + // the player.
|
| + SetVideoSurface();
|
| + player_->Start();
|
| +
|
| + // We should receive a browser seek request.
|
| + EXPECT_TRUE(WaitForCondition(
|
| + base::Bind(&MockDemuxerAndroid::ReceivedBrowserSeekRequest,
|
| + base::Unretained(demuxer_))));
|
| +
|
| + // Wait for playback to start again.
|
| + base::TimeDelta reconfigure_timeout = base::TimeDelta::FromMilliseconds(800);
|
| + EXPECT_TRUE(
|
| + WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackStarted,
|
| + base::Unretained(&manager_)),
|
| + reconfigure_timeout));
|
| +
|
| + // Timestamps should not go back.
|
| + EXPECT_LE(max_pts_before_backgrounding, manager_.pts_stat_.max());
|
| +}
|
| +
|
| } // namespace media
|
|
|