Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/filters/audio_renderer_impl.h" | 5 #include "media/filters/audio_renderer_impl.h" |
| 6 | 6 |
| 7 #include <math.h> | 7 #include <math.h> |
| 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/logging.h" | 12 #include "base/logging.h" |
| 13 #include "media/audio/audio_util.h" | 13 #include "media/audio/audio_util.h" |
| 14 #include "media/base/demuxer_stream.h" | |
| 14 | 15 |
| 15 namespace media { | 16 namespace media { |
| 16 | 17 |
| 17 AudioRendererImpl::AudioRendererImpl(media::AudioRendererSink* sink) | 18 AudioRendererImpl::AudioRendererImpl(media::AudioRendererSink* sink) |
| 18 : state_(kUninitialized), | 19 : state_(kUninitialized), |
| 19 pending_read_(false), | 20 pending_read_(false), |
| 20 received_end_of_stream_(false), | 21 received_end_of_stream_(false), |
| 21 rendered_end_of_stream_(false), | 22 rendered_end_of_stream_(false), |
| 22 audio_time_buffered_(kNoTimestamp()), | 23 audio_time_buffered_(kNoTimestamp()), |
| 23 current_time_(kNoTimestamp()), | 24 current_time_(kNoTimestamp()), |
| 24 bytes_per_frame_(0), | 25 bytes_per_frame_(0), |
| 25 bytes_per_second_(0), | |
| 26 stopped_(false), | 26 stopped_(false), |
| 27 sink_(sink), | 27 sink_(sink), |
| 28 is_initialized_(false), | |
| 29 underflow_disabled_(false) { | 28 underflow_disabled_(false) { |
| 30 } | 29 } |
| 31 | 30 |
| 32 void AudioRendererImpl::Play(const base::Closure& callback) { | 31 void AudioRendererImpl::Play(const base::Closure& callback) { |
| 33 { | 32 { |
| 34 base::AutoLock auto_lock(lock_); | 33 base::AutoLock auto_lock(lock_); |
| 35 DCHECK_EQ(kPaused, state_); | 34 DCHECK_EQ(kPaused, state_); |
| 36 state_ = kPlaying; | 35 state_ = kPlaying; |
| 37 callback.Run(); | 36 callback.Run(); |
| 38 } | 37 } |
| 39 | 38 |
| 40 if (stopped_) | 39 if (stopped_) |
| 41 return; | 40 return; |
| 42 | 41 |
| 43 if (GetPlaybackRate() != 0.0f) { | 42 if (GetPlaybackRate() != 0.0f) { |
| 44 DoPlay(); | 43 DoPlay(); |
| 45 } else { | 44 } else { |
| 46 DoPause(); | 45 DoPause(); |
| 47 } | 46 } |
| 48 } | 47 } |
| 49 | 48 |
| 50 void AudioRendererImpl::DoPlay() { | 49 void AudioRendererImpl::DoPlay() { |
| 51 earliest_end_time_ = base::Time::Now(); | 50 earliest_end_time_ = base::Time::Now(); |
| 52 DCHECK(sink_.get()); | |
|
Ami GONE FROM CHROMIUM
2012/09/07 14:40:02
this is just random cleanup?
| |
| 53 sink_->Play(); | 51 sink_->Play(); |
| 54 } | 52 } |
| 55 | 53 |
| 56 void AudioRendererImpl::Pause(const base::Closure& callback) { | 54 void AudioRendererImpl::Pause(const base::Closure& callback) { |
| 57 { | 55 { |
| 58 base::AutoLock auto_lock(lock_); | 56 base::AutoLock auto_lock(lock_); |
| 59 DCHECK(state_ == kPlaying || state_ == kUnderflow || | 57 DCHECK(state_ == kPlaying || state_ == kUnderflow || |
| 60 state_ == kRebuffering); | 58 state_ == kRebuffering); |
| 61 pause_cb_ = callback; | 59 pause_cb_ = callback; |
| 62 state_ = kPaused; | 60 state_ = kPaused; |
| 63 | 61 |
| 64 // Pause only when we've completed our pending read. | 62 // Pause only when we've completed our pending read. |
| 65 if (!pending_read_) | 63 if (!pending_read_) |
| 66 base::ResetAndReturn(&pause_cb_).Run(); | 64 base::ResetAndReturn(&pause_cb_).Run(); |
| 67 } | 65 } |
| 68 | 66 |
| 69 if (stopped_) | 67 if (stopped_) |
| 70 return; | 68 return; |
| 71 | 69 |
| 72 DoPause(); | 70 DoPause(); |
| 73 } | 71 } |
| 74 | 72 |
| 75 void AudioRendererImpl::DoPause() { | 73 void AudioRendererImpl::DoPause() { |
| 76 DCHECK(sink_.get()); | |
| 77 sink_->Pause(false); | 74 sink_->Pause(false); |
| 78 } | 75 } |
| 79 | 76 |
| 80 void AudioRendererImpl::Flush(const base::Closure& callback) { | 77 void AudioRendererImpl::Flush(const base::Closure& callback) { |
| 81 decoder_->Reset(callback); | 78 decoder_->Reset(callback); |
| 82 } | 79 } |
| 83 | 80 |
| 84 void AudioRendererImpl::Stop(const base::Closure& callback) { | 81 void AudioRendererImpl::Stop(const base::Closure& callback) { |
| 85 if (!stopped_) { | 82 if (!stopped_) { |
| 86 DCHECK(sink_.get()); | |
| 87 sink_->Stop(); | 83 sink_->Stop(); |
| 88 | |
| 89 stopped_ = true; | 84 stopped_ = true; |
| 90 } | 85 } |
| 91 { | 86 { |
| 92 base::AutoLock auto_lock(lock_); | 87 base::AutoLock auto_lock(lock_); |
| 93 state_ = kStopped; | 88 state_ = kStopped; |
| 94 algorithm_.reset(NULL); | 89 algorithm_.reset(NULL); |
| 95 time_cb_.Reset(); | 90 time_cb_.Reset(); |
| 96 underflow_cb_.Reset(); | 91 underflow_cb_.Reset(); |
| 97 } | 92 } |
| 98 if (!callback.is_null()) { | 93 if (!callback.is_null()) { |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 121 algorithm_->FlushBuffers(); | 116 algorithm_->FlushBuffers(); |
| 122 | 117 |
| 123 if (stopped_) | 118 if (stopped_) |
| 124 return; | 119 return; |
| 125 | 120 |
| 126 // Pause and flush the stream when we preroll to a new location. | 121 // Pause and flush the stream when we preroll to a new location. |
| 127 earliest_end_time_ = base::Time::Now(); | 122 earliest_end_time_ = base::Time::Now(); |
| 128 sink_->Pause(true); | 123 sink_->Pause(true); |
| 129 } | 124 } |
| 130 | 125 |
| 131 void AudioRendererImpl::Initialize(const scoped_refptr<AudioDecoder>& decoder, | 126 void AudioRendererImpl::Initialize(const scoped_refptr<DemuxerStream>& stream, |
| 127 const AudioDecoderList& decoders, | |
| 132 const PipelineStatusCB& init_cb, | 128 const PipelineStatusCB& init_cb, |
| 129 const StatisticsCB& statistics_cb, | |
| 133 const base::Closure& underflow_cb, | 130 const base::Closure& underflow_cb, |
| 134 const TimeCB& time_cb, | 131 const TimeCB& time_cb, |
| 135 const base::Closure& ended_cb, | 132 const base::Closure& ended_cb, |
| 136 const base::Closure& disabled_cb, | 133 const base::Closure& disabled_cb, |
| 137 const PipelineStatusCB& error_cb) { | 134 const PipelineStatusCB& error_cb) { |
| 138 DCHECK(decoder); | 135 base::AutoLock l(lock_); |
|
Ami GONE FROM CHROMIUM
2012/09/07 14:40:02
"l" is not stylish
| |
| 136 DCHECK(stream); | |
| 137 DCHECK(!decoders.empty()); | |
| 138 DCHECK_EQ(stream->type(), DemuxerStream::AUDIO); | |
| 139 DCHECK(!init_cb.is_null()); | 139 DCHECK(!init_cb.is_null()); |
| 140 DCHECK(!statistics_cb.is_null()); | |
| 140 DCHECK(!underflow_cb.is_null()); | 141 DCHECK(!underflow_cb.is_null()); |
| 141 DCHECK(!time_cb.is_null()); | 142 DCHECK(!time_cb.is_null()); |
| 142 DCHECK(!ended_cb.is_null()); | 143 DCHECK(!ended_cb.is_null()); |
| 143 DCHECK(!disabled_cb.is_null()); | 144 DCHECK(!disabled_cb.is_null()); |
| 144 DCHECK(!error_cb.is_null()); | 145 DCHECK(!error_cb.is_null()); |
| 145 DCHECK_EQ(kUninitialized, state_); | 146 DCHECK_EQ(kUninitialized, state_); |
| 146 decoder_ = decoder; | 147 init_cb_ = init_cb; |
| 148 statistics_cb_ = statistics_cb; | |
|
Ami GONE FROM CHROMIUM
2012/09/07 14:40:02
This is only used during the init dance, right?
Wh
| |
| 147 underflow_cb_ = underflow_cb; | 149 underflow_cb_ = underflow_cb; |
| 148 time_cb_ = time_cb; | 150 time_cb_ = time_cb; |
| 149 ended_cb_ = ended_cb; | 151 ended_cb_ = ended_cb; |
| 150 disabled_cb_ = disabled_cb; | 152 disabled_cb_ = disabled_cb; |
| 151 error_cb_ = error_cb; | 153 error_cb_ = error_cb; |
| 152 | 154 |
| 153 // Create a callback so our algorithm can request more reads. | 155 scoped_ptr<AudioDecoderList> decoder_list(new AudioDecoderList(decoders)); |
| 154 base::Closure cb = base::Bind(&AudioRendererImpl::ScheduleRead_Locked, this); | 156 InitializeNextDecoder(stream, decoder_list.Pass()); |
| 157 } | |
| 155 | 158 |
| 156 // Construct the algorithm. | 159 void AudioRendererImpl::InitializeNextDecoder( |
| 157 algorithm_.reset(new AudioRendererAlgorithm()); | 160 const scoped_refptr<DemuxerStream>& demuxer_stream, |
| 161 scoped_ptr<AudioDecoderList> decoders) { | |
| 162 lock_.AssertAcquired(); | |
|
Ami GONE FROM CHROMIUM
2012/09/07 14:40:02
this locking is making me very sad.
If you dropped
| |
| 163 DCHECK(!decoders->empty()); | |
| 158 | 164 |
| 159 // Initialize our algorithm with media properties, initial playback rate, | 165 scoped_refptr<AudioDecoder> decoder = decoders->front(); |
|
Ami GONE FROM CHROMIUM
2012/09/07 14:40:02
put it straight into decoder_ instead?
| |
| 160 // and a callback to request more reads from the data source. | 166 decoders->pop_front(); |
| 167 | |
| 168 DCHECK(decoder); | |
| 169 decoder_ = decoder; | |
| 170 | |
| 171 base::AutoUnlock auto_unlock(lock_); | |
| 172 decoder->Initialize( | |
| 173 demuxer_stream, | |
| 174 base::Bind(&AudioRendererImpl::OnDecoderInitDone, this, | |
| 175 demuxer_stream, | |
| 176 base::Passed(&decoders)), | |
| 177 statistics_cb_); | |
| 178 } | |
| 179 | |
| 180 void AudioRendererImpl::OnDecoderInitDone( | |
| 181 const scoped_refptr<DemuxerStream>& demuxer_stream, | |
| 182 scoped_ptr<AudioDecoderList> decoders, | |
| 183 PipelineStatus status) { | |
| 184 base::AutoLock auto_lock(lock_); | |
| 185 | |
| 186 if (stopped_ || state_ == kStopped) | |
| 187 return; | |
| 188 | |
| 189 if (!decoders->empty() && status == DECODER_ERROR_NOT_SUPPORTED) { | |
| 190 InitializeNextDecoder(demuxer_stream, decoders.Pass()); | |
| 191 return; | |
| 192 } | |
| 193 | |
| 194 if (status != PIPELINE_OK) { | |
| 195 base::ResetAndReturn(&init_cb_).Run(status); | |
| 196 return; | |
| 197 } | |
| 198 | |
| 199 // We're all good! Continue initializing the rest of the audio renderer based | |
| 200 // on the decoder format. | |
| 201 | |
| 161 ChannelLayout channel_layout = decoder_->channel_layout(); | 202 ChannelLayout channel_layout = decoder_->channel_layout(); |
| 162 int channels = ChannelLayoutToChannelCount(channel_layout); | 203 int channels = ChannelLayoutToChannelCount(channel_layout); |
| 163 int bits_per_channel = decoder_->bits_per_channel(); | 204 int bits_per_channel = decoder_->bits_per_channel(); |
| 164 int sample_rate = decoder_->samples_per_second(); | 205 int sample_rate = decoder_->samples_per_second(); |
| 165 // TODO(vrk): Add method to AudioDecoder to compute bytes per frame. | 206 // TODO(vrk): Add method to AudioDecoder to compute bytes per frame. |
| 166 bytes_per_frame_ = channels * bits_per_channel / 8; | 207 bytes_per_frame_ = channels * bits_per_channel / 8; |
| 167 | 208 |
| 168 bool config_ok = algorithm_->ValidateConfig(channels, sample_rate, | 209 algorithm_.reset(new AudioRendererAlgorithm()); |
|
scherkus (not reviewing)
2012/09/07 13:59:54
this was some miscellaneous cleanup
| |
| 169 bits_per_channel); | 210 if (!algorithm_->ValidateConfig(channels, sample_rate, bits_per_channel)) { |
| 170 if (!config_ok || is_initialized_) { | 211 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED); |
| 171 init_cb.Run(PIPELINE_ERROR_INITIALIZATION_FAILED); | |
| 172 return; | 212 return; |
| 173 } | 213 } |
| 174 | 214 |
| 175 if (config_ok) | 215 algorithm_->Initialize( |
| 176 algorithm_->Initialize(channels, sample_rate, bits_per_channel, 0.0f, cb); | 216 channels, sample_rate, bits_per_channel, 0.0f, |
| 217 base::Bind(&AudioRendererImpl::ScheduleRead_Locked, this)); | |
| 177 | 218 |
| 178 // We use the AUDIO_PCM_LINEAR flag because AUDIO_PCM_LOW_LATENCY | 219 // We use the AUDIO_PCM_LINEAR flag because AUDIO_PCM_LOW_LATENCY |
| 179 // does not currently support all the sample-rates that we require. | 220 // does not currently support all the sample-rates that we require. |
| 180 // Please see: http://code.google.com/p/chromium/issues/detail?id=103627 | 221 // Please see: http://code.google.com/p/chromium/issues/detail?id=103627 |
| 181 // for more details. | 222 // for more details. |
| 182 audio_parameters_ = AudioParameters( | 223 audio_parameters_ = AudioParameters( |
| 183 AudioParameters::AUDIO_PCM_LINEAR, channel_layout, sample_rate, | 224 AudioParameters::AUDIO_PCM_LINEAR, channel_layout, sample_rate, |
| 184 bits_per_channel, GetHighLatencyOutputBufferSize(sample_rate)); | 225 bits_per_channel, GetHighLatencyOutputBufferSize(sample_rate)); |
| 185 | 226 |
| 186 bytes_per_second_ = audio_parameters_.GetBytesPerSecond(); | 227 sink_->Initialize(audio_parameters_, this); |
| 228 sink_->Start(); | |
| 187 | 229 |
| 188 DCHECK(sink_.get()); | |
| 189 DCHECK(!is_initialized_); | |
| 190 | |
| 191 sink_->Initialize(audio_parameters_, this); | |
| 192 | |
| 193 sink_->Start(); | |
| 194 is_initialized_ = true; | |
| 195 | |
| 196 // Finally, execute the start callback. | |
| 197 state_ = kPaused; | 230 state_ = kPaused; |
| 198 init_cb.Run(PIPELINE_OK); | 231 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); |
| 199 } | 232 } |
| 200 | 233 |
| 201 void AudioRendererImpl::ResumeAfterUnderflow(bool buffer_more_audio) { | 234 void AudioRendererImpl::ResumeAfterUnderflow(bool buffer_more_audio) { |
| 202 base::AutoLock auto_lock(lock_); | 235 base::AutoLock auto_lock(lock_); |
| 203 if (state_ == kUnderflow) { | 236 if (state_ == kUnderflow) { |
| 204 if (buffer_more_audio) | 237 if (buffer_more_audio) |
| 205 algorithm_->IncreaseQueueCapacity(); | 238 algorithm_->IncreaseQueueCapacity(); |
| 206 | 239 |
| 207 state_ = kRebuffering; | 240 state_ = kRebuffering; |
| 208 } | 241 } |
| (...skipping 265 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 474 static_cast<int64>(ceil(predicted_play_time.InMicroseconds() * | 507 static_cast<int64>(ceil(predicted_play_time.InMicroseconds() * |
| 475 playback_rate))); | 508 playback_rate))); |
| 476 } | 509 } |
| 477 earliest_end_time_ = | 510 earliest_end_time_ = |
| 478 std::max(earliest_end_time_, | 511 std::max(earliest_end_time_, |
| 479 time_now + request_delay + predicted_play_time); | 512 time_now + request_delay + predicted_play_time); |
| 480 } | 513 } |
| 481 } | 514 } |
| 482 | 515 |
| 483 base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) { | 516 base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) { |
| 484 if (bytes_per_second_) { | 517 int bytes_per_second = audio_parameters_.GetBytesPerSecond(); |
| 485 return base::TimeDelta::FromMicroseconds( | 518 CHECK(bytes_per_second); |
|
scherkus (not reviewing)
2012/09/07 13:59:54
calling algorithm_->ValidateConfig() should preven
| |
| 486 base::Time::kMicrosecondsPerSecond * bytes / bytes_per_second_); | 519 return base::TimeDelta::FromMicroseconds( |
| 487 } | 520 base::Time::kMicrosecondsPerSecond * bytes / bytes_per_second); |
| 488 return base::TimeDelta(); | |
| 489 } | 521 } |
| 490 | 522 |
| 491 void AudioRendererImpl::OnRenderError() { | 523 void AudioRendererImpl::OnRenderError() { |
| 492 disabled_cb_.Run(); | 524 disabled_cb_.Run(); |
| 493 } | 525 } |
| 494 | 526 |
| 495 void AudioRendererImpl::DisableUnderflowForTesting() { | 527 void AudioRendererImpl::DisableUnderflowForTesting() { |
| 496 DCHECK(!is_initialized_); | |
| 497 underflow_disabled_ = true; | 528 underflow_disabled_ = true; |
| 498 } | 529 } |
| 499 | 530 |
| 500 void AudioRendererImpl::HandleAbortedReadOrDecodeError(bool is_decode_error) { | 531 void AudioRendererImpl::HandleAbortedReadOrDecodeError(bool is_decode_error) { |
| 501 PipelineStatus status = is_decode_error ? PIPELINE_ERROR_DECODE : PIPELINE_OK; | 532 PipelineStatus status = is_decode_error ? PIPELINE_ERROR_DECODE : PIPELINE_OK; |
| 502 switch (state_) { | 533 switch (state_) { |
| 503 case kUninitialized: | 534 case kUninitialized: |
| 504 NOTREACHED(); | 535 NOTREACHED(); |
| 505 return; | 536 return; |
| 506 case kPaused: | 537 case kPaused: |
| 507 if (status != PIPELINE_OK) | 538 if (status != PIPELINE_OK) |
| 508 error_cb_.Run(status); | 539 error_cb_.Run(status); |
| 509 base::ResetAndReturn(&pause_cb_).Run(); | 540 base::ResetAndReturn(&pause_cb_).Run(); |
| 510 return; | 541 return; |
| 511 case kPrerolling: | 542 case kPrerolling: |
| 512 state_ = kPaused; | 543 state_ = kPaused; |
| 513 base::ResetAndReturn(&preroll_cb_).Run(status); | 544 base::ResetAndReturn(&preroll_cb_).Run(status); |
| 514 return; | 545 return; |
| 515 case kPlaying: | 546 case kPlaying: |
| 516 case kUnderflow: | 547 case kUnderflow: |
| 517 case kRebuffering: | 548 case kRebuffering: |
| 518 case kStopped: | 549 case kStopped: |
| 519 if (status != PIPELINE_OK) | 550 if (status != PIPELINE_OK) |
| 520 error_cb_.Run(status); | 551 error_cb_.Run(status); |
| 521 return; | 552 return; |
| 522 } | 553 } |
| 523 } | 554 } |
| 524 | 555 |
| 525 } // namespace media | 556 } // namespace media |
| OLD | NEW |