diff options
author | vrk@chromium.org <vrk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-08 03:07:27 +0000 |
---|---|---|
committer | vrk@chromium.org <vrk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-08 03:07:27 +0000 |
commit | 0e5c161c19bf91d0529cd996441d8bf0bed7591d (patch) | |
tree | 46cbbc4d72ad5d37fdc74271ba722d26e6d5ef6d /media | |
parent | a4f3d05f4e8b58aefe879ba5233dbc20e7e4c4d4 (diff) | |
download | chromium_src-0e5c161c19bf91d0529cd996441d8bf0bed7591d.zip chromium_src-0e5c161c19bf91d0529cd996441d8bf0bed7591d.tar.gz chromium_src-0e5c161c19bf91d0529cd996441d8bf0bed7591d.tar.bz2 |
Cancel pending seek in ChunkDemuxer when a new seek occurs
This makes it so playback doesn't stall if one seeks into an unbuffered
region then seeks back into a buffered region.
BUG=132935
Review URL: https://chromiumcodereview.appspot.com/10835044
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@150489 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/filters/chunk_demuxer.cc | 39 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.h | 1 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_unittest.cc | 81 |
3 files changed, 121 insertions, 0 deletions
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index 73b45c34..90b0bb7 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc @@ -173,6 +173,7 @@ class ChunkDemuxerStream : public DemuxerStream { void StartWaitingForSeek(); void Seek(TimeDelta time); + void CancelPendingSeek(); bool IsSeekPending() const; // Add buffers to this stream. Buffers are stored in SourceBufferStreams, @@ -214,6 +215,7 @@ class ChunkDemuxerStream : public DemuxerStream { enum State { RETURNING_DATA_FOR_READS, WAITING_FOR_SEEK, + CANCELED, SHUTDOWN, }; @@ -280,12 +282,29 @@ void ChunkDemuxerStream::Seek(TimeDelta time) { DCHECK(read_cbs_.empty()); + // Ignore seek requests when canceled. + if (state_ == CANCELED) + return; + stream_->Seek(time); if (state_ == WAITING_FOR_SEEK) ChangeState_Locked(RETURNING_DATA_FOR_READS); } +void ChunkDemuxerStream::CancelPendingSeek() { + DVLOG(1) << "CancelPendingSeek()"; + ReadCBQueue read_cbs; + { + base::AutoLock auto_lock(lock_); + ChangeState_Locked(CANCELED); + std::swap(read_cbs_, read_cbs); + } + + for (ReadCBQueue::iterator it = read_cbs.begin(); it != read_cbs.end(); ++it) + it->Run(kAborted, NULL); +} + bool ChunkDemuxerStream::IsSeekPending() const { base::AutoLock auto_lock(lock_); return stream_->IsSeekPending(); @@ -490,6 +509,7 @@ bool ChunkDemuxerStream::GetNextBuffer_Locked( return true; } break; + case CANCELED: case WAITING_FOR_SEEK: // Null buffers should be returned in this state since we are waiting // for a seek. Any buffers in the SourceBuffer should NOT be returned @@ -540,6 +560,7 @@ void ChunkDemuxer::Stop(const base::Closure& callback) { void ChunkDemuxer::Seek(TimeDelta time, const PipelineStatusCB& cb) { DVLOG(1) << "Seek(" << time.InSecondsF() << ")"; DCHECK(time >= start_time_); + DCHECK(seek_cb_.is_null()); PipelineStatus status = PIPELINE_ERROR_INVALID_STATE; { @@ -605,6 +626,24 @@ void ChunkDemuxer::StartWaitingForSeek() { ChangeState_Locked(INITIALIZED); } +void ChunkDemuxer::CancelPendingSeek() { + PipelineStatusCB cb; + { + base::AutoLock auto_lock(lock_); + if (IsSeekPending_Locked() && !seek_cb_.is_null()) { + std::swap(cb, seek_cb_); + } + if (audio_) + audio_->CancelPendingSeek(); + + if (video_) + video_->CancelPendingSeek(); + } + + if (!cb.is_null()) + cb.Run(PIPELINE_OK); +} + ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, const std::string& type, std::vector<std::string>& codecs) { diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h index e366503..81029ac 100644 --- a/media/filters/chunk_demuxer.h +++ b/media/filters/chunk_demuxer.h @@ -47,6 +47,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { // Methods used by an external object to control this demuxer. void StartWaitingForSeek(); + void CancelPendingSeek(); // Registers a new |id| to use for AppendData() calls. |type| indicates // the MIME type for the data that we intend to append for this ID. diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc index 46c9e63..d266a8a 100644 --- a/media/filters/chunk_demuxer_unittest.cc +++ b/media/filters/chunk_demuxer_unittest.cc @@ -86,6 +86,14 @@ static void OnReadDone(const base::TimeDelta& expected_time, *called = true; } +static void OnReadDone_AbortExpected( + bool* called, DemuxerStream::Status status, + const scoped_refptr<DecoderBuffer>& buffer) { + EXPECT_EQ(status, DemuxerStream::kAborted); + EXPECT_EQ(NULL, buffer.get()); + *called = true; +} + static void OnReadDone_EOSExpected(bool* called, DemuxerStream::Status status, const scoped_refptr<DecoderBuffer>& buffer) { @@ -1489,6 +1497,79 @@ TEST_F(ChunkDemuxerTest, TestRemoveId) { GenerateSingleStreamExpectedReads(0, 4, video, kVideoBlockDuration); } +TEST_F(ChunkDemuxerTest, TestSeekCanceled) { + ASSERT_TRUE(InitDemuxer(true, true, false)); + + scoped_refptr<DemuxerStream> audio = + demuxer_->GetStream(DemuxerStream::AUDIO); + scoped_refptr<DemuxerStream> video = + demuxer_->GetStream(DemuxerStream::VIDEO); + + // Append cluster at the beginning of the stream. + scoped_ptr<Cluster> start_cluster(GenerateCluster(0, 4)); + ASSERT_TRUE(AppendData(start_cluster->data(), start_cluster->size())); + + // Seek to an unbuffered region. + demuxer_->StartWaitingForSeek(); + demuxer_->Seek(base::TimeDelta::FromSeconds(50), + NewExpectedStatusCB(PIPELINE_OK)); + + // Attempt to read in unbuffered area; should not fulfill the read. + bool audio_read_done = false; + bool video_read_done = false; + audio->Read(base::Bind(&OnReadDone_AbortExpected, &audio_read_done)); + video->Read(base::Bind(&OnReadDone_AbortExpected, &video_read_done)); + EXPECT_FALSE(audio_read_done); + EXPECT_FALSE(video_read_done); + + // Now cancel the pending seek, which should flush the reads with empty + // buffers. + demuxer_->CancelPendingSeek(); + EXPECT_TRUE(audio_read_done); + EXPECT_TRUE(video_read_done); + + // A seek back to the buffered region should succeed. + demuxer_->StartWaitingForSeek(); + demuxer_->Seek(base::TimeDelta::FromSeconds(0), + NewExpectedStatusCB(PIPELINE_OK)); + GenerateExpectedReads(0, 4, audio, video); +} + +TEST_F(ChunkDemuxerTest, TestSeekCanceledWhileWaitingForSeek) { + ASSERT_TRUE(InitDemuxer(true, true, false)); + + scoped_refptr<DemuxerStream> audio = + demuxer_->GetStream(DemuxerStream::AUDIO); + scoped_refptr<DemuxerStream> video = + demuxer_->GetStream(DemuxerStream::VIDEO); + + // Append cluster at the beginning of the stream. + scoped_ptr<Cluster> start_cluster(GenerateCluster(0, 4)); + ASSERT_TRUE(AppendData(start_cluster->data(), start_cluster->size())); + + // Start waiting for a seek. + demuxer_->StartWaitingForSeek(); + + // Now cancel the upcoming seek to an unbuffered region. + demuxer_->CancelPendingSeek(); + demuxer_->Seek(base::TimeDelta::FromSeconds(50), + NewExpectedStatusCB(PIPELINE_OK)); + + // Read requests should be fulfilled with empty buffers. + bool audio_read_done = false; + bool video_read_done = false; + audio->Read(base::Bind(&OnReadDone_AbortExpected, &audio_read_done)); + video->Read(base::Bind(&OnReadDone_AbortExpected, &video_read_done)); + EXPECT_TRUE(audio_read_done); + EXPECT_TRUE(video_read_done); + + // A seek back to the buffered region should succeed. + demuxer_->StartWaitingForSeek(); + demuxer_->Seek(base::TimeDelta::FromSeconds(0), + NewExpectedStatusCB(PIPELINE_OK)); + GenerateExpectedReads(0, 4, audio, video); +} + // Test that Seek() successfully seeks to all source IDs. TEST_F(ChunkDemuxerTest, TestSeekAudioAndVideoSources) { std::string audio_id = "audio1"; |