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 a455b38123c1b34c3d656357d395ec39143b4f5f..f8f855ca9829998ea1c95c012f159f4425966ad6 100644 |
--- a/media/base/android/media_codec_player_unittest.cc |
+++ b/media/base/android/media_codec_player_unittest.cc |
@@ -62,6 +62,8 @@ class MockMediaPlayerManager : public MediaPlayerManager { |
MockMediaPlayerManager() |
: playback_completed_(false), |
num_seeks_completed_(0), |
+ num_audio_codecs_created_(0), |
+ num_video_codecs_created_(0), |
weak_ptr_factory_(this) {} |
~MockMediaPlayerManager() override {} |
@@ -112,17 +114,25 @@ class MockMediaPlayerManager : public MediaPlayerManager { |
void OnDecodersTimeUpdate(DemuxerStream::Type stream_type, |
base::TimeDelta now_playing, |
base::TimeDelta last_buffered) { |
- PTSTime& hit = first_frame_hit_[stream_type]; |
- if (hit.is_null()) |
- hit = PTSTime(now_playing, base::TimeTicks::Now()); |
+ render_stat_[stream_type].AddValue( |
+ PTSTime(now_playing, base::TimeTicks::Now())); |
+ } |
+ |
+ // Notification called on MediaCodec creation. |
+ // Implementation dependent, used for testing only. |
+ void OnMediaCodecCreated(DemuxerStream::Type stream_type) { |
+ if (stream_type == DemuxerStream::AUDIO) |
+ ++num_audio_codecs_created_; |
+ else if (stream_type == DemuxerStream::VIDEO) |
+ ++num_video_codecs_created_; |
} |
// First frame information |
base::TimeDelta FirstFramePTS(DemuxerStream::Type stream_type) const { |
- return first_frame_hit_[stream_type].pts; |
+ return render_stat_[stream_type].min().pts; |
} |
base::TimeTicks FirstFrameTime(DemuxerStream::Type stream_type) const { |
- return first_frame_hit_[stream_type].time; |
+ return render_stat_[stream_type].min().time; |
} |
base::WeakPtr<MockMediaPlayerManager> GetWeakPtr() { |
@@ -138,9 +148,12 @@ class MockMediaPlayerManager : public MediaPlayerManager { |
} |
bool IsSeekCompleted() const { return num_seeks_completed_ > 0; } |
bool HasFirstFrame(DemuxerStream::Type stream_type) const { |
- return !first_frame_hit_[stream_type].is_null(); |
+ return render_stat_[stream_type].num_values() != 0; |
} |
+ int num_audio_codecs_created() const { return num_audio_codecs_created_; } |
+ int num_video_codecs_created() const { return num_video_codecs_created_; } |
+ |
struct MediaMetadata { |
base::TimeDelta duration; |
int width; |
@@ -150,20 +163,24 @@ class MockMediaPlayerManager : public MediaPlayerManager { |
}; |
MediaMetadata media_metadata_; |
- Minimax<base::TimeDelta> pts_stat_; |
- |
- private: |
- bool playback_completed_; |
- int num_seeks_completed_; |
- |
struct PTSTime { |
base::TimeDelta pts; |
base::TimeTicks time; |
+ |
PTSTime() : pts(), time() {} |
PTSTime(base::TimeDelta p, base::TimeTicks t) : pts(p), time(t) {} |
bool is_null() const { return time.is_null(); } |
+ bool operator<(const PTSTime& rhs) const { return time < rhs.time; } |
}; |
- PTSTime first_frame_hit_[DemuxerStream::NUM_TYPES]; |
+ Minimax<PTSTime> render_stat_[DemuxerStream::NUM_TYPES]; |
+ |
+ Minimax<base::TimeDelta> pts_stat_; |
+ |
+ private: |
+ bool playback_completed_; |
+ int num_seeks_completed_; |
+ int num_audio_codecs_created_; |
+ int num_video_codecs_created_; |
base::WeakPtrFactory<MockMediaPlayerManager> weak_ptr_factory_; |
@@ -382,6 +399,10 @@ void MockDemuxerAndroid::RequestDemuxerData(DemuxerStream::Type type) { |
if (!created) |
return; |
+ // Request key frame after |kConfigChanged| |
+ if (type == DemuxerStream::VIDEO && !chunk.demuxer_configs.empty()) |
+ video_factory_->RequestKeyFrame(); |
+ |
chunk.type = type; |
// Post to the Media thread. Use the weak pointer to prevent the data arrival |
@@ -1564,7 +1585,7 @@ TEST_F(MediaCodecPlayerTest, AVPrerollVideoEndsWhilePrerolling) { |
// Start the playback. |
player_->Start(); |
- // The video decoder should start prerolling |
+ // The video decoder should start prerolling. |
// Wait till preroll starts. |
EXPECT_TRUE(WaitForCondition( |
base::Bind(&MediaCodecPlayer::IsPrerollingForTests, |
@@ -1588,4 +1609,180 @@ TEST_F(MediaCodecPlayerTest, AVPrerollVideoEndsWhilePrerolling) { |
EXPECT_FALSE(manager_.HasFirstFrame(DemuxerStream::VIDEO)); |
} |
+TEST_F(MediaCodecPlayerTest, AVVideoConfigChangeWhilePlaying) { |
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); |
+ |
+ // Test that playback continues after video config change. |
+ |
+ // Initialize A/V playback |
+ base::TimeDelta duration = base::TimeDelta::FromMilliseconds(1600); |
+ base::TimeDelta config_change_position = |
+ base::TimeDelta::FromMilliseconds(1000); |
+ |
+ base::TimeDelta start_timeout = base::TimeDelta::FromMilliseconds(2000); |
+ base::TimeDelta completion_timeout = base::TimeDelta::FromMilliseconds(3000); |
+ |
+ demuxer_->SetAudioFactory( |
+ scoped_ptr<AudioFactory>(new AudioFactory(duration))); |
+ demuxer_->SetVideoFactory( |
+ scoped_ptr<VideoFactory>(new VideoFactory(duration))); |
+ |
+ demuxer_->video_factory()->RequestConfigChange(config_change_position); |
+ |
+ CreatePlayer(); |
+ SetVideoSurface(); |
+ |
+ // Wait till the player is initialized on media thread. |
+ EXPECT_TRUE(WaitForCondition(base::Bind(&MockDemuxerAndroid::IsInitialized, |
+ base::Unretained(demuxer_)))); |
+ |
+ if (!demuxer_->IsInitialized()) { |
+ DVLOG(0) << "AVConfigChangeWhilePlaying: demuxer is not initialized"; |
+ return; |
+ } |
+ |
+ // Ask decoders to always reconfigure after the player has been initialized. |
+ player_->SetAlwaysReconfigureForTests(DemuxerStream::VIDEO); |
+ |
+ // Set a testing callback to receive PTS from decoders. |
+ player_->SetDecodersTimeCallbackForTests( |
+ base::Bind(&MockMediaPlayerManager::OnDecodersTimeUpdate, |
+ base::Unretained(&manager_))); |
+ |
+ // Set a testing callback to receive MediaCodec creation events from decoders. |
+ player_->SetCodecCreatedCallbackForTests( |
+ base::Bind(&MockMediaPlayerManager::OnMediaCodecCreated, |
+ base::Unretained(&manager_))); |
+ |
+ // Post configuration after the player has been initialized. |
+ demuxer_->PostInternalConfigs(); |
+ |
+ // Start and wait for playback. |
+ player_->Start(); |
+ |
+ // Wait till we start to play. |
+ EXPECT_TRUE( |
+ WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackStarted, |
+ base::Unretained(&manager_)), |
+ start_timeout)); |
+ |
+ // Wait another 100 ms to make sure we are done with initial preroll. |
+ WaitForDelay(base::TimeDelta::FromMilliseconds(100)); |
+ |
+ EXPECT_FALSE(player_->IsPrerollingForTests(DemuxerStream::AUDIO)); |
+ EXPECT_FALSE(player_->IsPrerollingForTests(DemuxerStream::VIDEO)); |
+ |
+ // Wait till completion |
+ EXPECT_TRUE( |
+ WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackCompleted, |
+ base::Unretained(&manager_)), |
+ completion_timeout)); |
+ |
+ // The audio codec should be kept. |
+ EXPECT_EQ(1, manager_.num_audio_codecs_created()); |
+ |
+ // The video codec should be recreated upon config changes. |
+ EXPECT_EQ(2, manager_.num_video_codecs_created()); |
+ |
+ // Check that we did not miss video frames |
+ int expected_video_frames = duration / kVideoFramePeriod + 1; |
+ EXPECT_EQ(expected_video_frames, |
+ manager_.render_stat_[DemuxerStream::VIDEO].num_values()); |
+ |
+ // Check that we did not miss audio frames. We expect two postponed frames |
+ // that are not reported. |
+ // For Nexus 4 KitKat the AAC decoder seems to swallow the first frame |
+ // but reports the last pts twice, maybe it just shifts the reported PTS. |
+ int expected_audio_frames = duration / kAudioFramePeriod + 1 - 2; |
+ EXPECT_EQ(expected_audio_frames, |
+ manager_.render_stat_[DemuxerStream::AUDIO].num_values()); |
+} |
+ |
+TEST_F(MediaCodecPlayerTest, AVAudioConfigChangeWhilePlaying) { |
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); |
+ |
+ // Test that playback continues after audio config change. |
+ |
+ // Initialize A/V playback |
+ base::TimeDelta duration = base::TimeDelta::FromMilliseconds(1600); |
+ base::TimeDelta config_change_position = |
+ base::TimeDelta::FromMilliseconds(1000); |
+ |
+ base::TimeDelta start_timeout = base::TimeDelta::FromMilliseconds(2000); |
+ base::TimeDelta completion_timeout = base::TimeDelta::FromMilliseconds(3000); |
+ |
+ demuxer_->SetAudioFactory( |
+ scoped_ptr<AudioFactory>(new AudioFactory(duration))); |
+ demuxer_->SetVideoFactory( |
+ scoped_ptr<VideoFactory>(new VideoFactory(duration))); |
+ |
+ demuxer_->audio_factory()->RequestConfigChange(config_change_position); |
+ |
+ CreatePlayer(); |
+ SetVideoSurface(); |
+ |
+ // Wait till the player is initialized on media thread. |
+ EXPECT_TRUE(WaitForCondition(base::Bind(&MockDemuxerAndroid::IsInitialized, |
+ base::Unretained(demuxer_)))); |
+ |
+ if (!demuxer_->IsInitialized()) { |
+ DVLOG(0) << "AVConfigChangeWhilePlaying: demuxer is not initialized"; |
+ return; |
+ } |
+ |
+ // Ask decoders to always reconfigure after the player has been initialized. |
+ player_->SetAlwaysReconfigureForTests(DemuxerStream::AUDIO); |
+ |
+ // Set a testing callback to receive PTS from decoders. |
+ player_->SetDecodersTimeCallbackForTests( |
+ base::Bind(&MockMediaPlayerManager::OnDecodersTimeUpdate, |
+ base::Unretained(&manager_))); |
+ |
+ // Set a testing callback to receive MediaCodec creation events from decoders. |
+ player_->SetCodecCreatedCallbackForTests( |
+ base::Bind(&MockMediaPlayerManager::OnMediaCodecCreated, |
+ base::Unretained(&manager_))); |
+ |
+ // Post configuration after the player has been initialized. |
+ demuxer_->PostInternalConfigs(); |
+ |
+ // Start and wait for playback. |
+ player_->Start(); |
+ |
+ // Wait till we start to play. |
+ EXPECT_TRUE( |
+ WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackStarted, |
+ base::Unretained(&manager_)), |
+ start_timeout)); |
+ |
+ // Wait another 100 ms to make sure we are done with initial preroll. |
+ WaitForDelay(base::TimeDelta::FromMilliseconds(100)); |
liberato (no reviews please)
2015/08/27 20:42:26
can this be converted to a WaitForCondition or cal
Tima Vaisburd
2015/08/27 22:04:19
IsPlaybackStarted() comes when the first audio fra
Tima Vaisburd
2015/08/28 00:16:48
I removed the wait and the subsequent check for Is
|
+ |
+ EXPECT_FALSE(player_->IsPrerollingForTests(DemuxerStream::AUDIO)); |
+ EXPECT_FALSE(player_->IsPrerollingForTests(DemuxerStream::VIDEO)); |
+ |
+ // Wait till completion |
+ EXPECT_TRUE( |
+ WaitForCondition(base::Bind(&MockMediaPlayerManager::IsPlaybackCompleted, |
+ base::Unretained(&manager_)), |
+ completion_timeout)); |
+ |
+ // The audio codec should be recreated upon config changes. |
+ EXPECT_EQ(2, manager_.num_audio_codecs_created()); |
+ |
+ // The video codec should be kept. |
+ EXPECT_EQ(1, manager_.num_video_codecs_created()); |
+ |
+ // Check that we did not miss video frames. |
+ int expected_video_frames = duration / kVideoFramePeriod + 1; |
+ EXPECT_EQ(expected_video_frames, |
+ manager_.render_stat_[DemuxerStream::VIDEO].num_values()); |
+ |
+ // Check that we did not miss audio frames. We expect two postponed frames |
+ // that are not reported. |
+ int expected_audio_frames = duration / kAudioFramePeriod + 1 - 2; |
+ EXPECT_EQ(expected_audio_frames, |
+ manager_.render_stat_[DemuxerStream::AUDIO].num_values()); |
+} |
+ |
} // namespace media |