diff options
-rw-r--r-- | media/base/media_format.cc | 4 | ||||
-rw-r--r-- | media/base/media_format.h | 2 | ||||
-rw-r--r-- | media/filters/adaptive_demuxer.cc | 397 | ||||
-rw-r--r-- | media/filters/adaptive_demuxer.h | 135 | ||||
-rw-r--r-- | media/filters/adaptive_demuxer_unittest.cc | 74 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer.h | 4 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer_factory.h | 3 | ||||
-rw-r--r-- | media/media.gyp | 3 | ||||
-rw-r--r-- | media/tools/player_wtl/movie.cc | 6 | ||||
-rw-r--r-- | media/tools/player_x11/player_x11.cc | 7 |
10 files changed, 628 insertions, 7 deletions
diff --git a/media/base/media_format.cc b/media/base/media_format.cc index 45df6b6..242cfef 100644 --- a/media/base/media_format.cc +++ b/media/base/media_format.cc @@ -86,4 +86,8 @@ void MediaFormat::ReleaseValue(const std::string& key) { } } +bool MediaFormat::operator==(MediaFormat const& other) const { + return value_map_ == other.value_map_; +} + } // namespace media diff --git a/media/base/media_format.h b/media/base/media_format.h index 781afd4..419488b 100644 --- a/media/base/media_format.h +++ b/media/base/media_format.h @@ -52,6 +52,8 @@ class MediaFormat { bool GetAsReal(const std::string& key, double* out_value) const; bool GetAsString(const std::string& key, std::string* out_value) const; + bool operator==(MediaFormat const& other) const; + private: // Helper to return a value. Value* GetValue(const std::string& key) const; diff --git a/media/filters/adaptive_demuxer.cc b/media/filters/adaptive_demuxer.cc new file mode 100644 index 0000000..a533b4a --- /dev/null +++ b/media/filters/adaptive_demuxer.cc @@ -0,0 +1,397 @@ +// 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. + +#include "base/logging.h" +#include "base/string_number_conversions.h" +#include "base/string_split.h" +#include "base/string_util.h" +#include "media/filters/adaptive_demuxer.h" + +namespace media { + +// +// AdaptiveDemuxerStream +// +AdaptiveDemuxerStream::AdaptiveDemuxerStream( + StreamVector const& streams, int initial_stream) + : streams_(streams), current_stream_index_(initial_stream), + bitstream_converter_enabled_(false) { + DCheckSanity(); +} + +void AdaptiveDemuxerStream::DCheckSanity() { + // We only carry out sanity checks in debug mode. + if (!logging::DEBUG_MODE) + return; + DCHECK(streams_[current_stream_index_].get()); + + bool non_null_stream_seen = false; + Type type; + const MediaFormat* media_format; + for (size_t i = 0; i < streams_.size(); ++i) { + if (!streams_[i]) + continue; + + if (!non_null_stream_seen) { + non_null_stream_seen = true; + type = streams_[i]->type(); + media_format = &streams_[i]->media_format(); + } else { + DCHECK_EQ(streams_[i]->type(), type); + DCHECK(streams_[i]->media_format() == *media_format); + } + } +} + +AdaptiveDemuxerStream::~AdaptiveDemuxerStream() { + base::AutoLock auto_lock(lock_); + current_stream_index_ = -1; + streams_.clear(); +} + +DemuxerStream* AdaptiveDemuxerStream::current_stream() { + base::AutoLock auto_lock(lock_); + return streams_[current_stream_index_]; +} + +void AdaptiveDemuxerStream::Read(Callback1<Buffer*>::Type* read_callback) { + current_stream()->Read(read_callback); +} + +AVStream* AdaptiveDemuxerStream::GetAVStream() { + return current_stream()->GetAVStream(); +} + +DemuxerStream::Type AdaptiveDemuxerStream::type() { + return current_stream()->type(); +} + +const MediaFormat& AdaptiveDemuxerStream::media_format() { + return current_stream()->media_format(); +} + +void AdaptiveDemuxerStream::EnableBitstreamConverter() { + { + base::AutoLock auto_lock(lock_); + bitstream_converter_enabled_ = true; + } + current_stream()->EnableBitstreamConverter(); +} + +void AdaptiveDemuxerStream::ChangeCurrentStream(int index) { + bool needs_bitstream_converter_enabled; + { + base::AutoLock auto_lock(lock_); + current_stream_index_ = index; + DCHECK(streams_[current_stream_index_]); + needs_bitstream_converter_enabled = bitstream_converter_enabled_; + } + if (needs_bitstream_converter_enabled) + EnableBitstreamConverter(); +} + +// +// AdaptiveDemuxer +// + +AdaptiveDemuxer::AdaptiveDemuxer(DemuxerVector const& demuxers, + int initial_audio_demuxer_index, + int initial_video_demuxer_index) + : demuxers_(demuxers), + current_audio_demuxer_index_(initial_audio_demuxer_index), + current_video_demuxer_index_(initial_video_demuxer_index) { + DCHECK(!demuxers_.empty()); + DCHECK_GE(current_audio_demuxer_index_, -1); + DCHECK_GE(current_video_demuxer_index_, -1); + DCHECK_LT(current_audio_demuxer_index_, static_cast<int>(demuxers_.size())); + DCHECK_LT(current_video_demuxer_index_, static_cast<int>(demuxers_.size())); + DCHECK(current_audio_demuxer_index_ != -1 || + current_video_demuxer_index_ != -1); + AdaptiveDemuxerStream::StreamVector audio_streams, video_streams; + for (size_t i = 0; i < demuxers_.size(); ++i) { + audio_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::AUDIO)); + video_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::VIDEO)); + } + if (current_audio_demuxer_index_ >= 0) { + audio_stream_ = new AdaptiveDemuxerStream( + audio_streams, current_audio_demuxer_index_); + } + if (current_video_demuxer_index_ >= 0) { + video_stream_ = new AdaptiveDemuxerStream( + video_streams, current_video_demuxer_index_); + } + + // TODO(fischman): any streams in the underlying demuxers that aren't being + // consumed currently need to be sent to /dev/null or else FFmpegDemuxer will + // hold data for them in memory, waiting for them to get drained by a + // non-existent decoder. +} + +AdaptiveDemuxer::~AdaptiveDemuxer() {} + +void AdaptiveDemuxer::ChangeCurrentDemuxer(int audio_index, int video_index) { + // TODO(fischman): this is currently broken because when a new Demuxer is to + // be used we need to set_host(host()) it, and we need to set_host(NULL) the + // current Demuxer if it's no longer being used. + // TODO(fischman): remember to Stop active demuxers that are being abandoned. + base::AutoLock auto_lock(lock_); + current_audio_demuxer_index_ = audio_index; + current_video_demuxer_index_ = video_index; + if (audio_stream_) + audio_stream_->ChangeCurrentStream(audio_index); + if (video_stream_) + video_stream_->ChangeCurrentStream(video_index); +} + +Demuxer* AdaptiveDemuxer::current_demuxer(DemuxerStream::Type type) { + base::AutoLock auto_lock(lock_); + switch (type) { + case DemuxerStream::AUDIO: + return (current_audio_demuxer_index_ < 0) ? NULL : + demuxers_[current_audio_demuxer_index_]; + case DemuxerStream::VIDEO: + return (current_video_demuxer_index_ < 0) ? NULL : + demuxers_[current_video_demuxer_index_]; + default: + LOG(DFATAL) << "Unexpected type: " << type; + return NULL; + } +} + +// Helper class that wraps a FilterCallback and expects to get called a set +// number of times, after which the wrapped callback is fired (and deleted). +class CountingCallback { + public: + CountingCallback(int count, FilterCallback* orig_cb) + : remaining_count_(count), orig_cb_(orig_cb) { + DCHECK_GT(remaining_count_, 0); + DCHECK(orig_cb); + } + + FilterCallback* GetACallback() { + return NewCallback(this, &CountingCallback::OnChildCallbackDone); + } + + private: + void OnChildCallbackDone() { + bool fire_orig_cb = false; + { + base::AutoLock auto_lock(lock_); + if (--remaining_count_ == 0) + fire_orig_cb = true; + } + if (fire_orig_cb) { + orig_cb_->Run(); + delete this; + } + } + + base::Lock lock_; + int remaining_count_; + scoped_ptr<FilterCallback> orig_cb_; +}; + +void AdaptiveDemuxer::Stop(FilterCallback* callback) { + Demuxer* audio = current_demuxer(DemuxerStream::AUDIO); + Demuxer* video = current_demuxer(DemuxerStream::VIDEO); + int count = (audio ? 1 : 0) + (video && audio != video ? 1 : 0); + CountingCallback* wrapper = new CountingCallback(count, callback); + if (audio) + audio->Stop(wrapper->GetACallback()); + if (video && audio != video) + video->Stop(wrapper->GetACallback()); +} + +void AdaptiveDemuxer::Seek(base::TimeDelta time, FilterCallback* callback) { + Demuxer* audio = current_demuxer(DemuxerStream::AUDIO); + Demuxer* video = current_demuxer(DemuxerStream::VIDEO); + int count = (audio ? 1 : 0) + (video && audio != video ? 1 : 0); + CountingCallback* wrapper = new CountingCallback(count, callback); + if (audio) + audio->Seek(time, wrapper->GetACallback()); + if (video && audio != video) + video->Seek(time, wrapper->GetACallback()); +} + +void AdaptiveDemuxer::OnAudioRendererDisabled() { + Demuxer* audio = current_demuxer(DemuxerStream::AUDIO); + Demuxer* video = current_demuxer(DemuxerStream::VIDEO); + if (audio) audio->OnAudioRendererDisabled(); + if (video && audio != video) video->OnAudioRendererDisabled(); + // TODO(fischman): propagate to other demuxers if/when they're selected. +} + +void AdaptiveDemuxer::set_host(FilterHost* filter_host) { + Demuxer* audio = current_demuxer(DemuxerStream::AUDIO); + Demuxer* video = current_demuxer(DemuxerStream::VIDEO); + if (audio) audio->set_host(filter_host); + if (video && audio != video) video->set_host(filter_host); +} + +scoped_refptr<DemuxerStream> AdaptiveDemuxer::GetStream( + DemuxerStream::Type type) { + switch (type) { + case DemuxerStream::AUDIO: + return audio_stream_; + case DemuxerStream::VIDEO: + return video_stream_; + default: + LOG(DFATAL) << "Unexpected type " << type; + return NULL; + } +} + +// +// AdaptiveDemuxerFactory +// + +AdaptiveDemuxerFactory::AdaptiveDemuxerFactory( + DemuxerFactory* delegate_factory) + : delegate_factory_(delegate_factory) { + DCHECK(delegate_factory); +} + +AdaptiveDemuxerFactory::~AdaptiveDemuxerFactory() {} + +DemuxerFactory* AdaptiveDemuxerFactory::Clone() const { + return new AdaptiveDemuxerFactory(delegate_factory_->Clone()); +} + +// See AdaptiveDemuxerFactory's class-level comment for |url|'s format. +bool ParseAdaptiveUrl( + const std::string& url, int* audio_index, int* video_index, + std::vector<std::string>* urls) { + urls->clear(); + + if (url.empty()) + return false; + if (!StartsWithASCII(url, "x-adaptive:", false)) { + return ParseAdaptiveUrl( + "x-adaptive:0:0:" + url, audio_index, video_index, urls); + } + + std::vector<std::string> parts; + base::SplitStringDontTrim(url, ':', &parts); + if (parts.size() < 4 || + parts[0] != "x-adaptive" || + !base::StringToInt(parts[1], audio_index) || + !base::StringToInt(parts[2], video_index) || + *audio_index < -1 || *video_index < -1) { + return false; + } + + std::string::size_type first_url_pos = + parts[0].size() + 1 + parts[1].size() + 1 + parts[2].size() + 1; + std::string urls_str = url.substr(first_url_pos); + if (urls_str.empty()) + return false; + base::SplitStringDontTrim(urls_str, '^', urls); + if (urls->empty() || + *audio_index >= static_cast<int>(urls->size()) || + *video_index >= static_cast<int>(urls->size())) { + return false; + } + + return true; +} + +// Wrapper for a BuildCallback which accumulates the Demuxer's returned by a +// number of DemuxerFactory::Build() calls and eventually constructs an +// AdaptiveDemuxer and returns it to the |orig_cb| (or errors out if any +// individual Demuxer fails construction). +class DemuxerAccumulator { + public: + // Takes ownership of |orig_cb|. + DemuxerAccumulator(int audio_index, int video_index, + int count, DemuxerFactory::BuildCallback* orig_cb) + : audio_index_(audio_index), video_index_(video_index), + remaining_count_(count), orig_cb_(orig_cb), + demuxers_(count, static_cast<Demuxer*>(NULL)), + statuses_(count, PIPELINE_OK) { + DCHECK_GT(remaining_count_, 0); + DCHECK(orig_cb_.get()); + } + + DemuxerFactory::BuildCallback* GetNthCallback(int n) { + return new IndividualCallback(this, n); + } + + private: + // Wrapper for a BuildCallback that can carry one extra piece of data: the + // index of this callback in the original list of outstanding requests. + struct IndividualCallback : public DemuxerFactory::BuildCallback { + IndividualCallback(DemuxerAccumulator* accumulator, int index) + : accumulator_(accumulator), index_(index) {} + + virtual void RunWithParams(const Tuple2<PipelineStatus, Demuxer*>& params) { + accumulator_->Run(index_, params.a, params.b); + } + DemuxerAccumulator* accumulator_; + int index_; + }; + + // When an individual callback is fired, it calls this method. + void Run(int index, PipelineStatus status, Demuxer* demuxer) { + bool fire_orig_cb = false; + { + base::AutoLock auto_lock(lock_); + DCHECK(!demuxers_[index]); + demuxers_[index] = demuxer; + statuses_[index] = status; + if (--remaining_count_ == 0) + fire_orig_cb = true; + } + if (fire_orig_cb) + DoneAccumulating(); + } + + void DoneAccumulating() { + PipelineStatus overall_status = PIPELINE_OK; + for (size_t i = 0; i < statuses_.size(); ++i) { + if (statuses_[i] != PIPELINE_OK) { + overall_status = statuses_[i]; + break; + } + } + if (overall_status == PIPELINE_OK) { + orig_cb_->Run( + PIPELINE_OK, + new AdaptiveDemuxer(demuxers_, audio_index_, video_index_)); + } else { + orig_cb_->Run(overall_status, static_cast<Demuxer*>(NULL)); + } + + delete this; + } + + // Self-delete in DoneAccumulating() only. + ~DemuxerAccumulator() {} + + int audio_index_; + int video_index_; + int remaining_count_; + scoped_ptr<DemuxerFactory::BuildCallback> orig_cb_; + base::Lock lock_; // Guards vectors of results below. + AdaptiveDemuxer::DemuxerVector demuxers_; + std::vector<PipelineStatus> statuses_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(DemuxerAccumulator); +}; + +void AdaptiveDemuxerFactory::Build(const std::string& url, BuildCallback* cb) { + std::vector<std::string> urls; + int audio_index, video_index; + if (!ParseAdaptiveUrl(url, &audio_index, &video_index, &urls)) { + cb->Run(Tuple2<PipelineStatus, Demuxer*>( + DEMUXER_ERROR_COULD_NOT_OPEN, NULL)); + delete cb; + return; + } + DemuxerAccumulator* accumulator = new DemuxerAccumulator( + audio_index, video_index, urls.size(), cb); + for (size_t i = 0; i < urls.size(); ++i) + delegate_factory_->Build(urls[i], accumulator->GetNthCallback(i)); +} + +} // namespace media diff --git a/media/filters/adaptive_demuxer.h b/media/filters/adaptive_demuxer.h new file mode 100644 index 0000000..23cd570 --- /dev/null +++ b/media/filters/adaptive_demuxer.h @@ -0,0 +1,135 @@ +// 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. + +// Implements a Demuxer that can switch among different data sources mid-stream. +// Uses FFmpegDemuxer under the covers, so see the caveats at the top of +// ffmpeg_demuxer.h. + +#ifndef MEDIA_FILTERS_ADAPTIVE_DEMUXER_H_ +#define MEDIA_FILTERS_ADAPTIVE_DEMUXER_H_ + +#include <deque> +#include <vector> + +#include "base/callback.h" +#include "base/synchronization/lock.h" +#include "media/base/buffers.h" +#include "media/base/filter_factories.h" +#include "media/base/filters.h" +#include "media/base/pipeline.h" +#include "media/base/media_format.h" + +namespace media { + +class AdaptiveDemuxer; + +class AdaptiveDemuxerStream : public DemuxerStream { + public: + typedef std::vector<scoped_refptr<DemuxerStream> > StreamVector; + + // Keeps references to the passed-in streams. |streams| must be non-empty and + // all the streams in it must agree on type and media_format (or be NULL). + // |initial_stream| must be a valid index into |streams| and specifies the + // current stream on construction. + AdaptiveDemuxerStream(StreamVector const& streams, int initial_stream); + virtual ~AdaptiveDemuxerStream(); + + // Change the stream to satisfy subsequent Read() requests from. The + // referenced pointer must not be NULL. + void ChangeCurrentStream(int index); + + // DemuxerStream methods. + virtual void Read(Callback1<Buffer*>::Type* read_callback); + virtual Type type(); + virtual const MediaFormat& media_format(); + virtual void EnableBitstreamConverter(); + virtual AVStream* GetAVStream(); + + private: + // Returns a pointer to the current stream. + DemuxerStream* current_stream(); + + // DEBUG_MODE-only CHECK that the data members are in a reasonable state. + void DCheckSanity(); + + StreamVector streams_; + // Guards the members below. Only held for simple variable reads/writes, not + // during async operation. + base::Lock lock_; + int current_stream_index_; + bool bitstream_converter_enabled_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(AdaptiveDemuxerStream); +}; + +class AdaptiveDemuxer : public Demuxer { + public: + typedef std::vector<scoped_refptr<Demuxer> > DemuxerVector; + + // |demuxers| must be non-empty, and the index arguments must be valid indexes + // into |demuxers|, or -1 to indicate no demuxer is serving that type. + AdaptiveDemuxer(DemuxerVector const& demuxers, + int initial_audio_demuxer_index, + int initial_video_demuxer_index); + virtual ~AdaptiveDemuxer(); + + // Change which demuxers the streams will use. + void ChangeCurrentDemuxer(int audio_index, int video_index); + + // Filter implementation. + virtual void Stop(FilterCallback* callback); + virtual void Seek(base::TimeDelta time, FilterCallback* callback); + virtual void OnAudioRendererDisabled(); + virtual void set_host(FilterHost* filter_host); + // TODO(fischman): add support for SetPlaybackRate(). + + // Demuxer implementation. + virtual scoped_refptr<DemuxerStream> GetStream(DemuxerStream::Type type); + + private: + // Returns a pointer to the currently active demuxer of the given type. + Demuxer* current_demuxer(DemuxerStream::Type type); + + DemuxerVector demuxers_; + scoped_refptr<AdaptiveDemuxerStream> audio_stream_; + scoped_refptr<AdaptiveDemuxerStream> video_stream_; + // Guards the members below. Only held for simple variable reads/writes, not + // during async operation. + base::Lock lock_; + int current_audio_demuxer_index_; + int current_video_demuxer_index_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(AdaptiveDemuxer); +}; + +// AdaptiveDemuxerFactory wraps an underlying DemuxerFactory that knows how to +// build demuxers for a single URL, and implements a primitive (for now) version +// of multi-file manifests. The manifest is encoded in the |url| parameter to +// Build() as: +// x-adaptive:<initial_audio_index>:<initial_video_index>:<URL>[^<URL>]* where +// <URL>'s are "real" media URLs which are passed to the underlying +// DemuxerFactory's Build() method individually. For backward-compatibility, +// the manifest URL may also simply be a regular URL in which case an implicit +// "x-adaptive:0:0:" is prepended. +class AdaptiveDemuxerFactory : public DemuxerFactory { + public: + // Takes a reference to |demuxer_factory|. + AdaptiveDemuxerFactory(DemuxerFactory* delegate_factory); + virtual ~AdaptiveDemuxerFactory(); + + // DemuxerFactory methods. + // If any of the underlying Demuxers encounter construction errors, only the + // first one (in manifest order) will get reported back in |cb|. + virtual void Build(const std::string& url, BuildCallback* cb); + virtual DemuxerFactory* Clone() const; + + private: + scoped_ptr<DemuxerFactory> delegate_factory_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(AdaptiveDemuxerFactory); +}; + +} // namespace media + +#endif // MEDIA_FILTERS_ADAPTIVE_DEMUXER_H_ diff --git a/media/filters/adaptive_demuxer_unittest.cc b/media/filters/adaptive_demuxer_unittest.cc new file mode 100644 index 0000000..3d8b31a --- /dev/null +++ b/media/filters/adaptive_demuxer_unittest.cc @@ -0,0 +1,74 @@ +// 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. + +#include "base/string_number_conversions.h" +#include "media/filters/adaptive_demuxer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +bool ParseAdaptiveUrl( + const std::string& url, int* audio_index, int* video_index, + std::vector<std::string>* urls); + +TEST(ParseAdaptiveUrlTest, BackwardsCompatible) { + std::string manifest = "http://youtube.com/video.webm"; + int audio_index; + int video_index; + std::vector<std::string> urls; + ASSERT_TRUE(ParseAdaptiveUrl(manifest, &audio_index, &video_index, &urls)); + EXPECT_EQ(audio_index, 0); + EXPECT_EQ(video_index, 0); + ASSERT_EQ(urls.size(), 1u); + EXPECT_EQ(urls[0], manifest); +} + +TEST(ParseAdaptiveUrlTest, CombinedManifest) { + std::string video1 = "http://youtube.com/video1.webm"; + std::string video2 = "http://youtube.com/video2.webm"; + for (int a = 0; a <= 1; ++a) { + for (int v = 0; v <= 1; ++v) { + std::string manifest = "x-adaptive:" + + base::IntToString(a) + ":" + base::IntToString(v) + ":" + + video1 + "^" + video2; + int audio_index; + int video_index; + std::vector<std::string> urls; + ASSERT_TRUE(ParseAdaptiveUrl( + manifest, &audio_index, &video_index, &urls)); + EXPECT_EQ(audio_index, a); + EXPECT_EQ(video_index, v); + ASSERT_EQ(urls.size(), 2u); + EXPECT_EQ(urls[0], video1); + EXPECT_EQ(urls[1], video2); + } + } +} + +TEST(ParseAdaptiveUrlTest, Errors) { + int audio_index; + int video_index; + std::vector<std::string> urls; + // Indexes are required on x-adaptive URLs. + EXPECT_FALSE(ParseAdaptiveUrl( + "x-adaptive:file.webm", &audio_index, &video_index, &urls)); + // Empty URL list is not allowed. + EXPECT_FALSE(ParseAdaptiveUrl( + "x-adaptive:0:0:", &audio_index, &video_index, &urls)); + EXPECT_FALSE(ParseAdaptiveUrl( + "x-adaptive:0:0", &audio_index, &video_index, &urls)); + // Empty URL is not allowed. + EXPECT_FALSE(ParseAdaptiveUrl( + "", &audio_index, &video_index, &urls)); + // Malformed indexes parameter. + EXPECT_FALSE(ParseAdaptiveUrl( + "x-adaptive:0:file.webm", &audio_index, &video_index, &urls)); + EXPECT_FALSE(ParseAdaptiveUrl( + "x-adaptive::0:file.webm", &audio_index, &video_index, &urls)); + // Invalid index for URL list. + EXPECT_FALSE(ParseAdaptiveUrl( + "x-adaptive:1:0:file.webm", &audio_index, &video_index, &urls)); +} + +} // namespace media diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h index 57e1a3d..9dc5800 100644 --- a/media/filters/ffmpeg_demuxer.h +++ b/media/filters/ffmpeg_demuxer.h @@ -50,8 +50,8 @@ class ScopedPtrAVFree; class FFmpegDemuxerStream : public DemuxerStream { public: - // Maintains a reference to |demuxer| and initializes itself using information - // inside |stream|. + // Keeps a copy of |demuxer| and initializes itself using information + // inside |stream|. Both parameters must outlive |this|. FFmpegDemuxerStream(FFmpegDemuxer* demuxer, AVStream* stream); // Returns true is this stream has pending reads, false otherwise. diff --git a/media/filters/ffmpeg_demuxer_factory.h b/media/filters/ffmpeg_demuxer_factory.h index 2306df5..b467d75 100644 --- a/media/filters/ffmpeg_demuxer_factory.h +++ b/media/filters/ffmpeg_demuxer_factory.h @@ -22,6 +22,7 @@ class FFmpegDemuxerFactory : public DemuxerFactory { MessageLoop* loop); virtual ~FFmpegDemuxerFactory(); + // DemuxerFactory methods. virtual void Build(const std::string& url, BuildCallback* cb); virtual DemuxerFactory* Clone() const; @@ -29,7 +30,7 @@ class FFmpegDemuxerFactory : public DemuxerFactory { scoped_ptr<DataSourceFactory> data_source_factory_; MessageLoop* loop_; // Unowned. - DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxerFactory); + DISALLOW_IMPLICIT_CONSTRUCTORS(FFmpegDemuxerFactory); }; } // namespace media diff --git a/media/media.gyp b/media/media.gyp index 7b560676..c5b457e 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -125,6 +125,8 @@ 'ffmpeg/ffmpeg_common.h', 'ffmpeg/file_protocol.cc', 'ffmpeg/file_protocol.h', + 'filters/adaptive_demuxer.cc', + 'filters/adaptive_demuxer.h', 'filters/audio_file_reader.cc', 'filters/audio_file_reader.h', 'filters/audio_renderer_algorithm_base.cc', @@ -374,6 +376,7 @@ 'base/state_matrix_unittest.cc', 'base/video_frame_unittest.cc', 'base/yuv_convert_unittest.cc', + 'filters/adaptive_demuxer_unittest.cc', 'filters/audio_renderer_algorithm_ola_unittest.cc', 'filters/audio_renderer_base_unittest.cc', 'filters/bitstream_converter_unittest.cc', diff --git a/media/tools/player_wtl/movie.cc b/media/tools/player_wtl/movie.cc index 3090a48..62da5e5 100644 --- a/media/tools/player_wtl/movie.cc +++ b/media/tools/player_wtl/movie.cc @@ -10,6 +10,7 @@ #include "media/base/filter_collection.h" #include "media/base/message_loop_factory_impl.h" #include "media/base/pipeline_impl.h" +#include "media/filters/adaptive_demuxer.h" #include "media/filters/audio_renderer_impl.h" #include "media/filters/ffmpeg_audio_decoder.h" #include "media/filters/ffmpeg_demuxer_factory.h" @@ -18,6 +19,7 @@ #include "media/filters/null_audio_renderer.h" #include "media/tools/player_wtl/wtl_renderer.h" +using media::AdaptiveDemuxerFactory; using media::AudioRendererImpl; using media::FFmpegAudioDecoder; using media::FFmpegDemuxerFactory; @@ -69,8 +71,8 @@ bool Movie::Open(const wchar_t* url, WtlVideoRenderer* video_renderer) { // Create filter collection. scoped_ptr<FilterCollection> collection(new FilterCollection()); - collection->SetDemuxerFactory(new FFmpegDemuxerFactory( - new FileDataSourceFactory(), pipeline_loop)); + collection->SetDemuxerFactory(new AdaptiveDemuxerFactory( + new FFmpegDemuxerFactory(new FileDataSourceFactory(), pipeline_loop))); collection->AddAudioDecoder(new FFmpegAudioDecoder( message_loop_factory_->GetMessageLoop("AudioDecoderThread"))); collection->AddVideoDecoder(new FFmpegVideoDecoder( diff --git a/media/tools/player_x11/player_x11.cc b/media/tools/player_x11/player_x11.cc index 9624330..ea841aa 100644 --- a/media/tools/player_x11/player_x11.cc +++ b/media/tools/player_x11/player_x11.cc @@ -19,6 +19,7 @@ #include "media/base/media_switches.h" #include "media/base/message_loop_factory_impl.h" #include "media/base/pipeline_impl.h" +#include "media/filters/adaptive_demuxer.h" #include "media/filters/audio_renderer_impl.h" #include "media/filters/ffmpeg_audio_decoder.h" #include "media/filters/ffmpeg_demuxer_factory.h" @@ -111,8 +112,10 @@ bool InitPipeline(MessageLoop* message_loop, // Create our filter factories. scoped_ptr<media::FilterCollection> collection( new media::FilterCollection()); - collection->SetDemuxerFactory(new media::FFmpegDemuxerFactory( - new media::FileDataSourceFactory(), message_loop)); + collection->SetDemuxerFactory( + new media::AdaptiveDemuxerFactory( + new media::FFmpegDemuxerFactory( + new media::FileDataSourceFactory(), message_loop))); collection->AddAudioDecoder(new media::FFmpegAudioDecoder( message_loop_factory->GetMessageLoop("AudioDecoderThread"))); if (CommandLine::ForCurrentProcess()->HasSwitch( |