Commit 367bd682 authored by qinmin's avatar qinmin Committed by Commit bot

Let audio wait for video to finish prerolling after seek

After a seek, both audio and video decoder job will start prerolling.
However, audio decoder job can finish prerolling much earlier than video decoder job.
As a result, audio will start playing before video starts.
And we will see some fast motion when video try to catch up with audio after it starts rendering.

internal b/17527385

Review URL: https://codereview.chromium.org/596093002

Cr-Commit-Position: refs/heads/master@{#296607}
parent 67c19dd9
...@@ -101,6 +101,8 @@ class MediaDecoderJob { ...@@ -101,6 +101,8 @@ class MediaDecoderJob {
bool is_content_encrypted() const { return is_content_encrypted_; } bool is_content_encrypted() const { return is_content_encrypted_; }
bool prerolling() const { return prerolling_; }
protected: protected:
// Creates a new MediaDecoderJob instance. // Creates a new MediaDecoderJob instance.
// |decoder_task_runner| - Thread on which the decoder task will run. // |decoder_task_runner| - Thread on which the decoder task will run.
......
...@@ -44,6 +44,7 @@ MediaSourcePlayer::MediaSourcePlayer( ...@@ -44,6 +44,7 @@ MediaSourcePlayer::MediaSourcePlayer(
is_waiting_for_key_(false), is_waiting_for_key_(false),
is_waiting_for_audio_decoder_(false), is_waiting_for_audio_decoder_(false),
is_waiting_for_video_decoder_(false), is_waiting_for_video_decoder_(false),
prerolling_(false),
weak_factory_(this) { weak_factory_(this) {
audio_decoder_job_.reset(new AudioDecoderJob( audio_decoder_job_.reset(new AudioDecoderJob(
base::Bind(&DemuxerAndroid::RequestDemuxerData, base::Bind(&DemuxerAndroid::RequestDemuxerData,
...@@ -346,6 +347,7 @@ void MediaSourcePlayer::OnDemuxerSeekDone( ...@@ -346,6 +347,7 @@ void MediaSourcePlayer::OnDemuxerSeekDone(
audio_decoder_job_->BeginPrerolling(preroll_timestamp_); audio_decoder_job_->BeginPrerolling(preroll_timestamp_);
if (HasVideo()) if (HasVideo())
video_decoder_job_->BeginPrerolling(preroll_timestamp_); video_decoder_job_->BeginPrerolling(preroll_timestamp_);
prerolling_ = true;
if (!doing_browser_seek_) if (!doing_browser_seek_)
manager()->OnSeekComplete(player_id(), current_time); manager()->OnSeekComplete(player_id(), current_time);
...@@ -509,6 +511,14 @@ void MediaSourcePlayer::MediaDecoderCallback( ...@@ -509,6 +511,14 @@ void MediaSourcePlayer::MediaDecoderCallback(
if (status == MEDIA_CODEC_STOPPED) if (status == MEDIA_CODEC_STOPPED)
return; return;
if (prerolling_ && IsPrerollFinished(is_audio)) {
if (IsPrerollFinished(!is_audio)) {
prerolling_ = false;
StartInternal();
}
return;
}
if (is_clock_manager) { if (is_clock_manager) {
// If we have a valid timestamp, start the starvation callback. Otherwise, // If we have a valid timestamp, start the starvation callback. Otherwise,
// reset the |start_time_ticks_| so that the next frame will not suffer // reset the |start_time_ticks_| so that the next frame will not suffer
...@@ -526,6 +536,12 @@ void MediaSourcePlayer::MediaDecoderCallback( ...@@ -526,6 +536,12 @@ void MediaSourcePlayer::MediaDecoderCallback(
DecodeMoreVideo(); DecodeMoreVideo();
} }
bool MediaSourcePlayer::IsPrerollFinished(bool is_audio) const {
if (is_audio)
return !HasAudio() || !audio_decoder_job_->prerolling();
return !HasVideo() || !video_decoder_job_->prerolling();
}
void MediaSourcePlayer::DecodeMoreAudio() { void MediaSourcePlayer::DecodeMoreAudio() {
DVLOG(1) << __FUNCTION__; DVLOG(1) << __FUNCTION__;
DCHECK(!audio_decoder_job_->is_decoding()); DCHECK(!audio_decoder_job_->is_decoding());
...@@ -589,11 +605,11 @@ void MediaSourcePlayer::ClearDecodingData() { ...@@ -589,11 +605,11 @@ void MediaSourcePlayer::ClearDecodingData() {
start_time_ticks_ = base::TimeTicks(); start_time_ticks_ = base::TimeTicks();
} }
bool MediaSourcePlayer::HasVideo() { bool MediaSourcePlayer::HasVideo() const {
return video_decoder_job_->HasStream(); return video_decoder_job_->HasStream();
} }
bool MediaSourcePlayer::HasAudio() { bool MediaSourcePlayer::HasAudio() const {
return audio_decoder_job_->HasStream(); return audio_decoder_job_->HasStream();
} }
......
...@@ -90,6 +90,8 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid, ...@@ -90,6 +90,8 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
base::TimeDelta current_presentation_timestamp, base::TimeDelta current_presentation_timestamp,
base::TimeDelta max_presentation_timestamp); base::TimeDelta max_presentation_timestamp);
bool IsPrerollFinished(bool is_audio) const;
// Gets MediaCrypto object from |drm_bridge_|. // Gets MediaCrypto object from |drm_bridge_|.
base::android::ScopedJavaLocalRef<jobject> GetMediaCrypto(); base::android::ScopedJavaLocalRef<jobject> GetMediaCrypto();
...@@ -107,8 +109,8 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid, ...@@ -107,8 +109,8 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
void DecodeMoreVideo(); void DecodeMoreVideo();
// Functions check whether audio/video is present. // Functions check whether audio/video is present.
bool HasVideo(); bool HasVideo() const;
bool HasAudio(); bool HasAudio() const;
// Functions that check whether audio/video stream has reached end of output // Functions that check whether audio/video stream has reached end of output
// or are not present in player configuration. // or are not present in player configuration.
...@@ -258,6 +260,9 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid, ...@@ -258,6 +260,9 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
// Test-only callback for hooking the completion of the next decode cycle. // Test-only callback for hooking the completion of the next decode cycle.
base::Closure decode_callback_for_testing_; base::Closure decode_callback_for_testing_;
// Whether audio or video decoder is in the process of prerolling.
bool prerolling_;
// Weak pointer passed to media decoder jobs for callbacks. // Weak pointer passed to media decoder jobs for callbacks.
// NOTE: Weak pointers must be invalidated before all other member variables. // NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<MediaSourcePlayer> weak_factory_; base::WeakPtrFactory<MediaSourcePlayer> weak_factory_;
......
...@@ -479,12 +479,19 @@ class MediaSourcePlayerTest : public testing::Test { ...@@ -479,12 +479,19 @@ class MediaSourcePlayerTest : public testing::Test {
// Preroll the decoder job to |target_timestamp|. The first access unit // Preroll the decoder job to |target_timestamp|. The first access unit
// to decode will have a timestamp equal to |start_timestamp|. // to decode will have a timestamp equal to |start_timestamp|.
// |is_clock_manager| indicates whether the decoder serves as the clock
// manager for the player.
// TODO(qinmin): Add additional test cases for out-of-order decodes. // TODO(qinmin): Add additional test cases for out-of-order decodes.
// See http://crbug.com/331421. // See http://crbug.com/331421.
void PrerollDecoderToTime(bool is_audio, void PrerollDecoderToTime(bool is_audio,
const base::TimeDelta& start_timestamp, const base::TimeDelta& start_timestamp,
const base::TimeDelta& target_timestamp) { const base::TimeDelta& target_timestamp,
EXPECT_EQ(target_timestamp, player_.GetCurrentTime()); bool is_clock_manager) {
// For streams with both audio and video, it is possible that audio rolls
// past the |target_timestamp|. As a result, the current time may be larger
// than the |target_timestamp| for video as it may not be the clock manager.
EXPECT_TRUE(!is_clock_manager ||
target_timestamp == player_.GetCurrentTime());
// |start_timestamp| must be smaller than |target_timestamp|. // |start_timestamp| must be smaller than |target_timestamp|.
EXPECT_LE(start_timestamp, target_timestamp); EXPECT_LE(start_timestamp, target_timestamp);
DemuxerData data = is_audio ? CreateReadFromDemuxerAckForAudio(1) : DemuxerData data = is_audio ? CreateReadFromDemuxerAckForAudio(1) :
...@@ -502,7 +509,8 @@ class MediaSourcePlayerTest : public testing::Test { ...@@ -502,7 +509,8 @@ class MediaSourcePlayerTest : public testing::Test {
player_.OnDemuxerDataAvailable(data); player_.OnDemuxerDataAvailable(data);
EXPECT_TRUE(GetMediaDecoderJob(is_audio)->is_decoding()); EXPECT_TRUE(GetMediaDecoderJob(is_audio)->is_decoding());
EXPECT_TRUE(GetMediaCodecBridge(is_audio)); EXPECT_TRUE(GetMediaCodecBridge(is_audio));
EXPECT_EQ(target_timestamp, player_.GetCurrentTime()); EXPECT_TRUE(!is_clock_manager ||
target_timestamp == player_.GetCurrentTime());
current_timestamp += 30; current_timestamp += 30;
WaitForDecodeDone(is_audio, !is_audio); WaitForDecodeDone(is_audio, !is_audio);
} }
...@@ -1516,7 +1524,7 @@ TEST_F(MediaSourcePlayerTest, PrerollAudioAfterSeek) { ...@@ -1516,7 +1524,7 @@ TEST_F(MediaSourcePlayerTest, PrerollAudioAfterSeek) {
SeekPlayerWithAbort(true, base::TimeDelta::FromMilliseconds(100)); SeekPlayerWithAbort(true, base::TimeDelta::FromMilliseconds(100));
EXPECT_TRUE(IsPrerolling(true)); EXPECT_TRUE(IsPrerolling(true));
PrerollDecoderToTime( PrerollDecoderToTime(
true, base::TimeDelta(), base::TimeDelta::FromMilliseconds(100)); true, base::TimeDelta(), base::TimeDelta::FromMilliseconds(100), true);
} }
TEST_F(MediaSourcePlayerTest, PrerollVideoAfterSeek) { TEST_F(MediaSourcePlayerTest, PrerollVideoAfterSeek) {
...@@ -1529,7 +1537,7 @@ TEST_F(MediaSourcePlayerTest, PrerollVideoAfterSeek) { ...@@ -1529,7 +1537,7 @@ TEST_F(MediaSourcePlayerTest, PrerollVideoAfterSeek) {
SeekPlayerWithAbort(false, base::TimeDelta::FromMilliseconds(100)); SeekPlayerWithAbort(false, base::TimeDelta::FromMilliseconds(100));
EXPECT_TRUE(IsPrerolling(false)); EXPECT_TRUE(IsPrerolling(false));
PrerollDecoderToTime( PrerollDecoderToTime(
false, base::TimeDelta(), base::TimeDelta::FromMilliseconds(100)); false, base::TimeDelta(), base::TimeDelta::FromMilliseconds(100), true);
} }
TEST_F(MediaSourcePlayerTest, SeekingAfterCompletingPrerollRestartsPreroll) { TEST_F(MediaSourcePlayerTest, SeekingAfterCompletingPrerollRestartsPreroll) {
...@@ -1607,7 +1615,7 @@ TEST_F(MediaSourcePlayerTest, PrerollContinuesAcrossReleaseAndStart) { ...@@ -1607,7 +1615,7 @@ TEST_F(MediaSourcePlayerTest, PrerollContinuesAcrossReleaseAndStart) {
EXPECT_TRUE(IsPrerolling(true)); EXPECT_TRUE(IsPrerolling(true));
// Send data after the seek position. // Send data after the seek position.
PrerollDecoderToTime(true, target_timestamp, target_timestamp); PrerollDecoderToTime(true, target_timestamp, target_timestamp, true);
} }
TEST_F(MediaSourcePlayerTest, PrerollContinuesAcrossConfigChange) { TEST_F(MediaSourcePlayerTest, PrerollContinuesAcrossConfigChange) {
...@@ -1628,8 +1636,9 @@ TEST_F(MediaSourcePlayerTest, PrerollContinuesAcrossConfigChange) { ...@@ -1628,8 +1636,9 @@ TEST_F(MediaSourcePlayerTest, PrerollContinuesAcrossConfigChange) {
DemuxerData data = CreateReadFromDemuxerAckWithConfigChanged( DemuxerData data = CreateReadFromDemuxerAckWithConfigChanged(
true, 0, configs); true, 0, configs);
player_.OnDemuxerDataAvailable(data); player_.OnDemuxerDataAvailable(data);
PrerollDecoderToTime( PrerollDecoderToTime(
true, base::TimeDelta(), base::TimeDelta::FromMilliseconds(100)); true, base::TimeDelta(), base::TimeDelta::FromMilliseconds(100), true);
} }
TEST_F(MediaSourcePlayerTest, PrerollContinuesAfterUnchangedConfigs) { TEST_F(MediaSourcePlayerTest, PrerollContinuesAfterUnchangedConfigs) {
...@@ -1651,7 +1660,59 @@ TEST_F(MediaSourcePlayerTest, PrerollContinuesAfterUnchangedConfigs) { ...@@ -1651,7 +1660,59 @@ TEST_F(MediaSourcePlayerTest, PrerollContinuesAfterUnchangedConfigs) {
true, 0, configs); true, 0, configs);
player_.OnDemuxerDataAvailable(data); player_.OnDemuxerDataAvailable(data);
PrerollDecoderToTime( PrerollDecoderToTime(
true, base::TimeDelta(), base::TimeDelta::FromMilliseconds(100)); true, base::TimeDelta(), base::TimeDelta::FromMilliseconds(100), true);
}
TEST_F(MediaSourcePlayerTest, AudioPrerollFinishesBeforeVideo) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that after audio finishes prerolling, it will wait for video to finish
// prerolling before advancing together.
CreateNextTextureAndSetVideoSurface();
Start(CreateAudioVideoDemuxerConfigs());
// Initiate a seek.
base::TimeDelta seek_position = base::TimeDelta::FromMilliseconds(100);
player_.SeekTo(seek_position);
player_.OnDemuxerDataAvailable(CreateAbortedAck(true));
player_.OnDemuxerDataAvailable(CreateAbortedAck(false));
WaitForDecodeDone(true, true);
// Verify that the seek is requested.
EXPECT_EQ(1, demuxer_->num_seek_requests());
player_.OnDemuxerSeekDone(kNoTimestamp());
EXPECT_EQ(4, demuxer_->num_data_requests());
EXPECT_EQ(player_.GetCurrentTime().InMillisecondsF(), 100.0);
EXPECT_EQ(GetPrerollTimestamp().InMillisecondsF(), 100.0);
// Send both audio and video data to finish prefetching.
base::TimeDelta seek_ack_position = base::TimeDelta::FromMilliseconds(70);
DemuxerData audio_data = CreateReadFromDemuxerAckForAudio(0);
audio_data.access_units[0].timestamp = seek_ack_position;
DemuxerData video_data = CreateReadFromDemuxerAckForVideo();
video_data.access_units[0].timestamp = seek_ack_position;
player_.OnDemuxerDataAvailable(audio_data);
player_.OnDemuxerDataAvailable(video_data);
WaitForAudioDecodeDone();
WaitForVideoDecodeDone();
// Send audio data at and after the seek position. Audio should finish
// prerolling and stop decoding.
EXPECT_EQ(6, demuxer_->num_data_requests());
PrerollDecoderToTime(true, seek_position, seek_position, true);
EXPECT_FALSE(GetMediaDecoderJob(true)->is_decoding());
EXPECT_FALSE(IsPrerolling(true));
EXPECT_TRUE(IsPrerolling(false));
// Send video data to let video finish prerolling.
PrerollDecoderToTime(false, seek_position, seek_position, false);
EXPECT_FALSE(IsPrerolling(false));
// Both audio and video decoders should start decoding again.
player_.OnDemuxerDataAvailable(audio_data);
player_.OnDemuxerDataAvailable(video_data);
EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
EXPECT_TRUE(GetMediaDecoderJob(false)->is_decoding());
} }
TEST_F(MediaSourcePlayerTest, SimultaneousAudioVideoConfigChange) { TEST_F(MediaSourcePlayerTest, SimultaneousAudioVideoConfigChange) {
...@@ -1777,7 +1838,7 @@ TEST_F(MediaSourcePlayerTest, BrowserSeek_PrerollAfterBrowserSeek) { ...@@ -1777,7 +1838,7 @@ TEST_F(MediaSourcePlayerTest, BrowserSeek_PrerollAfterBrowserSeek) {
EXPECT_EQ(3, demuxer_->num_data_requests()); EXPECT_EQ(3, demuxer_->num_data_requests());
PrerollDecoderToTime( PrerollDecoderToTime(
false, base::TimeDelta(), base::TimeDelta::FromMilliseconds(100)); false, base::TimeDelta(), base::TimeDelta::FromMilliseconds(100), true);
} }
TEST_F(MediaSourcePlayerTest, VideoDemuxerConfigChange) { TEST_F(MediaSourcePlayerTest, VideoDemuxerConfigChange) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment