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..68bfbd6d8ca6a5e1280f4df78ffdf9b26012c2f2 100644 |
| --- a/media/filters/adaptive_demuxer.cc |
| +++ b/media/filters/adaptive_demuxer.cc |
| @@ -4,20 +4,156 @@ |
| #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 "base/timer.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: |
| + typedef std::vector<int> StreamIdVector; |
| + |
| + StreamSwitchManager(AdaptiveDemuxer* demuxer, |
| + const StreamIdVector& video_ids); |
| + virtual ~StreamSwitchManager(); |
| + |
| + // 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); |
| + |
| + // 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_; |
| + |
| + // Periodic timer that calls OnSwitchTimer(). |
| + base::RepeatingTimer<StreamSwitchManager> timer_; |
| + |
| + // The stream IDs for the video streams. |
| + StreamIdVector video_ids_; |
| + |
| + // An index into |video_ids_| for the current video stream. |
| + int current_id_index_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(StreamSwitchManager); |
| +}; |
| + |
| +StreamSwitchManager::StreamSwitchManager( |
| + AdaptiveDemuxer* demuxer, |
| + const StreamIdVector& video_ids) |
| + : demuxer_(demuxer), |
| + playing_(false), |
| + switch_pending_(false), |
| + video_ids_(video_ids), |
| + current_id_index_(-1) { |
| + DCHECK(demuxer); |
| + message_loop_ = MessageLoop::current(); |
| + demuxer_ = demuxer; |
| + 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; |
| + } |
| + } |
| + } |
| +} |
| + |
| +StreamSwitchManager::~StreamSwitchManager() {} |
| + |
| +void StreamSwitchManager::Play() { |
| + DCHECK_EQ(MessageLoop::current(), message_loop_); |
| + DCHECK(!playing_); |
| + playing_ = true; |
| + |
| + if (video_ids_.size() > 1) { |
| + timer_.Start(base::TimeDelta::FromMilliseconds(kSwitchTimerPeriod), |
| + this, &StreamSwitchManager::OnSwitchTimer); |
| + } |
| +} |
| + |
| +void StreamSwitchManager::Pause() { |
| + DCHECK_EQ(MessageLoop::current(), message_loop_); |
| + DCHECK(playing_); |
| + playing_ = false; |
| + timer_.Stop(); |
| +} |
| + |
| +void StreamSwitchManager::Stop() { |
| + DCHECK_EQ(MessageLoop::current(), message_loop_); |
| + DCHECK(!playing_); |
| + demuxer_ = NULL; |
| +} |
| + |
| +void StreamSwitchManager::OnSwitchTimer() { |
| + DCHECK_EQ(MessageLoop::current(), message_loop_); |
| + |
| + if (!playing_ || !demuxer_ || (video_ids_.size() < 2) || switch_pending_) |
| + return; |
| + |
| + // 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(); |
| + |
| + current_id_index_ = new_id_index; |
| + switch_pending_ = true; |
| + demuxer_->ChangeVideoStream(video_ids_[new_id_index], |
| + base::Bind(&StreamSwitchManager::OnSwitchDone, |
| + this)); |
| +} |
| + |
| +void StreamSwitchManager::OnSwitchDone(PipelineStatus status) { |
| + if (MessageLoop::current() != message_loop_) { |
| + message_loop_->PostTask( |
| + FROM_HERE, |
| + NewRunnableMethod(this, &StreamSwitchManager::OnSwitchDone, status)); |
| + return; |
| + } |
| + |
| + switch_pending_ = false; |
| +} |
| + |
| // |
| // 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 +193,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 +235,216 @@ void AdaptiveDemuxerStream::EnableBitstreamConverter() { |
| current_stream()->EnableBitstreamConverter(); |
| } |
| -void AdaptiveDemuxerStream::ChangeCurrentStream(int index) { |
| - bool needs_bitstream_converter_enabled; |
| +void AdaptiveDemuxerStream::OnAdaptiveDemuxerSeek(base::TimeDelta time) { |
| + base::AutoLock auto_lock(lock_); |
| + |
| + last_buffer_timestamp_ = time; |
| + |
| + // TODO(acolwell): Figure out what to do if this happens during a stream |
| + // switch. |
| +} |
| + |
| +// This method initiates a stream switch. The diagram below shows the steps |
| +// involved. |
| +// |
| +// +-----------------------+ |
| +// ChangeCurrentStream() -> | Store stream switch | |
| +// | index, seek_function, | |
| +// | and switch_cb. | |
| +// +-----------------------+ |
| +// | |
| +// V |
| +// +-------------------+ Yes |
| +// | Are there pending | -----> (Wait for OnReadDone()) |
| +// | Read()s on the | | |
| +// | current stream? | <-----+ V |
| +// +-------------------+ +--- OnReadDone() |
| +// | No |
| +// V |
| +// StartSwitch() |
| +// | |
| +// V |
| +// +------------------------+ No |
| +// | Have buffer timestamp? |-----+ |
| +// +------------------------+ | |
| +// | Yes | |
| +// V | |
| +// +-----------------------------+ | |
| +// | Seek stream to timestamp | | |
| +// | using switch_seek_function_ | | |
| +// +-----------------------------+ | |
| +// | | |
| +// V | |
| +// OnSwitchSeekDone() <-----+ |
| +// | |
| +// V |
| +// +------------------+ No |
| +// | Seek successful? |----------+ |
| +// +------------------+ | |
| +// | Yes | |
| +// V | |
| +// +------------------------------+ | |
| +// | Update current_stream_index_ | | |
| +// +------------------------------+ | |
| +// | | |
| +// V | |
| +// +---------------------------+ | |
| +// | Call Read() on new stream |<---+ |
| +// | for deferred reads. | |
| +// +---------------------------+ |
| +// | |
| +// V |
| +// 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); |
| + |
| + bool start_switch = false; |
| + |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + |
| + DCHECK_LE(index, streams_.size()); |
| + DCHECK(streams_[index].get()); |
| + DCHECK_NE(index, current_stream_index_); |
| + DCHECK(!IsSwitchPending_Locked()); |
| + |
| + // TODO(acolwell): Still need to handle the case where the stream has ended. |
| + |
| + switch_cb_ = switch_cb; |
| + switch_index_ = index; |
| + switch_seek_function_ = seek_function; |
| + |
| + start_switch = CanStartSwitch_Locked(); |
| + } |
| + |
| + 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(); |
| + |
| + if (pending_reads_ == 0) { |
| + DCHECK(IsSwitchPending_Locked()); |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +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(acolwell): 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(kNoTimestamp, PIPELINE_OK); |
| + return; |
| + } |
| + |
| + DCHECK(!seek_function.is_null()); |
| + seek_function.Run(seek_point, |
| + base::Bind(&AdaptiveDemuxerStream::OnSwitchSeekDone, this)); |
| +} |
| + |
| +void AdaptiveDemuxerStream::OnSwitchSeekDone(base::TimeDelta seek_time, |
| + PipelineStatus status) { |
| + 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(); |
| + |
| + DCHECK(stream); |
| + DCHECK(!switch_cb.is_null()); |
| + |
| + // 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. |
| + switch_cb.Run(status); |
| } |
| // |
| @@ -101,7 +456,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_(NULL){ |
|
Ami GONE FROM CHROMIUM
2011/05/25 05:39:01
Unnecessary?
acolwell GONE FROM CHROMIUM
2011/05/25 21:43:46
Done.
|
| DCHECK(!demuxers_.empty()); |
| DCHECK_GE(current_audio_demuxer_index_, -1); |
| DCHECK_GE(current_video_demuxer_index_, -1); |
| @@ -110,9 +468,13 @@ AdaptiveDemuxer::AdaptiveDemuxer(DemuxerVector const& demuxers, |
| DCHECK(current_audio_demuxer_index_ != -1 || |
| current_video_demuxer_index_ != -1); |
| AdaptiveDemuxerStream::StreamVector audio_streams, video_streams; |
| + StreamSwitchManager::StreamIdVector video_ids; |
| for (size_t i = 0; i < demuxers_.size(); ++i) { |
| + DemuxerStream* video = demuxers_[i]->GetStream(DemuxerStream::VIDEO); |
| audio_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::AUDIO)); |
| - video_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::VIDEO)); |
| + video_streams.push_back(video); |
| + if (video) |
| + video_ids.push_back(i); |
| } |
| if (current_audio_demuxer_index_ >= 0) { |
| audio_stream_ = new AdaptiveDemuxerStream( |
| @@ -123,6 +485,7 @@ AdaptiveDemuxer::AdaptiveDemuxer(DemuxerVector const& demuxers, |
| video_streams, current_video_demuxer_index_); |
| } |
| + stream_switch_manager_ = new StreamSwitchManager(this, video_ids); |
|
Ami GONE FROM CHROMIUM
2011/05/25 05:39:01
reset() more idiomatic for scoped_refptr?
acolwell GONE FROM CHROMIUM
2011/05/25 21:43:46
scoped_refptr doesn't have a reset().
|
| // 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 +494,91 @@ 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. |
| +// V |
| +// Call video_stream_->ChangeCurrentStream() |
| +// | |
| +// V |
| +// (Wait for ChangeVideoStreamDone()) |
| +// | |
| +// V |
| +// ChangeVideoStreamDone() |
| +// | |
| +// V |
| +// +------------------------+ No |
| +// | Was switch successful? |---> done_cb(ERROR) |
| +// +------------------------+ |
| +// | |
| +// V |
| +// Update current_video_demuxer_index_ |
| +// & SetPlaybackRate() on new stream. |
| +// | |
| +// V |
| +// 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. |
| + 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 { |
| + 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; |
| + } |
| + |
| + video_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_; |
| +} |
| + |
| +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) { |
| @@ -162,7 +599,7 @@ Demuxer* AdaptiveDemuxer::current_demuxer(DemuxerStream::Type type) { |
| // 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). |
| // |
| -// TODO: Remove this class once Stop() is converted to FilterStatusCB. |
| +// TODO(acolwell): Remove this class once Stop() is converted to FilterStatusCB. |
| class CountingCallback { |
| public: |
| CountingCallback(int count, FilterCallback* orig_cb) |
| @@ -241,18 +678,28 @@ 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 |
| // them before they are destroyed. |
| // |
| - // TODO: Remove CountingCallback once Stop() is converted to FilterStatusCB. |
| + // TODO(acolwell): Remove CountingCallback once Stop() is converted to |
| + // FilterStatusCB. |
| CountingCallback* wrapper = new CountingCallback(demuxers_.size(), callback); |
| for (size_t i = 0; i < demuxers_.size(); ++i) |
| demuxers_[i]->Stop(wrapper->GetACallback()); |
| } |
| void AdaptiveDemuxer::Seek(base::TimeDelta time, const FilterStatusCB& cb) { |
| + |
| + if (audio_stream_) |
| + audio_stream_->OnAdaptiveDemuxerSeek(time); |
| + |
| + if (video_stream_) |
| + video_stream_->OnAdaptiveDemuxerSeek(time); |
| + |
| Demuxer* audio = current_demuxer(DemuxerStream::AUDIO); |
| Demuxer* video = current_demuxer(DemuxerStream::VIDEO); |
| int count = (audio ? 1 : 0) + (video && audio != video ? 1 : 0); |
| @@ -276,6 +723,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 +758,91 @@ 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, |
| + seek_cb, seek_point); |
| + } 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(base::TimeDelta(), status); |
| + 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, |
| + seek_cb, seek_point); |
| + } 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(base::TimeDelta(), PIPELINE_ERROR_INITIALIZATION_FAILED); |
| + return; |
| + } |
| + |
| + demuxer->Seek(seek_point, filter_cb); |
| +} |
| + |
| +// static |
| +void AdaptiveDemuxer::OnStreamSeekDone( |
| + const AdaptiveDemuxerStream::SeekFunctionCB& seek_cb, |
| + base::TimeDelta seek_point, |
| + PipelineStatus status) { |
| + seek_cb.Run(seek_point, status); |
| +} |
| + |
| // |
| // AdaptiveDemuxerFactory |
| // |