| Index: media/filters/chunk_demuxer.cc
|
| diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
|
| index dbf1b031bec9d75e5d5cb2b82521d7c81b707366..5a8ad88db67400a859d337f4afb55c2bca2328b3 100644
|
| --- a/media/filters/chunk_demuxer.cc
|
| +++ b/media/filters/chunk_demuxer.cc
|
| @@ -12,12 +12,12 @@
|
| #include "base/callback_helpers.h"
|
| #include "base/location.h"
|
| #include "base/message_loop/message_loop_proxy.h"
|
| +#include "base/stl_util.h"
|
| #include "media/base/audio_decoder_config.h"
|
| #include "media/base/bind_to_loop.h"
|
| #include "media/base/stream_parser_buffer.h"
|
| #include "media/base/video_decoder_config.h"
|
| #include "media/filters/stream_parser_factory.h"
|
| -#include "media/webm/webm_webvtt_parser.h"
|
|
|
| using base::TimeDelta;
|
|
|
| @@ -35,16 +35,20 @@ class SourceState {
|
| typedef base::Callback<void(
|
| TimeDelta, ChunkDemuxerStream*)> IncreaseDurationCB;
|
|
|
| + typedef base::Callback<void(
|
| + ChunkDemuxerStream*, const TextTrackConfig&)> NewTextTrackCB;
|
| +
|
| SourceState(scoped_ptr<StreamParser> stream_parser, const LogCB& log_cb,
|
| const CreateDemuxerStreamCB& create_demuxer_stream_cb,
|
| const IncreaseDurationCB& increase_duration_cb);
|
|
|
| + ~SourceState();
|
| +
|
| void Init(const StreamParser::InitCB& init_cb,
|
| bool allow_audio,
|
| bool allow_video,
|
| - const StreamParser::NewTextBuffersCB& text_cb,
|
| const StreamParser::NeedKeyCB& need_key_cb,
|
| - const AddTextTrackCB& add_text_track_cb);
|
| + const NewTextTrackCB& new_text_track_cb);
|
|
|
| // Appends new data to the StreamParser.
|
| // Returns true if the data was successfully appended. Returns false if an
|
| @@ -66,6 +70,11 @@ class SourceState {
|
| }
|
| void set_append_window_end(TimeDelta end) { append_window_end_ = end; }
|
|
|
| + void TextStartReturningData();
|
| + void TextAbortReads();
|
| + void TextSeek(TimeDelta seek_time);
|
| + void TextCompletePendingReadIfPossible();
|
| +
|
| private:
|
| // Called by the |stream_parser_| when a new initialization segment is
|
| // encountered.
|
| @@ -73,7 +82,8 @@ class SourceState {
|
| // processing decoder configurations.
|
| bool OnNewConfigs(bool allow_audio, bool allow_video,
|
| const AudioDecoderConfig& audio_config,
|
| - const VideoDecoderConfig& video_config);
|
| + const VideoDecoderConfig& video_config,
|
| + const TextTrackConfigMap& text_configs);
|
|
|
| // Called by the |stream_parser_| at the beginning of a new media segment.
|
| void OnNewMediaSegment();
|
| @@ -91,12 +101,12 @@ class SourceState {
|
| const StreamParser::BufferQueue& video_buffers);
|
|
|
| // Called by the |stream_parser_| when new text buffers have been parsed. It
|
| - // applies |timestamp_offset_| to all buffers in |buffers| and then calls
|
| - // |new_buffers_cb| with the modified buffers.
|
| + // applies |timestamp_offset_| to all buffers in |buffers| and then appends
|
| + // the (modified) buffers to the demuxer stream associated with
|
| + // the track having |text_track_number|.
|
| // Returns true on a successful call. Returns false if an error occured while
|
| // processing the buffers.
|
| - bool OnTextBuffers(const StreamParser::NewTextBuffersCB& new_buffers_cb,
|
| - TextTrack* text_track,
|
| + bool OnTextBuffers(int text_track_number,
|
| const StreamParser::BufferQueue& buffers);
|
|
|
| // Helper function that adds |timestamp_offset_| to each buffer in |buffers|.
|
| @@ -115,6 +125,7 @@ class SourceState {
|
|
|
| CreateDemuxerStreamCB create_demuxer_stream_cb_;
|
| IncreaseDurationCB increase_duration_cb_;
|
| + NewTextTrackCB new_text_track_cb_;
|
|
|
| // The offset to apply to media segment timestamps.
|
| TimeDelta timestamp_offset_;
|
| @@ -142,6 +153,9 @@ class SourceState {
|
| ChunkDemuxerStream* video_;
|
| bool video_needs_keyframe_;
|
|
|
| + typedef std::map<int, ChunkDemuxerStream*> TextStreamMap;
|
| + TextStreamMap text_stream_map_;
|
| +
|
| LogCB log_cb_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(SourceState);
|
| @@ -193,6 +207,7 @@ class ChunkDemuxerStream : public DemuxerStream {
|
| // Returns false if the new config should trigger an error.
|
| bool UpdateAudioConfig(const AudioDecoderConfig& config, const LogCB& log_cb);
|
| bool UpdateVideoConfig(const VideoDecoderConfig& config, const LogCB& log_cb);
|
| + void UpdateTextConfig(const TextTrackConfig& config, const LogCB& log_cb);
|
|
|
| void MarkEndOfStream();
|
| void UnmarkEndOfStream();
|
| @@ -204,6 +219,10 @@ class ChunkDemuxerStream : public DemuxerStream {
|
| virtual AudioDecoderConfig audio_decoder_config() OVERRIDE;
|
| virtual VideoDecoderConfig video_decoder_config() OVERRIDE;
|
|
|
| + // Returns the text track configuration. It is an error to call this method
|
| + // if type() != TEXT.
|
| + TextTrackConfig text_track_config();
|
| +
|
| void set_memory_limit_for_testing(int memory_limit) {
|
| stream_->set_memory_limit_for_testing(memory_limit);
|
| }
|
| @@ -227,7 +246,7 @@ class ChunkDemuxerStream : public DemuxerStream {
|
| bool GetNextBuffer_Locked(DemuxerStream::Status* status,
|
| scoped_refptr<StreamParserBuffer>* buffer);
|
|
|
| - // Specifies the type of the stream (must be AUDIO or VIDEO for now).
|
| + // Specifies the type of the stream.
|
| Type type_;
|
|
|
| scoped_ptr<SourceBufferStream> stream_;
|
| @@ -258,13 +277,20 @@ SourceState::SourceState(scoped_ptr<StreamParser> stream_parser,
|
| DCHECK(!increase_duration_cb_.is_null());
|
| }
|
|
|
| +SourceState::~SourceState() {
|
| + for (TextStreamMap::iterator itr = text_stream_map_.begin();
|
| + itr != text_stream_map_.end(); ++itr) {
|
| + itr->second->Shutdown();
|
| + delete itr->second;
|
| + }
|
| +}
|
| +
|
| void SourceState::Init(const StreamParser::InitCB& init_cb,
|
| bool allow_audio,
|
| bool allow_video,
|
| - const StreamParser::NewTextBuffersCB& text_cb,
|
| const StreamParser::NeedKeyCB& need_key_cb,
|
| - const AddTextTrackCB& add_text_track_cb) {
|
| - StreamParser::NewBuffersCB audio_cb;
|
| + const NewTextTrackCB& new_text_track_cb) {
|
| + new_text_track_cb_ = new_text_track_cb;
|
|
|
| stream_parser_->Init(init_cb,
|
| base::Bind(&SourceState::OnNewConfigs,
|
| @@ -274,9 +300,9 @@ void SourceState::Init(const StreamParser::InitCB& init_cb,
|
| base::Bind(&SourceState::OnNewBuffers,
|
| base::Unretained(this)),
|
| base::Bind(&SourceState::OnTextBuffers,
|
| - base::Unretained(this), text_cb),
|
| + base::Unretained(this)),
|
| need_key_cb,
|
| - add_text_track_cb,
|
| + !new_text_track_cb.is_null(),
|
| base::Bind(&SourceState::OnNewMediaSegment,
|
| base::Unretained(this)),
|
| base::Bind(&SourceState::OnEndOfMediaSegment,
|
| @@ -303,6 +329,35 @@ void SourceState::Abort() {
|
| can_update_offset_ = true;
|
| }
|
|
|
| +
|
| +void SourceState::TextStartReturningData() {
|
| + for (TextStreamMap::iterator itr = text_stream_map_.begin();
|
| + itr != text_stream_map_.end(); ++itr) {
|
| + itr->second->StartReturningData();
|
| + }
|
| +}
|
| +
|
| +void SourceState::TextAbortReads() {
|
| + for (TextStreamMap::iterator itr = text_stream_map_.begin();
|
| + itr != text_stream_map_.end(); ++itr) {
|
| + itr->second->AbortReads();
|
| + }
|
| +}
|
| +
|
| +void SourceState::TextSeek(TimeDelta seek_time) {
|
| + for (TextStreamMap::iterator itr = text_stream_map_.begin();
|
| + itr != text_stream_map_.end(); ++itr) {
|
| + itr->second->Seek(seek_time);
|
| + }
|
| +}
|
| +
|
| +void SourceState::TextCompletePendingReadIfPossible() {
|
| + for (TextStreamMap::iterator itr = text_stream_map_.begin();
|
| + itr != text_stream_map_.end(); ++itr) {
|
| + itr->second->CompletePendingReadIfPossible();
|
| + }
|
| +}
|
| +
|
| void SourceState::AdjustBufferTimestamps(
|
| const StreamParser::BufferQueue& buffers) {
|
| if (timestamp_offset_ == TimeDelta())
|
| @@ -318,7 +373,8 @@ void SourceState::AdjustBufferTimestamps(
|
|
|
| bool SourceState::OnNewConfigs(bool allow_audio, bool allow_video,
|
| const AudioDecoderConfig& audio_config,
|
| - const VideoDecoderConfig& video_config) {
|
| + const VideoDecoderConfig& video_config,
|
| + const TextTrackConfigMap& text_configs) {
|
| DVLOG(1) << "OnNewConfigs(" << allow_audio << ", " << allow_video
|
| << ", " << audio_config.IsValidConfig()
|
| << ", " << video_config.IsValidConfig() << ")";
|
| @@ -377,6 +433,51 @@ bool SourceState::OnNewConfigs(bool allow_audio, bool allow_video,
|
| success &= video_->UpdateVideoConfig(video_config, log_cb_);
|
| }
|
|
|
| + if (text_stream_map_.empty()) {
|
| + for (TextTrackConfigMap::const_iterator itr = text_configs.begin();
|
| + itr != text_configs.end(); ++itr) {
|
| + ChunkDemuxerStream* const text_stream =
|
| + create_demuxer_stream_cb_.Run(DemuxerStream::TEXT);
|
| + text_stream->UpdateTextConfig(itr->second, log_cb_);
|
| + text_stream_map_[itr->first] = text_stream;
|
| + }
|
| + } else {
|
| + const size_t text_count = text_stream_map_.size();
|
| + if (text_configs.size() != text_count) {
|
| + success &= false;
|
| + } else if (text_count == 1) {
|
| + TextTrackConfigMap::const_iterator config_itr = text_configs.begin();
|
| + const TextTrackConfig& new_config = config_itr->second;
|
| + TextStreamMap::iterator stream_itr = text_stream_map_.begin();
|
| + ChunkDemuxerStream* text_stream = stream_itr->second;
|
| + TextTrackConfig old_config = text_stream->text_track_config();
|
| + if (!new_config.Matches(old_config)) {
|
| + success &= false;
|
| + } else {
|
| + text_stream_map_.clear();
|
| + text_stream_map_[config_itr->first] = text_stream;
|
| + }
|
| + } else {
|
| + for (TextTrackConfigMap::const_iterator config_itr = text_configs.begin();
|
| + config_itr != text_configs.end(); ++config_itr) {
|
| + TextStreamMap::iterator stream_itr =
|
| + text_stream_map_.find(config_itr->first);
|
| + if (stream_itr == text_stream_map_.end()) {
|
| + success &= false;
|
| + break;
|
| + }
|
| +
|
| + const TextTrackConfig& new_config = config_itr->second;
|
| + ChunkDemuxerStream* stream = stream_itr->second;
|
| + TextTrackConfig old_config = stream->text_track_config();
|
| + if (!new_config.Matches(old_config)) {
|
| + success &= false;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed");
|
| return success;
|
| }
|
| @@ -432,6 +533,11 @@ bool SourceState::OnNewBuffers(const StreamParser::BufferQueue& audio_buffers,
|
|
|
| if (video_)
|
| video_->OnNewMediaSegment(segment_timestamp);
|
| +
|
| + for (TextStreamMap::iterator itr = text_stream_map_.begin();
|
| + itr != text_stream_map_.end(); ++itr) {
|
| + itr->second->OnNewMediaSegment(segment_timestamp);
|
| + }
|
| }
|
|
|
| if (!filtered_audio.empty()) {
|
| @@ -450,15 +556,17 @@ bool SourceState::OnNewBuffers(const StreamParser::BufferQueue& audio_buffers,
|
| }
|
|
|
| bool SourceState::OnTextBuffers(
|
| - const StreamParser::NewTextBuffersCB& new_buffers_cb,
|
| - TextTrack* text_track,
|
| + int text_track_number,
|
| const StreamParser::BufferQueue& buffers) {
|
| - if (new_buffers_cb.is_null())
|
| + DCHECK(!buffers.empty());
|
| +
|
| + TextStreamMap::iterator itr = text_stream_map_.find(text_track_number);
|
| + if (itr == text_stream_map_.end())
|
| return false;
|
|
|
| AdjustBufferTimestamps(buffers);
|
|
|
| - return new_buffers_cb.Run(text_track, buffers);
|
| + return itr->second->Append(buffers);
|
| }
|
|
|
| void SourceState::FilterWithAppendWindow(
|
| @@ -649,6 +757,15 @@ bool ChunkDemuxerStream::UpdateVideoConfig(const VideoDecoderConfig& config,
|
| return stream_->UpdateVideoConfig(config);
|
| }
|
|
|
| +void ChunkDemuxerStream::UpdateTextConfig(const TextTrackConfig& config,
|
| + const LogCB& log_cb) {
|
| + DCHECK_EQ(type_, TEXT);
|
| + base::AutoLock auto_lock(lock_);
|
| + DCHECK(!stream_);
|
| + DCHECK_EQ(state_, UNINITIALIZED);
|
| + stream_.reset(new SourceBufferStream(config, log_cb));
|
| +}
|
| +
|
| void ChunkDemuxerStream::MarkEndOfStream() {
|
| base::AutoLock auto_lock(lock_);
|
| stream_->MarkEndOfStream();
|
| @@ -685,6 +802,12 @@ VideoDecoderConfig ChunkDemuxerStream::video_decoder_config() {
|
| return stream_->GetCurrentVideoDecoderConfig();
|
| }
|
|
|
| +TextTrackConfig ChunkDemuxerStream::text_track_config() {
|
| + CHECK_EQ(type_, TEXT);
|
| + base::AutoLock auto_lock(lock_);
|
| + return stream_->GetCurrentTextTrackConfig();
|
| +}
|
| +
|
| void ChunkDemuxerStream::ChangeState_Locked(State state) {
|
| lock_.AssertAcquired();
|
| DVLOG(1) << "ChunkDemuxerStream::ChangeState_Locked() : "
|
| @@ -744,14 +867,14 @@ void ChunkDemuxerStream::CompletePendingReadIfPossible_Locked() {
|
|
|
| ChunkDemuxer::ChunkDemuxer(const base::Closure& open_cb,
|
| const NeedKeyCB& need_key_cb,
|
| - const AddTextTrackCB& add_text_track_cb,
|
| + bool enable_text,
|
| const LogCB& log_cb)
|
| : state_(WAITING_FOR_INIT),
|
| cancel_next_seek_(false),
|
| host_(NULL),
|
| open_cb_(open_cb),
|
| need_key_cb_(need_key_cb),
|
| - add_text_track_cb_(add_text_track_cb),
|
| + enable_text_(enable_text),
|
| log_cb_(log_cb),
|
| duration_(kNoTimestamp()),
|
| user_specified_duration_(-1) {
|
| @@ -821,6 +944,7 @@ void ChunkDemuxer::OnAudioRendererDisabled() {
|
|
|
| // Demuxer implementation.
|
| DemuxerStream* ChunkDemuxer::GetStream(DemuxerStream::Type type) {
|
| + DCHECK_NE(type, DemuxerStream::TEXT);
|
| base::AutoLock auto_lock(lock_);
|
| if (type == DemuxerStream::VIDEO)
|
| return video_.get();
|
| @@ -906,13 +1030,19 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
|
| base::Bind(&ChunkDemuxer::IncreaseDurationIfNecessary,
|
| base::Unretained(this))));
|
|
|
| + SourceState::NewTextTrackCB new_text_track_cb;
|
| +
|
| + if (enable_text_) {
|
| + new_text_track_cb = base::Bind(&ChunkDemuxer::OnNewTextTrack,
|
| + base::Unretained(this));
|
| + }
|
| +
|
| source_state->Init(
|
| base::Bind(&ChunkDemuxer::OnSourceInitDone, base::Unretained(this)),
|
| has_audio,
|
| has_video,
|
| - base::Bind(&ChunkDemuxer::OnTextBuffers, base::Unretained(this)),
|
| need_key_cb_,
|
| - add_text_track_cb_);
|
| + new_text_track_cb);
|
|
|
| source_state_map_[id] = source_state.release();
|
| return kOk;
|
| @@ -1342,6 +1472,10 @@ ChunkDemuxer::CreateDemuxerStream(DemuxerStream::Type type) {
|
| video_.reset(new ChunkDemuxerStream(DemuxerStream::VIDEO));
|
| return video_.get();
|
| break;
|
| + case DemuxerStream::TEXT: {
|
| + return new ChunkDemuxerStream(DemuxerStream::TEXT);
|
| + break;
|
| + }
|
| case DemuxerStream::UNKNOWN:
|
| case DemuxerStream::NUM_TYPES:
|
| NOTREACHED();
|
| @@ -1351,30 +1485,11 @@ ChunkDemuxer::CreateDemuxerStream(DemuxerStream::Type type) {
|
| return NULL;
|
| }
|
|
|
| -bool ChunkDemuxer::OnTextBuffers(
|
| - TextTrack* text_track,
|
| - const StreamParser::BufferQueue& buffers) {
|
| +void ChunkDemuxer::OnNewTextTrack(ChunkDemuxerStream* text_stream,
|
| + const TextTrackConfig& config) {
|
| lock_.AssertAcquired();
|
| DCHECK_NE(state_, SHUTDOWN);
|
| -
|
| - // TODO(matthewjheaney): IncreaseDurationIfNecessary
|
| -
|
| - for (StreamParser::BufferQueue::const_iterator itr = buffers.begin();
|
| - itr != buffers.end(); ++itr) {
|
| - const StreamParserBuffer* const buffer = itr->get();
|
| - const TimeDelta start = buffer->timestamp();
|
| - const TimeDelta end = start + buffer->duration();
|
| -
|
| - std::string id, settings, content;
|
| -
|
| - WebMWebVTTParser::Parse(buffer->data(),
|
| - buffer->data_size(),
|
| - &id, &settings, &content);
|
| -
|
| - text_track->addWebVTTCue(start, end, id, content, settings);
|
| - }
|
| -
|
| - return true;
|
| + host_->AddTextStream(text_stream, config);
|
| }
|
|
|
| bool ChunkDemuxer::IsValidId(const std::string& source_id) const {
|
| @@ -1428,6 +1543,11 @@ void ChunkDemuxer::StartReturningData() {
|
|
|
| if (video_)
|
| video_->StartReturningData();
|
| +
|
| + for (SourceStateMap::iterator itr = source_state_map_.begin();
|
| + itr != source_state_map_.end(); ++itr) {
|
| + itr->second->TextStartReturningData();
|
| + }
|
| }
|
|
|
| void ChunkDemuxer::AbortPendingReads() {
|
| @@ -1436,6 +1556,11 @@ void ChunkDemuxer::AbortPendingReads() {
|
|
|
| if (video_)
|
| video_->AbortReads();
|
| +
|
| + for (SourceStateMap::iterator itr = source_state_map_.begin();
|
| + itr != source_state_map_.end(); ++itr) {
|
| + itr->second->TextAbortReads();
|
| + }
|
| }
|
|
|
| void ChunkDemuxer::SeekAllSources(TimeDelta seek_time) {
|
| @@ -1444,6 +1569,11 @@ void ChunkDemuxer::SeekAllSources(TimeDelta seek_time) {
|
|
|
| if (video_)
|
| video_->Seek(seek_time);
|
| +
|
| + for (SourceStateMap::iterator itr = source_state_map_.begin();
|
| + itr != source_state_map_.end(); ++itr) {
|
| + itr->second->TextSeek(seek_time);
|
| + }
|
| }
|
|
|
| void ChunkDemuxer::CompletePendingReadsIfPossible() {
|
| @@ -1452,6 +1582,11 @@ void ChunkDemuxer::CompletePendingReadsIfPossible() {
|
|
|
| if (video_)
|
| video_->CompletePendingReadIfPossible();
|
| +
|
| + for (SourceStateMap::iterator itr = source_state_map_.begin();
|
| + itr != source_state_map_.end(); ++itr) {
|
| + itr->second->TextCompletePendingReadIfPossible();
|
| + }
|
| }
|
|
|
| } // namespace media
|
|
|