diff options
author | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-17 22:27:56 +0000 |
---|---|---|
committer | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-17 22:27:56 +0000 |
commit | 1590b7ba918f421004a50330851836c6397595c8 (patch) | |
tree | 707f3f2ac976a353eb43a12274367324d6b384ea | |
parent | a5faf8bbc066f23192ac55d9a3741876b3a16eb4 (diff) | |
download | chromium_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.h | 9 | ||||
-rw-r--r-- | media/base/demuxer_stream.h | 30 | ||||
-rw-r--r-- | media/filters/audio_renderer_impl.cc | 55 | ||||
-rw-r--r-- | media/filters/audio_renderer_impl.h | 7 | ||||
-rw-r--r-- | media/filters/audio_renderer_impl_unittest.cc | 9 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.cc | 35 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_unittest.cc | 15 | ||||
-rw-r--r-- | media/filters/ffmpeg_audio_decoder.cc | 31 | ||||
-rw-r--r-- | media/filters/ffmpeg_audio_decoder.h | 10 | ||||
-rw-r--r-- | media/filters/ffmpeg_audio_decoder_unittest.cc | 7 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer.cc | 13 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer_unittest.cc | 11 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decoder.cc | 27 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decoder.h | 10 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decoder_unittest.cc | 6 | ||||
-rw-r--r-- | media/filters/gpu_video_decoder.cc | 13 | ||||
-rw-r--r-- | media/filters/gpu_video_decoder.h | 4 | ||||
-rw-r--r-- | media/tools/player_x11/player_x11.cc | 8 | ||||
-rw-r--r-- | media/tools/seek_tester/seek_tester.cc | 2 |
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 |