Chromium Code Reviews| Index: media/filters/adaptive_demuxer.cc |
| diff --git a/media/filters/adaptive_demuxer.cc b/media/filters/adaptive_demuxer.cc |
| index 5599b15c3b0eb9b7d61d28f1c40d9b6a8f71cc85..56dd3c9eefe227a89dadf3c81cd92747ef6044e3 100644 |
| --- a/media/filters/adaptive_demuxer.cc |
| +++ b/media/filters/adaptive_demuxer.cc |
| @@ -4,20 +4,172 @@ |
| #include "base/bind.h" |
| #include "base/logging.h" |
| +#include "base/message_loop.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_split.h" |
| #include "base/string_util.h" |
| +#include "media/ffmpeg/ffmpeg_common.h" |
| #include "media/filters/adaptive_demuxer.h" |
| namespace media { |
| +static const int64 kSwitchTimerPeriod = 5000; // In milliseconds. |
| + |
| +// Object that decides when to switch streams. |
| +class StreamSwitchManager |
| + : public base::RefCountedThreadSafe<StreamSwitchManager> { |
| + public: |
| + StreamSwitchManager(); |
| + virtual ~StreamSwitchManager(); |
| + |
| + void Init(AdaptiveDemuxer* demuxer); |
| + |
| + // Playback events. These methods are called when playback starts, pauses, or |
| + // stops. |
| + void Play(); |
| + void Pause(); |
| + void Stop(); |
| + |
| + private: |
| + // Method called periodically to determine if we should switch |
| + // streams. |
| + void OnSwitchTimer(); |
| + |
| + // Called when the demuxer completes a stream switch. |
| + void OnSwitchDone(PipelineStatus status); |
| + |
| + // Helper method that schedules OnSwitchTimer() to be called. |
| + void StartSwitchTimer(); |
| + |
| + // The demuxer that owns this object. |
| + AdaptiveDemuxer* demuxer_; |
| + |
| + // Message loop this object runs on. |
| + MessageLoop* message_loop_; |
| + |
| + // Is clip playing or not? |
| + bool playing_; |
| + |
| + // Is a stream switch in progress? |
| + bool switch_pending_; |
| + |
| + // Has an OnSwitchTimer() call been scheduled? |
| + bool switch_timer_running_; |
| + |
| + // The stream IDs for the video streams. |
| + AdaptiveDemuxer::StreamIdVector video_ids_; |
| + |
| + // An index into |video_ids_| for the current video stream. |
| + int current_id_index_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(StreamSwitchManager); |
| +}; |
| + |
| +StreamSwitchManager::StreamSwitchManager() |
| + : demuxer_(NULL), |
| + playing_(false), |
| + switch_pending_(false), |
| + switch_timer_running_(false), |
| + current_id_index_(-1) { |
| +} |
| + |
| +StreamSwitchManager::~StreamSwitchManager() {} |
| + |
| +void StreamSwitchManager::Init(AdaptiveDemuxer* demuxer) { |
| + DCHECK(demuxer); |
| + message_loop_ = MessageLoop::current(); |
| + demuxer_ = demuxer; |
| + video_ids_ = demuxer->GetVideoIds(); |
| + current_id_index_ = -1; |
| + |
| + if (video_ids_.size() > 0) { |
| + // Find the index in video_ids_ for the current video ID. |
| + int current_id = demuxer->GetCurrentVideoId(); |
| + current_id_index_ = 0; |
| + for (int i = 0; i < video_ids_.size(); i++) { |
| + if (current_id == video_ids_[i]) { |
| + current_id_index_ = i; |
| + break; |
| + } |
| + } |
| + } |
| +} |
| + |
| +void StreamSwitchManager::Play() { |
| + DCHECK_EQ(MessageLoop::current(), message_loop_); |
| + DCHECK(!playing_); |
| + playing_ = true; |
| + |
| + if (video_ids_.size() > 1 && !switch_timer_running_) { |
| + StartSwitchTimer(); |
| + } |
| +} |
| + |
| +void StreamSwitchManager::Pause() { |
| + DCHECK_EQ(MessageLoop::current(), message_loop_); |
| + DCHECK(playing_); |
| + playing_ = false; |
| +} |
| + |
| +void StreamSwitchManager::Stop() { |
| + DCHECK_EQ(MessageLoop::current(), message_loop_); |
| + DCHECK(!playing_); |
| + demuxer_ = NULL; |
| +} |
| + |
| +void StreamSwitchManager::OnSwitchTimer() { |
| + DCHECK_EQ(MessageLoop::current(), message_loop_); |
| + |
| + switch_timer_running_ = false; |
| + |
| + if (!playing_) |
| + return; |
| + |
| + if (!switch_pending_) { |
| + // Select the stream to switch to. For now we are just rotating |
| + // through the available streams. |
| + int new_id_index = (current_id_index_ + 1) % video_ids_.size(); |
| + |
| + // Did we select a new stream? |
| + if (new_id_index != current_id_index_) { |
| + current_id_index_ = new_id_index; |
| + switch_pending_ = true; |
| + demuxer_->ChangeVideoStream(video_ids_[new_id_index], |
| + base::Bind(&StreamSwitchManager::OnSwitchDone, |
| + this)); |
| + } |
| + } |
| + |
| + StartSwitchTimer(); |
| +} |
| + |
| +void StreamSwitchManager::OnSwitchDone(PipelineStatus status) { |
| + if (MessageLoop::current() != message_loop_) { |
|
scherkus (not reviewing)
2011/05/23 23:17:06
nothing actionable right now but some food for tho
acolwell GONE FROM CHROMIUM
2011/05/23 23:46:40
I was going to comment on this when I made the cha
|
| + message_loop_->PostTask( |
| + FROM_HERE, |
| + NewRunnableMethod(this,&StreamSwitchManager::OnSwitchDone, status)); |
| + } |
| + switch_pending_ = false; |
| +} |
| + |
| +void StreamSwitchManager::StartSwitchTimer() { |
| + DCHECK_EQ(MessageLoop::current(), message_loop_); |
| + switch_timer_running_ = true; |
| + message_loop_->PostDelayedTask( |
| + FROM_HERE, NewRunnableMethod(this, &StreamSwitchManager::OnSwitchTimer), |
| + kSwitchTimerPeriod); |
| +} |
| + |
| // |
| // AdaptiveDemuxerStream |
| // |
| AdaptiveDemuxerStream::AdaptiveDemuxerStream( |
| StreamVector const& streams, int initial_stream) |
| : streams_(streams), current_stream_index_(initial_stream), |
| - bitstream_converter_enabled_(false) { |
| + bitstream_converter_enabled_(false), |
| + pending_reads_(0), |
| + switch_index_(-1), |
| + last_buffer_timestamp_(kNoTimestamp) { |
| DCheckSanity(); |
| } |
| @@ -57,7 +209,26 @@ DemuxerStream* AdaptiveDemuxerStream::current_stream() { |
| } |
| void AdaptiveDemuxerStream::Read(const ReadCallback& read_callback) { |
| - current_stream()->Read(read_callback); |
| + DemuxerStream* stream = NULL; |
| + |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + |
| + read_cb_queue_.push_back(read_callback); |
| + |
| + // Check to make sure we aren't doing a stream switch. We only want to |
| + // make calls on |streams_[current_stream_index_]| when we aren't |
| + // in the middle of a stream switch. Since the callback is stored in |
| + // |read_cb_queue_| we will issue the Read() on the new stream once |
| + // the switch has completed. |
| + if (!IsSwitchPending_Locked()) { |
| + stream = streams_[current_stream_index_]; |
| + pending_reads_++; |
| + } |
| + } |
| + |
| + if (stream) |
| + stream->Read(base::Bind(&AdaptiveDemuxerStream::OnReadDone, this)); |
| } |
| AVStream* AdaptiveDemuxerStream::GetAVStream() { |
| @@ -80,16 +251,220 @@ void AdaptiveDemuxerStream::EnableBitstreamConverter() { |
| current_stream()->EnableBitstreamConverter(); |
| } |
| -void AdaptiveDemuxerStream::ChangeCurrentStream(int index) { |
| - bool needs_bitstream_converter_enabled; |
| +void AdaptiveDemuxerStream::OnAdaptiveDemuxerSeek() { |
| + base::AutoLock auto_lock(lock_); |
| + |
| + last_buffer_timestamp_ = kNoTimestamp; |
| + |
| + // TODO Figure out what to do if this happens during a stream switch. |
|
scherkus (not reviewing)
2011/05/23 23:17:06
TODO -> TODO(acolwell):
acolwell GONE FROM CHROMIUM
2011/05/23 23:46:40
Done.
|
| +} |
| + |
| +// This method initiates a stream switch. The diagram below shows the steps |
| +// involved. |
| +// |
| +// +-----------------------+ |
| +// ChangeCurrentStream() -> | Store stream switch | |
| +// | index, seek_function, | |
| +// | and switch_cb. | |
| +// +-----------------------+ |
| +// | |
| +// \|/ |
| +// +-------------------+ Yes |
| +// | Are there pending | -----> (Wait for OnReadDone()) |
| +// | Read()s on the | | |
| +// | current stream? | <-----+ \|/ |
| +// +-------------------+ +--- OnReadDone() |
| +// | No |
| +// \|/ |
| +// StartSwitch() |
| +// | |
| +// \|/ |
| +// +------------------------+ No |
| +// | Have buffer timestamp? |-----+ |
| +// +------------------------+ | |
| +// | Yes | |
| +// \|/ | |
| +// +-----------------------------+ | |
| +// | Seek stream to timestamp | | |
| +// | using switch_seek_function_ | | |
| +// +-----------------------------+ | |
| +// | | |
| +// \|/ | |
| +// OnSwitchSeekDone() <-----+ |
| +// | |
| +// \|/ |
| +// +------------------+ No |
| +// | Seek successful? |----------+ |
| +// +------------------+ | |
| +// | Yes | |
| +// \|/ | |
| +// +------------------------------+ | |
| +// | Update current_stream_index_ | | |
| +// +------------------------------+ | |
| +// | | |
| +// \|/ | |
| +// +---------------------------+ | |
| +// | Call Read() on new stream |<---+ |
| +// | for deferred reads. | |
| +// +---------------------------+ |
| +// | |
| +// \|/ |
| +// switch_cb(OK | ERROR) |
| +// |
| +// NOTE: Any AdaptiveDemuxerStream::Read() calls that occur during the stream |
| +// switch will be deferred until the switch has completed. The callbacks |
| +// will be queued on |read_cb_queue_|, but no Read() will be issued on the |
| +// current stream. |
| +void AdaptiveDemuxerStream::ChangeCurrentStream( |
| + int index, |
| + const SeekFunction& seek_function, |
| + const PipelineStatusCB& switch_cb) { |
| + DCHECK_GE(index, 0); |
| + |
| + PipelineStatusCB error_cb; |
| + PipelineStatus error_status = PIPELINE_ERROR_INVALID_STATE; |
| + |
| + bool start_switch = false; |
| + |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + |
| + DCHECK_LE(index, streams_.size()); |
| + DCHECK(streams_[index].get()); |
| + DCHECK(!IsSwitchPending_Locked()); |
| + |
| + // TODO - Still need to handle the case where the stream has ended. |
| + if (index == current_stream_index_) { |
| + error_cb = switch_cb; |
| + error_status = PIPELINE_OK; |
| + } else { |
| + switch_cb_ = switch_cb; |
| + switch_index_ = index; |
| + switch_seek_function_ = seek_function; |
| + |
| + start_switch = CanStartSwitch_Locked(); |
| + } |
| + } |
| + |
| + if (!error_cb.is_null()) { |
| + error_cb.Run(error_status); |
| + return; |
| + } |
| + |
| + if (start_switch) |
| + StartSwitch(); |
| +} |
| + |
| +void AdaptiveDemuxerStream::OnReadDone(Buffer* buffer) { |
| + ReadCallback read_cb; |
| + bool start_switch = false; |
| + |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + |
| + pending_reads_--; |
| + |
| + DCHECK_GE(pending_reads_, 0); |
| + DCHECK_GE(read_cb_queue_.size(), 0); |
| + |
| + read_cb = read_cb_queue_.front(); |
| + read_cb_queue_.pop_front(); |
| + |
| + if (buffer && !buffer->IsEndOfStream()) |
| + last_buffer_timestamp_ = buffer->GetTimestamp(); |
| + |
| + start_switch = IsSwitchPending_Locked() && CanStartSwitch_Locked(); |
| + } |
| + |
| + if (!read_cb.is_null()) |
| + read_cb.Run(buffer); |
| + |
| + if (start_switch) |
| + StartSwitch(); |
| +} |
| + |
| +bool AdaptiveDemuxerStream::IsSwitchPending_Locked() const { |
| + lock_.AssertAcquired(); |
| + return !switch_cb_.is_null(); |
| +} |
| + |
| +bool AdaptiveDemuxerStream::CanStartSwitch_Locked() const { |
| + lock_.AssertAcquired(); |
| + return (pending_reads_ == 0); |
| +} |
| + |
| +void AdaptiveDemuxerStream::StartSwitch() { |
| + SeekFunction seek_function; |
| + base::TimeDelta seek_point; |
| + |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + DCHECK(IsSwitchPending_Locked()); |
| + DCHECK_EQ(pending_reads_, 0); |
| + DCHECK_GE(switch_index_, 0); |
| + |
| + seek_point = last_buffer_timestamp_; |
| + seek_function = switch_seek_function_; |
| + |
| + // TODO add code to call switch_cb_ if we are at the end of the stream now. |
| + } |
| + |
| + if (seek_point == kNoTimestamp) { |
| + // We haven't seen a buffer so there is no need to seek. Just move on to |
| + // the next stage in the switch process. |
| + OnSwitchSeekDone(PIPELINE_OK, kNoTimestamp); |
| + return; |
| + } |
| + |
| + DCHECK(!seek_function.is_null()); |
| + seek_function.Run(seek_point, |
| + base::Bind(&AdaptiveDemuxerStream::OnSwitchSeekDone, this)); |
| +} |
| + |
| +void AdaptiveDemuxerStream::OnSwitchSeekDone(PipelineStatus status, |
| + base::TimeDelta seek_time) { |
| + DemuxerStream* stream = NULL; |
| + PipelineStatusCB switch_cb; |
| + int reads_to_request = 0; |
| + bool needs_bitstream_converter_enabled = false; |
| + |
| { |
| base::AutoLock auto_lock(lock_); |
| - current_stream_index_ = index; |
| - DCHECK(streams_[current_stream_index_]); |
| - needs_bitstream_converter_enabled = bitstream_converter_enabled_; |
| + |
| + if (status == PIPELINE_OK) { |
| + DCHECK(streams_[switch_index_]); |
| + |
| + current_stream_index_ = switch_index_; |
| + needs_bitstream_converter_enabled = bitstream_converter_enabled_; |
| + } |
| + |
| + // Clear stream switch state. |
| + switch_cb = switch_cb_; |
| + switch_cb_.Reset(); |
| + switch_index_ = -1; |
| + switch_seek_function_.Reset(); |
| + |
| + // Get the number of outstanding Read()s on this object. |
| + reads_to_request = read_cb_queue_.size(); |
| + |
| + DCHECK_EQ(pending_reads_, 0); |
| + pending_reads_ = reads_to_request; |
| + |
| + stream = streams_[current_stream_index_]; |
| } |
| + |
| if (needs_bitstream_converter_enabled) |
| EnableBitstreamConverter(); |
| + |
| + if (stream) { |
| + // Make the Read() calls that were deferred during the stream switch. |
| + for(;reads_to_request > 0; --reads_to_request) |
| + stream->Read(base::Bind(&AdaptiveDemuxerStream::OnReadDone, this)); |
| + } |
| + |
| + // Signal that the stream switch has completed. |
| + if (!switch_cb.is_null()) |
| + switch_cb.Run(status); |
| } |
| // |
| @@ -101,7 +476,10 @@ AdaptiveDemuxer::AdaptiveDemuxer(DemuxerVector const& demuxers, |
| int initial_video_demuxer_index) |
| : demuxers_(demuxers), |
| current_audio_demuxer_index_(initial_audio_demuxer_index), |
| - current_video_demuxer_index_(initial_video_demuxer_index) { |
| + current_video_demuxer_index_(initial_video_demuxer_index), |
| + playback_rate_(0), |
| + switch_pending_(false), |
| + stream_switch_manager_(new StreamSwitchManager()){ |
| DCHECK(!demuxers_.empty()); |
| DCHECK_GE(current_audio_demuxer_index_, -1); |
| DCHECK_GE(current_video_demuxer_index_, -1); |
| @@ -123,6 +501,8 @@ AdaptiveDemuxer::AdaptiveDemuxer(DemuxerVector const& demuxers, |
| video_streams, current_video_demuxer_index_); |
| } |
| + stream_switch_manager_->Init(this); |
| + |
| // 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 |
| @@ -131,17 +511,106 @@ AdaptiveDemuxer::AdaptiveDemuxer(DemuxerVector const& demuxers, |
| AdaptiveDemuxer::~AdaptiveDemuxer() {} |
| -void AdaptiveDemuxer::ChangeCurrentDemuxer(int audio_index, int video_index) { |
| +// Switches the current video stream. The diagram below describes the switch |
| +// process. |
| +// +-------------------------------+ |
| +// ChangeVideoStream() ---> | video_index | |
| +// | == | Yes. |
| +// | current_video_demuxer_index_? |--> done_cb(OK) |
| +// +-------------------------------+ |
| +// | No. |
| +// \|/ |
| +// Call video_stream_->ChangeCurrentStream() |
| +// | |
| +// \|/ |
| +// (Wait for ChangeVideoStreamDone()) |
| +// | |
| +// \|/ |
| +// ChangeVideoStreamDone() |
| +// | |
| +// \|/ |
| +// +------------------------+ No |
| +// | Was switch successful? |---> done_cb(ERROR) |
| +// +------------------------+ |
| +// | |
| +// \|/ |
| +// Update current_video_demuxer_index_ |
| +// & SetPlaybackRate() on new stream. |
| +// | |
| +// \|/ |
| +// done_cb(OK). |
| +// |
| +void AdaptiveDemuxer::ChangeVideoStream(int video_index, |
| + const PipelineStatusCB& done_cb) { |
| // 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. |
| + AdaptiveDemuxerStream* stream = NULL; |
| + bool switching_to_current_stream = false; |
| + AdaptiveDemuxerStream::SeekFunction seek_function; |
| + |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + |
| + DCHECK(video_stream_); |
| + DCHECK(!switch_pending_); |
| + if (current_video_demuxer_index_ == video_index) { |
| + switching_to_current_stream = true; |
| + } else { |
| + stream = video_stream_; |
| + switch_pending_ = true; |
| + seek_function = base::Bind(&AdaptiveDemuxer::StartStreamSwitchSeek, |
| + this, |
| + DemuxerStream::VIDEO, |
| + video_index); |
| + } |
| + } |
| + |
| + if (switching_to_current_stream) { |
| + done_cb.Run(PIPELINE_OK); |
| + return; |
| + } |
| + |
| + DCHECK(stream); |
| + stream->ChangeCurrentStream( |
| + video_index, seek_function, |
| + base::Bind(&AdaptiveDemuxer::ChangeVideoStreamDone, this, video_index, |
| + done_cb)); |
| +} |
| + |
| +int AdaptiveDemuxer::GetCurrentVideoId() const { |
| 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); |
| + return current_video_demuxer_index_; |
| +} |
| + |
| +AdaptiveDemuxer::StreamIdVector AdaptiveDemuxer::GetVideoIds() const { |
| + StreamIdVector ids; |
| + base::AutoLock auto_lock(lock_); |
| + |
| + for (int i = 0; i < demuxers_.size(); i++) { |
| + if (demuxers_[i]->GetStream(DemuxerStream::VIDEO)) |
| + ids.push_back(i); |
| + } |
| + |
| + return ids; |
| +} |
| + |
| +void AdaptiveDemuxer::ChangeVideoStreamDone(int new_stream_index, |
| + const PipelineStatusCB& done_cb, |
| + PipelineStatus status) { |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + |
| + switch_pending_ = false; |
| + |
| + if (status == PIPELINE_OK) { |
| + demuxers_[current_video_demuxer_index_]->SetPlaybackRate(0); |
| + current_video_demuxer_index_ = new_stream_index; |
| + demuxers_[current_video_demuxer_index_]->SetPlaybackRate(playback_rate_); |
| + } |
| + } |
| + |
| + done_cb.Run(status); |
| } |
| Demuxer* AdaptiveDemuxer::current_demuxer(DemuxerStream::Type type) { |
| @@ -241,6 +710,8 @@ class CountingStatusCB : public base::RefCountedThreadSafe<CountingStatusCB> { |
| }; |
| void AdaptiveDemuxer::Stop(FilterCallback* callback) { |
| + stream_switch_manager_->Stop(); |
| + |
| // Stop() must be called on all of the demuxers even though only one demuxer |
| // is actively delivering audio and another one is delivering video. This |
| // just satisfies the contract that all demuxers must have Stop() called on |
| @@ -253,6 +724,13 @@ void AdaptiveDemuxer::Stop(FilterCallback* callback) { |
| } |
| void AdaptiveDemuxer::Seek(base::TimeDelta time, const FilterStatusCB& cb) { |
| + |
| + if (audio_stream_) |
| + audio_stream_->OnAdaptiveDemuxerSeek(); |
| + |
| + if (video_stream_) |
| + video_stream_->OnAdaptiveDemuxerSeek(); |
| + |
| Demuxer* audio = current_demuxer(DemuxerStream::AUDIO); |
| Demuxer* video = current_demuxer(DemuxerStream::VIDEO); |
| int count = (audio ? 1 : 0) + (video && audio != video ? 1 : 0); |
| @@ -276,6 +754,17 @@ void AdaptiveDemuxer::set_host(FilterHost* filter_host) { |
| } |
| void AdaptiveDemuxer::SetPlaybackRate(float playback_rate) { |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + if (playback_rate_ == 0 && playback_rate > 0) { |
| + stream_switch_manager_->Play(); |
| + } else if (playback_rate_ > 0 && playback_rate == 0) { |
| + stream_switch_manager_->Pause(); |
| + } |
| + |
| + playback_rate_ = playback_rate; |
| + } |
| + |
| Demuxer* audio = current_demuxer(DemuxerStream::AUDIO); |
| Demuxer* video = current_demuxer(DemuxerStream::VIDEO); |
| if (audio) audio->SetPlaybackRate(playback_rate); |
| @@ -300,6 +789,96 @@ scoped_refptr<DemuxerStream> AdaptiveDemuxer::GetStream( |
| } |
| } |
| +void AdaptiveDemuxer::StartStreamSwitchSeek( |
| + DemuxerStream::Type type, |
| + int stream_index, |
| + base::TimeDelta seek_time, |
| + const AdaptiveDemuxerStream::SeekFunctionCB& seek_cb) { |
| + DCHECK_GE(stream_index, 0); |
| + DCHECK(!seek_cb.is_null()); |
| + |
| + Demuxer* demuxer = NULL; |
| + base::TimeDelta seek_point; |
| + FilterStatusCB filter_cb; |
| + |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + |
| + demuxer = demuxers_[stream_index]; |
| + |
| + if (GetSeekTimeAfter(demuxer->GetStream(type)->GetAVStream(), seek_time, |
| + &seek_point)) { |
| + // We found a seek point. |
| + filter_cb = base::Bind(&AdaptiveDemuxer::OnStreamSeekDone, this, |
| + seek_point, seek_cb); |
| + } else { |
| + // We didn't find a seek point. Assume we don't have index data for it |
| + // yet. Seek to the specified time to force index data to be loaded. |
| + seek_point = seek_time; |
| + filter_cb = base::Bind(&AdaptiveDemuxer::OnIndexSeekDone, this, |
| + type, stream_index, seek_time, seek_cb); |
| + } |
| + } |
| + |
| + DCHECK(demuxer); |
| + demuxer->Seek(seek_point, filter_cb); |
| +} |
| + |
| +void AdaptiveDemuxer::OnIndexSeekDone( |
| + DemuxerStream::Type type, |
| + int stream_index, |
| + base::TimeDelta seek_time, |
| + const AdaptiveDemuxerStream::SeekFunctionCB& seek_cb, |
| + PipelineStatus status) { |
| + base::TimeDelta seek_point; |
| + FilterStatusCB filter_cb; |
| + |
| + Demuxer* demuxer = NULL; |
| + |
| + if (status != PIPELINE_OK) { |
| + seek_cb.Run(status, base::TimeDelta()); |
| + return; |
| + } |
| + |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + |
| + demuxer = demuxers_[stream_index]; |
| + DCHECK(demuxer); |
| + |
| + // Look for a seek point now that we have index data. |
| + if (GetSeekTimeAfter(demuxer->GetStream(type)->GetAVStream(), seek_time, |
| + &seek_point)) { |
| + filter_cb = base::Bind(&AdaptiveDemuxer::OnStreamSeekDone, this, |
| + seek_point, seek_cb); |
| + } else { |
| + // Failed again to find a seek point. Clear the demuxer so that |
| + // a seek error will be reported. |
| + demuxer = NULL; |
| + } |
| + } |
| + |
| + if (!demuxer) { |
| + seek_cb.Run(PIPELINE_ERROR_INITIALIZATION_FAILED, base::TimeDelta()); |
| + return; |
| + } |
| + |
| + demuxer->Seek(seek_point, filter_cb); |
| +} |
| + |
| +void AdaptiveDemuxer::OnStreamSeekDone( |
| + base::TimeDelta seek_point, |
| + const AdaptiveDemuxerStream::SeekFunctionCB& seek_cb, |
| + PipelineStatus status) { |
| + if (status != PIPELINE_OK) { |
| + seek_cb.Run(status, base::TimeDelta()); |
| + return; |
| + } |
| + |
| + seek_cb.Run(PIPELINE_OK, seek_point); |
| +} |
| + |
| + |
| // |
| // AdaptiveDemuxerFactory |
| // |