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 |