summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorwolenetz@chromium.org <wolenetz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-24 02:07:00 +0000
committerwolenetz@chromium.org <wolenetz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-24 02:07:00 +0000
commit85eb95d47c73880b935aa1bc50ef4cb552ad4578 (patch)
treedbb89d401a523bf26b68aaa959dca12ad3a1734e /media
parentff77bfd1cce9d8a3be10901c4e1e1f81d62ab9d6 (diff)
downloadchromium_src-85eb95d47c73880b935aa1bc50ef4cb552ad4578.zip
chromium_src-85eb95d47c73880b935aa1bc50ef4cb552ad4578.tar.gz
chromium_src-85eb95d47c73880b935aa1bc50ef4cb552ad4578.tar.bz2
Restore preroll state when decoder job created
Adds resumption of MediaDecoderJob preroll when MediaSourcePlayer re-creates the job(s), and adds related MediaSourcePlayerTests. Includes test hooks to verify job's prerolling status, also to be used by eventual fix for bug 309184. R=qinmin@chromium.org,acolwell@chromium.org BUG=309122, 309184 TEST=All MediaSourcePlayerTests pass on Android with MediaCodecBridge available. Review URL: https://codereview.chromium.org/33543004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@230600 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/base/android/media_decoder_job.cc33
-rw-r--r--media/base/android/media_decoder_job.h22
-rw-r--r--media/base/android/media_source_player.cc10
-rw-r--r--media/base/android/media_source_player.h4
-rw-r--r--media/base/android/media_source_player_unittest.cc328
5 files changed, 325 insertions, 72 deletions
diff --git a/media/base/android/media_decoder_job.cc b/media/base/android/media_decoder_job.cc
index 59db63a..5ea284e 100644
--- a/media/base/android/media_decoder_job.cc
+++ b/media/base/android/media_decoder_job.cc
@@ -28,6 +28,7 @@ MediaDecoderJob::MediaDecoderJob(
media_codec_bridge_(media_codec_bridge),
needs_flush_(false),
input_eos_encountered_(false),
+ prerolling_(true),
weak_this_(this),
request_data_cb_(request_data_cb),
access_unit_index_(0),
@@ -121,6 +122,16 @@ void MediaDecoderJob::Flush() {
on_data_received_cb_.Reset();
}
+void MediaDecoderJob::BeginPrerolling(
+ const base::TimeDelta& preroll_timestamp) {
+ DVLOG(1) << __FUNCTION__ << "(" << preroll_timestamp.InSecondsF() << ")";
+ DCHECK(ui_loop_->BelongsToCurrentThread());
+ DCHECK(!is_decoding());
+
+ preroll_timestamp_ = preroll_timestamp;
+ prerolling_ = true;
+}
+
void MediaDecoderJob::Release() {
DCHECK(ui_loop_->BelongsToCurrentThread());
@@ -282,11 +293,9 @@ void MediaDecoderJob::DecodeInternal(
&output_eos_encountered);
if (status != MEDIA_CODEC_OK) {
- if (status == MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED) {
- if (media_codec_bridge_->GetOutputBuffers())
- status = MEDIA_CODEC_OK;
- else
- status = MEDIA_CODEC_ERROR;
+ if (status == MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED &&
+ !media_codec_bridge_->GetOutputBuffers()) {
+ status = MEDIA_CODEC_ERROR;
}
callback.Run(status, kNoTimestamp(), 0);
return;
@@ -299,9 +308,12 @@ void MediaDecoderJob::DecodeInternal(
status = MEDIA_CODEC_INPUT_END_OF_STREAM;
// Check whether we need to render the output.
- // TODO(qinmin): comparing |unit.timestamp| with |preroll_timestamp_| is not
- // accurate due to data reordering. Need to use the |presentation_timestamp|
- // for video, and use |size| to calculate the timestamp for audio.
+ // TODO(qinmin): comparing most recently queued input's |unit.timestamp| with
+ // |preroll_timestamp_| is not accurate due to data reordering and possible
+ // input queueing without immediate dequeue when |input_status| !=
+ // |MEDIA_CODEC_OK|. Need to use the |presentation_timestamp| for video, and
+ // use |size| to calculate the timestamp for audio. See
+ // http://crbug.com/310823 and b/11356652.
bool render_output = unit.timestamp >= preroll_timestamp_ &&
(status != MEDIA_CODEC_OUTPUT_END_OF_STREAM || size != 0u);
base::TimeDelta time_to_render;
@@ -350,6 +362,11 @@ void MediaDecoderJob::OnDecodeCompleted(
}
DCHECK(!decode_cb_.is_null());
+
+ // If output was queued for rendering, then we have completed prerolling.
+ if (presentation_timestamp != kNoTimestamp())
+ prerolling_ = false;
+
switch (status) {
case MEDIA_CODEC_OK:
case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER:
diff --git a/media/base/android/media_decoder_job.h b/media/base/android/media_decoder_job.h
index ea3956e..e9530b39 100644
--- a/media/base/android/media_decoder_job.h
+++ b/media/base/android/media_decoder_job.h
@@ -27,6 +27,9 @@ class MediaDecoderJob {
// Callback when a decoder job finishes its work. Args: whether decode
// finished successfully, presentation time, audio output bytes.
+ // If the presentation time is equal to kNoTimestamp(), the decoder job
+ // skipped rendering of the decoded output and the callback target should
+ // update its clock to avoid introducing extra delays to the next frame.
typedef base::Callback<void(MediaCodecStatus, const base::TimeDelta&,
size_t)> DecoderCallback;
// Callback when a decoder job finishes releasing the output buffer.
@@ -66,9 +69,10 @@ class MediaDecoderJob {
// Flush the decoder.
void Flush();
- void set_preroll_timestamp(const base::TimeDelta& preroll_timestamp) {
- preroll_timestamp_ = preroll_timestamp;
- }
+ // Enter prerolling state. The job must not currently be decoding.
+ void BeginPrerolling(const base::TimeDelta& preroll_timestamp);
+
+ bool prerolling() const { return prerolling_; }
bool is_decoding() const { return !decode_cb_.is_null(); }
@@ -120,9 +124,8 @@ class MediaDecoderJob {
const DecoderCallback& callback);
// Called on the UI thread to indicate that one decode cycle has completed.
- // If the |presentation_timestamp| is equal to kNoTimestamp(),
- // the caller should ignore the output of this frame and update its clock to
- // avoid introducing extra delays to the next frame.
+ // Completes any pending job destruction or any pending decode stop. If
+ // destruction was not pending, passes its arguments to |decode_cb_|.
void OnDecodeCompleted(MediaCodecStatus status,
const base::TimeDelta& presentation_timestamp,
size_t audio_output_bytes);
@@ -149,6 +152,13 @@ class MediaDecoderJob {
// is not very accurate.
base::TimeDelta preroll_timestamp_;
+ // Indicates prerolling state. If true, this job has not yet decoded output
+ // that it will render, since the most recent of job construction or
+ // BeginPrerolling(). If false, |preroll_timestamp_| has been reached.
+ // TODO(qinmin): Comparing access unit's timestamp with |preroll_timestamp_|
+ // is not very accurate.
+ bool prerolling_;
+
// Weak pointer passed to media decoder jobs for callbacks. It is bounded to
// the decoder thread.
base::WeakPtrFactory<MediaDecoderJob> weak_this_;
diff --git a/media/base/android/media_source_player.cc b/media/base/android/media_source_player.cc
index 016f8b5..508cd44 100644
--- a/media/base/android/media_source_player.cc
+++ b/media/base/android/media_source_player.cc
@@ -394,10 +394,11 @@ void MediaSourcePlayer::OnDemuxerSeekDone() {
// to preroll media decoder jobs. Currently |start_presentation_timestamp_|
// is calculated from decoder output, while preroll relies on the access
// unit's timestamp. There are some differences between the two.
+ preroll_timestamp_ = current_time;
if (audio_decoder_job_)
- audio_decoder_job_->set_preroll_timestamp(current_time);
+ audio_decoder_job_->BeginPrerolling(preroll_timestamp_);
if (video_decoder_job_)
- video_decoder_job_->set_preroll_timestamp(current_time);
+ video_decoder_job_->BeginPrerolling(preroll_timestamp_);
ProcessPendingEvents();
}
@@ -658,6 +659,7 @@ void MediaSourcePlayer::ConfigureAudioDecoderJob() {
if (audio_decoder_job_) {
SetVolumeInternal();
+ audio_decoder_job_->BeginPrerolling(preroll_timestamp_);
reconfig_audio_decoder_ = false;
}
}
@@ -692,8 +694,10 @@ void MediaSourcePlayer::ConfigureVideoDecoderJob() {
base::Bind(&DemuxerAndroid::RequestDemuxerData,
base::Unretained(demuxer_.get()),
DemuxerStream::VIDEO)));
- if (video_decoder_job_)
+ if (video_decoder_job_) {
+ video_decoder_job_->BeginPrerolling(preroll_timestamp_);
reconfig_video_decoder_ = false;
+ }
// Inform the fullscreen view the player is ready.
// TODO(qinmin): refactor MediaPlayerBridge so that we have a better way
diff --git a/media/base/android/media_source_player.h b/media/base/android/media_source_player.h
index 61215b4..652ab76 100644
--- a/media/base/android/media_source_player.h
+++ b/media/base/android/media_source_player.h
@@ -207,6 +207,10 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
bool reconfig_audio_decoder_;
bool reconfig_video_decoder_;
+ // Track the most recent preroll target. Decoder re-creation needs this to
+ // resume any in-progress preroll.
+ base::TimeDelta preroll_timestamp_;
+
// A cancelable task that is posted when the audio decoder starts requesting
// new data. This callback runs if no data arrives before the timeout period
// elapses.
diff --git a/media/base/android/media_source_player_unittest.cc b/media/base/android/media_source_player_unittest.cc
index 8826848..7d20ef4 100644
--- a/media/base/android/media_source_player_unittest.cc
+++ b/media/base/android/media_source_player_unittest.cc
@@ -92,11 +92,14 @@ class MockDemuxerAndroid : public DemuxerAndroid {
explicit MockDemuxerAndroid(base::MessageLoop* message_loop)
: message_loop_(message_loop),
num_data_requests_(0),
- num_seek_requests_(0) {}
+ num_seek_requests_(0),
+ num_config_requests_(0) {}
virtual ~MockDemuxerAndroid() {}
virtual void Initialize(DemuxerAndroidClient* client) OVERRIDE {}
- virtual void RequestDemuxerConfigs() OVERRIDE {}
+ virtual void RequestDemuxerConfigs() OVERRIDE {
+ num_config_requests_++;
+ }
virtual void RequestDemuxerData(DemuxerStream::Type type) OVERRIDE {
num_data_requests_++;
if (message_loop_->is_running())
@@ -109,6 +112,7 @@ class MockDemuxerAndroid : public DemuxerAndroid {
int num_data_requests() const { return num_data_requests_; }
int num_seek_requests() const { return num_seek_requests_; }
+ int num_config_requests() const { return num_config_requests_; }
private:
base::MessageLoop* message_loop_;
@@ -119,6 +123,9 @@ class MockDemuxerAndroid : public DemuxerAndroid {
// The number of seek requests this object has seen.
int num_seek_requests_;
+ // The number of demuxer config requests this object has seen.
+ int num_config_requests_;
+
DISALLOW_COPY_AND_ASSIGN(MockDemuxerAndroid);
};
@@ -141,6 +148,17 @@ class MediaSourcePlayerTest : public testing::Test {
player_.video_decoder_job_.get());
}
+ // Get the per-job prerolling status from the MediaSourcePlayer's job matching
+ // |is_audio|. Caller must guard against NPE if the player's job is NULL.
+ bool IsPrerolling(bool is_audio) {
+ return GetMediaDecoderJob(is_audio)->prerolling();
+ }
+
+ // Get the preroll timestamp from the MediaSourcePlayer.
+ base::TimeDelta GetPrerollTimestamp() {
+ return player_.preroll_timestamp_;
+ }
+
// Starts an audio decoder job.
void StartAudioDecoderJob() {
DemuxerConfigs configs;
@@ -201,13 +219,56 @@ class MediaSourcePlayerTest : public testing::Test {
}
DemuxerData CreateEOSAck(bool is_audio) {
- DemuxerData data;
- data.type = is_audio ? DemuxerStream::AUDIO : DemuxerStream::VIDEO;
- data.access_units.resize(1);
- data.access_units[0].status = DemuxerStream::kOk;
- data.access_units[0].end_of_stream = true;
- return data;
- }
+ DemuxerData data;
+ data.type = is_audio ? DemuxerStream::AUDIO : DemuxerStream::VIDEO;
+ data.access_units.resize(1);
+ data.access_units[0].status = DemuxerStream::kOk;
+ data.access_units[0].end_of_stream = true;
+ return data;
+ }
+
+ DemuxerData CreateAbortedAck(bool is_audio) {
+ DemuxerData data;
+ data.type = is_audio ? DemuxerStream::AUDIO : DemuxerStream::VIDEO;
+ data.access_units.resize(1);
+ data.access_units[0].status = DemuxerStream::kAborted;
+ return data;
+ }
+
+ // Seek, including simulated receipt of |kAborted| read between SeekTo()
+ // and OnDemuxerSeekDone(). Use this helper method only when the player
+ // already has created the decoder job.
+ void SeekPlayer(bool is_audio, const base::TimeDelta& seek_time) {
+ EXPECT_TRUE(GetMediaDecoderJob(is_audio));
+
+ int original_num_seeks = demuxer_->num_seek_requests();
+ int original_num_data_requests = demuxer_->num_data_requests();
+
+ // Initiate a seek. Skip the round-trip of requesting seek from renderer.
+ // Instead behave as if the renderer has asked us to seek.
+ player_.SeekTo(seek_time);
+
+ // Verify that the seek does not occur until previously outstanding data
+ // request is satisfied.
+ EXPECT_EQ(original_num_seeks, demuxer_->num_seek_requests());
+
+ // Simulate seeking causes the demuxer to abort the outstanding read caused
+ // by the seek.
+ player_.OnDemuxerDataAvailable(CreateAbortedAck(is_audio));
+
+ // Verify that the seek is requested now that the outstanding read is
+ // completed by aborted access unit.
+ EXPECT_EQ(original_num_seeks + 1, demuxer_->num_seek_requests());
+
+ // Send back the seek done notification. This should trigger the player to
+ // call OnReadFromDemuxer() again.
+ EXPECT_EQ(original_num_data_requests, demuxer_->num_data_requests());
+ player_.OnDemuxerSeekDone();
+ EXPECT_EQ(original_num_data_requests + 1, demuxer_->num_data_requests());
+
+ // No other seek should have been requested.
+ EXPECT_EQ(original_num_seeks + 1, demuxer_->num_seek_requests());
+ }
base::TimeTicks StartTimeTicks() {
return player_.start_time_ticks_;
@@ -318,34 +379,8 @@ TEST_F(MediaSourcePlayerTest, ReadFromDemuxerAfterSeek) {
StartAudioDecoderJob();
EXPECT_TRUE(GetMediaDecoderJob(true));
EXPECT_EQ(1, demuxer_->num_data_requests());
-
- // Initiate a seek. Skip the round-trip of requesting seek from renderer.
- // Instead behave as if the renderer has asked us to seek.
- player_.SeekTo(base::TimeDelta());
-
- // Verify that the seek does not occur until the initial prefetch
- // completes.
- EXPECT_EQ(0, demuxer_->num_seek_requests());
-
- // Simulate aborted read caused by the seek. This aborts the initial
- // prefetch.
- DemuxerData data;
- data.type = DemuxerStream::AUDIO;
- data.access_units.resize(1);
- data.access_units[0].status = DemuxerStream::kAborted;
- player_.OnDemuxerDataAvailable(data);
-
- // Verify that the seek is requested now that the initial prefetch
- // has completed.
- EXPECT_EQ(1, demuxer_->num_seek_requests());
-
- // Sending back the seek done notification. This should trigger the player to
- // call OnReadFromDemuxer() again.
- player_.OnDemuxerSeekDone();
+ SeekPlayer(true, base::TimeDelta());
EXPECT_EQ(2, demuxer_->num_data_requests());
-
- // Reconfirm exactly 1 seek request has been made of demuxer.
- EXPECT_EQ(1, demuxer_->num_seek_requests());
}
TEST_F(MediaSourcePlayerTest, SetSurfaceWhileSeeking) {
@@ -403,7 +438,7 @@ TEST_F(MediaSourcePlayerTest, ChangeMultipleSurfaceWhileDecoding) {
player_.SetVideoSurface(surface.Pass());
// Wait for the decoder job to finish decoding.
- while(GetMediaDecoderJob(false)->is_decoding())
+ while (GetMediaDecoderJob(false)->is_decoding())
message_loop_.RunUntilIdle();
// A seek should be initiated to request Iframe.
EXPECT_EQ(1, demuxer_->num_seek_requests());
@@ -548,7 +583,7 @@ TEST_F(MediaSourcePlayerTest, StartTimeTicksResetAfterDecoderUnderruns) {
// Send new data to the decoder so it can finish the currently
// pending decode.
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(3));
- while(GetMediaDecoderJob(true)->is_decoding())
+ while (GetMediaDecoderJob(true)->is_decoding())
message_loop_.RunUntilIdle();
// Verify the start time ticks is cleared at this point because the
@@ -629,14 +664,11 @@ TEST_F(MediaSourcePlayerTest, NoRequestForDataAfterAbort) {
EXPECT_EQ(1, demuxer_->num_data_requests());
// Send an aborted access unit.
- DemuxerData data;
- data.type = DemuxerStream::AUDIO;
- data.access_units.resize(1);
- data.access_units[0].status = DemuxerStream::kAborted;
- player_.OnDemuxerDataAvailable(data);
+ player_.OnDemuxerDataAvailable(CreateAbortedAck(true));
+
EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
// Wait for the decoder job to finish decoding.
- while(GetMediaDecoderJob(true)->is_decoding())
+ while (GetMediaDecoderJob(true)->is_decoding())
message_loop_.RunUntilIdle();
// No request will be sent for new data.
@@ -661,35 +693,220 @@ TEST_F(MediaSourcePlayerTest, DemuxerDataArrivesAfterRelease) {
EXPECT_EQ(1, demuxer_->num_data_requests());
}
-TEST_F(MediaSourcePlayerTest, PrerollAfterSeek) {
- if (!MediaCodecBridge::IsAvailable()) {
- LOG(INFO) << "Could not run test - not supported on device.";
- return;
+TEST_F(MediaSourcePlayerTest, PrerollAudioAfterSeek) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ // Test decoder job will preroll the media to the seek position.
+ StartAudioDecoderJob();
+ EXPECT_TRUE(GetMediaDecoderJob(true));
+ EXPECT_EQ(1, demuxer_->num_data_requests());
+
+ SeekPlayer(true, base::TimeDelta::FromMilliseconds(100));
+ EXPECT_TRUE(IsPrerolling(true));
+ EXPECT_EQ(100.0, GetPrerollTimestamp().InMillisecondsF());
+
+ // Send some data before the seek position.
+ for (int i = 1; i < 4; ++i) {
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(i));
+ EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
+ message_loop_.Run();
}
+ EXPECT_EQ(100.0, player_.GetCurrentTime().InMillisecondsF());
+ EXPECT_TRUE(IsPrerolling(true));
+
+ // Send data after the seek position.
+ DemuxerData data = CreateReadFromDemuxerAckForAudio(3);
+ data.access_units[0].timestamp = base::TimeDelta::FromMilliseconds(100);
+ player_.OnDemuxerDataAvailable(data);
+ message_loop_.Run();
+ EXPECT_LT(100.0, player_.GetCurrentTime().InMillisecondsF());
+ EXPECT_FALSE(IsPrerolling(true));
+}
+
+TEST_F(MediaSourcePlayerTest, PrerollVideoAfterSeek) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test decoder job will preroll the media to the seek position.
+ CreateAndSetVideoSurface();
+ StartVideoDecoderJob();
+ EXPECT_TRUE(GetMediaDecoderJob(false));
+ EXPECT_EQ(1, demuxer_->num_data_requests());
+
+ SeekPlayer(false, base::TimeDelta::FromMilliseconds(100));
+ EXPECT_TRUE(IsPrerolling(false));
+ EXPECT_EQ(100.0, GetPrerollTimestamp().InMillisecondsF());
+
+ // Send some data before the seek position.
+ DemuxerData data;
+ for (int i = 1; i < 4; ++i) {
+ data = CreateReadFromDemuxerAckForVideo();
+ data.access_units[0].timestamp = base::TimeDelta::FromMilliseconds(i * 30);
+ player_.OnDemuxerDataAvailable(data);
+ EXPECT_TRUE(GetMediaDecoderJob(false)->is_decoding());
+ message_loop_.Run();
+ }
+ EXPECT_EQ(100.0, player_.GetCurrentTime().InMillisecondsF());
+ EXPECT_TRUE(IsPrerolling(false));
+
+ // Send data at the seek position.
+ data = CreateReadFromDemuxerAckForVideo();
+ data.access_units[0].timestamp = base::TimeDelta::FromMilliseconds(100);
+ player_.OnDemuxerDataAvailable(data);
+ message_loop_.Run();
+
+ // TODO(wolenetz/qinmin): Player's maintenance of current time for video-only
+ // streams depends on decoder output, which may be initially inaccurate, and
+ // encoded video test data may also need updating. Verify at least that AU
+ // timestamp-based preroll logic has determined video preroll has completed.
+ EXPECT_FALSE(IsPrerolling(false));
+}
+
+TEST_F(MediaSourcePlayerTest, SeekingAfterCompletingPrerollRestartsPreroll) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ // Test decoder job will begin prerolling upon seek, when it was not
+ // prerolling prior to the seek.
+ StartAudioDecoderJob();
+ MediaDecoderJob* decoder_job = GetMediaDecoderJob(true);
+ EXPECT_TRUE(decoder_job);
+ EXPECT_EQ(1, demuxer_->num_data_requests());
+ EXPECT_TRUE(IsPrerolling(true));
+
+ // Complete the initial preroll by feeding data to the decoder.
+ for (int i = 0; i < 4; ++i) {
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(i));
+ EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
+ message_loop_.Run();
+ }
+ EXPECT_LT(0.0, player_.GetCurrentTime().InMillisecondsF());
+ EXPECT_FALSE(IsPrerolling(true));
+
+ SeekPlayer(true, base::TimeDelta::FromMilliseconds(500));
+
+ // Prerolling should have begun again.
+ EXPECT_TRUE(IsPrerolling(true));
+ EXPECT_EQ(500.0, GetPrerollTimestamp().InMillisecondsF());
+
+ // Send data at and after the seek position. Prerolling should complete.
+ for (int i = 0; i < 4; ++i) {
+ DemuxerData data = CreateReadFromDemuxerAckForAudio(i);
+ data.access_units[0].timestamp = base::TimeDelta::FromMilliseconds(
+ 500 + 30 * (i - 1));
+ player_.OnDemuxerDataAvailable(data);
+ EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
+ message_loop_.Run();
+ }
+ EXPECT_LT(500.0, player_.GetCurrentTime().InMillisecondsF());
+ EXPECT_FALSE(IsPrerolling(true));
+
+ // Throughout this test, we should have not re-created the decoder job, so
+ // IsPrerolling() transition from false to true was not due to constructor
+ // initialization. It was due to BeginPrerolling().
+ EXPECT_EQ(decoder_job, GetMediaDecoderJob(true));
+}
+
+TEST_F(MediaSourcePlayerTest, PrerollContinuesAcrossReleaseAndStart) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ // Test decoder job will resume media prerolling if interrupted by Release()
+ // and Start().
+ StartAudioDecoderJob();
+ EXPECT_TRUE(GetMediaDecoderJob(true));
+ EXPECT_EQ(1, demuxer_->num_data_requests());
+
+ SeekPlayer(true, base::TimeDelta::FromMilliseconds(100));
+ EXPECT_TRUE(IsPrerolling(true));
+ EXPECT_EQ(100.0, GetPrerollTimestamp().InMillisecondsF());
+
+ // Send some data before the seek position.
+ // Test uses 'large' number of iterations because decoder job may not get
+ // MEDIA_CODEC_OK output status until after a few dequeue output attempts.
+ // This allows decoder status to stabilize prior to AU timestamp reaching
+ // the preroll target.
+ DemuxerData data;
+ for (int i = 0; i < 10; ++i) {
+ data = CreateReadFromDemuxerAckForAudio(3);
+ data.access_units[0].timestamp = base::TimeDelta::FromMilliseconds(i * 10);
+ if (i == 1) {
+ // While still prerolling, Release() and Start() the player.
+ // TODO(qinmin): Simulation of multiple in-flight data requests (one from
+ // before Release(), one from after Start()) is not included here, and
+ // neither is any data enqueued for later decode if it arrives after
+ // Release() and before Start(). See http://crbug.com/306314. Assumption
+ // for this test, to prevent flakiness until the bug is fixed, is the
+ // first request's data arrives before Start(). Though that data is not
+ // seen by decoder, this assumption allows preroll continuation
+ // verification and prevents multiple in-flight data requests.
+ player_.Release();
+ player_.OnDemuxerDataAvailable(data);
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(NULL, GetMediaDecoderJob(true));
+ StartAudioDecoderJob();
+ EXPECT_TRUE(GetMediaDecoderJob(true));
+ } else {
+ player_.OnDemuxerDataAvailable(data);
+ EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
+ message_loop_.Run();
+ }
+ EXPECT_TRUE(IsPrerolling(true));
+ }
+ EXPECT_EQ(100.0, player_.GetCurrentTime().InMillisecondsF());
+ EXPECT_TRUE(IsPrerolling(true));
+
+ // Send data after the seek position.
+ data = CreateReadFromDemuxerAckForAudio(3);
+ data.access_units[0].timestamp = base::TimeDelta::FromMilliseconds(100);
+ player_.OnDemuxerDataAvailable(data);
+ message_loop_.Run();
+ EXPECT_LT(100.0, player_.GetCurrentTime().InMillisecondsF());
+ EXPECT_FALSE(IsPrerolling(true));
+}
+
+TEST_F(MediaSourcePlayerTest, PrerollContinuesAcrossConfigChange) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ // Test decoder job will resume media prerolling if interrupted by
+ // |kConfigChanged| and OnDemuxerConfigsAvailable().
StartAudioDecoderJob();
EXPECT_TRUE(GetMediaDecoderJob(true));
EXPECT_EQ(1, demuxer_->num_data_requests());
- // Initiate a seek to 100 ms. This will abort the intial prefetch.
- player_.SeekTo(base::TimeDelta::FromMilliseconds(100));
+ SeekPlayer(true, base::TimeDelta::FromMilliseconds(100));
+ EXPECT_TRUE(IsPrerolling(true));
+ EXPECT_EQ(100.0, GetPrerollTimestamp().InMillisecondsF());
+
+ // In response to data request, simulate that demuxer signals config change by
+ // sending an AU with |kConfigChanged|. Player should prepare to reconfigure
+ // the audio decoder job, and should request new demuxer configs.
DemuxerData data;
data.type = DemuxerStream::AUDIO;
data.access_units.resize(1);
- data.access_units[0].status = DemuxerStream::kAborted;
+ data.access_units[0].status = DemuxerStream::kConfigChanged;
+ EXPECT_EQ(0, demuxer_->num_config_requests());
player_.OnDemuxerDataAvailable(data);
- EXPECT_EQ(1, demuxer_->num_seek_requests());
- player_.OnDemuxerSeekDone();
- EXPECT_EQ(2, demuxer_->num_data_requests());
+ EXPECT_EQ(1, demuxer_->num_config_requests());
+
+ // Simulate arrival of new configs.
+ DemuxerConfigs configs;
+ configs.audio_codec = kCodecVorbis;
+ configs.audio_channels = 2;
+ configs.audio_sampling_rate = 44100;
+ configs.is_audio_encrypted = false;
+ configs.duration_ms = kDefaultDurationInMs;
+ scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile("vorbis-extradata");
+ configs.audio_extra_data = std::vector<uint8>(
+ buffer->data(),
+ buffer->data() + buffer->data_size());
+ player_.OnDemuxerConfigsAvailable(configs);
- // Send some data with before the seek position.
+ // Send some data before the seek position.
for (int i = 1; i < 4; ++i) {
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(i));
EXPECT_TRUE(GetMediaDecoderJob(true)->is_decoding());
message_loop_.Run();
}
EXPECT_EQ(100.0, player_.GetCurrentTime().InMillisecondsF());
+ EXPECT_TRUE(IsPrerolling(true));
// Send data after the seek position.
data = CreateReadFromDemuxerAckForAudio(3);
@@ -697,6 +914,7 @@ TEST_F(MediaSourcePlayerTest, PrerollAfterSeek) {
player_.OnDemuxerDataAvailable(data);
message_loop_.Run();
EXPECT_LT(100.0, player_.GetCurrentTime().InMillisecondsF());
+ EXPECT_FALSE(IsPrerolling(true));
}
// TODO(xhwang): Enable this test when the test devices are updated.