summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-17 22:27:56 +0000
committeracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-17 22:27:56 +0000
commit1590b7ba918f421004a50330851836c6397595c8 (patch)
tree707f3f2ac976a353eb43a12274367324d6b384ea
parenta5faf8bbc066f23192ac55d9a3741876b3a16eb4 (diff)
downloadchromium_src-1590b7ba918f421004a50330851836c6397595c8.zip
chromium_src-1590b7ba918f421004a50330851836c6397595c8.tar.gz
chromium_src-1590b7ba918f421004a50330851836c6397595c8.tar.bz2
Add status parameter to DemuxerStream::ReadCB
BUG=122913 TEST=None Review URL: https://chromiumcodereview.appspot.com/10669022 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@147108 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--media/base/audio_decoder.h9
-rw-r--r--media/base/demuxer_stream.h30
-rw-r--r--media/filters/audio_renderer_impl.cc55
-rw-r--r--media/filters/audio_renderer_impl.h7
-rw-r--r--media/filters/audio_renderer_impl_unittest.cc9
-rw-r--r--media/filters/chunk_demuxer.cc35
-rw-r--r--media/filters/chunk_demuxer_unittest.cc15
-rw-r--r--media/filters/ffmpeg_audio_decoder.cc31
-rw-r--r--media/filters/ffmpeg_audio_decoder.h10
-rw-r--r--media/filters/ffmpeg_audio_decoder_unittest.cc7
-rw-r--r--media/filters/ffmpeg_demuxer.cc13
-rw-r--r--media/filters/ffmpeg_demuxer_unittest.cc11
-rw-r--r--media/filters/ffmpeg_video_decoder.cc27
-rw-r--r--media/filters/ffmpeg_video_decoder.h10
-rw-r--r--media/filters/ffmpeg_video_decoder_unittest.cc6
-rw-r--r--media/filters/gpu_video_decoder.cc13
-rw-r--r--media/filters/gpu_video_decoder.h4
-rw-r--r--media/tools/player_x11/player_x11.cc8
-rw-r--r--media/tools/seek_tester/seek_tester.cc2
19 files changed, 209 insertions, 93 deletions
diff --git a/media/base/audio_decoder.h b/media/base/audio_decoder.h
index e435a1c..e6f09ed 100644
--- a/media/base/audio_decoder.h
+++ b/media/base/audio_decoder.h
@@ -19,6 +19,13 @@ class DemuxerStream;
class MEDIA_EXPORT AudioDecoder
: public base::RefCountedThreadSafe<AudioDecoder> {
public:
+ // Status codes for read operations.
+ enum Status {
+ kOk,
+ kAborted,
+ kDecodeError,
+ };
+
// Initialize an AudioDecoder with the given DemuxerStream, executing the
// callback upon completion.
// statistics_cb is used to update global pipeline statistics.
@@ -36,7 +43,7 @@ class MEDIA_EXPORT AudioDecoder
// indicate the end of the stream. A NULL buffer pointer indicates an aborted
// Read(). This can happen if the DemuxerStream gets flushed and doesn't have
// any more data to return.
- typedef base::Callback<void(scoped_refptr<Buffer>)> ReadCB;
+ typedef base::Callback<void(Status, const scoped_refptr<Buffer>&)> ReadCB;
virtual void Read(const ReadCB& read_cb) = 0;
// Reset decoder state, dropping any queued encoded data.
diff --git a/media/base/demuxer_stream.h b/media/base/demuxer_stream.h
index 1266a71..65457c3 100644
--- a/media/base/demuxer_stream.h
+++ b/media/base/demuxer_stream.h
@@ -26,12 +26,34 @@ class MEDIA_EXPORT DemuxerStream
NUM_TYPES, // Always keep this entry as the last one!
};
+ // Status returned in the Read() callback.
+ // kOk : Indicates the second parameter is Non-NULL and contains media data
+ // or the end of the stream.
+ // kAborted : Indicates an aborted Read(). This can happen if the
+ // DemuxerStream gets flushed and doesn't have any more data to
+ // return. The second parameter MUST be NULL when this status is
+ // returned.
+ // kConfigChange : Indicates that the AudioDecoderConfig or
+ // VideoDecoderConfig for the stream has changed.
+ // The DemuxerStream expects an audio_decoder_config() or
+ // video_decoder_config() call before Read() will start
+ // returning DecoderBuffers again. The decoder will need this
+ // new configuration to properly decode the buffers read
+ // from this point forward. The second parameter MUST be NULL
+ // when this status is returned.
+ enum Status {
+ kOk,
+ kAborted,
+ kConfigChanged,
+ };
+
// Request a buffer to returned via the provided callback.
//
- // Non-NULL buffer pointers will contain media data or signal the end of the
- // stream. A NULL pointer indicates an aborted Read(). This can happen if the
- // DemuxerStream gets flushed and doesn't have any more data to return.
- typedef base::Callback<void(const scoped_refptr<DecoderBuffer>&)> ReadCB;
+ // The first parameter indicates the status of the read.
+ // The second parameter is non-NULL and contains media data
+ // or the end of the stream if the first parameter is kOk. NULL otherwise.
+ typedef base::Callback<void(Status,
+ const scoped_refptr<DecoderBuffer>&)>ReadCB;
virtual void Read(const ReadCB& read_cb) = 0;
// Returns the audio decoder configuration. It is an error to call this method
diff --git a/media/filters/audio_renderer_impl.cc b/media/filters/audio_renderer_impl.cc
index c8841cd..1fa0d38 100644
--- a/media/filters/audio_renderer_impl.cc
+++ b/media/filters/audio_renderer_impl.cc
@@ -72,10 +72,8 @@ void AudioRendererImpl::Pause(const base::Closure& callback) {
state_ = kPaused;
// Pause only when we've completed our pending read.
- if (!pending_read_) {
- pause_cb_.Run();
- pause_cb_.Reset();
- }
+ if (!pending_read_)
+ base::ResetAndReturn(&pause_cb_).Run();
}
if (stopped_)
@@ -234,7 +232,8 @@ AudioRendererImpl::~AudioRendererImpl() {
DCHECK(!algorithm_.get());
}
-void AudioRendererImpl::DecodedAudioReady(scoped_refptr<Buffer> buffer) {
+void AudioRendererImpl::DecodedAudioReady(AudioDecoder::Status status,
+ const scoped_refptr<Buffer>& buffer) {
base::AutoLock auto_lock(lock_);
DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying ||
state_ == kUnderflow || state_ == kRebuffering || state_ == kStopped);
@@ -242,7 +241,20 @@ void AudioRendererImpl::DecodedAudioReady(scoped_refptr<Buffer> buffer) {
CHECK(pending_read_);
pending_read_ = false;
- if (buffer && buffer->IsEndOfStream()) {
+ if (status == AudioDecoder::kAborted) {
+ HandleAbortedReadOrDecodeError(false);
+ return;
+ }
+
+ if (status == AudioDecoder::kDecodeError) {
+ HandleAbortedReadOrDecodeError(true);
+ return;
+ }
+
+ DCHECK_EQ(status, AudioDecoder::kOk);
+ DCHECK(buffer);
+
+ if (buffer->IsEndOfStream()) {
received_end_of_stream_ = true;
// Transition to kPlaying if we are currently handling an underflow since
@@ -256,7 +268,7 @@ void AudioRendererImpl::DecodedAudioReady(scoped_refptr<Buffer> buffer) {
NOTREACHED();
return;
case kPaused:
- if (buffer && !buffer->IsEndOfStream())
+ if (!buffer->IsEndOfStream())
algorithm_->EnqueueBuffer(buffer);
DCHECK(!pending_read_);
base::ResetAndReturn(&pause_cb_).Run();
@@ -266,7 +278,7 @@ void AudioRendererImpl::DecodedAudioReady(scoped_refptr<Buffer> buffer) {
ScheduleRead_Locked();
return;
}
- if (buffer && !buffer->IsEndOfStream()) {
+ if (!buffer->IsEndOfStream()) {
algorithm_->EnqueueBuffer(buffer);
if (!algorithm_->IsQueueFull())
return;
@@ -277,7 +289,7 @@ void AudioRendererImpl::DecodedAudioReady(scoped_refptr<Buffer> buffer) {
case kPlaying:
case kUnderflow:
case kRebuffering:
- if (buffer && !buffer->IsEndOfStream())
+ if (!buffer->IsEndOfStream())
algorithm_->EnqueueBuffer(buffer);
return;
case kStopped:
@@ -515,4 +527,29 @@ void AudioRendererImpl::DisableUnderflowForTesting() {
underflow_disabled_ = true;
}
+void AudioRendererImpl::HandleAbortedReadOrDecodeError(bool is_decode_error) {
+ PipelineStatus status = is_decode_error ? PIPELINE_ERROR_DECODE : PIPELINE_OK;
+ switch (state_) {
+ case kUninitialized:
+ NOTREACHED();
+ return;
+ case kPaused:
+ if (status != PIPELINE_OK)
+ host_->SetError(status);
+ base::ResetAndReturn(&pause_cb_).Run();
+ return;
+ case kSeeking:
+ state_ = kPaused;
+ base::ResetAndReturn(&seek_cb_).Run(status);
+ return;
+ case kPlaying:
+ case kUnderflow:
+ case kRebuffering:
+ case kStopped:
+ if (status != PIPELINE_OK)
+ host_->SetError(status);
+ return;
+ }
+}
+
} // namespace media
diff --git a/media/filters/audio_renderer_impl.h b/media/filters/audio_renderer_impl.h
index 3be42f8..1121115 100644
--- a/media/filters/audio_renderer_impl.h
+++ b/media/filters/audio_renderer_impl.h
@@ -73,7 +73,12 @@ class MEDIA_EXPORT AudioRendererImpl
FRIEND_TEST_ALL_PREFIXES(AudioRendererImplTest, Underflow_EndOfStream);
// Callback from the audio decoder delivering decoded audio samples.
- void DecodedAudioReady(scoped_refptr<Buffer> buffer);
+ void DecodedAudioReady(AudioDecoder::Status status,
+ const scoped_refptr<Buffer>& buffer);
+
+ // Helper functions for AudioDecoder::Status values passed to
+ // DecodedAudioReady().
+ void HandleAbortedReadOrDecodeError(bool is_decode_error);
// Fills the given buffer with audio data by delegating to its |algorithm_|.
// FillBuffer() also takes care of updating the clock. Returns the number of
diff --git a/media/filters/audio_renderer_impl_unittest.cc b/media/filters/audio_renderer_impl_unittest.cc
index 909bbb0..7ecccce 100644
--- a/media/filters/audio_renderer_impl_unittest.cc
+++ b/media/filters/audio_renderer_impl_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/bind.h"
+#include "base/callback_helpers.h"
#include "base/gtest_prod_util.h"
#include "base/stl_util.h"
#include "media/base/data_buffer.h"
@@ -144,15 +145,11 @@ class AudioRendererImplTest : public ::testing::Test {
buffer->SetDuration(base::TimeDelta::FromMilliseconds(8000 * size / bps));
next_timestamp_ += buffer->GetDuration();
- AudioDecoder::ReadCB read_cb;
- std::swap(read_cb, read_cb_);
- read_cb.Run(buffer);
+ base::ResetAndReturn(&read_cb_).Run(AudioDecoder::kOk, buffer);
}
void AbortPendingRead() {
- AudioDecoder::ReadCB read_cb;
- std::swap(read_cb, read_cb_);
- read_cb.Run(NULL);
+ base::ResetAndReturn(&read_cb_).Run(AudioDecoder::kAborted, NULL);
}
// Delivers an end of stream buffer to |renderer_|.
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 564bbf54..af7927b 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -211,9 +211,10 @@ class ChunkDemuxerStream : public DemuxerStream {
void CreateReadDoneClosures_Locked(ClosureQueue* closures);
// Gets the value to pass to the next Read() callback. Returns true if
- // |buffer| should be passed to the callback. False indicates that |buffer|
- // was not set and more data is needed.
- bool GetNextBuffer_Locked(scoped_refptr<StreamParserBuffer>* buffer);
+ // |status| and |buffer| should be passed to the callback. False indicates
+ // that |status| and |buffer| were not set and more data is needed.
+ bool GetNextBuffer_Locked(DemuxerStream::Status* status,
+ scoped_refptr<StreamParserBuffer>* buffer);
// Specifies the type of the stream (must be AUDIO or VIDEO for now).
Type type_;
@@ -253,7 +254,7 @@ void ChunkDemuxerStream::StartWaitingForSeek() {
}
for (ReadCBQueue::iterator it = read_cbs.begin(); it != read_cbs.end(); ++it)
- it->Run(NULL);
+ it->Run(kAborted, NULL);
}
void ChunkDemuxerStream::Seek(TimeDelta time) {
@@ -385,34 +386,36 @@ void ChunkDemuxerStream::Shutdown() {
// Pass end of stream buffers to all callbacks to signal that no more data
// will be sent.
for (ReadCBQueue::iterator it = read_cbs.begin(); it != read_cbs.end(); ++it)
- it->Run(StreamParserBuffer::CreateEOSBuffer());
+ it->Run(DemuxerStream::kOk, StreamParserBuffer::CreateEOSBuffer());
}
// Helper function that makes sure |read_cb| runs on |message_loop|.
static void RunOnMessageLoop(const DemuxerStream::ReadCB& read_cb,
MessageLoop* message_loop,
+ DemuxerStream::Status status,
const scoped_refptr<DecoderBuffer>& buffer) {
if (MessageLoop::current() != message_loop) {
message_loop->PostTask(FROM_HERE, base::Bind(
- &RunOnMessageLoop, read_cb, message_loop, buffer));
+ &RunOnMessageLoop, read_cb, message_loop, status, buffer));
return;
}
- read_cb.Run(buffer);
+ read_cb.Run(status, buffer);
}
// DemuxerStream methods.
void ChunkDemuxerStream::Read(const ReadCB& read_cb) {
+ DemuxerStream::Status status = kOk;
scoped_refptr<StreamParserBuffer> buffer;
{
base::AutoLock auto_lock(lock_);
- if (!read_cbs_.empty() || !GetNextBuffer_Locked(&buffer)) {
+ if (!read_cbs_.empty() || !GetNextBuffer_Locked(&status, &buffer)) {
DeferRead_Locked(read_cb);
return;
}
}
- read_cb.Run(buffer);
+ read_cb.Run(status, buffer);
}
DemuxerStream::Type ChunkDemuxerStream::type() { return type_; }
@@ -452,25 +455,31 @@ void ChunkDemuxerStream::CreateReadDoneClosures_Locked(ClosureQueue* closures) {
if (state_ != RETURNING_DATA_FOR_READS)
return;
+ DemuxerStream::Status status;
scoped_refptr<StreamParserBuffer> buffer;
while (!read_cbs_.empty()) {
- if (!GetNextBuffer_Locked(&buffer))
+ if (!GetNextBuffer_Locked(&status, &buffer))
return;
- closures->push_back(base::Bind(read_cbs_.front(), buffer));
+ closures->push_back(base::Bind(read_cbs_.front(),
+ status, buffer));
read_cbs_.pop_front();
}
}
bool ChunkDemuxerStream::GetNextBuffer_Locked(
+ DemuxerStream::Status* status,
scoped_refptr<StreamParserBuffer>* buffer) {
lock_.AssertAcquired();
switch (state_) {
case RETURNING_DATA_FOR_READS:
- if (stream_->GetNextBuffer(buffer))
+ if (stream_->GetNextBuffer(buffer)) {
+ *status = DemuxerStream::kOk;
return true;
+ }
if (end_of_stream_) {
+ *status = DemuxerStream::kOk;
*buffer = StreamParserBuffer::CreateEOSBuffer();
return true;
}
@@ -480,10 +489,12 @@ bool ChunkDemuxerStream::GetNextBuffer_Locked(
// for a seek. Any buffers in the SourceBuffer should NOT be returned
// because they are associated with the seek.
DCHECK(read_cbs_.empty());
+ *status = DemuxerStream::kAborted;
*buffer = NULL;
return true;
case SHUTDOWN:
DCHECK(read_cbs_.empty());
+ *status = DemuxerStream::kOk;
*buffer = StreamParserBuffer::CreateEOSBuffer();
return true;
}
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index 2e074ac..a05d234 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -72,13 +72,17 @@ MATCHER_P(HasTimestamp, timestamp_in_ms, "") {
static void OnReadDone(const base::TimeDelta& expected_time,
bool* called,
+ DemuxerStream::Status status,
const scoped_refptr<DecoderBuffer>& buffer) {
+ EXPECT_EQ(status, DemuxerStream::kOk);
EXPECT_EQ(expected_time, buffer->GetTimestamp());
*called = true;
}
static void OnReadDone_EOSExpected(bool* called,
+ DemuxerStream::Status status,
const scoped_refptr<DecoderBuffer>& buffer) {
+ EXPECT_EQ(status, DemuxerStream::kOk);
EXPECT_TRUE(buffer->IsEndOfStream());
*called = true;
}
@@ -517,10 +521,12 @@ class ChunkDemuxerTest : public testing::Test {
EXPECT_EQ(ss.str(), expected);
}
- MOCK_METHOD1(ReadDone, void(const scoped_refptr<DecoderBuffer>&));
+ MOCK_METHOD2(ReadDone, void(DemuxerStream::Status status,
+ const scoped_refptr<DecoderBuffer>&));
void ExpectRead(DemuxerStream* stream, int64 timestamp_in_ms) {
- EXPECT_CALL(*this, ReadDone(HasTimestamp(timestamp_in_ms)));
+ EXPECT_CALL(*this, ReadDone(DemuxerStream::kOk,
+ HasTimestamp(timestamp_in_ms)));
stream->Read(base::Bind(&ChunkDemuxerTest::ReadDone,
base::Unretained(this)));
}
@@ -965,7 +971,10 @@ class EndOfStreamHelper {
private:
static void OnEndOfStreamReadDone(
- bool* called, const scoped_refptr<DecoderBuffer>& buffer) {
+ bool* called,
+ DemuxerStream::Status status,
+ const scoped_refptr<DecoderBuffer>& buffer) {
+ EXPECT_EQ(status, DemuxerStream::kOk);
EXPECT_TRUE(buffer->IsEndOfStream());
*called = true;
}
diff --git a/media/filters/ffmpeg_audio_decoder.cc b/media/filters/ffmpeg_audio_decoder.cc
index 58f3dcd..e0f869b 100644
--- a/media/filters/ffmpeg_audio_decoder.cc
+++ b/media/filters/ffmpeg_audio_decoder.cc
@@ -5,6 +5,7 @@
#include "media/filters/ffmpeg_audio_decoder.h"
#include "base/bind.h"
+#include "base/callback_helpers.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/data_buffer.h"
#include "media/base/decoder_buffer.h"
@@ -165,13 +166,18 @@ void FFmpegAudioDecoder::DoRead(const ReadCB& read_cb) {
}
void FFmpegAudioDecoder::DoDecodeBuffer(
+ DemuxerStream::Status status,
const scoped_refptr<DecoderBuffer>& input) {
DCHECK_EQ(MessageLoop::current(), message_loop_);
DCHECK(!read_cb_.is_null());
- if (!input) {
- // DemuxeStream::Read() was aborted so we abort the decoder's pending read.
- DeliverSamples(NULL);
+ if (status != DemuxerStream::kOk) {
+ DCHECK(!input);
+ // TODO(acolwell): Add support for reinitializing the decoder when
+ // |status| == kConfigChanged. For now we just trigger a decode error.
+ AudioDecoder::Status decoder_status =
+ (status == DemuxerStream::kAborted) ? kAborted : kDecodeError;
+ base::ResetAndReturn(&read_cb_).Run(decoder_status, NULL);
return;
}
@@ -244,11 +250,10 @@ void FFmpegAudioDecoder::DoDecodeBuffer(
// Decoding finished successfully, update stats and execute callback.
statistics_cb_.Run(statistics);
- if (output) {
- DeliverSamples(output);
- } else {
+ if (output)
+ base::ResetAndReturn(&read_cb_).Run(kOk, output);
+ else
ReadFromDemuxerStream();
- }
}
void FFmpegAudioDecoder::ReadFromDemuxerStream() {
@@ -258,11 +263,14 @@ void FFmpegAudioDecoder::ReadFromDemuxerStream() {
}
void FFmpegAudioDecoder::DecodeBuffer(
+ DemuxerStream::Status status,
const scoped_refptr<DecoderBuffer>& buffer) {
+ DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status;
+
// TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read
// callback on the same execution stack so we can get rid of forced task post.
message_loop_->PostTask(FROM_HERE, base::Bind(
- &FFmpegAudioDecoder::DoDecodeBuffer, this, buffer));
+ &FFmpegAudioDecoder::DoDecodeBuffer, this, status, buffer));
}
void FFmpegAudioDecoder::UpdateDurationAndTimestamp(
@@ -295,11 +303,4 @@ base::TimeDelta FFmpegAudioDecoder::CalculateDuration(int size) {
return base::TimeDelta::FromMicroseconds(static_cast<int64>(microseconds));
}
-void FFmpegAudioDecoder::DeliverSamples(const scoped_refptr<Buffer>& samples) {
- // Reset the callback before running to protect against reentrancy.
- ReadCB read_cb = read_cb_;
- read_cb_.Reset();
- read_cb.Run(samples);
-}
-
} // namespace media
diff --git a/media/filters/ffmpeg_audio_decoder.h b/media/filters/ffmpeg_audio_decoder.h
index bf3b1e4..46af8f0 100644
--- a/media/filters/ffmpeg_audio_decoder.h
+++ b/media/filters/ffmpeg_audio_decoder.h
@@ -10,6 +10,7 @@
#include "base/callback.h"
#include "base/message_loop.h"
#include "media/base/audio_decoder.h"
+#include "media/base/demuxer_stream.h"
struct AVCodecContext;
struct AVFrame;
@@ -43,11 +44,13 @@ class MEDIA_EXPORT FFmpegAudioDecoder : public AudioDecoder {
const StatisticsCB& statistics_cb);
void DoReset(const base::Closure& closure);
void DoRead(const ReadCB& read_cb);
- void DoDecodeBuffer(const scoped_refptr<DecoderBuffer>& input);
+ void DoDecodeBuffer(DemuxerStream::Status status,
+ const scoped_refptr<DecoderBuffer>& input);
// Reads from the demuxer stream with corresponding callback method.
void ReadFromDemuxerStream();
- void DecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer);
+ void DecodeBuffer(DemuxerStream::Status status,
+ const scoped_refptr<DecoderBuffer>& buffer);
// Updates the output buffer's duration and timestamp based on the input
// buffer. Will fall back to an estimated timestamp if the input lacks a
@@ -57,9 +60,6 @@ class MEDIA_EXPORT FFmpegAudioDecoder : public AudioDecoder {
// Calculates duration based on size of decoded audio bytes.
base::TimeDelta CalculateDuration(int size);
- // Delivers decoded samples to |read_cb_| and resets the callback.
- void DeliverSamples(const scoped_refptr<Buffer>& samples);
-
// This is !is_null() iff Initialize() hasn't been called.
base::Callback<MessageLoop*()> message_loop_factory_cb_;
MessageLoop* message_loop_;
diff --git a/media/filters/ffmpeg_audio_decoder_unittest.cc b/media/filters/ffmpeg_audio_decoder_unittest.cc
index b81b73b..f3dfaea 100644
--- a/media/filters/ffmpeg_audio_decoder_unittest.cc
+++ b/media/filters/ffmpeg_audio_decoder_unittest.cc
@@ -82,8 +82,10 @@ class FFmpegAudioDecoderTest : public testing::Test {
CHECK(!encoded_audio_.empty()) << "ReadPacket() called too many times";
scoped_refptr<DecoderBuffer> buffer(encoded_audio_.front());
+ DemuxerStream::Status status =
+ buffer ? DemuxerStream::kOk : DemuxerStream::kAborted;
encoded_audio_.pop_front();
- read_cb.Run(buffer);
+ read_cb.Run(status, buffer);
}
void Read() {
@@ -92,7 +94,8 @@ class FFmpegAudioDecoderTest : public testing::Test {
message_loop_.RunAllPending();
}
- void DecodeFinished(scoped_refptr<Buffer> buffer) {
+ void DecodeFinished(AudioDecoder::Status status,
+ const scoped_refptr<Buffer>& buffer) {
decoded_audio_.push_back(buffer);
}
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index 2b66a04..5912e83 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -124,7 +124,8 @@ void FFmpegDemuxerStream::Stop() {
buffer_queue_.clear();
for (ReadQueue::iterator it = read_queue_.begin();
it != read_queue_.end(); ++it) {
- it->Run(scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer()));
+ it->Run(DemuxerStream::kOk,
+ scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer()));
}
read_queue_.clear();
stopped_ = true;
@@ -147,7 +148,8 @@ void FFmpegDemuxerStream::Read(const ReadCB& read_cb) {
//
// TODO(scherkus): it would be cleaner if we replied with an error message.
if (stopped_) {
- read_cb.Run(scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer()));
+ read_cb.Run(DemuxerStream::kOk,
+ scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer()));
return;
}
@@ -163,7 +165,7 @@ void FFmpegDemuxerStream::Read(const ReadCB& read_cb) {
// Send the oldest buffer back.
scoped_refptr<DecoderBuffer> buffer = buffer_queue_.front();
buffer_queue_.pop_front();
- read_cb.Run(buffer);
+ read_cb.Run(DemuxerStream::kOk, buffer);
}
void FFmpegDemuxerStream::ReadTask(const ReadCB& read_cb) {
@@ -174,7 +176,8 @@ void FFmpegDemuxerStream::ReadTask(const ReadCB& read_cb) {
//
// TODO(scherkus): it would be cleaner if we replied with an error message.
if (stopped_) {
- read_cb.Run(scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer()));
+ read_cb.Run(DemuxerStream::kOk,
+ scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer()));
return;
}
@@ -202,7 +205,7 @@ void FFmpegDemuxerStream::FulfillPendingRead() {
read_queue_.pop_front();
// Execute the callback.
- read_cb.Run(buffer);
+ read_cb.Run(DemuxerStream::kOk, buffer);
}
void FFmpegDemuxerStream::EnableBitstreamConverter() {
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index cb492f5..e1525e9 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -39,7 +39,9 @@ MATCHER(IsEndOfStreamBuffer,
}
static void EosOnReadDone(bool* got_eos_buffer,
+ DemuxerStream::Status status,
const scoped_refptr<DecoderBuffer>& buffer) {
+ EXPECT_EQ(status, DemuxerStream::kOk);
if (buffer->IsEndOfStream()) {
*got_eos_buffer = true;
EXPECT_TRUE(!buffer->GetData());
@@ -101,11 +103,13 @@ class FFmpegDemuxerTest : public testing::Test {
// This makes it easier to track down where test failures occur.
void OnReadDone(const tracked_objects::Location& location,
int size, int64 timestampInMicroseconds,
+ DemuxerStream::Status status,
const scoped_refptr<DecoderBuffer>& buffer) {
std::string location_str;
location.Write(true, false, &location_str);
location_str += "\n";
SCOPED_TRACE(location_str);
+ EXPECT_EQ(status, DemuxerStream::kOk);
OnReadDoneCalled(size, timestampInMicroseconds);
EXPECT_TRUE(buffer.get() != NULL);
EXPECT_EQ(size, buffer->GetDataSize());
@@ -411,7 +415,8 @@ class MockReadCB : public base::RefCountedThreadSafe<MockReadCB> {
MockReadCB() {}
MOCK_METHOD0(OnDelete, void());
- MOCK_METHOD1(Run, void(const scoped_refptr<DecoderBuffer>& buffer));
+ MOCK_METHOD2(Run, void(DemuxerStream::Status status,
+ const scoped_refptr<DecoderBuffer>& buffer));
protected:
virtual ~MockReadCB() {
@@ -446,7 +451,7 @@ TEST_F(FFmpegDemuxerTest, Stop) {
// The callback should be immediately deleted. We'll use a checkpoint to
// verify that it has indeed been deleted.
- EXPECT_CALL(*callback, Run(IsEndOfStreamBuffer()));
+ EXPECT_CALL(*callback, Run(DemuxerStream::kOk, IsEndOfStreamBuffer()));
EXPECT_CALL(*callback, OnDelete());
EXPECT_CALL(*this, CheckPoint(1));
@@ -486,7 +491,7 @@ TEST_F(FFmpegDemuxerTest, StreamReadAfterStopAndDemuxerDestruction) {
// The callback should be immediately deleted. We'll use a checkpoint to
// verify that it has indeed been deleted.
- EXPECT_CALL(*callback, Run(IsEndOfStreamBuffer()));
+ EXPECT_CALL(*callback, Run(DemuxerStream::kOk, IsEndOfStreamBuffer()));
EXPECT_CALL(*callback, OnDelete());
EXPECT_CALL(*this, CheckPoint(1));
diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc
index ed41fc0..09cba03 100644
--- a/media/filters/ffmpeg_video_decoder.cc
+++ b/media/filters/ffmpeg_video_decoder.cc
@@ -282,14 +282,17 @@ void FFmpegVideoDecoder::ReadFromDemuxerStream() {
}
void FFmpegVideoDecoder::DecryptOrDecodeBuffer(
+ DemuxerStream::Status status,
const scoped_refptr<DecoderBuffer>& buffer) {
+ DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status;
// TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read
// callback on the same execution stack so we can get rid of forced task post.
message_loop_->PostTask(FROM_HERE, base::Bind(
- &FFmpegVideoDecoder::DoDecryptOrDecodeBuffer, this, buffer));
+ &FFmpegVideoDecoder::DoDecryptOrDecodeBuffer, this, status, buffer));
}
void FFmpegVideoDecoder::DoDecryptOrDecodeBuffer(
+ DemuxerStream::Status status,
const scoped_refptr<DecoderBuffer>& buffer) {
DCHECK_EQ(MessageLoop::current(), message_loop_);
DCHECK_NE(state_, kUninitialized);
@@ -297,16 +300,20 @@ void FFmpegVideoDecoder::DoDecryptOrDecodeBuffer(
DCHECK(!read_cb_.is_null());
if (!reset_cb_.is_null()) {
- DeliverFrame(NULL);
+ base::ResetAndReturn(&read_cb_).Run(kOk, NULL);
DoReset();
return;
}
- if (!buffer) {
- DeliverFrame(NULL);
+ if (status != DemuxerStream::kOk) {
+ DecoderStatus decoder_status =
+ (status == DemuxerStream::kAborted) ? kOk : kDecodeError;
+ base::ResetAndReturn(&read_cb_).Run(decoder_status, NULL);
return;
}
+ DCHECK_EQ(status, DemuxerStream::kOk);
+
if (buffer->GetDecryptConfig() && buffer->GetDataSize()) {
decryptor_->Decrypt(buffer,
base::Bind(&FFmpegVideoDecoder::BufferDecrypted, this));
@@ -332,7 +339,7 @@ void FFmpegVideoDecoder::DoBufferDecrypted(
DCHECK(!read_cb_.is_null());
if (!reset_cb_.is_null()) {
- DeliverFrame(NULL);
+ base::ResetAndReturn(&read_cb_).Run(kOk, NULL);
DoReset();
return;
}
@@ -408,7 +415,7 @@ void FFmpegVideoDecoder::DecodeBuffer(
if (!video_frame) {
if (state_ == kFlushCodec) {
state_ = kDecodeFinished;
- DeliverFrame(VideoFrame::CreateEmptyFrame());
+ base::ResetAndReturn(&read_cb_).Run(kOk, VideoFrame::CreateEmptyFrame());
return;
}
@@ -416,7 +423,7 @@ void FFmpegVideoDecoder::DecodeBuffer(
return;
}
- DeliverFrame(video_frame);
+ base::ResetAndReturn(&read_cb_).Run(kOk, video_frame);
}
bool FFmpegVideoDecoder::Decode(
@@ -511,12 +518,6 @@ bool FFmpegVideoDecoder::Decode(
return true;
}
-void FFmpegVideoDecoder::DeliverFrame(
- const scoped_refptr<VideoFrame>& video_frame) {
- // Reset the callback before running to protect against reentrancy.
- base::ResetAndReturn(&read_cb_).Run(kOk, video_frame);
-}
-
void FFmpegVideoDecoder::ReleaseFFmpegResources() {
if (codec_context_) {
av_free(codec_context_->extradata);
diff --git a/media/filters/ffmpeg_video_decoder.h b/media/filters/ffmpeg_video_decoder.h
index 0f07755..0ba475d 100644
--- a/media/filters/ffmpeg_video_decoder.h
+++ b/media/filters/ffmpeg_video_decoder.h
@@ -8,6 +8,7 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "media/base/decryptor.h"
+#include "media/base/demuxer_stream.h"
#include "media/base/video_decoder.h"
class MessageLoop;
@@ -57,11 +58,13 @@ class MEDIA_EXPORT FFmpegVideoDecoder : public VideoDecoder {
// Reads from the demuxer stream with corresponding callback method.
void ReadFromDemuxerStream();
- void DecryptOrDecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer);
+ void DecryptOrDecodeBuffer(DemuxerStream::Status status,
+ const scoped_refptr<DecoderBuffer>& buffer);
// Carries out the buffer processing operation scheduled by
// DecryptOrDecodeBuffer().
- void DoDecryptOrDecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer);
+ void DoDecryptOrDecodeBuffer(DemuxerStream::Status status,
+ const scoped_refptr<DecoderBuffer>& buffer);
// Callback called by the decryptor to deliver decrypted data buffer and
// reporting decrypt status. This callback could be called synchronously or
@@ -77,9 +80,6 @@ class MEDIA_EXPORT FFmpegVideoDecoder : public VideoDecoder {
bool Decode(const scoped_refptr<DecoderBuffer>& buffer,
scoped_refptr<VideoFrame>* video_frame);
- // Delivers the frame to |read_cb_| and resets the callback.
- void DeliverFrame(const scoped_refptr<VideoFrame>& video_frame);
-
// Releases resources associated with |codec_context_| and |av_frame_|
// and resets them to NULL.
void ReleaseFFmpegResources();
diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc
index a9468879..0131826 100644
--- a/media/filters/ffmpeg_video_decoder_unittest.cc
+++ b/media/filters/ffmpeg_video_decoder_unittest.cc
@@ -56,7 +56,7 @@ static const uint8 kWrongKey[] = {
static const uint8 kKeyId[] = { 0x4b, 0x65, 0x79, 0x20, 0x49, 0x44 };
ACTION_P(ReturnBuffer, buffer) {
- arg0.Run(buffer);
+ arg0.Run(buffer ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer);
}
class FFmpegVideoDecoderTest : public testing::Test {
@@ -510,7 +510,7 @@ TEST_F(FFmpegVideoDecoderTest, Reset_DuringPendingRead) {
EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
- read_cb.Run(i_frame_buffer_);
+ read_cb.Run(DemuxerStream::kOk, i_frame_buffer_);
message_loop_.RunAllPending();
}
@@ -570,7 +570,7 @@ TEST_F(FFmpegVideoDecoderTest, AbortPendingReadDuringFlush) {
message_loop_.RunAllPending();
// Signal an aborted demuxer read.
- read_cb.Run(NULL);
+ read_cb.Run(DemuxerStream::kAborted, NULL);
// Make sure we get a NULL video frame returned.
EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc
index e5bca0d..cbbfc9e 100644
--- a/media/filters/gpu_video_decoder.cc
+++ b/media/filters/gpu_video_decoder.cc
@@ -210,20 +210,27 @@ void GpuVideoDecoder::Read(const ReadCB& read_cb) {
}
void GpuVideoDecoder::RequestBufferDecode(
+ DemuxerStream::Status status,
const scoped_refptr<DecoderBuffer>& buffer) {
+ DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status;
+
if (!gvd_loop_proxy_->BelongsToCurrentThread()) {
gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind(
- &GpuVideoDecoder::RequestBufferDecode, this, buffer));
+ &GpuVideoDecoder::RequestBufferDecode, this, status, buffer));
return;
}
demuxer_read_in_progress_ = false;
- if (!buffer) {
+ if (status != DemuxerStream::kOk) {
if (pending_read_cb_.is_null())
return;
+ // TODO(acolwell): Add support for reinitializing the decoder when
+ // |status| == kConfigChanged. For now we just trigger a decode error.
+ DecoderStatus decoder_status =
+ (status == DemuxerStream::kAborted) ? kOk : kDecodeError;
gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind(
- pending_read_cb_, kOk, scoped_refptr<VideoFrame>()));
+ pending_read_cb_, decoder_status, scoped_refptr<VideoFrame>()));
pending_read_cb_.Reset();
return;
}
diff --git a/media/filters/gpu_video_decoder.h b/media/filters/gpu_video_decoder.h
index 1ccbbde..9363b53 100644
--- a/media/filters/gpu_video_decoder.h
+++ b/media/filters/gpu_video_decoder.h
@@ -10,6 +10,7 @@
#include <map>
#include "media/base/pipeline_status.h"
+#include "media/base/demuxer_stream.h"
#include "media/base/video_decoder.h"
#include "media/video/video_decode_accelerator.h"
@@ -101,7 +102,8 @@ class MEDIA_EXPORT GpuVideoDecoder
void EnsureDemuxOrDecode();
// Callback to pass to demuxer_stream_->Read() for receiving encoded bits.
- void RequestBufferDecode(const scoped_refptr<DecoderBuffer>& buffer);
+ void RequestBufferDecode(DemuxerStream::Status status,
+ const scoped_refptr<DecoderBuffer>& buffer);
// Enqueue a frame for later delivery (or drop it on the floor if a
// vda->Reset() is in progress) and trigger out-of-line delivery of the oldest
diff --git a/media/tools/player_x11/player_x11.cc b/media/tools/player_x11/player_x11.cc
index bc8ef38..83e12ef 100644
--- a/media/tools/player_x11/player_x11.cc
+++ b/media/tools/player_x11/player_x11.cc
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <X11/XKBlib.h>
-#include <X11/Xlib.h>
#include <signal.h>
#include <iostream> // NOLINT
@@ -31,6 +29,12 @@
#include "media/filters/file_data_source.h"
#include "media/filters/video_renderer_base.h"
#include "media/tools/player_x11/data_source_logger.h"
+
+// Include X11 headers here because X11/Xlib.h #define's Status
+// which causes compiler errors with Status enum declarations
+// in media::DemuxerStream & media::AudioDecoder.
+#include <X11/XKBlib.h>
+#include <X11/Xlib.h>
#include "media/tools/player_x11/gl_video_renderer.h"
#include "media/tools/player_x11/x11_video_renderer.h"
diff --git a/media/tools/seek_tester/seek_tester.cc b/media/tools/seek_tester/seek_tester.cc
index dcc30f7..0f948c8 100644
--- a/media/tools/seek_tester/seek_tester.cc
+++ b/media/tools/seek_tester/seek_tester.cc
@@ -42,7 +42,9 @@ void QuitMessageLoop(MessageLoop* loop, media::PipelineStatus status) {
void TimestampExtractor(uint64* timestamp_ms,
MessageLoop* loop,
+ media::DemuxerStream::Status status,
const scoped_refptr<media::DecoderBuffer>& buffer) {
+ CHECK_EQ(status, media::DemuxerStream::kOk);
if (buffer->GetTimestamp() == media::kNoTimestamp())
*timestamp_ms = -1;
else