Chromium Code Reviews| Index: media/base/android/media_source_player_unittest.cc |
| diff --git a/media/base/android/media_source_player_unittest.cc b/media/base/android/media_source_player_unittest.cc |
| index e329ac6eee6dc529fc3f4f6f46c40146036f6f21..72288b6f202138491eba54d9c49feb6b00cbbb9c 100644 |
| --- a/media/base/android/media_source_player_unittest.cc |
| +++ b/media/base/android/media_source_player_unittest.cc |
| @@ -5,12 +5,14 @@ |
| #include <string> |
| #include "base/basictypes.h" |
| +#include "base/callback_helpers.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/strings/stringprintf.h" |
| #include "media/base/android/media_codec_bridge.h" |
| #include "media/base/android/media_drm_bridge.h" |
| #include "media/base/android/media_player_manager.h" |
| #include "media/base/android/media_source_player.h" |
| +#include "media/base/bind_to_loop.h" |
| #include "media/base/decoder_buffer.h" |
| #include "media/base/test_data_util.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| @@ -80,9 +82,20 @@ class MockMediaPlayerManager : public MediaPlayerManager { |
| const std::string& session_id, |
| const std::vector<uint8>& message, |
| const std::string& destination_url) OVERRIDE {} |
| + virtual void OnMediaDecoderCallback() OVERRIDE { |
| + if (media_decoder_test_cb_.is_null()) |
| + return; |
| + base::ResetAndReturn(&media_decoder_test_cb_).Run(); |
| + } |
| + |
| + // Hook the next OnMediaDecoderCallback() to run the supplied Closure. |
| + void HookNextMediaDecoderCallback(const base::Closure& decode_cb) { |
| + media_decoder_test_cb_ = decode_cb; |
| + } |
| private: |
| base::MessageLoop* message_loop_; |
| + base::Closure media_decoder_test_cb_; |
| DISALLOW_COPY_AND_ASSIGN(MockMediaPlayerManager); |
| }; |
| @@ -142,6 +155,7 @@ class MediaSourcePlayerTest : public testing::Test { |
| : manager_(&message_loop_), |
| demuxer_(new MockDemuxerAndroid(&message_loop_)), |
| player_(0, &manager_, scoped_ptr<DemuxerAndroid>(demuxer_)), |
| + decoder_callback_hook_executed_(false), |
| surface_texture_a_is_next_(true) {} |
| virtual ~MediaSourcePlayerTest() {} |
| @@ -167,6 +181,41 @@ class MediaSourcePlayerTest : public testing::Test { |
| return player_.preroll_timestamp_; |
| } |
| + // Check if the player is pending a surface change event. |
| + bool IsPendingSurfaceChange() { |
| + return player_.IsEventPending(player_.SURFACE_CHANGE_EVENT_PENDING); |
| + } |
| + |
| + // Check if the player is pending a prefetch request event. |
| + bool IsPendingPrefetchRequest() { |
| + return player_.IsEventPending(player_.PREFETCH_REQUEST_EVENT_PENDING); |
| + } |
| + |
| + // Check if the player is pending a prefetch done event. |
| + bool IsPendingPrefetchDone() { |
| + return player_.IsEventPending(player_.PREFETCH_DONE_EVENT_PENDING); |
| + } |
| + |
| + // Simulate player has reached starvation timeout. |
| + void TriggerPlayerStarvation() { |
| + player_.decoder_starvation_callback_.Cancel(); |
| + player_.OnDecoderStarved(); |
| + } |
| + |
| + // Asynch test callback posted upon decode completion to verify that a pending |
| + // prefetch done event is cleared across |player_|'s Release(). This helps |
| + // ensure the ReleaseWithOnPrefetchDoneAlreadyPosted test scenario is met. |
| + void ReleaseWithPendingPrefetchDoneVerification() { |
| + EXPECT_TRUE(player_.IsPlaying()); |
| + EXPECT_FALSE(IsPendingPrefetchRequest()); |
| + EXPECT_TRUE(IsPendingPrefetchDone()); |
| + player_.Release(); |
| + EXPECT_FALSE(IsPendingPrefetchDone()); |
| + EXPECT_FALSE(player_.IsPlaying()); |
| + EXPECT_FALSE(decoder_callback_hook_executed_); |
| + decoder_callback_hook_executed_ = true; |
| + } |
| + |
| DemuxerConfigs CreateAudioDemuxerConfigs() { |
| DemuxerConfigs configs; |
| configs.audio_codec = kCodecVorbis; |
| @@ -468,12 +517,14 @@ class MediaSourcePlayerTest : public testing::Test { |
| scheme_uuid, security_level, container, codecs); |
| } |
| - protected: |
| base::MessageLoop message_loop_; |
| MockMediaPlayerManager manager_; |
| MockDemuxerAndroid* demuxer_; // Owned by |player_|. |
| MediaSourcePlayer player_; |
| + // Track whether a possibly asynch decoder callback test hook has run. |
| + bool decoder_callback_hook_executed_; |
| + |
| // We need to keep the surface texture while the decoder is actively decoding. |
| // Otherwise, it may trigger unexpected crashes on some devices. To switch |
| // surfaces, tests need to create a new surface texture without releasing |
| @@ -1379,6 +1430,88 @@ TEST_F(MediaSourcePlayerTest, NewSurfaceWhileChangingConfigs) { |
| EXPECT_EQ(0, demuxer_->num_seek_requests()); |
| } |
| +TEST_F(MediaSourcePlayerTest, ReleaseClearsSurfaceChangeAndPrefetchDone) { |
| + SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); |
| + |
| + // Test that Release() clears both a pending surface change event as well |
| + // as a pending prefetch done event. |
| + StartVideoDecoderJob(); |
| + CreateNextTextureAndSetVideoSurface(); |
| + EXPECT_EQ(1, demuxer_->num_data_requests()); |
| + EXPECT_TRUE(GetMediaDecoderJob(false)); |
| + |
| + EXPECT_FALSE(IsPendingSurfaceChange()); |
| + CreateNextTextureAndSetVideoSurface(); |
| + EXPECT_TRUE(IsPendingSurfaceChange()); |
| + EXPECT_TRUE(IsPendingPrefetchDone()); |
| + |
| + player_.Release(); |
| + EXPECT_FALSE(IsPendingSurfaceChange()); |
| + EXPECT_FALSE(IsPendingPrefetchDone()); |
| + |
| + // Player should have no decoder job until after Start() and setting non-empty |
| + // surface. |
| + EXPECT_FALSE(GetMediaDecoderJob(false)); |
| + EXPECT_FALSE(player_.IsPlaying()); |
| + StartVideoDecoderJob(); |
| + EXPECT_FALSE(GetMediaDecoderJob(false)); |
| + CreateNextTextureAndSetVideoSurface(); |
| + EXPECT_TRUE(GetMediaDecoderJob(false)); |
| +} |
| + |
| +TEST_F(MediaSourcePlayerTest, ReleaseWithOnPrefetchDoneAlreadyPosted) { |
| + SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); |
| + |
| + // Test that if OnPrefetchDone() had already been posted before and is |
| + // executed after Release(), player does not DCHECK. |
| + StartAudioDecoderJob(); |
| + |
| + // Escape the original prefetch by decoding a single access unit. |
| + player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(0)); |
| + message_loop_.Run(); |
| + |
| + // Prime the job with a few more access units, so that a later prefetch, |
| + // triggered by starvation to simulate decoder underrun, can trivially |
| + // post task to run OnPrefetchDone(). |
| + player_.OnDemuxerDataAvailable( |
| + CreateReadFromDemuxerAckWithConfigChanged(true, 4)); |
| + |
| + EXPECT_TRUE(GetMediaDecoderJob(true) && |
| + GetMediaDecoderJob(true)->is_decoding()); |
| + EXPECT_FALSE(IsPendingPrefetchRequest()); |
| + EXPECT_FALSE(IsPendingPrefetchDone()); |
| + |
| + // Simulate decoder underrun, so trivial prefetch starts while still decoding. |
| + // The prefetch and posting of OnPrefetchDone() will not occur until next |
| + // MediaDecoderCallBack() occurs. |
| + TriggerPlayerStarvation(); |
| + EXPECT_TRUE(IsPendingPrefetchRequest()); |
| + EXPECT_FALSE(IsPendingPrefetchDone()); |
| + |
| + // Upon the next successful decode callback, post a task to Release() |
| + // |player_|, such that the trivial OnPrefetchDone() task posting also occurs |
| + // and should execute after the Release(). |
| + manager_.HookNextMediaDecoderCallback(media::BindToLoop( |
| + message_loop_.message_loop_proxy(), |
| + base::Bind( |
| + &MediaSourcePlayerTest_ReleaseWithOnPrefetchDoneAlreadyPosted_Test:: |
| + ReleaseWithPendingPrefetchDoneVerification, |
| + base::Unretained(this)))); |
| + |
| + while (GetMediaDecoderJob(true)) |
| + message_loop_.RunUntilIdle(); |
| + EXPECT_TRUE(decoder_callback_hook_executed_); |
| + EXPECT_FALSE(IsPendingPrefetchRequest()); |
| + EXPECT_FALSE(IsPendingPrefetchDone()); |
| + EXPECT_EQ(2, demuxer_->num_data_requests()); |
| + EXPECT_FALSE(GetMediaDecoderJob(true)); |
|
wolenetz
2013/10/30 00:06:45
This line was redundant with while(), above.
|
| + EXPECT_FALSE(player_.IsPlaying()); |
| + |
| + // Player should have no decoder job until after Start(). |
| + StartAudioDecoderJob(); |
| + EXPECT_TRUE(GetMediaDecoderJob(true)); |
| +} |
| + |
| // TODO(xhwang): Enable this test when the test devices are updated. |
| TEST_F(MediaSourcePlayerTest, DISABLED_IsTypeSupported_Widevine) { |
| if (!MediaCodecBridge::IsAvailable() || !MediaDrmBridge::IsAvailable()) { |