| 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..4987751b3b4e63bbb1da6ba1ca7d37bbcacb346c 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,148 @@ 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),
|
| - time_source_(NULL),
|
| - time_ticking_(false),
|
| - ended_(false),
|
| + last_media_time_usec_(0),
|
| 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();
|
| + time_update_timer_.Reset();
|
| + 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));
|
| + 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 force) {
|
| + const uint64_t media_time = renderer_->GetMediaTime().InMicroseconds();
|
| + if (!force && media_time == last_media_time_usec_)
|
| + return;
|
| +
|
| client()->OnTimeUpdate(media_time, media_time);
|
| + last_media_time_usec_ = media_time;
|
| }
|
|
|
| void MojoRendererService::SchedulePeriodicMediaTimeUpdates() {
|
| - // Update media time immediately.
|
| - UpdateMediaTime();
|
| -
|
| - // Then setup periodic time update.
|
| + UpdateMediaTime(true);
|
| time_update_timer_.Start(
|
| FROM_HERE,
|
| - TimeUpdateInterval(),
|
| - base::Bind(&MojoRendererService::UpdateMediaTime, weak_this_));
|
| + base::TimeDelta::FromMilliseconds(kTimeUpdateIntervalMs),
|
| + base::Bind(&MojoRendererService::UpdateMediaTime, weak_this_, false));
|
| }
|
|
|
| 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
|
|
|