Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(234)

Unified Diff: media/filters/chunk_demuxer.cc

Issue 9295020: Fix ChunkDemuxer seek deadlock (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix copyright year. Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/filters/audio_renderer_base_unittest.cc ('k') | media/filters/ffmpeg_audio_decoder.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/filters/chunk_demuxer.cc
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 0c27733b13c8dcbf7b7ecdc3984afafccd57a656..95cd9e7493f08cbcc3b288786d0572812a9e39ee 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -24,12 +24,14 @@ class ChunkDemuxerStream : public DemuxerStream {
public:
typedef std::deque<scoped_refptr<Buffer> > BufferQueue;
typedef std::deque<ReadCallback> ReadCBQueue;
+ typedef std::deque<base::Closure> ClosureQueue;
explicit ChunkDemuxerStream(const AudioDecoderConfig& audio_config);
explicit ChunkDemuxerStream(const VideoDecoderConfig& video_config);
virtual ~ChunkDemuxerStream();
void Flush();
+ void Seek(base::TimeDelta time);
// Checks if it is ok to add the |buffers| to the stream.
bool CanAddBuffers(const BufferQueue& buffers) const;
@@ -40,22 +42,41 @@ class ChunkDemuxerStream : public DemuxerStream {
bool GetLastBufferTimestamp(base::TimeDelta* timestamp) const;
// DemuxerStream methods.
- virtual void Read(const ReadCallback& read_callback);
- virtual Type type();
- virtual void EnableBitstreamConverter();
- virtual const AudioDecoderConfig& audio_decoder_config();
- virtual const VideoDecoderConfig& video_decoder_config();
+ virtual void Read(const ReadCallback& read_callback) OVERRIDE;
+ virtual Type type() OVERRIDE;
+ virtual void EnableBitstreamConverter() OVERRIDE;
+ virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE;
+ virtual const VideoDecoderConfig& video_decoder_config() OVERRIDE;
private:
+ enum State {
+ RETURNING_DATA_FOR_READS,
+ WAITING_FOR_SEEK,
+ RECEIVED_EOS_WHILE_WAITING_FOR_SEEK, // EOS = End of stream.
+ RECEIVED_EOS,
+ RETURNING_EOS_FOR_READS,
+ SHUTDOWN,
+ };
+
+ // Assigns |state_| to |state|
+ void ChangeState_Locked(State state);
+
+ // Adds the callback to |read_cbs_| so it can be called later when we
+ // have data.
+ void DeferRead_Locked(const ReadCallback& read_cb);
+
+ // Creates closures that bind ReadCallbacks in |read_cbs_| to data in
+ // |buffers_| and pops the callbacks & buffers from the respecive queues.
+ void CreateReadDoneClosures_Locked(ClosureQueue* closures);
+
Type type_;
AudioDecoderConfig audio_config_;
VideoDecoderConfig video_config_;
mutable base::Lock lock_;
+ State state_;
ReadCBQueue read_cbs_;
BufferQueue buffers_;
- bool shutdown_called_;
- bool received_end_of_stream_;
// Keeps track of the timestamp of the last buffer we have
// added to |buffers_|. This is used to enforce buffers with strictly
@@ -67,8 +88,7 @@ class ChunkDemuxerStream : public DemuxerStream {
ChunkDemuxerStream::ChunkDemuxerStream(const AudioDecoderConfig& audio_config)
: type_(AUDIO),
- shutdown_called_(false),
- received_end_of_stream_(false),
+ state_(RETURNING_DATA_FOR_READS),
last_buffer_timestamp_(kNoTimestamp()) {
audio_config_.CopyFrom(audio_config);
}
@@ -76,8 +96,7 @@ ChunkDemuxerStream::ChunkDemuxerStream(const AudioDecoderConfig& audio_config)
ChunkDemuxerStream::ChunkDemuxerStream(const VideoDecoderConfig& video_config)
: type_(VIDEO),
- shutdown_called_(false),
- received_end_of_stream_(false),
+ state_(RETURNING_DATA_FOR_READS),
last_buffer_timestamp_(kNoTimestamp()) {
video_config_.CopyFrom(video_config);
}
@@ -86,10 +105,34 @@ ChunkDemuxerStream::~ChunkDemuxerStream() {}
void ChunkDemuxerStream::Flush() {
DVLOG(1) << "Flush()";
+ ReadCBQueue read_cbs;
+ {
+ base::AutoLock auto_lock(lock_);
+ buffers_.clear();
+ ChangeState_Locked(WAITING_FOR_SEEK);
+ last_buffer_timestamp_ = kNoTimestamp();
+
+ std::swap(read_cbs_, read_cbs);
+ }
+
+ for (ReadCBQueue::iterator it = read_cbs.begin(); it != read_cbs.end(); ++it)
+ it->Run(scoped_refptr<Buffer>());
+}
+
+void ChunkDemuxerStream::Seek(base::TimeDelta time) {
base::AutoLock auto_lock(lock_);
- buffers_.clear();
- received_end_of_stream_ = false;
- last_buffer_timestamp_ = kNoTimestamp();
+
+ DCHECK(read_cbs_.empty());
+
+ if (state_ == WAITING_FOR_SEEK) {
+ ChangeState_Locked(RETURNING_DATA_FOR_READS);
+ return;
+ }
+
+ if (state_ == RECEIVED_EOS_WHILE_WAITING_FOR_SEEK) {
+ ChangeState_Locked(RECEIVED_EOS);
+ return;
+ }
}
bool ChunkDemuxerStream::CanAddBuffers(const BufferQueue& buffers) const {
@@ -109,7 +152,7 @@ void ChunkDemuxerStream::AddBuffers(const BufferQueue& buffers) {
if (buffers.empty())
return;
- std::deque<base::Closure> callbacks;
+ ClosureQueue closures;
{
base::AutoLock auto_lock(lock_);
@@ -117,17 +160,15 @@ void ChunkDemuxerStream::AddBuffers(const BufferQueue& buffers) {
itr != buffers.end(); itr++) {
// Make sure we aren't trying to add a buffer after we have received and
// "end of stream" buffer.
- DCHECK(!received_end_of_stream_);
+ DCHECK_NE(state_, RECEIVED_EOS_WHILE_WAITING_FOR_SEEK);
+ DCHECK_NE(state_, RECEIVED_EOS);
+ DCHECK_NE(state_, RETURNING_EOS_FOR_READS);
if ((*itr)->IsEndOfStream()) {
- received_end_of_stream_ = true;
-
- // Push enough EOS buffers to satisfy outstanding Read() requests.
- if (read_cbs_.size() > buffers_.size()) {
- size_t pending_read_without_data = read_cbs_.size() - buffers_.size();
- for (size_t i = 0; i <= pending_read_without_data; ++i) {
- buffers_.push_back(*itr);
- }
+ if (state_ == WAITING_FOR_SEEK) {
+ ChangeState_Locked(RECEIVED_EOS_WHILE_WAITING_FOR_SEEK);
+ } else {
+ ChangeState_Locked(RECEIVED_EOS);
}
} else {
base::TimeDelta current_ts = (*itr)->GetTimestamp();
@@ -141,38 +182,27 @@ void ChunkDemuxerStream::AddBuffers(const BufferQueue& buffers) {
}
}
- while (!buffers_.empty() && !read_cbs_.empty()) {
- callbacks.push_back(base::Bind(read_cbs_.front(), buffers_.front()));
- buffers_.pop_front();
- read_cbs_.pop_front();
- }
+ CreateReadDoneClosures_Locked(&closures);
}
- while (!callbacks.empty()) {
- callbacks.front().Run();
- callbacks.pop_front();
- }
+ for (ClosureQueue::iterator it = closures.begin(); it != closures.end(); ++it)
+ it->Run();
}
void ChunkDemuxerStream::Shutdown() {
- std::deque<ReadCallback> callbacks;
+ ReadCBQueue read_cbs;
{
base::AutoLock auto_lock(lock_);
- shutdown_called_ = true;
+ ChangeState_Locked(SHUTDOWN);
- // Collect all the pending Read() callbacks.
- while (!read_cbs_.empty()) {
- callbacks.push_back(read_cbs_.front());
- read_cbs_.pop_front();
- }
+ std::swap(read_cbs_, read_cbs);
+ buffers_.clear();
}
// Pass end of stream buffers to all callbacks to signal that no more data
// will be sent.
- while (!callbacks.empty()) {
- callbacks.front().Run(CreateEOSBuffer());
- callbacks.pop_front();
- }
+ for (ReadCBQueue::iterator it = read_cbs.begin(); it != read_cbs.end(); ++it)
+ it->Run(CreateEOSBuffer());
}
bool ChunkDemuxerStream::GetLastBufferTimestamp(
@@ -206,33 +236,46 @@ void ChunkDemuxerStream::Read(const ReadCallback& read_callback) {
{
base::AutoLock auto_lock(lock_);
- if (shutdown_called_ || (received_end_of_stream_ && buffers_.empty())) {
- buffer = CreateEOSBuffer();
- } else {
- if (buffers_.empty()) {
- // Wrap & store |read_callback| so that it will
- // get called on the current MessageLoop.
- read_cbs_.push_back(base::Bind(&RunOnMessageLoop,
- read_callback,
- MessageLoop::current()));
- return;
- }
+ switch(state_) {
+ case RETURNING_DATA_FOR_READS:
+ // If we don't have any buffers ready or already have
+ // pending reads, then defer this read.
+ if (buffers_.empty() || !read_cbs_.empty()) {
+ DeferRead_Locked(read_callback);
+ return;
+ }
- if (!read_cbs_.empty()) {
- // Wrap & store |read_callback| so that it will
- // get called on the current MessageLoop.
- read_cbs_.push_back(base::Bind(&RunOnMessageLoop,
- read_callback,
- MessageLoop::current()));
- return;
- }
+ buffer = buffers_.front();
+ buffers_.pop_front();
+ break;
+
+ case WAITING_FOR_SEEK:
+ case RECEIVED_EOS_WHILE_WAITING_FOR_SEEK:
+ // Null buffers should be returned in this state since we are waiting
+ // for a seek. Any buffers in |buffers_| should NOT be returned because
+ // they are associated with the seek.
+ DCHECK(read_cbs_.empty());
+ break;
+ case RECEIVED_EOS:
+ DCHECK(read_cbs_.empty());
+
+ if (buffers_.empty()) {
+ ChangeState_Locked(RETURNING_EOS_FOR_READS);
+ buffer = CreateEOSBuffer();
+ } else {
+ buffer = buffers_.front();
+ buffers_.pop_front();
+ }
+ break;
- buffer = buffers_.front();
- buffers_.pop_front();
+ case RETURNING_EOS_FOR_READS:
+ case SHUTDOWN:
+ DCHECK(buffers_.empty());
+ DCHECK(read_cbs_.empty());
+ buffer = CreateEOSBuffer();
}
}
- DCHECK(buffer.get());
read_callback.Run(buffer);
}
@@ -250,6 +293,44 @@ const VideoDecoderConfig& ChunkDemuxerStream::video_decoder_config() {
return video_config_;
}
+void ChunkDemuxerStream::ChangeState_Locked(State state) {
+ lock_.AssertAcquired();
+ state_ = state;
+}
+
+void ChunkDemuxerStream::DeferRead_Locked(const ReadCallback& read_cb) {
+ lock_.AssertAcquired();
+ // Wrap & store |read_callback| so that it will
+ // get called on the current MessageLoop.
+ read_cbs_.push_back(base::Bind(&RunOnMessageLoop, read_cb,
+ MessageLoop::current()));
+}
+
+void ChunkDemuxerStream::CreateReadDoneClosures_Locked(ClosureQueue* closures) {
+ lock_.AssertAcquired();
+
+ if (state_ != RETURNING_DATA_FOR_READS && state_ != RECEIVED_EOS)
+ return;
+
+ while (!buffers_.empty() && !read_cbs_.empty()) {
+ closures->push_back(base::Bind(read_cbs_.front(), buffers_.front()));
+ buffers_.pop_front();
+ read_cbs_.pop_front();
+ }
+
+ if (state_ != RECEIVED_EOS || !buffers_.empty() || read_cbs_.empty())
+ return;
+
+ // Push enough EOS buffers to satisfy outstanding Read() requests.
+ scoped_refptr<Buffer> end_of_stream_buffer = CreateEOSBuffer();
+ while (!read_cbs_.empty()) {
+ closures->push_back(base::Bind(read_cbs_.front(), end_of_stream_buffer));
+ read_cbs_.pop_front();
+ }
+
+ ChangeState_Locked(RETURNING_EOS_FOR_READS);
+}
+
ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client)
: state_(WAITING_FOR_INIT),
client_(client),
@@ -306,6 +387,12 @@ void ChunkDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
base::AutoLock auto_lock(lock_);
if (state_ == INITIALIZED || state_ == ENDED) {
+ if (audio_)
+ audio_->Seek(time);
+
+ if (video_)
+ video_->Seek(time);
+
if (seek_waits_for_data_) {
DVLOG(1) << "Seek() : waiting for more data to arrive.";
seek_cb_ = cb;
« no previous file with comments | « media/filters/audio_renderer_base_unittest.cc ('k') | media/filters/ffmpeg_audio_decoder.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698