Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/mojo/services/mojo_renderer_service.h" | 5 #include "media/mojo/services/mojo_renderer_service.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/callback_helpers.h" | 8 #include "base/callback_helpers.h" |
| 9 #include "base/memory/scoped_vector.h" | 9 #include "base/memory/scoped_vector.h" |
| 10 #include "media/base/audio_decoder.h" | 10 #include "media/base/audio_decoder.h" |
| 11 #include "media/base/audio_renderer.h" | 11 #include "media/base/audio_renderer.h" |
| 12 #include "media/base/audio_renderer_sink.h" | 12 #include "media/base/audio_renderer_sink.h" |
| 13 #include "media/base/decryptor.h" | 13 #include "media/base/decryptor.h" |
| 14 #include "media/base/media_log.h" | 14 #include "media/base/media_log.h" |
| 15 #include "media/base/video_renderer.h" | |
| 15 #include "media/filters/audio_renderer_impl.h" | 16 #include "media/filters/audio_renderer_impl.h" |
| 17 #include "media/filters/renderer_impl.h" | |
| 16 #include "media/mojo/services/mojo_demuxer_stream_adapter.h" | 18 #include "media/mojo/services/mojo_demuxer_stream_adapter.h" |
| 17 #include "media/mojo/services/renderer_config.h" | 19 #include "media/mojo/services/renderer_config.h" |
| 18 #include "mojo/application/application_runner_chromium.h" | 20 #include "mojo/application/application_runner_chromium.h" |
| 19 #include "mojo/public/c/system/main.h" | 21 #include "mojo/public/c/system/main.h" |
| 20 #include "mojo/public/cpp/application/application_connection.h" | 22 #include "mojo/public/cpp/application/application_connection.h" |
| 21 #include "mojo/public/cpp/application/application_delegate.h" | 23 #include "mojo/public/cpp/application/application_delegate.h" |
| 22 #include "mojo/public/cpp/application/interface_factory_impl.h" | 24 #include "mojo/public/cpp/application/interface_factory_impl.h" |
| 23 | 25 |
| 24 namespace media { | 26 namespace media { |
| 25 | 27 |
| 26 // Time interval to update media time. | 28 // Time interval to update media time. |
| 27 const int kTimeUpdateIntervalMs = 50; | 29 const int kTimeUpdateIntervalMs = 50; |
| 28 | 30 |
| 29 static void LogMediaSourceError(const scoped_refptr<MediaLog>& media_log, | 31 static void LogMediaSourceError(const scoped_refptr<MediaLog>& media_log, |
| 30 const std::string& error) { | 32 const std::string& error) { |
| 31 media_log->AddEvent(media_log->CreateMediaSourceErrorEvent(error)); | 33 media_log->AddEvent(media_log->CreateMediaSourceErrorEvent(error)); |
| 32 } | 34 } |
| 33 | 35 |
| 34 static base::TimeDelta TimeUpdateInterval() { | 36 // Shim DemuxerStreamProvider wrapper for a single DemuxerStream. |
| 35 return base::TimeDelta::FromMilliseconds(kTimeUpdateIntervalMs); | 37 // TODO(dalecurtis): Once we support more than one DemuxerStream we'll need a |
| 36 } | 38 // more complicated shim which can handle a mojo::Array<DemuxerStream>. |
| 39 class DemuxerStreamProviderShim : public DemuxerStreamProvider { | |
| 40 public: | |
| 41 DemuxerStreamProviderShim(scoped_ptr<MojoDemuxerStreamAdapter> stream) | |
| 42 : stream_(stream.Pass()) {} | |
| 43 | |
| 44 ~DemuxerStreamProviderShim() override {} | |
| 45 | |
| 46 DemuxerStream* GetStream(DemuxerStream::Type type) override { | |
| 47 return type != stream_->type() ? nullptr : stream_.get(); | |
| 48 }; | |
| 49 | |
| 50 Liveness GetLiveness() const override { | |
| 51 return DemuxerStreamProvider::LIVENESS_UNKNOWN; | |
| 52 } | |
| 53 | |
| 54 private: | |
| 55 scoped_ptr<MojoDemuxerStreamAdapter> stream_; | |
| 56 | |
| 57 DISALLOW_COPY_AND_ASSIGN(DemuxerStreamProviderShim); | |
| 58 }; | |
| 37 | 59 |
| 38 class MojoRendererApplication | 60 class MojoRendererApplication |
| 39 : public mojo::ApplicationDelegate, | 61 : public mojo::ApplicationDelegate, |
| 40 public mojo::InterfaceFactory<mojo::MediaRenderer> { | 62 public mojo::InterfaceFactory<mojo::MediaRenderer> { |
| 41 public: | 63 public: |
| 42 // mojo::ApplicationDelegate implementation. | 64 // mojo::ApplicationDelegate implementation. |
| 43 bool ConfigureIncomingConnection( | 65 bool ConfigureIncomingConnection( |
| 44 mojo::ApplicationConnection* connection) override { | 66 mojo::ApplicationConnection* connection) override { |
| 45 connection->AddService(this); | 67 connection->AddService(this); |
| 46 return true; | 68 return true; |
| 47 } | 69 } |
| 48 | 70 |
| 49 // mojo::InterfaceFactory<mojo::MediaRenderer> implementation. | 71 // mojo::InterfaceFactory<mojo::MediaRenderer> implementation. |
| 50 void Create(mojo::ApplicationConnection* connection, | 72 void Create(mojo::ApplicationConnection* connection, |
| 51 mojo::InterfaceRequest<mojo::MediaRenderer> request) override { | 73 mojo::InterfaceRequest<mojo::MediaRenderer> request) override { |
| 52 mojo::BindToRequest(new MojoRendererService(connection), &request); | 74 mojo::BindToRequest(new MojoRendererService(connection), &request); |
| 53 } | 75 } |
| 54 }; | 76 }; |
| 55 | 77 |
| 56 // TODO(xhwang): This class looks insanely similar to RendererImpl. We should | 78 static void MojoTrampoline(const mojo::Closure& closure) { |
| 57 // really host a Renderer in this class instead of a AudioRenderer. | 79 closure.Run(); |
| 80 } | |
| 58 | 81 |
| 59 MojoRendererService::MojoRendererService( | 82 MojoRendererService::MojoRendererService( |
| 60 mojo::ApplicationConnection* connection) | 83 mojo::ApplicationConnection* connection) |
| 61 : state_(STATE_UNINITIALIZED), | 84 : 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.
| |
| 62 time_source_(NULL), | |
| 63 time_ticking_(false), | |
| 64 ended_(false), | |
| 65 weak_factory_(this), | 85 weak_factory_(this), |
| 66 weak_this_(weak_factory_.GetWeakPtr()) { | 86 weak_this_(weak_factory_.GetWeakPtr()) { |
| 67 DVLOG(1) << __FUNCTION__; | 87 DVLOG(1) << __FUNCTION__; |
| 68 | 88 |
| 69 scoped_refptr<base::SingleThreadTaskRunner> runner( | 89 scoped_refptr<base::SingleThreadTaskRunner> task_runner( |
| 70 base::MessageLoop::current()->task_runner()); | 90 base::MessageLoop::current()->task_runner()); |
| 71 scoped_refptr<MediaLog> media_log(new MediaLog()); | 91 scoped_refptr<MediaLog> media_log(new MediaLog()); |
| 72 RendererConfig* renderer_config = RendererConfig::Get(); | 92 RendererConfig* renderer_config = RendererConfig::Get(); |
| 73 audio_renderer_sink_ = renderer_config->GetAudioRendererSink(); | 93 audio_renderer_sink_ = renderer_config->GetAudioRendererSink(); |
| 74 | 94 |
| 75 audio_renderer_.reset(new AudioRendererImpl( | 95 scoped_ptr<AudioRenderer> audio_renderer(new AudioRendererImpl( |
| 76 runner, | 96 task_runner, |
| 77 audio_renderer_sink_.get(), | 97 audio_renderer_sink_.get(), |
| 78 renderer_config->GetAudioDecoders( | 98 renderer_config->GetAudioDecoders( |
| 79 runner, base::Bind(&LogMediaSourceError, media_log)) | 99 task_runner, |
| 80 .Pass(), | 100 base::Bind(&LogMediaSourceError, media_log)).Pass(), |
| 81 SetDecryptorReadyCB(), | 101 SetDecryptorReadyCB(), |
| 82 renderer_config->GetAudioHardwareConfig(), | 102 renderer_config->GetAudioHardwareConfig(), |
| 83 media_log)); | 103 media_log)); |
| 104 scoped_ptr<VideoRenderer> video_renderer(nullptr); | |
| 105 | |
| 106 // Create renderer. | |
| 107 renderer_.reset(new RendererImpl( | |
| 108 task_runner, audio_renderer.Pass(), video_renderer.Pass())); | |
| 84 } | 109 } |
| 85 | 110 |
| 86 MojoRendererService::~MojoRendererService() { | 111 MojoRendererService::~MojoRendererService() { |
| 87 } | 112 } |
| 88 | 113 |
| 89 void MojoRendererService::Initialize(mojo::DemuxerStreamPtr stream, | 114 void MojoRendererService::Initialize(mojo::DemuxerStreamPtr stream, |
| 90 const mojo::Callback<void()>& callback) { | 115 const mojo::Closure& callback) { |
| 91 DVLOG(1) << __FUNCTION__; | 116 DVLOG(1) << __FUNCTION__; |
| 92 DCHECK_EQ(state_, STATE_UNINITIALIZED) << state_; | 117 DCHECK_EQ(state_, STATE_UNINITIALIZED); |
| 93 DCHECK(client()); | 118 DCHECK(client()); |
| 94 | 119 |
| 95 init_cb_ = callback; | |
| 96 state_ = STATE_INITIALIZING; | 120 state_ = STATE_INITIALIZING; |
| 97 stream_.reset(new MojoDemuxerStreamAdapter( | 121 stream_provider_.reset(new DemuxerStreamProviderShim( |
| 98 stream.Pass(), | 122 make_scoped_ptr(new MojoDemuxerStreamAdapter( |
| 99 base::Bind(&MojoRendererService::OnStreamReady, weak_this_))); | 123 stream.Pass(), |
| 124 base::Bind(&MojoRendererService::OnStreamReady, | |
| 125 weak_this_, | |
| 126 callback))).Pass())); | |
| 100 } | 127 } |
| 101 | 128 |
| 102 void MojoRendererService::Flush(const mojo::Callback<void()>& callback) { | 129 void MojoRendererService::Flush(const mojo::Closure& callback) { |
| 103 DVLOG(2) << __FUNCTION__; | 130 DVLOG(2) << __FUNCTION__; |
| 104 DCHECK_EQ(state_, STATE_PLAYING) << state_; | 131 DCHECK_EQ(state_, STATE_PLAYING); |
| 105 | 132 |
| 106 state_ = STATE_FLUSHING; | 133 state_ = STATE_FLUSHING; |
| 107 if (time_ticking_) | 134 renderer_->Flush(base::Bind(&MojoTrampoline, callback)); |
| 108 PausePlayback(); | |
| 109 | |
| 110 // TODO(xhwang): This is not completed. Finish the flushing path. | |
| 111 NOTIMPLEMENTED(); | |
| 112 } | 135 } |
| 113 | 136 |
| 114 void MojoRendererService::StartPlayingFrom(int64_t time_delta_usec) { | 137 void MojoRendererService::StartPlayingFrom(int64_t time_delta_usec) { |
| 115 DVLOG(2) << __FUNCTION__ << ": " << time_delta_usec; | 138 DVLOG(2) << __FUNCTION__ << ": " << time_delta_usec; |
| 116 base::TimeDelta time = base::TimeDelta::FromMicroseconds(time_delta_usec); | 139 renderer_->StartPlayingFrom( |
| 117 time_source_->SetMediaTime(time); | 140 base::TimeDelta::FromMicroseconds(time_delta_usec)); |
| 118 audio_renderer_->StartPlaying(); | 141 |
| 142 // TODO(dalecurtis): Time updates shouldn't always tick, but without adding | |
| 143 // a bunch of additional state to this class plus RendererImpl, how to tell? | |
| 144 SchedulePeriodicMediaTimeUpdates(); | |
| 119 } | 145 } |
| 120 | 146 |
| 121 void MojoRendererService::SetPlaybackRate(float playback_rate) { | 147 void MojoRendererService::SetPlaybackRate(float playback_rate) { |
| 122 DVLOG(2) << __FUNCTION__ << ": " << playback_rate; | 148 DVLOG(2) << __FUNCTION__ << ": " << playback_rate; |
| 123 | 149 DCHECK_EQ(state_, STATE_PLAYING); |
| 124 // Playback rate changes are only carried out while playing. | 150 renderer_->SetPlaybackRate(playback_rate); |
| 125 if (state_ != STATE_PLAYING) | |
| 126 return; | |
| 127 | |
| 128 time_source_->SetPlaybackRate(playback_rate); | |
| 129 } | 151 } |
| 130 | 152 |
| 131 void MojoRendererService::SetVolume(float volume) { | 153 void MojoRendererService::SetVolume(float volume) { |
| 132 if (audio_renderer_) | 154 renderer_->SetVolume(volume); |
| 133 audio_renderer_->SetVolume(volume); | |
| 134 } | 155 } |
| 135 | 156 |
| 136 void MojoRendererService::OnStreamReady() { | 157 void MojoRendererService::OnStreamReady(const mojo::Closure& callback) { |
| 137 DCHECK_EQ(state_, STATE_INITIALIZING) << state_; | 158 DCHECK_EQ(state_, STATE_INITIALIZING); |
| 138 audio_renderer_->Initialize( | 159 |
| 139 stream_.get(), | 160 renderer_->Initialize( |
| 140 base::Bind(&MojoRendererService::OnAudioRendererInitializeDone, | 161 stream_provider_.get(), |
| 141 weak_this_), | 162 base::Bind( |
| 163 &MojoRendererService::OnRendererInitializeDone, weak_this_, callback), | |
| 142 base::Bind(&MojoRendererService::OnUpdateStatistics, weak_this_), | 164 base::Bind(&MojoRendererService::OnUpdateStatistics, weak_this_), |
| 143 base::Bind(&MojoRendererService::OnBufferingStateChanged, weak_this_), | 165 base::Bind(&MojoRendererService::OnRendererEnded, weak_this_), |
| 144 base::Bind(&MojoRendererService::OnAudioRendererEnded, weak_this_), | 166 base::Bind(&MojoRendererService::OnError, weak_this_), |
| 145 base::Bind(&MojoRendererService::OnError, weak_this_)); | 167 base::Bind(&MojoRendererService::OnBufferingStateChanged, weak_this_)); |
| 146 } | 168 } |
| 147 | 169 |
| 148 void MojoRendererService::OnAudioRendererInitializeDone(PipelineStatus status) { | 170 void MojoRendererService::OnRendererInitializeDone( |
| 149 DVLOG(1) << __FUNCTION__ << ": " << status; | 171 const mojo::Closure& callback) { |
| 150 DCHECK_EQ(state_, STATE_INITIALIZING) << state_; | 172 DVLOG(1) << __FUNCTION__; |
| 151 | 173 |
| 152 if (status != PIPELINE_OK) { | 174 if (state_ == STATE_ERROR) { |
| 153 state_ = STATE_ERROR; | 175 renderer_.reset(); |
| 154 audio_renderer_.reset(); | 176 } else { |
| 155 client()->OnError(); | 177 DCHECK_EQ(state_, STATE_INITIALIZING); |
| 156 init_cb_.Run(); | 178 state_ = STATE_PLAYING; |
| 157 init_cb_.reset(); | |
| 158 return; | |
| 159 } | 179 } |
| 160 | 180 |
| 161 time_source_ = audio_renderer_->GetTimeSource(); | 181 callback.Run(); |
| 162 | |
| 163 state_ = STATE_PLAYING; | |
| 164 init_cb_.Run(); | |
| 165 init_cb_.reset(); | |
| 166 } | 182 } |
| 167 | 183 |
| 168 void MojoRendererService::OnUpdateStatistics(const PipelineStatistics& stats) { | 184 void MojoRendererService::OnUpdateStatistics(const PipelineStatistics& stats) { |
| 169 NOTIMPLEMENTED(); | |
| 170 } | 185 } |
| 171 | 186 |
| 172 void MojoRendererService::UpdateMediaTime() { | 187 void MojoRendererService::UpdateMediaTime(bool only_if_changed) { |
| 173 uint64_t media_time = time_source_->CurrentMediaTime().InMicroseconds(); | 188 const uint64_t media_time = renderer_->GetMediaTime().InMicroseconds(); |
| 189 if (only_if_changed && media_time == last_media_time_) | |
| 190 return; | |
| 191 | |
| 174 client()->OnTimeUpdate(media_time, media_time); | 192 client()->OnTimeUpdate(media_time, media_time); |
| 193 last_media_time_ = media_time; | |
| 175 } | 194 } |
| 176 | 195 |
| 177 void MojoRendererService::SchedulePeriodicMediaTimeUpdates() { | 196 void MojoRendererService::SchedulePeriodicMediaTimeUpdates() { |
| 178 // Update media time immediately. | 197 UpdateMediaTime(false); |
| 179 UpdateMediaTime(); | |
| 180 | |
| 181 // Then setup periodic time update. | |
| 182 time_update_timer_.Start( | 198 time_update_timer_.Start( |
| 183 FROM_HERE, | 199 FROM_HERE, |
| 184 TimeUpdateInterval(), | 200 base::TimeDelta::FromMilliseconds(kTimeUpdateIntervalMs), |
| 185 base::Bind(&MojoRendererService::UpdateMediaTime, weak_this_)); | 201 base::Bind(&MojoRendererService::UpdateMediaTime, weak_this_, true)); |
| 186 } | 202 } |
| 187 | 203 |
| 188 void MojoRendererService::OnBufferingStateChanged( | 204 void MojoRendererService::OnBufferingStateChanged( |
| 189 media::BufferingState new_buffering_state) { | 205 BufferingState new_buffering_state) { |
| 190 DVLOG(2) << __FUNCTION__ << "(" << buffering_state_ << ", " | 206 DVLOG(2) << __FUNCTION__ << "(" << new_buffering_state << ") "; |
| 191 << new_buffering_state << ") "; | 207 client()->OnBufferingStateChange( |
| 192 bool was_waiting_for_enough_data = WaitingForEnoughData(); | 208 static_cast<mojo::BufferingState>(new_buffering_state)); |
| 193 | |
| 194 buffering_state_ = new_buffering_state; | |
| 195 | |
| 196 // Renderer underflowed. | |
| 197 if (!was_waiting_for_enough_data && WaitingForEnoughData()) { | |
| 198 PausePlayback(); | |
| 199 // TODO(xhwang): Notify client of underflow condition. | |
| 200 return; | |
| 201 } | |
| 202 | |
| 203 // Renderer prerolled. | |
| 204 if (was_waiting_for_enough_data && !WaitingForEnoughData()) { | |
| 205 StartPlayback(); | |
| 206 client()->OnBufferingStateChange( | |
| 207 static_cast<mojo::BufferingState>(new_buffering_state)); | |
| 208 return; | |
| 209 } | |
| 210 } | 209 } |
| 211 | 210 |
| 212 void MojoRendererService::OnAudioRendererEnded() { | 211 void MojoRendererService::OnRendererEnded() { |
| 213 DVLOG(1) << __FUNCTION__; | 212 DVLOG(1) << __FUNCTION__; |
| 214 | |
| 215 if (state_ != STATE_PLAYING) | |
| 216 return; | |
| 217 | |
| 218 DCHECK(!ended_); | |
| 219 ended_ = true; | |
| 220 | |
| 221 if (time_ticking_) | |
| 222 PausePlayback(); | |
| 223 | |
| 224 client()->OnEnded(); | 213 client()->OnEnded(); |
| 214 time_update_timer_.Reset(); | |
| 225 } | 215 } |
| 226 | 216 |
| 227 void MojoRendererService::OnError(PipelineStatus error) { | 217 void MojoRendererService::OnError(PipelineStatus error) { |
| 218 DVLOG(1) << __FUNCTION__; | |
| 219 state_ = STATE_ERROR; | |
| 228 client()->OnError(); | 220 client()->OnError(); |
| 229 } | 221 } |
| 230 | 222 |
| 231 bool MojoRendererService::WaitingForEnoughData() const { | |
| 232 DCHECK(audio_renderer_); | |
| 233 | |
| 234 return state_ == STATE_PLAYING && buffering_state_ != BUFFERING_HAVE_ENOUGH; | |
| 235 } | |
| 236 | |
| 237 void MojoRendererService::StartPlayback() { | |
| 238 DVLOG(1) << __FUNCTION__; | |
| 239 DCHECK_EQ(state_, STATE_PLAYING); | |
| 240 DCHECK(!time_ticking_); | |
| 241 DCHECK(!WaitingForEnoughData()); | |
| 242 | |
| 243 time_ticking_ = true; | |
| 244 time_source_->StartTicking(); | |
| 245 | |
| 246 SchedulePeriodicMediaTimeUpdates(); | |
| 247 } | |
| 248 | |
| 249 void MojoRendererService::PausePlayback() { | |
| 250 DVLOG(1) << __FUNCTION__; | |
| 251 DCHECK(time_ticking_); | |
| 252 switch (state_) { | |
| 253 case STATE_PLAYING: | |
| 254 DCHECK(ended_ || WaitingForEnoughData()) | |
| 255 << "Playback should only pause due to ending or underflowing"; | |
| 256 break; | |
| 257 | |
| 258 case STATE_FLUSHING: | |
| 259 // It's OK to pause playback when flushing. | |
| 260 break; | |
| 261 | |
| 262 case STATE_UNINITIALIZED: | |
| 263 case STATE_INITIALIZING: | |
| 264 case STATE_ERROR: | |
| 265 NOTREACHED() << "Invalid state: " << state_; | |
| 266 break; | |
| 267 } | |
| 268 | |
| 269 time_ticking_ = false; | |
| 270 time_source_->StopTicking(); | |
| 271 | |
| 272 // Cancel repeating time update timer and update the current media time. | |
| 273 time_update_timer_.Stop(); | |
| 274 UpdateMediaTime(); | |
| 275 } | |
| 276 | |
| 277 } // namespace media | 223 } // namespace media |
| 278 | 224 |
| 279 MojoResult MojoMain(MojoHandle shell_handle) { | 225 MojoResult MojoMain(MojoHandle shell_handle) { |
| 280 mojo::ApplicationRunnerChromium runner(new media::MojoRendererApplication); | 226 mojo::ApplicationRunnerChromium runner(new media::MojoRendererApplication); |
| 281 return runner.Run(shell_handle); | 227 return runner.Run(shell_handle); |
| 282 } | 228 } |
| OLD | NEW |