Chromium Code Reviews| Index: media/mojo/services/mojo_renderer_service.cc |
| diff --git a/media/mojo/services/mojo_renderer_service.cc b/media/mojo/services/mojo_renderer_service.cc |
| index e9eee8e446a4a3319a943615da8e43dc32abf780..83dde742f016d99413a84a7532542a8ac46453ff 100644 |
| --- a/media/mojo/services/mojo_renderer_service.cc |
| +++ b/media/mojo/services/mojo_renderer_service.cc |
| @@ -12,7 +12,9 @@ |
| #include "media/base/audio_renderer_sink.h" |
| #include "media/base/decryptor.h" |
| #include "media/base/media_log.h" |
| +#include "media/base/video_renderer.h" |
| #include "media/filters/audio_renderer_impl.h" |
| +#include "media/filters/renderer_impl.h" |
| #include "media/mojo/services/mojo_demuxer_stream_adapter.h" |
| #include "media/mojo/services/renderer_config.h" |
| #include "mojo/application/application_runner_chromium.h" |
| @@ -31,9 +33,29 @@ static void LogMediaSourceError(const scoped_refptr<MediaLog>& media_log, |
| media_log->AddEvent(media_log->CreateMediaSourceErrorEvent(error)); |
| } |
| -static base::TimeDelta TimeUpdateInterval() { |
| - return base::TimeDelta::FromMilliseconds(kTimeUpdateIntervalMs); |
| -} |
| +// Shim DemuxerStreamProvider wrapper for a single DemuxerStream. |
| +// TODO(dalecurtis): Once we support more than one DemuxerStream we'll need a |
| +// more complicated shim which can handle a mojo::Array<DemuxerStream>. |
| +class DemuxerStreamProviderShim : public DemuxerStreamProvider { |
| + public: |
| + DemuxerStreamProviderShim(scoped_ptr<MojoDemuxerStreamAdapter> stream) |
| + : stream_(stream.Pass()) {} |
| + |
| + ~DemuxerStreamProviderShim() override {} |
| + |
| + DemuxerStream* GetStream(DemuxerStream::Type type) override { |
| + return type != stream_->type() ? nullptr : stream_.get(); |
| + }; |
| + |
| + Liveness GetLiveness() const override { |
| + return DemuxerStreamProvider::LIVENESS_UNKNOWN; |
| + } |
| + |
| + private: |
| + scoped_ptr<MojoDemuxerStreamAdapter> stream_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DemuxerStreamProviderShim); |
| +}; |
| class MojoRendererApplication |
| : public mojo::ApplicationDelegate, |
| @@ -53,225 +75,149 @@ class MojoRendererApplication |
| } |
| }; |
| -// TODO(xhwang): This class looks insanely similar to RendererImpl. We should |
| -// really host a Renderer in this class instead of a AudioRenderer. |
| +static void MojoTrampoline(const mojo::Closure& closure) { |
| + closure.Run(); |
| +} |
| MojoRendererService::MojoRendererService( |
| mojo::ApplicationConnection* connection) |
| : state_(STATE_UNINITIALIZED), |
|
xhwang
2014/10/28 22:43:28
You still need to initialize last_media_time_
DaleCurtis
2014/10/29 00:11:19
Done.
|
| - time_source_(NULL), |
| - time_ticking_(false), |
| - ended_(false), |
| weak_factory_(this), |
| weak_this_(weak_factory_.GetWeakPtr()) { |
| DVLOG(1) << __FUNCTION__; |
| - scoped_refptr<base::SingleThreadTaskRunner> runner( |
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner( |
| base::MessageLoop::current()->task_runner()); |
| scoped_refptr<MediaLog> media_log(new MediaLog()); |
| RendererConfig* renderer_config = RendererConfig::Get(); |
| audio_renderer_sink_ = renderer_config->GetAudioRendererSink(); |
| - audio_renderer_.reset(new AudioRendererImpl( |
| - runner, |
| + scoped_ptr<AudioRenderer> audio_renderer(new AudioRendererImpl( |
| + task_runner, |
| audio_renderer_sink_.get(), |
| renderer_config->GetAudioDecoders( |
| - runner, base::Bind(&LogMediaSourceError, media_log)) |
| - .Pass(), |
| + task_runner, |
| + base::Bind(&LogMediaSourceError, media_log)).Pass(), |
| SetDecryptorReadyCB(), |
| renderer_config->GetAudioHardwareConfig(), |
| media_log)); |
| + scoped_ptr<VideoRenderer> video_renderer(nullptr); |
| + |
| + // Create renderer. |
| + renderer_.reset(new RendererImpl( |
| + task_runner, audio_renderer.Pass(), video_renderer.Pass())); |
| } |
| MojoRendererService::~MojoRendererService() { |
| } |
| void MojoRendererService::Initialize(mojo::DemuxerStreamPtr stream, |
| - const mojo::Callback<void()>& callback) { |
| + const mojo::Closure& callback) { |
| DVLOG(1) << __FUNCTION__; |
| - DCHECK_EQ(state_, STATE_UNINITIALIZED) << state_; |
| + DCHECK_EQ(state_, STATE_UNINITIALIZED); |
| DCHECK(client()); |
| - init_cb_ = callback; |
| state_ = STATE_INITIALIZING; |
| - stream_.reset(new MojoDemuxerStreamAdapter( |
| - stream.Pass(), |
| - base::Bind(&MojoRendererService::OnStreamReady, weak_this_))); |
| + stream_provider_.reset(new DemuxerStreamProviderShim( |
| + make_scoped_ptr(new MojoDemuxerStreamAdapter( |
| + stream.Pass(), |
| + base::Bind(&MojoRendererService::OnStreamReady, |
| + weak_this_, |
| + callback))).Pass())); |
| } |
| -void MojoRendererService::Flush(const mojo::Callback<void()>& callback) { |
| +void MojoRendererService::Flush(const mojo::Closure& callback) { |
| DVLOG(2) << __FUNCTION__; |
| - DCHECK_EQ(state_, STATE_PLAYING) << state_; |
| + DCHECK_EQ(state_, STATE_PLAYING); |
| state_ = STATE_FLUSHING; |
| - if (time_ticking_) |
| - PausePlayback(); |
| - |
| - // TODO(xhwang): This is not completed. Finish the flushing path. |
| - NOTIMPLEMENTED(); |
| + renderer_->Flush(base::Bind(&MojoTrampoline, callback)); |
| } |
| void MojoRendererService::StartPlayingFrom(int64_t time_delta_usec) { |
| DVLOG(2) << __FUNCTION__ << ": " << time_delta_usec; |
| - base::TimeDelta time = base::TimeDelta::FromMicroseconds(time_delta_usec); |
| - time_source_->SetMediaTime(time); |
| - audio_renderer_->StartPlaying(); |
| + renderer_->StartPlayingFrom( |
| + base::TimeDelta::FromMicroseconds(time_delta_usec)); |
| + |
| + // TODO(dalecurtis): Time updates shouldn't always tick, but without adding |
| + // a bunch of additional state to this class plus RendererImpl, how to tell? |
| + SchedulePeriodicMediaTimeUpdates(); |
| } |
| void MojoRendererService::SetPlaybackRate(float playback_rate) { |
| DVLOG(2) << __FUNCTION__ << ": " << playback_rate; |
| - |
| - // Playback rate changes are only carried out while playing. |
| - if (state_ != STATE_PLAYING) |
| - return; |
| - |
| - time_source_->SetPlaybackRate(playback_rate); |
| + DCHECK_EQ(state_, STATE_PLAYING); |
| + renderer_->SetPlaybackRate(playback_rate); |
| } |
| void MojoRendererService::SetVolume(float volume) { |
| - if (audio_renderer_) |
| - audio_renderer_->SetVolume(volume); |
| + renderer_->SetVolume(volume); |
| } |
| -void MojoRendererService::OnStreamReady() { |
| - DCHECK_EQ(state_, STATE_INITIALIZING) << state_; |
| - audio_renderer_->Initialize( |
| - stream_.get(), |
| - base::Bind(&MojoRendererService::OnAudioRendererInitializeDone, |
| - weak_this_), |
| +void MojoRendererService::OnStreamReady(const mojo::Closure& callback) { |
| + DCHECK_EQ(state_, STATE_INITIALIZING); |
| + |
| + renderer_->Initialize( |
| + stream_provider_.get(), |
| + base::Bind( |
| + &MojoRendererService::OnRendererInitializeDone, weak_this_, callback), |
| base::Bind(&MojoRendererService::OnUpdateStatistics, weak_this_), |
| - base::Bind(&MojoRendererService::OnBufferingStateChanged, weak_this_), |
| - base::Bind(&MojoRendererService::OnAudioRendererEnded, weak_this_), |
| - base::Bind(&MojoRendererService::OnError, weak_this_)); |
| + base::Bind(&MojoRendererService::OnRendererEnded, weak_this_), |
| + base::Bind(&MojoRendererService::OnError, weak_this_), |
| + base::Bind(&MojoRendererService::OnBufferingStateChanged, weak_this_)); |
| } |
| -void MojoRendererService::OnAudioRendererInitializeDone(PipelineStatus status) { |
| - DVLOG(1) << __FUNCTION__ << ": " << status; |
| - DCHECK_EQ(state_, STATE_INITIALIZING) << state_; |
| +void MojoRendererService::OnRendererInitializeDone( |
| + const mojo::Closure& callback) { |
| + DVLOG(1) << __FUNCTION__; |
| - if (status != PIPELINE_OK) { |
| - state_ = STATE_ERROR; |
| - audio_renderer_.reset(); |
| - client()->OnError(); |
| - init_cb_.Run(); |
| - init_cb_.reset(); |
| - return; |
| + if (state_ == STATE_ERROR) { |
| + renderer_.reset(); |
| + } else { |
| + DCHECK_EQ(state_, STATE_INITIALIZING); |
| + state_ = STATE_PLAYING; |
| } |
| - time_source_ = audio_renderer_->GetTimeSource(); |
| - |
| - state_ = STATE_PLAYING; |
| - init_cb_.Run(); |
| - init_cb_.reset(); |
| + callback.Run(); |
| } |
| void MojoRendererService::OnUpdateStatistics(const PipelineStatistics& stats) { |
| - NOTIMPLEMENTED(); |
| } |
| -void MojoRendererService::UpdateMediaTime() { |
| - uint64_t media_time = time_source_->CurrentMediaTime().InMicroseconds(); |
| +void MojoRendererService::UpdateMediaTime(bool only_if_changed) { |
| + const uint64_t media_time = renderer_->GetMediaTime().InMicroseconds(); |
| + if (only_if_changed && media_time == last_media_time_) |
| + return; |
| + |
| client()->OnTimeUpdate(media_time, media_time); |
| + last_media_time_ = media_time; |
| } |
| void MojoRendererService::SchedulePeriodicMediaTimeUpdates() { |
| - // Update media time immediately. |
| - UpdateMediaTime(); |
| - |
| - // Then setup periodic time update. |
| + UpdateMediaTime(false); |
| time_update_timer_.Start( |
| FROM_HERE, |
| - TimeUpdateInterval(), |
| - base::Bind(&MojoRendererService::UpdateMediaTime, weak_this_)); |
| + base::TimeDelta::FromMilliseconds(kTimeUpdateIntervalMs), |
| + base::Bind(&MojoRendererService::UpdateMediaTime, weak_this_, true)); |
| } |
| void MojoRendererService::OnBufferingStateChanged( |
| - media::BufferingState new_buffering_state) { |
| - DVLOG(2) << __FUNCTION__ << "(" << buffering_state_ << ", " |
| - << new_buffering_state << ") "; |
| - bool was_waiting_for_enough_data = WaitingForEnoughData(); |
| - |
| - buffering_state_ = new_buffering_state; |
| - |
| - // Renderer underflowed. |
| - if (!was_waiting_for_enough_data && WaitingForEnoughData()) { |
| - PausePlayback(); |
| - // TODO(xhwang): Notify client of underflow condition. |
| - return; |
| - } |
| - |
| - // Renderer prerolled. |
| - if (was_waiting_for_enough_data && !WaitingForEnoughData()) { |
| - StartPlayback(); |
| - client()->OnBufferingStateChange( |
| - static_cast<mojo::BufferingState>(new_buffering_state)); |
| - return; |
| - } |
| + BufferingState new_buffering_state) { |
| + DVLOG(2) << __FUNCTION__ << "(" << new_buffering_state << ") "; |
| + client()->OnBufferingStateChange( |
| + static_cast<mojo::BufferingState>(new_buffering_state)); |
| } |
| -void MojoRendererService::OnAudioRendererEnded() { |
| +void MojoRendererService::OnRendererEnded() { |
| DVLOG(1) << __FUNCTION__; |
| - |
| - if (state_ != STATE_PLAYING) |
| - return; |
| - |
| - DCHECK(!ended_); |
| - ended_ = true; |
| - |
| - if (time_ticking_) |
| - PausePlayback(); |
| - |
| client()->OnEnded(); |
| + time_update_timer_.Reset(); |
| } |
| void MojoRendererService::OnError(PipelineStatus error) { |
| - client()->OnError(); |
| -} |
| - |
| -bool MojoRendererService::WaitingForEnoughData() const { |
| - DCHECK(audio_renderer_); |
| - |
| - return state_ == STATE_PLAYING && buffering_state_ != BUFFERING_HAVE_ENOUGH; |
| -} |
| - |
| -void MojoRendererService::StartPlayback() { |
| DVLOG(1) << __FUNCTION__; |
| - DCHECK_EQ(state_, STATE_PLAYING); |
| - DCHECK(!time_ticking_); |
| - DCHECK(!WaitingForEnoughData()); |
| - |
| - time_ticking_ = true; |
| - time_source_->StartTicking(); |
| - |
| - SchedulePeriodicMediaTimeUpdates(); |
| -} |
| - |
| -void MojoRendererService::PausePlayback() { |
| - DVLOG(1) << __FUNCTION__; |
| - DCHECK(time_ticking_); |
| - switch (state_) { |
| - case STATE_PLAYING: |
| - DCHECK(ended_ || WaitingForEnoughData()) |
| - << "Playback should only pause due to ending or underflowing"; |
| - break; |
| - |
| - case STATE_FLUSHING: |
| - // It's OK to pause playback when flushing. |
| - break; |
| - |
| - case STATE_UNINITIALIZED: |
| - case STATE_INITIALIZING: |
| - case STATE_ERROR: |
| - NOTREACHED() << "Invalid state: " << state_; |
| - break; |
| - } |
| - |
| - time_ticking_ = false; |
| - time_source_->StopTicking(); |
| - |
| - // Cancel repeating time update timer and update the current media time. |
| - time_update_timer_.Stop(); |
| - UpdateMediaTime(); |
| + state_ = STATE_ERROR; |
| + client()->OnError(); |
| } |
| } // namespace media |