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

Unified Diff: media/filters/ffmpeg_demuxer.cc

Issue 11411332: Replace trampolining in FFmpegDemuxer with explicit thread calling convention checks. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 8 years 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/ffmpeg_demuxer.h ('k') | media/filters/ffmpeg_demuxer_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/filters/ffmpeg_demuxer.cc
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index 326b8818998e8e79d6a4937e144e59a1e6a668ef..a53e7c19bf2ced14724d7f57a158d8e40e1f5fcc 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
@@ -100,18 +101,18 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) {
last_packet_timestamp_ = buffer->GetTimestamp();
buffer_queue_.Push(buffer);
- SatisfyPendingReads();
+ SatisfyPendingRead();
}
void FFmpegDemuxerStream::SetEndOfStream() {
DCHECK(message_loop_->BelongsToCurrentThread());
end_of_stream_ = true;
- SatisfyPendingReads();
+ SatisfyPendingRead();
}
void FFmpegDemuxerStream::FlushBuffers() {
DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK(read_queue_.empty()) << "Read requests should be empty";
+ DCHECK(read_cb_.is_null()) << "There should be no pending read";
buffer_queue_.Clear();
end_of_stream_ = false;
last_packet_timestamp_ = kNoTimestamp();
@@ -120,11 +121,10 @@ void FFmpegDemuxerStream::FlushBuffers() {
void FFmpegDemuxerStream::Stop() {
DCHECK(message_loop_->BelongsToCurrentThread());
buffer_queue_.Clear();
- for (ReadQueue::iterator it = read_queue_.begin();
- it != read_queue_.end(); ++it) {
- it->Run(DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer());
+ if (!read_cb_.is_null()) {
+ base::ResetAndReturn(&read_cb_).Run(
+ DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer());
}
- read_queue_.clear();
stopped_ = true;
end_of_stream_ = true;
}
@@ -134,52 +134,49 @@ base::TimeDelta FFmpegDemuxerStream::duration() {
}
DemuxerStream::Type FFmpegDemuxerStream::type() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
return type_;
}
void FFmpegDemuxerStream::Read(const ReadCB& read_cb) {
- if (!message_loop_->BelongsToCurrentThread()) {
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &FFmpegDemuxerStream::Read, this, read_cb));
- return;
- }
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ CHECK(read_cb_.is_null()) << "Overlapping reads are not supported";
+ read_cb_ = BindToCurrentLoop(read_cb);
// Don't accept any additional reads if we've been told to stop.
// The |demuxer_| may have been destroyed in the pipeline thread.
//
// TODO(scherkus): it would be cleaner to reply with an error message.
if (stopped_) {
- read_cb.Run(DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer());
+ base::ResetAndReturn(&read_cb_).Run(
+ DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer());
return;
}
- read_queue_.push_back(read_cb);
- SatisfyPendingReads();
+ SatisfyPendingRead();
}
void FFmpegDemuxerStream::EnableBitstreamConverter() {
- if (!message_loop_->BelongsToCurrentThread()) {
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &FFmpegDemuxerStream::EnableBitstreamConverter, this));
- return;
- }
+ DCHECK(message_loop_->BelongsToCurrentThread());
CHECK(bitstream_converter_.get());
bitstream_converter_enabled_ = true;
}
const AudioDecoderConfig& FFmpegDemuxerStream::audio_decoder_config() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
CHECK_EQ(type_, AUDIO);
return audio_config_;
}
const VideoDecoderConfig& FFmpegDemuxerStream::video_decoder_config() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
CHECK_EQ(type_, VIDEO);
return video_config_;
}
FFmpegDemuxerStream::~FFmpegDemuxerStream() {
DCHECK(stopped_);
- DCHECK(read_queue_.empty());
+ DCHECK(read_cb_.is_null());
DCHECK(buffer_queue_.IsEmpty());
}
@@ -191,24 +188,16 @@ Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const {
return buffered_ranges_;
}
-void FFmpegDemuxerStream::SatisfyPendingReads() {
+void FFmpegDemuxerStream::SatisfyPendingRead() {
DCHECK(message_loop_->BelongsToCurrentThread());
- while (!read_queue_.empty()) {
- scoped_refptr<DecoderBuffer> buffer;
-
+ if (!read_cb_.is_null()) {
if (!buffer_queue_.IsEmpty()) {
- buffer = buffer_queue_.Pop();
+ base::ResetAndReturn(&read_cb_).Run(
+ DemuxerStream::kOk, buffer_queue_.Pop());
} else if (end_of_stream_) {
- buffer = DecoderBuffer::CreateEOSBuffer();
- } else {
- break;
+ base::ResetAndReturn(&read_cb_).Run(
+ DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer());
}
-
- // Send buffer back on a new execution stack to avoid recursing.
- ReadCB read_cb = read_queue_.front();
- read_queue_.pop_front();
- message_loop_->PostTask(FROM_HERE, base::Bind(
- read_cb, DemuxerStream::kOk, buffer));
}
// Have capacity? Ask for more!
@@ -257,34 +246,78 @@ FFmpegDemuxer::FFmpegDemuxer(
FFmpegDemuxer::~FFmpegDemuxer() {}
void FFmpegDemuxer::Stop(const base::Closure& callback) {
- // Post a task to notify the streams to stop as well.
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&FFmpegDemuxer::StopTask, this, callback));
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ url_protocol_.Abort();
+ data_source_->Stop(BindToCurrentLoop(base::Bind(
+ &FFmpegDemuxer::OnDataSourceStopped, this, BindToCurrentLoop(callback))));
}
void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&FFmpegDemuxer::SeekTask, this, time, cb));
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ CHECK(!pending_seek_);
+
+ // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|,
+ // otherwise we can end up waiting for a pre-seek read to complete even though
+ // we know we're going to drop it on the floor.
+
+ // Always seek to a timestamp less than or equal to the desired timestamp.
+ int flags = AVSEEK_FLAG_BACKWARD;
+
+ // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg
+ // will attempt to use the lowest-index video stream, if present, followed by
+ // the lowest-index audio stream.
+ pending_seek_ = true;
+ base::PostTaskAndReplyWithResult(
+ blocking_thread_.message_loop_proxy(), FROM_HERE,
+ base::Bind(&av_seek_frame, glue_->format_context(), -1,
+ time.InMicroseconds(), flags),
+ base::Bind(&FFmpegDemuxer::OnSeekFrameDone, this, cb));
}
void FFmpegDemuxer::SetPlaybackRate(float playback_rate) {
- DCHECK(data_source_.get());
+ DCHECK(message_loop_->BelongsToCurrentThread());
data_source_->SetPlaybackRate(playback_rate);
}
void FFmpegDemuxer::OnAudioRendererDisabled() {
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &FFmpegDemuxer::DisableAudioStreamTask, this));
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ audio_disabled_ = true;
+ StreamVector::iterator iter;
+ for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
+ if (*iter && (*iter)->type() == DemuxerStream::AUDIO) {
+ (*iter)->Stop();
+ }
+ }
}
void FFmpegDemuxer::Initialize(DemuxerHost* host,
const PipelineStatusCB& status_cb) {
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &FFmpegDemuxer::InitializeTask, this, host, status_cb));
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ host_ = host;
+
+ // TODO(scherkus): DataSource should have a host by this point,
+ // see http://crbug.com/122071
+ data_source_->set_host(host);
+
+ glue_.reset(new FFmpegGlue(&url_protocol_));
+ AVFormatContext* format_context = glue_->format_context();
+
+ // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we
+ // don't use. FFmpeg will only read ID3v1 tags if no other metadata is
+ // available, so add a metadata entry to ensure some is always present.
+ av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0);
+
+ // Open the AVFormatContext using our glue layer.
+ CHECK(blocking_thread_.Start());
+ base::PostTaskAndReplyWithResult(
+ blocking_thread_.message_loop_proxy(), FROM_HERE,
+ base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())),
+ base::Bind(&FFmpegDemuxer::OnOpenContextDone, this, status_cb));
}
scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream(
DemuxerStream::Type type) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
return GetFFmpegStream(type);
}
@@ -300,6 +333,7 @@ scoped_refptr<FFmpegDemuxerStream> FFmpegDemuxer::GetFFmpegStream(
}
base::TimeDelta FFmpegDemuxer::GetStartTime() const {
+ DCHECK(message_loop_->BelongsToCurrentThread());
return start_time_;
}
@@ -339,31 +373,6 @@ static int CalculateBitrate(
return bytes * 8000000.0 / duration_us;
}
-void FFmpegDemuxer::InitializeTask(DemuxerHost* host,
- const PipelineStatusCB& status_cb) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- host_ = host;
-
- // TODO(scherkus): DataSource should have a host by this point,
- // see http://crbug.com/122071
- data_source_->set_host(host);
-
- glue_.reset(new FFmpegGlue(&url_protocol_));
- AVFormatContext* format_context = glue_->format_context();
-
- // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we
- // don't use. FFmpeg will only read ID3v1 tags if no other metadata is
- // available, so add a metadata entry to ensure some is always present.
- av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0);
-
- // Open the AVFormatContext using our glue layer.
- CHECK(blocking_thread_.Start());
- base::PostTaskAndReplyWithResult(
- blocking_thread_.message_loop_proxy(), FROM_HERE,
- base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())),
- base::Bind(&FFmpegDemuxer::OnOpenContextDone, this, status_cb));
-}
-
void FFmpegDemuxer::OnOpenContextDone(const PipelineStatusCB& status_cb,
bool result) {
DCHECK(message_loop_->BelongsToCurrentThread());
@@ -478,28 +487,6 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
status_cb.Run(PIPELINE_OK);
}
-void FFmpegDemuxer::SeekTask(base::TimeDelta time, const PipelineStatusCB& cb) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- CHECK(!pending_seek_);
-
- // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|,
- // otherwise we can end up waiting for a pre-seek read to complete even though
- // we know we're going to drop it on the floor.
-
- // Always seek to a timestamp less than or equal to the desired timestamp.
- int flags = AVSEEK_FLAG_BACKWARD;
-
- // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg
- // will attempt to use the lowest-index video stream, if present, followed by
- // the lowest-index audio stream.
- pending_seek_ = true;
- base::PostTaskAndReplyWithResult(
- blocking_thread_.message_loop_proxy(), FROM_HERE,
- base::Bind(&av_seek_frame, glue_->format_context(), -1,
- time.InMicroseconds(), flags),
- base::Bind(&FFmpegDemuxer::OnSeekFrameDone, this, cb));
-}
-
void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) {
DCHECK(message_loop_->BelongsToCurrentThread());
CHECK(pending_seek_);
@@ -524,17 +511,17 @@ void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) {
(*iter)->FlushBuffers();
}
- // Resume demuxing until capacity.
- DemuxTask();
+ // Resume reading until capacity.
+ ReadFrameIfNeeded();
// Notify we're finished seeking.
cb.Run(PIPELINE_OK);
}
-void FFmpegDemuxer::DemuxTask() {
+void FFmpegDemuxer::ReadFrameIfNeeded() {
DCHECK(message_loop_->BelongsToCurrentThread());
- // Make sure we have work to do before demuxing.
+ // Make sure we have work to do before reading.
if (!blocking_thread_.IsRunning() || !StreamsHaveAvailableCapacity() ||
pending_read_ || pending_seek_) {
return;
@@ -600,15 +587,8 @@ void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) {
demuxer_stream->EnqueuePacket(packet.Pass());
}
- // Keep demuxing until we've reached capacity.
- DemuxTask();
-}
-
-void FFmpegDemuxer::StopTask(const base::Closure& callback) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- url_protocol_.Abort();
- data_source_->Stop(BindToLoop(message_loop_, base::Bind(
- &FFmpegDemuxer::OnDataSourceStopped, this, callback)));
+ // Keep reading until we've reached capacity.
+ ReadFrameIfNeeded();
}
void FFmpegDemuxer::OnDataSourceStopped(const base::Closure& callback) {
@@ -628,17 +608,6 @@ void FFmpegDemuxer::OnDataSourceStopped(const base::Closure& callback) {
callback.Run();
}
-void FFmpegDemuxer::DisableAudioStreamTask() {
- DCHECK(message_loop_->BelongsToCurrentThread());
- audio_disabled_ = true;
- StreamVector::iterator iter;
- for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
- if (*iter && (*iter)->type() == DemuxerStream::AUDIO) {
- (*iter)->Stop();
- }
- }
-}
-
bool FFmpegDemuxer::StreamsHaveAvailableCapacity() {
DCHECK(message_loop_->BelongsToCurrentThread());
StreamVector::iterator iter;
@@ -664,7 +633,7 @@ void FFmpegDemuxer::StreamHasEnded() {
void FFmpegDemuxer::NotifyCapacityAvailable() {
DCHECK(message_loop_->BelongsToCurrentThread());
- DemuxTask();
+ ReadFrameIfNeeded();
}
void FFmpegDemuxer::NotifyBufferingChanged() {
« no previous file with comments | « media/filters/ffmpeg_demuxer.h ('k') | media/filters/ffmpeg_demuxer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698