| 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/renderers/renderer_impl.h" | 5 #include "media/renderers/renderer_impl.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/callback.h" | 10 #include "base/callback.h" |
| 11 #include "base/callback_helpers.h" | 11 #include "base/callback_helpers.h" |
| 12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
| 13 #include "base/compiler_specific.h" | 13 #include "base/compiler_specific.h" |
| 14 #include "base/location.h" | 14 #include "base/location.h" |
| 15 #include "base/single_thread_task_runner.h" | 15 #include "base/single_thread_task_runner.h" |
| 16 #include "base/strings/string_number_conversions.h" | 16 #include "base/strings/string_number_conversions.h" |
| 17 #include "media/base/audio_decoder_config.h" | 17 #include "media/base/audio_decoder_config.h" |
| 18 #include "media/base/audio_renderer.h" | 18 #include "media/base/audio_renderer.h" |
| 19 #include "media/base/bind_to_current_loop.h" | 19 #include "media/base/bind_to_current_loop.h" |
| 20 #include "media/base/demuxer_stream_provider.h" | 20 #include "media/base/demuxer_stream_provider.h" |
| 21 #include "media/base/media_switches.h" | 21 #include "media/base/media_switches.h" |
| 22 #include "media/base/renderer_client.h" | 22 #include "media/base/renderer_client.h" |
| 23 #include "media/base/stream_position.h" |
| 23 #include "media/base/time_source.h" | 24 #include "media/base/time_source.h" |
| 24 #include "media/base/video_decoder_config.h" | 25 #include "media/base/video_decoder_config.h" |
| 25 #include "media/base/video_renderer.h" | 26 #include "media/base/video_renderer.h" |
| 26 #include "media/base/wall_clock_time_source.h" | 27 #include "media/base/wall_clock_time_source.h" |
| 27 | 28 |
| 28 namespace media { | 29 namespace media { |
| 29 | 30 |
| 30 // See |video_underflow_threshold_|. | 31 // See |video_underflow_threshold_|. |
| 31 static const int kDefaultVideoUnderflowThresholdMs = 3000; | 32 static const int kDefaultVideoUnderflowThresholdMs = 3000; |
| 32 | 33 |
| 33 static const int kAudioRestartUnderflowThresholdMs = 2000; | 34 static const int kAudioRestartUnderflowThresholdMs = 2000; |
| 34 | 35 |
| 35 class RendererImpl::RendererClientInternal : public RendererClient { | 36 class RendererImpl::RendererClientInternal : public RendererClient { |
| 36 public: | 37 public: |
| 37 RendererClientInternal(DemuxerStream::Type type, RendererImpl* renderer) | 38 RendererClientInternal(DemuxerStream::Type type, RendererImpl* renderer) |
| 38 : type_(type), renderer_(renderer) { | 39 : type_(type), renderer_(renderer) { |
| 39 DCHECK((type_ == DemuxerStream::AUDIO) || (type_ == DemuxerStream::VIDEO)); | 40 DCHECK((type_ == DemuxerStream::AUDIO) || (type_ == DemuxerStream::VIDEO)); |
| 40 } | 41 } |
| 41 | 42 |
| 42 void OnError(PipelineStatus error) override { renderer_->OnError(error); } | 43 void OnError(PipelineStatus error) override { renderer_->OnError(error); } |
| 43 void OnEnded() override { renderer_->OnRendererEnded(type_); } | 44 void OnEnded() override { renderer_->OnRendererEnded(type_); } |
| 44 void OnStatisticsUpdate(const PipelineStatistics& stats) override { | 45 void OnStatisticsUpdate(const PipelineStatistics& stats) override { |
| 45 renderer_->OnStatisticsUpdate(stats); | 46 renderer_->OnStatisticsUpdate(stats); |
| 46 } | 47 } |
| 48 void OnFirstVideoFrameTimestampKnown(base::TimeDelta timestamp) override { |
| 49 renderer_->OnFirstVideoFrameTimestampKnown(timestamp); |
| 50 } |
| 47 void OnBufferingStateChange(BufferingState state) override { | 51 void OnBufferingStateChange(BufferingState state) override { |
| 48 renderer_->OnBufferingStateChange(type_, state); | 52 renderer_->OnBufferingStateChange(type_, state); |
| 49 } | 53 } |
| 50 void OnWaitingForDecryptionKey() override { | 54 void OnWaitingForDecryptionKey() override { |
| 51 renderer_->OnWaitingForDecryptionKey(); | 55 renderer_->OnWaitingForDecryptionKey(); |
| 52 } | 56 } |
| 53 void OnVideoNaturalSizeChange(const gfx::Size& size) override { | 57 void OnVideoNaturalSizeChange(const gfx::Size& size) override { |
| 54 DCHECK(type_ == DemuxerStream::VIDEO); | 58 DCHECK(type_ == DemuxerStream::VIDEO); |
| 55 renderer_->OnVideoNaturalSizeChange(size); | 59 renderer_->OnVideoNaturalSizeChange(size); |
| 56 } | 60 } |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 197 | 201 |
| 198 flush_cb_ = flush_cb; | 202 flush_cb_ = flush_cb; |
| 199 state_ = STATE_FLUSHING; | 203 state_ = STATE_FLUSHING; |
| 200 | 204 |
| 201 if (time_ticking_) | 205 if (time_ticking_) |
| 202 PausePlayback(); | 206 PausePlayback(); |
| 203 | 207 |
| 204 FlushAudioRenderer(); | 208 FlushAudioRenderer(); |
| 205 } | 209 } |
| 206 | 210 |
| 207 void RendererImpl::StartPlayingFrom(base::TimeDelta time) { | 211 void RendererImpl::StartPlayingFrom(StreamPosition position) { |
| 208 DVLOG(1) << __func__; | 212 DVLOG(1) << __func__; |
| 209 DCHECK(task_runner_->BelongsToCurrentThread()); | 213 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 210 | 214 |
| 211 if (state_ != STATE_PLAYING) { | 215 if (state_ != STATE_PLAYING) { |
| 212 DCHECK_EQ(state_, STATE_ERROR); | 216 DCHECK_EQ(state_, STATE_ERROR); |
| 213 return; | 217 return; |
| 214 } | 218 } |
| 215 | 219 |
| 216 time_source_->SetMediaTime(time); | 220 time_source_->SetMediaTime(position.time); |
| 217 | 221 |
| 222 // XXX: There's no synchronization b/w the renderers. Is it safe to assume the |
| 223 // demuxers will give us frames close enough to one another? If not we could |
| 224 // start the video renderer, wait until we know the time of the first frame, |
| 225 // and then start the audio renderer. |
| 218 if (audio_renderer_) | 226 if (audio_renderer_) |
| 219 audio_renderer_->StartPlaying(); | 227 audio_renderer_->StartPlayingFrom(position); |
| 220 if (video_renderer_) | 228 if (video_renderer_) |
| 221 video_renderer_->StartPlayingFrom(time); | 229 video_renderer_->StartPlayingFrom(position); |
| 222 } | 230 } |
| 223 | 231 |
| 224 void RendererImpl::RestartStreamPlayback(DemuxerStream* stream, | 232 void RendererImpl::RestartStreamPlayback(DemuxerStream* stream, |
| 225 bool enabled, | 233 bool enabled, |
| 226 base::TimeDelta time) { | 234 base::TimeDelta time) { |
| 227 DCHECK(task_runner_->BelongsToCurrentThread()); | 235 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 228 DCHECK(stream); | 236 DCHECK(stream); |
| 229 bool video = (stream->type() == DemuxerStream::VIDEO); | 237 bool video = (stream->type() == DemuxerStream::VIDEO); |
| 230 DVLOG(1) << __func__ << (video ? " video" : " audio") << " stream=" << stream | 238 DVLOG(1) << __func__ << (video ? " video" : " audio") << " stream=" << stream |
| 231 << " enabled=" << stream->enabled() << " time=" << time.InSecondsF(); | 239 << " enabled=" << stream->enabled() << " time=" << time.InSecondsF(); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 255 base::Bind(&RendererImpl::RestartAudioRenderer, weak_this_, time)); | 263 base::Bind(&RendererImpl::RestartAudioRenderer, weak_this_, time)); |
| 256 } | 264 } |
| 257 } | 265 } |
| 258 | 266 |
| 259 void RendererImpl::RestartVideoRenderer(base::TimeDelta time) { | 267 void RendererImpl::RestartVideoRenderer(base::TimeDelta time) { |
| 260 DVLOG(3) << __func__; | 268 DVLOG(3) << __func__; |
| 261 DCHECK(task_runner_->BelongsToCurrentThread()); | 269 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 262 DCHECK(video_renderer_); | 270 DCHECK(video_renderer_); |
| 263 DCHECK_EQ(state_, STATE_PLAYING); | 271 DCHECK_EQ(state_, STATE_PLAYING); |
| 264 video_ended_ = false; | 272 video_ended_ = false; |
| 265 video_renderer_->StartPlayingFrom(time); | 273 // XXX: Converted without thought. |
| 274 video_renderer_->StartPlayingFrom(StreamPosition::Precise(time)); |
| 266 } | 275 } |
| 267 | 276 |
| 268 void RendererImpl::RestartAudioRenderer(base::TimeDelta time) { | 277 void RendererImpl::RestartAudioRenderer(base::TimeDelta time) { |
| 269 DVLOG(3) << __func__; | 278 DVLOG(3) << __func__; |
| 270 DCHECK(task_runner_->BelongsToCurrentThread()); | 279 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 271 DCHECK_EQ(state_, STATE_PLAYING); | 280 DCHECK_EQ(state_, STATE_PLAYING); |
| 272 DCHECK(time_source_); | 281 DCHECK(time_source_); |
| 273 DCHECK(audio_renderer_); | 282 DCHECK(audio_renderer_); |
| 274 audio_ended_ = false; | 283 audio_ended_ = false; |
| 275 audio_renderer_->StartPlaying(); | 284 // XXX: Converted without thought. |
| 285 audio_renderer_->StartPlayingFrom(StreamPosition::Precise(time)); |
| 276 } | 286 } |
| 277 | 287 |
| 278 void RendererImpl::SetPlaybackRate(double playback_rate) { | 288 void RendererImpl::SetPlaybackRate(double playback_rate) { |
| 279 DVLOG(1) << __func__ << "(" << playback_rate << ")"; | 289 DVLOG(1) << __func__ << "(" << playback_rate << ")"; |
| 280 DCHECK(task_runner_->BelongsToCurrentThread()); | 290 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 281 | 291 |
| 282 // Playback rate changes are only carried out while playing. | 292 // Playback rate changes are only carried out while playing. |
| 283 if (state_ != STATE_PLAYING) | 293 if (state_ != STATE_PLAYING) |
| 284 return; | 294 return; |
| 285 | 295 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 336 << "Underflow must be disabled for clockless video playback"; | 346 << "Underflow must be disabled for clockless video playback"; |
| 337 | 347 |
| 338 clockless_video_playback_enabled_for_testing_ = true; | 348 clockless_video_playback_enabled_for_testing_ = true; |
| 339 } | 349 } |
| 340 | 350 |
| 341 bool RendererImpl::GetWallClockTimes( | 351 bool RendererImpl::GetWallClockTimes( |
| 342 const std::vector<base::TimeDelta>& media_timestamps, | 352 const std::vector<base::TimeDelta>& media_timestamps, |
| 343 std::vector<base::TimeTicks>* wall_clock_times) { | 353 std::vector<base::TimeTicks>* wall_clock_times) { |
| 344 // No BelongsToCurrentThread() checking because this can be called from other | 354 // No BelongsToCurrentThread() checking because this can be called from other |
| 345 // threads. | 355 // threads. |
| 346 // | |
| 347 // TODO(scherkus): Currently called from VideoRendererImpl's internal thread, | |
| 348 // which should go away at some point http://crbug.com/110814 | |
| 349 if (clockless_video_playback_enabled_for_testing_) { | 356 if (clockless_video_playback_enabled_for_testing_) { |
| 350 if (media_timestamps.empty()) { | 357 if (media_timestamps.empty()) { |
| 351 *wall_clock_times = std::vector<base::TimeTicks>(1, | 358 *wall_clock_times = std::vector<base::TimeTicks>(1, |
| 352 base::TimeTicks::Now()); | 359 base::TimeTicks::Now()); |
| 353 } else { | 360 } else { |
| 354 *wall_clock_times = std::vector<base::TimeTicks>(); | 361 *wall_clock_times = std::vector<base::TimeTicks>(); |
| 355 for (auto const &media_time : media_timestamps) { | 362 for (auto const &media_time : media_timestamps) { |
| 356 wall_clock_times->push_back(base::TimeTicks() + media_time); | 363 wall_clock_times->push_back(base::TimeTicks() + media_time); |
| 357 } | 364 } |
| 358 } | 365 } |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 555 video_ended_ = false; | 562 video_ended_ = false; |
| 556 state_ = STATE_PLAYING; | 563 state_ = STATE_PLAYING; |
| 557 base::ResetAndReturn(&flush_cb_).Run(); | 564 base::ResetAndReturn(&flush_cb_).Run(); |
| 558 } | 565 } |
| 559 | 566 |
| 560 void RendererImpl::OnStatisticsUpdate(const PipelineStatistics& stats) { | 567 void RendererImpl::OnStatisticsUpdate(const PipelineStatistics& stats) { |
| 561 DCHECK(task_runner_->BelongsToCurrentThread()); | 568 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 562 client_->OnStatisticsUpdate(stats); | 569 client_->OnStatisticsUpdate(stats); |
| 563 } | 570 } |
| 564 | 571 |
| 572 // XXX: Revisit: I don't really like this. I considered letting the video |
| 573 // renderer own the wall clock time source and manage this itself, but it didn't |
| 574 // work for some reason. Try again. |
| 575 void RendererImpl::OnFirstVideoFrameTimestampKnown(base::TimeDelta timestamp) { |
| 576 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 577 LOG(ERROR) << __func__ << " " << timestamp.InMicroseconds(); |
| 578 if (wall_clock_time_source_) |
| 579 wall_clock_time_source_->SetMediaTime(timestamp); |
| 580 } |
| 581 |
| 565 namespace { | 582 namespace { |
| 566 | 583 |
| 567 const char* BufferingStateStr(BufferingState state) { | 584 const char* BufferingStateStr(BufferingState state) { |
| 568 switch (state) { | 585 switch (state) { |
| 569 case BUFFERING_HAVE_NOTHING: | 586 case BUFFERING_HAVE_NOTHING: |
| 570 return "HAVE_NOTHING"; | 587 return "HAVE_NOTHING"; |
| 571 case BUFFERING_HAVE_ENOUGH: | 588 case BUFFERING_HAVE_ENOUGH: |
| 572 return "HAVE_ENOUGH"; | 589 return "HAVE_ENOUGH"; |
| 573 } | 590 } |
| 574 NOTREACHED(); | 591 NOTREACHED(); |
| (...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 849 DCHECK(task_runner_->BelongsToCurrentThread()); | 866 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 850 client_->OnVideoNaturalSizeChange(size); | 867 client_->OnVideoNaturalSizeChange(size); |
| 851 } | 868 } |
| 852 | 869 |
| 853 void RendererImpl::OnVideoOpacityChange(bool opaque) { | 870 void RendererImpl::OnVideoOpacityChange(bool opaque) { |
| 854 DCHECK(task_runner_->BelongsToCurrentThread()); | 871 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 855 client_->OnVideoOpacityChange(opaque); | 872 client_->OnVideoOpacityChange(opaque); |
| 856 } | 873 } |
| 857 | 874 |
| 858 } // namespace media | 875 } // namespace media |
| OLD | NEW |