diff options
author | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-21 15:55:50 +0000 |
---|---|---|
committer | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-21 15:55:50 +0000 |
commit | 81bb33281c4924303a6408b08187cc63608f87ac (patch) | |
tree | 5647f3661b1afd6d0ed4157eb9841ccdef1a28e9 /media | |
parent | 44531dd1760538f33f7b6d41f5754a2dd3b732f3 (diff) | |
download | chromium_src-81bb33281c4924303a6408b08187cc63608f87ac.zip chromium_src-81bb33281c4924303a6408b08187cc63608f87ac.tar.gz chromium_src-81bb33281c4924303a6408b08187cc63608f87ac.tar.bz2 |
End of stream logic implemented & removed the need for pending_buffers_.
BUG=86536
TEST=ChunkDemuxerTest.*
Review URL: http://codereview.chromium.org/7401029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93405 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/filters/chunk_demuxer.cc | 138 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.h | 16 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_client.h | 26 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_factory.cc | 51 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_factory.h | 52 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_unittest.cc | 55 | ||||
-rw-r--r-- | media/media.gyp | 1 |
7 files changed, 184 insertions, 155 deletions
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index c404408..ea50ba4 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc @@ -14,6 +14,7 @@ #include "media/base/filter_host.h" #include "media/base/data_buffer.h" #include "media/ffmpeg/ffmpeg_common.h" +#include "media/filters/chunk_demuxer_client.h" #include "media/filters/ffmpeg_glue.h" #include "media/filters/in_memory_url_protocol.h" #include "media/webm/webm_cluster_parser.h" @@ -53,11 +54,8 @@ static const uint8 kEmptyCluster[] = { 0x1F, 0x43, 0xB6, 0x75, 0x80 // CLUSTER (size = 0) }; -static Buffer* CreateBuffer(const uint8* data, size_t size) { - scoped_array<uint8> buf(new uint8[size]); - memcpy(buf.get(), data, size); - return new DataBuffer(buf.release(), size); -} +// Create an "end of stream" buffer. +static Buffer* CreateEOSBuffer() { return new DataBuffer(0, 0); } class ChunkDemuxerStream : public DemuxerStream { public: @@ -142,13 +140,15 @@ void ChunkDemuxerStream::AddBuffers(const BufferQueue& buffers) { for (BufferQueue::const_iterator itr = buffers.begin(); itr != buffers.end(); itr++) { - base::TimeDelta current_ts = (*itr)->GetTimestamp(); - if (last_buffer_timestamp_ != kNoTimestamp) { - DCHECK_GT(current_ts.ToInternalValue(), - last_buffer_timestamp_.ToInternalValue()); - } + if (!(*itr)->IsEndOfStream()) { + base::TimeDelta current_ts = (*itr)->GetTimestamp(); + if (last_buffer_timestamp_ != kNoTimestamp) { + DCHECK_GT(current_ts.ToInternalValue(), + last_buffer_timestamp_.ToInternalValue()); + } - last_buffer_timestamp_ = current_ts; + last_buffer_timestamp_ = current_ts; + } buffers_.push_back(*itr); } @@ -181,9 +181,10 @@ void ChunkDemuxerStream::Shutdown() { } } - // Pass NULL to all callbacks to signify read failure. + // Pass end of stream buffers to all callbacks to signal that no more data + // will be sent. while (!callbacks.empty()) { - callbacks.front().Run(NULL); + callbacks.front().Run(CreateEOSBuffer()); callbacks.pop_front(); } } @@ -229,7 +230,9 @@ void ChunkDemuxerStream::Read(const ReadCallback& read_callback) { { base::AutoLock auto_lock(lock_); - if (!shutdown_called_) { + if (shutdown_called_) { + buffer = CreateEOSBuffer(); + } else { if (buffers_.empty()) { // Wrap & store |read_callback| so that it will // get called on the current MessageLoop. @@ -253,6 +256,7 @@ void ChunkDemuxerStream::Read(const ReadCallback& read_callback) { } } + DCHECK(buffer.get()); read_callback.Run(buffer); } @@ -262,11 +266,13 @@ void ChunkDemuxerStream::EnableBitstreamConverter() {} AVStream* ChunkDemuxerStream::GetAVStream() { return av_stream_; } -ChunkDemuxer::ChunkDemuxer() +ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client) : state_(WAITING_FOR_INIT), + client_(client), format_context_(NULL), buffered_bytes_(0), seek_waits_for_data_(true) { + DCHECK(client); } ChunkDemuxer::~ChunkDemuxer() { @@ -280,19 +286,16 @@ ChunkDemuxer::~ChunkDemuxer() { } void ChunkDemuxer::Init(PipelineStatusCB cb) { - base::AutoLock auto_lock(lock_); - DCHECK_EQ(state_, WAITING_FOR_INIT); - - ChangeState(INITIALIZING); - init_cb_ = cb; - - if (pending_buffers_.empty()) - return; + VLOG(1) << "Init()"; + { + base::AutoLock auto_lock(lock_); + DCHECK_EQ(state_, WAITING_FOR_INIT); - scoped_refptr<Buffer> buf = pending_buffers_.front(); - pending_buffers_.pop_front(); + ChangeState(INITIALIZING); + init_cb_ = cb; + } - ParseInfoAndTracks_Locked(buf->GetData(), buf->GetDataSize()); + client_->DemuxerOpened(this); } // Filter implementation. @@ -305,6 +308,8 @@ void ChunkDemuxer::set_host(FilterHost* filter_host) { void ChunkDemuxer::Stop(FilterCallback* callback) { VLOG(1) << "Stop()"; + Shutdown(); + callback->Run(); delete callback; } @@ -351,15 +356,21 @@ base::TimeDelta ChunkDemuxer::GetStartTime() const { } void ChunkDemuxer::FlushData() { + VLOG(1) << "FlushData()"; base::AutoLock auto_lock(lock_); + DCHECK(state_ == INITIALIZED || state_ == ENDED || state_ == SHUTDOWN); + + if (state_ == SHUTDOWN) + return; + if (audio_.get()) audio_->Flush(); if (video_.get()) video_->Flush(); - pending_buffers_.clear(); seek_waits_for_data_ = true; + ChangeState(INITIALIZED); } bool ChunkDemuxer::AppendData(const uint8* data, unsigned length) { @@ -373,13 +384,7 @@ bool ChunkDemuxer::AppendData(const uint8* data, unsigned length) { FilterStatusCB cb; { base::AutoLock auto_lock(lock_); - switch(state_) { - case WAITING_FOR_INIT: - pending_buffers_.push_back(CreateBuffer(data, length)); - return true; - break; - case INITIALIZING: if (!ParseInfoAndTracks_Locked(data, length)) { VLOG(1) << "AppendData(): parsing info & tracks failed"; @@ -395,6 +400,8 @@ bool ChunkDemuxer::AppendData(const uint8* data, unsigned length) { } break; + case WAITING_FOR_INIT: + case ENDED: case INIT_ERROR: case SHUTDOWN: VLOG(1) << "AppendData(): called in unexpected state " << state_; @@ -438,11 +445,53 @@ bool ChunkDemuxer::AppendData(const uint8* data, unsigned length) { return true; } +void ChunkDemuxer::EndOfStream(PipelineStatus status) { + VLOG(1) << "EndOfStream(" << status << ")"; + base::AutoLock auto_lock(lock_); + DCHECK((state_ == INITIALIZING) || (state_ == INITIALIZED) || + (state_ == SHUTDOWN)); + + if (state_ == SHUTDOWN) + return; + + if (state_ == INITIALIZING) { + InitFailed_Locked(); + return; + } + + ChangeState(ENDED); + + if (status != PIPELINE_OK) { + host()->SetError(status); + return; + } + + // Create an end of stream buffer. + ChunkDemuxerStream::BufferQueue buffers; + buffers.push_back(CreateEOSBuffer()); + + if (audio_.get()) + audio_->AddBuffers(buffers); + + if (video_.get()) + video_->AddBuffers(buffers); +} + +bool ChunkDemuxer::HasEnded() { + base::AutoLock auto_lock(lock_); + return (state_ == ENDED); +} + + void ChunkDemuxer::Shutdown() { + VLOG(1) << "Shutdown()"; FilterStatusCB cb; { base::AutoLock auto_lock(lock_); + if (state_ == SHUTDOWN) + return; + std::swap(cb, seek_cb_); if (audio_.get()) @@ -456,6 +505,8 @@ void ChunkDemuxer::Shutdown() { if (!cb.is_null()) cb.Run(PIPELINE_ERROR_ABORT); + + client_->DemuxerClosed(); } void ChunkDemuxer::ChangeState(State new_state) { @@ -502,7 +553,7 @@ bool ChunkDemuxer::ParseInfoAndTracks_Locked(const uint8* data, int size) { format_context_ = CreateFormatContext(data, size); - if (!format_context_ || !SetupStreams() || !ParsePendingBuffers_Locked()) { + if (!format_context_ || !SetupStreams()) { InitFailed_Locked(); return false; } @@ -578,25 +629,6 @@ bool ChunkDemuxer::SetupStreams() { return !no_supported_streams; } -bool ChunkDemuxer::ParsePendingBuffers_Locked() { - bool had_pending_buffers = !pending_buffers_.empty(); - // Handle any buffers that came in between the time the pipeline was - // started and Init() was called. - while(!pending_buffers_.empty()) { - scoped_refptr<media::Buffer> buf = pending_buffers_.front(); - pending_buffers_.pop_front(); - - if (!ParseAndAppendData_Locked(buf->GetData(), buf->GetDataSize())) { - pending_buffers_.clear(); - ChangeState(INIT_ERROR); - return false; - } - } - - seek_waits_for_data_ = !had_pending_buffers; - return true; -} - bool ChunkDemuxer::ParseAndAppendData_Locked(const uint8* data, int length) { if (!cluster_parser_.get()) return false; diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h index 8013c4f..43ec0a5 100644 --- a/media/filters/chunk_demuxer.h +++ b/media/filters/chunk_demuxer.h @@ -15,13 +15,14 @@ struct AVFormatContext; namespace media { +class ChunkDemuxerClient; class ChunkDemuxerStream; // Demuxer implementation that allows chunks of WebM media data to be passed // from JavaScript to the media stack. class ChunkDemuxer : public Demuxer { public: - ChunkDemuxer(); + explicit ChunkDemuxer(ChunkDemuxerClient* client); virtual ~ChunkDemuxer(); void Init(PipelineStatusCB cb); @@ -37,9 +38,11 @@ class ChunkDemuxer : public Demuxer { virtual scoped_refptr<DemuxerStream> GetStream(DemuxerStream::Type type); virtual base::TimeDelta GetStartTime() const; - // Methods used by MediaDataSink + // Methods used by an external object to control this demuxer. void FlushData(); bool AppendData(const uint8* data, unsigned length); + void EndOfStream(PipelineStatus status); + bool HasEnded(); void Shutdown(); private: @@ -47,6 +50,7 @@ class ChunkDemuxer : public Demuxer { WAITING_FOR_INIT, INITIALIZING, INITIALIZED, + ENDED, INIT_ERROR, SHUTDOWN, }; @@ -67,10 +71,6 @@ class ChunkDemuxer : public Demuxer { // found. bool SetupStreams(); - // Parse all the buffers in |pending_buffers_|. Returns false if parsing one - // of the buffers fails. - bool ParsePendingBuffers_Locked(); - // Parse a buffer that was passed to AppendData(). |data| is expected to // contain one or more WebM Clusters. Returns false if parsing the data fails. bool ParseAndAppendData_Locked(const uint8* data, int length); @@ -81,6 +81,7 @@ class ChunkDemuxer : public Demuxer { base::Lock lock_; State state_; + ChunkDemuxerClient* client_; PipelineStatusCB init_cb_; FilterStatusCB seek_cb_; @@ -95,9 +96,6 @@ class ChunkDemuxer : public Demuxer { scoped_ptr<WebMClusterParser> cluster_parser_; - typedef std::list<scoped_refptr<media::Buffer> > BufferList; - BufferList pending_buffers_; - // Should a Seek() call wait for more data before calling the // callback. bool seek_waits_for_data_; diff --git a/media/filters/chunk_demuxer_client.h b/media/filters/chunk_demuxer_client.h new file mode 100644 index 0000000..691f81e --- /dev/null +++ b/media/filters/chunk_demuxer_client.h @@ -0,0 +1,26 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_FILTERS_CHUNK_DEMUXER_CLIENT_H_ +#define MEDIA_FILTERS_CHUNK_DEMUXER_CLIENT_H_ + +namespace media { + +class ChunkDemuxer; + +// Interface used to notify an external object when a ChunkDemuxer +// is opened & closed. +class ChunkDemuxerClient { + public: + // Called when a ChunkDemuxer object is opened. + virtual void DemuxerOpened(ChunkDemuxer* demuxer) = 0; + + // The ChunkDemuxer passed via last DemuxerOpened() call is now + // closed. Any further calls on the demuxer will result in an error. + virtual void DemuxerClosed() = 0; +}; + +} // namespace media + +#endif // MEDIA_FILTERS_CHUNK_DEMUXER_CLIENT_H_ diff --git a/media/filters/chunk_demuxer_factory.cc b/media/filters/chunk_demuxer_factory.cc index a354b39..ed3346c 100644 --- a/media/filters/chunk_demuxer_factory.cc +++ b/media/filters/chunk_demuxer_factory.cc @@ -30,55 +30,34 @@ static void InitDone(MessageLoop* message_loop, NewRunnableFunction(&DoInitDone, cb, demuxer, status)); } -MediaDataSink::MediaDataSink(const scoped_refptr<ChunkDemuxer>& demuxer) - : demuxer_(demuxer) { +ChunkDemuxerFactory::ChunkDemuxerFactory(const std::string& url, + DemuxerFactory* delegate_factory, + ChunkDemuxerClient* client) + : url_(url), + delegate_factory_(delegate_factory), + client_(client) { + DCHECK(delegate_factory_.get()); } -MediaDataSink::~MediaDataSink() {} - -void MediaDataSink::Flush() { - demuxer_->FlushData(); -} - -bool MediaDataSink::AppendData(const uint8* data, unsigned length) { - return demuxer_->AppendData(data, length); -} - -void MediaDataSink::Shutdown() { - demuxer_->Shutdown(); -} - -const char ChunkDemuxerFactory::kURLPrefix[] = "x-media-chunks:"; - -ChunkDemuxerFactory::ChunkDemuxerFactory() {} - ChunkDemuxerFactory::~ChunkDemuxerFactory() {} -bool ChunkDemuxerFactory::IsUrlSupported(const std::string& url) const { - return (url.find(kURLPrefix) == 0); -} - -MediaDataSink* ChunkDemuxerFactory::CreateMediaDataSink() { - demuxer_ = new ChunkDemuxer(); - return new MediaDataSink(demuxer_); -} - void ChunkDemuxerFactory::Build(const std::string& url, BuildCallback* cb) { - if (!IsUrlSupported(url) || !demuxer_.get()) { - cb->Run(DEMUXER_ERROR_COULD_NOT_OPEN, static_cast<Demuxer*>(NULL)); - delete cb; + // Check to see if this is the URL we are looking for. If not delegate + // building to the delegate factory. + if (url != url_) { + delegate_factory_->Build(url, cb); return; } + scoped_refptr<ChunkDemuxer> demuxer(new ChunkDemuxer(client_)); // Call Init() on demuxer. Note that ownership is being passed to the // callback here. - demuxer_->Init(base::Bind(&InitDone, MessageLoop::current(), cb, - scoped_refptr<Demuxer>(demuxer_.get()))); - demuxer_ = NULL; + demuxer->Init(base::Bind(&InitDone, MessageLoop::current(), cb, + scoped_refptr<Demuxer>(demuxer.get()))); } DemuxerFactory* ChunkDemuxerFactory::Clone() const { - return new ChunkDemuxerFactory(); + return new ChunkDemuxerFactory(url_, delegate_factory_->Clone(), client_); } } // namespace media diff --git a/media/filters/chunk_demuxer_factory.h b/media/filters/chunk_demuxer_factory.h index 614820e..a3d004a 100644 --- a/media/filters/chunk_demuxer_factory.h +++ b/media/filters/chunk_demuxer_factory.h @@ -11,56 +11,30 @@ namespace media { class ChunkDemuxer; +class ChunkDemuxerClient; -// Class used by an external object to send media data to the -// Demuxer. This object is created by the DemuxerFactory and -// contains the Demuxer that will be returned in the next Build() -// call on the factory. The external object tells the factory -// to create one of these objects before it starts the Pipeline. -// It does this because the external object may need to make AddData() -// calls before the pipeline has completely initialized. This class -// allows data from these calls to be queued until initialization -// completes. It represents the minimal operations needed by -// the external object to talk to the Demuxer. It also allows -// the external object to have a longer lifetime than the pipeline. -class MediaDataSink { - public: - MediaDataSink(const scoped_refptr<ChunkDemuxer>& demuxer); - ~MediaDataSink(); - - // Flush all data passed via AddData(). - void Flush(); - - // Sends media data to the demuxer. Returns true if the data is valid. - bool AppendData(const uint8* data, unsigned length); - - // Signals that playback is shutting down and further AddData() calls - // should fail. This also cancels pending Read()s on DemuxerStreams. - void Shutdown(); - - private: - scoped_refptr<ChunkDemuxer> demuxer_; - DISALLOW_IMPLICIT_CONSTRUCTORS(MediaDataSink); -}; - +// Factory for building ChunkDemuxers. The factory will only build a +// ChunkDemuxer for build URLs that match the one passed into the constructor. +// All other URLs are delegated to |delegate_factory_|. The url passed to +// the constructor represents the "special" URL that indicates that the +// ChunkDemuxer should be used for playback. class ChunkDemuxerFactory : public DemuxerFactory { public: - // Takes a reference to |demuxer_factory|. - ChunkDemuxerFactory(); + // Takes ownership of |delegate_factory|. + ChunkDemuxerFactory(const std::string& url, DemuxerFactory* delegate_factory, + ChunkDemuxerClient* client); virtual ~ChunkDemuxerFactory(); - bool IsUrlSupported(const std::string& url) const; - MediaDataSink* CreateMediaDataSink(); - // DemuxerFactory methods. virtual void Build(const std::string& url, BuildCallback* cb); virtual DemuxerFactory* Clone() const; private: - static const char kURLPrefix[]; - scoped_refptr<ChunkDemuxer> demuxer_; + std::string url_; + scoped_ptr<DemuxerFactory> delegate_factory_; + ChunkDemuxerClient* client_; - DISALLOW_COPY_AND_ASSIGN(ChunkDemuxerFactory); + DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerFactory); }; } // namespace media diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc index 695cee3..13d0c93 100644 --- a/media/filters/chunk_demuxer_unittest.cc +++ b/media/filters/chunk_demuxer_unittest.cc @@ -10,12 +10,14 @@ #include "media/base/mock_callback.h" #include "media/base/mock_ffmpeg.h" #include "media/filters/chunk_demuxer.h" +#include "media/filters/chunk_demuxer_client.h" #include "media/webm/cluster_builder.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::InSequence; using ::testing::Return; using ::testing::SetArgumentPointee; +using ::testing::NiceMock; using ::testing::_; namespace media { @@ -31,6 +33,18 @@ static const int kTracksSizeOffset = 4; static const int kVideoTrackNum = 1; static const int kAudioTrackNum = 2; +class MockChunkDemuxerClient : public ChunkDemuxerClient { + public: + MockChunkDemuxerClient() {} + virtual ~MockChunkDemuxerClient() {} + + MOCK_METHOD1(DemuxerOpened, void(ChunkDemuxer* demuxer)); + MOCK_METHOD0(DemuxerClosed, void()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockChunkDemuxerClient); +}; + class ChunkDemuxerTest : public testing::Test{ protected: enum CodecsIndex { @@ -40,7 +54,8 @@ class ChunkDemuxerTest : public testing::Test{ }; ChunkDemuxerTest() - : demuxer_(new ChunkDemuxer()) { + : client_(new MockChunkDemuxerClient()), + demuxer_(new ChunkDemuxer(client_.get())) { memset(&format_context_, 0, sizeof(format_context_)); memset(&streams_, 0, sizeof(streams_)); memset(&codecs_, 0, sizeof(codecs_)); @@ -166,8 +181,11 @@ class ChunkDemuxerTest : public testing::Test{ bool init_done_called = false; PipelineStatus expectedStatus = (has_audio || has_video) ? PIPELINE_OK : DEMUXER_ERROR_COULD_NOT_OPEN; + + EXPECT_CALL(*client_, DemuxerOpened(_)); demuxer_->Init(base::Bind(&ChunkDemuxerTest::InitDoneCalled, - &init_done_called, expectedStatus)); + &init_done_called, + expectedStatus)); EXPECT_FALSE(init_done_called); @@ -178,6 +196,7 @@ class ChunkDemuxerTest : public testing::Test{ void ShutdownDemuxer() { if (demuxer_) { + EXPECT_CALL(*client_, DemuxerClosed()); demuxer_->Shutdown(); } @@ -201,6 +220,7 @@ class ChunkDemuxerTest : public testing::Test{ AVCodecContext codecs_[MAX_CODECS_INDEX]; AVStream streams_[MAX_CODECS_INDEX]; + scoped_ptr<MockChunkDemuxerClient> client_; scoped_refptr<ChunkDemuxer> demuxer_; private: @@ -237,7 +257,8 @@ TEST_F(ChunkDemuxerTest, TestInit) { bool has_audio = (i & 0x1) != 0; bool has_video = (i & 0x2) != 0; - demuxer_ = new ChunkDemuxer(); + client_.reset(new MockChunkDemuxerClient()); + demuxer_ = new ChunkDemuxer(client_.get()); InitDemuxer(has_audio, has_video); EXPECT_EQ(demuxer_->GetStream(DemuxerStream::AUDIO).get() != NULL, has_audio); @@ -283,23 +304,13 @@ TEST_F(ChunkDemuxerTest, TestAppendDataAfterSeek) { Checkpoint(2); } -// Test the case where AppendData() is called before Init(). This can happen -// when JavaScript starts sending data before the pipeline is completely -// initialized. +// Test the case where AppendData() is called before Init(). TEST_F(ChunkDemuxerTest, TestAppendDataBeforeInit) { - AppendInfoTracks(true, true); + scoped_array<uint8> info_tracks; + int info_tracks_size = 0; + CreateInfoTracks(true, true, &info_tracks, &info_tracks_size); - ClusterBuilder cb; - cb.SetClusterTimecode(0); - AddSimpleBlock(&cb, kVideoTrackNum, 0); - scoped_ptr<Cluster> cluster(cb.Finish()); - - EXPECT_TRUE(demuxer_->AppendData(cluster->data(), cluster->size())); - - demuxer_->Init(NewExpectedStatusCB(PIPELINE_OK)); - - demuxer_->Seek(base::TimeDelta::FromSeconds(0), - NewExpectedStatusCB(PIPELINE_OK)); + EXPECT_FALSE(demuxer_->AppendData(info_tracks.get(), info_tracks_size)); } static void OnReadDone(const base::TimeDelta& expected_time, @@ -444,6 +455,7 @@ TEST_F(ChunkDemuxerTest, TestInvalidBlockSequences) { // Test the case where a cluster is passed to AppendData() before // INFO & TRACKS data. TEST_F(ChunkDemuxerTest, TestClusterBeforeInfoTracks) { + EXPECT_CALL(*client_, DemuxerOpened(_)); demuxer_->Init(NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_OPEN)); ClusterBuilder cb; @@ -454,4 +466,11 @@ TEST_F(ChunkDemuxerTest, TestClusterBeforeInfoTracks) { EXPECT_FALSE(demuxer_->AppendData(cluster->data(), cluster->size())); } + +// Test cases where we get an EndOfStream() call during initialization. +TEST_F(ChunkDemuxerTest, TestEOSDuringInit) { + EXPECT_CALL(*client_, DemuxerOpened(_)); + demuxer_->Init(NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_OPEN)); + demuxer_->EndOfStream(PIPELINE_OK); +} } // namespace media diff --git a/media/media.gyp b/media/media.gyp index df86e03..d024f57 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -152,6 +152,7 @@ 'filters/bitstream_converter.h', 'filters/chunk_demuxer.cc', 'filters/chunk_demuxer.h', + 'filters/chunk_demuxer_client.h', 'filters/chunk_demuxer_factory.cc', 'filters/chunk_demuxer_factory.h', 'filters/decoder_base.h', |