Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(442)

Unified Diff: media/base/android/media_codec_player_unittest.cc

Issue 1242913004: MediaCodecPlayer implementation (stage 3 - browser seek and surface change) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@mtplayer-seek
Patch Set: Addressed Min comments, added unit tests. Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698