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 <algorithm> | 9 #include <algorithm> |
| 10 | 10 |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/callback.h" | 12 #include "base/callback.h" |
| 13 #include "base/callback_helpers.h" | 13 #include "base/callback_helpers.h" |
| 14 #include "base/command_line.h" | 14 #include "base/command_line.h" |
| 15 #include "base/logging.h" | 15 #include "base/logging.h" |
| 16 #include "base/message_loop_proxy.h" | |
| 16 #include "media/audio/audio_util.h" | 17 #include "media/audio/audio_util.h" |
| 18 #include "media/base/bind_to_loop.h" | |
| 17 #include "media/base/demuxer_stream.h" | 19 #include "media/base/demuxer_stream.h" |
| 18 #include "media/base/media_switches.h" | 20 #include "media/base/media_switches.h" |
| 19 | 21 |
| 20 namespace media { | 22 namespace media { |
| 21 | 23 |
| 22 AudioRendererImpl::AudioRendererImpl(media::AudioRendererSink* sink) | 24 AudioRendererImpl::AudioRendererImpl(media::AudioRendererSink* sink) |
| 23 : state_(kUninitialized), | 25 : sink_(sink), |
| 26 state_(kUninitialized), | |
| 24 pending_read_(false), | 27 pending_read_(false), |
| 25 received_end_of_stream_(false), | 28 received_end_of_stream_(false), |
| 26 rendered_end_of_stream_(false), | 29 rendered_end_of_stream_(false), |
| 27 audio_time_buffered_(kNoTimestamp()), | 30 audio_time_buffered_(kNoTimestamp()), |
| 28 current_time_(kNoTimestamp()), | 31 current_time_(kNoTimestamp()), |
| 29 bytes_per_frame_(0), | |
| 30 stopped_(false), | |
| 31 sink_(sink), | |
| 32 underflow_disabled_(false), | 32 underflow_disabled_(false), |
| 33 preroll_aborted_(false) { | 33 preroll_aborted_(false) { |
| 34 // We're created on the render thread, but this thread checker is for another. | |
| 35 pipeline_thread_checker_.DetachFromThread(); | |
| 34 } | 36 } |
| 35 | 37 |
| 36 void AudioRendererImpl::Play(const base::Closure& callback) { | 38 void AudioRendererImpl::Play(const base::Closure& callback) { |
| 39 DCHECK(pipeline_thread_checker_.CalledOnValidThread()); | |
| 40 | |
| 37 { | 41 { |
| 38 base::AutoLock auto_lock(lock_); | 42 base::AutoLock auto_lock(lock_); |
| 39 DCHECK_EQ(kPaused, state_); | 43 DCHECK_EQ(kPaused, state_); |
| 40 state_ = kPlaying; | 44 state_ = kPlaying; |
| 41 callback.Run(); | 45 callback.Run(); |
|
Ami GONE FROM CHROMIUM
2012/11/03 00:11:45
running the callback under lock looks strange. Is
DaleCurtis
2012/11/03 01:32:47
Yeah, it should removed, but that's a base class c
| |
| 42 } | 46 } |
| 43 | 47 |
| 44 if (stopped_) | 48 // Since playback rate is only set on the pipeline thread, we can query the |
| 45 return; | 49 // algorithm directly instead of using GetPlaybackRate(). |
| 46 | 50 if (algorithm_->playback_rate() != 0.0f) { |
|
Ami GONE FROM CHROMIUM
2012/11/03 00:11:45
depending on impl detail of algorithm_ that this i
DaleCurtis
2012/11/03 01:32:47
Done.
| |
| 47 if (GetPlaybackRate() != 0.0f) { | |
| 48 DoPlay(); | 51 DoPlay(); |
| 49 } else { | 52 } else { |
| 50 DoPause(); | 53 DoPause(); |
| 51 } | 54 } |
| 52 } | 55 } |
| 53 | 56 |
| 54 void AudioRendererImpl::DoPlay() { | 57 void AudioRendererImpl::DoPlay() { |
| 55 earliest_end_time_ = base::Time::Now(); | 58 DCHECK(pipeline_thread_checker_.CalledOnValidThread()); |
| 59 DCHECK(sink_); | |
| 60 { | |
| 61 base::AutoLock auto_lock(lock_); | |
| 62 earliest_end_time_ = base::Time::Now(); | |
| 63 } | |
| 56 sink_->Play(); | 64 sink_->Play(); |
| 57 } | 65 } |
| 58 | 66 |
| 59 void AudioRendererImpl::Pause(const base::Closure& callback) { | 67 void AudioRendererImpl::Pause(const base::Closure& callback) { |
| 68 DCHECK(pipeline_thread_checker_.CalledOnValidThread()); | |
| 69 | |
| 60 { | 70 { |
| 61 base::AutoLock auto_lock(lock_); | 71 base::AutoLock auto_lock(lock_); |
| 62 DCHECK(state_ == kPlaying || state_ == kUnderflow || | 72 DCHECK(state_ == kPlaying || state_ == kUnderflow || |
| 63 state_ == kRebuffering); | 73 state_ == kRebuffering); |
| 64 pause_cb_ = callback; | 74 pause_cb_ = callback; |
| 65 state_ = kPaused; | 75 state_ = kPaused; |
| 66 | 76 |
| 67 // Pause only when we've completed our pending read. | 77 // Pause only when we've completed our pending read. |
| 68 if (!pending_read_) | 78 if (!pending_read_) |
| 69 base::ResetAndReturn(&pause_cb_).Run(); | 79 base::ResetAndReturn(&pause_cb_).Run(); |
| 70 } | 80 } |
| 71 | 81 |
| 72 if (stopped_) | |
| 73 return; | |
| 74 | |
| 75 DoPause(); | 82 DoPause(); |
| 76 } | 83 } |
| 77 | 84 |
| 78 void AudioRendererImpl::DoPause() { | 85 void AudioRendererImpl::DoPause() { |
| 86 DCHECK(pipeline_thread_checker_.CalledOnValidThread()); | |
| 87 DCHECK(sink_); | |
| 79 sink_->Pause(false); | 88 sink_->Pause(false); |
| 80 } | 89 } |
| 81 | 90 |
| 82 void AudioRendererImpl::Flush(const base::Closure& callback) { | 91 void AudioRendererImpl::Flush(const base::Closure& callback) { |
| 92 DCHECK(pipeline_thread_checker_.CalledOnValidThread()); | |
| 83 decoder_->Reset(callback); | 93 decoder_->Reset(callback); |
| 84 } | 94 } |
| 85 | 95 |
| 86 void AudioRendererImpl::Stop(const base::Closure& callback) { | 96 void AudioRendererImpl::Stop(const base::Closure& callback) { |
| 97 DCHECK(pipeline_thread_checker_.CalledOnValidThread()); | |
| 87 DCHECK(!callback.is_null()); | 98 DCHECK(!callback.is_null()); |
| 88 | 99 |
| 100 if (sink_) { | |
| 101 sink_->Stop(); | |
| 102 sink_ = NULL; | |
| 103 } | |
| 104 | |
| 89 { | 105 { |
| 90 base::AutoLock auto_lock(lock_); | 106 base::AutoLock auto_lock(lock_); |
| 91 if (!stopped_) { | |
| 92 sink_->Stop(); | |
| 93 stopped_ = true; | |
| 94 } | |
| 95 | |
| 96 state_ = kStopped; | 107 state_ = kStopped; |
| 97 algorithm_.reset(NULL); | 108 algorithm_.reset(NULL); |
| 98 init_cb_.Reset(); | 109 init_cb_.Reset(); |
| 99 underflow_cb_.Reset(); | 110 underflow_cb_.Reset(); |
| 100 time_cb_.Reset(); | 111 time_cb_.Reset(); |
| 101 } | 112 } |
| 102 | 113 |
| 103 callback.Run(); | 114 callback.Run(); |
| 104 } | 115 } |
| 105 | 116 |
| 106 void AudioRendererImpl::Preroll(base::TimeDelta time, | 117 void AudioRendererImpl::Preroll(base::TimeDelta time, |
| 107 const PipelineStatusCB& cb) { | 118 const PipelineStatusCB& cb) { |
| 108 base::AutoLock auto_lock(lock_); | 119 DCHECK(pipeline_thread_checker_.CalledOnValidThread()); |
| 109 DCHECK_EQ(kPaused, state_); | 120 DCHECK(sink_); |
| 110 DCHECK(!pending_read_) << "Pending read must complete before seeking"; | |
| 111 DCHECK(pause_cb_.is_null()); | |
| 112 DCHECK(preroll_cb_.is_null()); | |
| 113 state_ = kPrerolling; | |
| 114 preroll_cb_ = cb; | |
| 115 preroll_timestamp_ = time; | |
| 116 | 121 |
| 117 // Throw away everything and schedule our reads. | 122 { |
| 118 audio_time_buffered_ = kNoTimestamp(); | 123 base::AutoLock auto_lock(lock_); |
| 119 current_time_ = kNoTimestamp(); | 124 DCHECK_EQ(kPaused, state_); |
| 120 received_end_of_stream_ = false; | 125 DCHECK(!pending_read_) << "Pending read must complete before seeking"; |
| 121 rendered_end_of_stream_ = false; | 126 DCHECK(pause_cb_.is_null()); |
| 122 preroll_aborted_ = false; | 127 DCHECK(preroll_cb_.is_null()); |
| 128 state_ = kPrerolling; | |
| 129 preroll_cb_ = cb; | |
| 130 preroll_timestamp_ = time; | |
| 123 | 131 |
| 124 // |algorithm_| will request more reads. | 132 // Throw away everything and schedule our reads. |
| 125 algorithm_->FlushBuffers(); | 133 audio_time_buffered_ = kNoTimestamp(); |
| 134 current_time_ = kNoTimestamp(); | |
| 135 received_end_of_stream_ = false; | |
| 136 rendered_end_of_stream_ = false; | |
| 137 preroll_aborted_ = false; | |
| 126 | 138 |
| 127 if (stopped_) | 139 // |algorithm_| will request more reads. |
| 128 return; | 140 algorithm_->FlushBuffers(); |
| 141 earliest_end_time_ = base::Time::Now(); | |
| 142 } | |
| 129 | 143 |
| 130 // Pause and flush the stream when we preroll to a new location. | 144 // Pause and flush the stream when we preroll to a new location. |
| 131 earliest_end_time_ = base::Time::Now(); | |
| 132 sink_->Pause(true); | 145 sink_->Pause(true); |
| 133 } | 146 } |
| 134 | 147 |
| 135 void AudioRendererImpl::Initialize(const scoped_refptr<DemuxerStream>& stream, | 148 void AudioRendererImpl::Initialize(const scoped_refptr<DemuxerStream>& stream, |
| 136 const AudioDecoderList& decoders, | 149 const AudioDecoderList& decoders, |
| 137 const PipelineStatusCB& init_cb, | 150 const PipelineStatusCB& init_cb, |
| 138 const StatisticsCB& statistics_cb, | 151 const StatisticsCB& statistics_cb, |
| 139 const base::Closure& underflow_cb, | 152 const base::Closure& underflow_cb, |
| 140 const TimeCB& time_cb, | 153 const TimeCB& time_cb, |
| 141 const base::Closure& ended_cb, | 154 const base::Closure& ended_cb, |
| 142 const base::Closure& disabled_cb, | 155 const base::Closure& disabled_cb, |
| 143 const PipelineStatusCB& error_cb) { | 156 const PipelineStatusCB& error_cb) { |
| 144 base::AutoLock auto_lock(lock_); | 157 DCHECK(pipeline_thread_checker_.CalledOnValidThread()); |
| 145 DCHECK(stream); | 158 DCHECK(stream); |
| 146 DCHECK(!decoders.empty()); | 159 DCHECK(!decoders.empty()); |
| 147 DCHECK_EQ(stream->type(), DemuxerStream::AUDIO); | 160 DCHECK_EQ(stream->type(), DemuxerStream::AUDIO); |
| 148 DCHECK(!init_cb.is_null()); | 161 DCHECK(!init_cb.is_null()); |
| 149 DCHECK(!statistics_cb.is_null()); | 162 DCHECK(!statistics_cb.is_null()); |
| 150 DCHECK(!underflow_cb.is_null()); | 163 DCHECK(!underflow_cb.is_null()); |
| 151 DCHECK(!time_cb.is_null()); | 164 DCHECK(!time_cb.is_null()); |
| 152 DCHECK(!ended_cb.is_null()); | 165 DCHECK(!ended_cb.is_null()); |
| 153 DCHECK(!disabled_cb.is_null()); | 166 DCHECK(!disabled_cb.is_null()); |
| 154 DCHECK(!error_cb.is_null()); | 167 DCHECK(!error_cb.is_null()); |
| 155 DCHECK_EQ(kUninitialized, state_); | 168 DCHECK_EQ(kUninitialized, state_); |
| 169 DCHECK(sink_); | |
| 156 | 170 |
| 157 init_cb_ = init_cb; | 171 init_cb_ = init_cb; |
| 158 statistics_cb_ = statistics_cb; | 172 statistics_cb_ = statistics_cb; |
| 159 underflow_cb_ = underflow_cb; | 173 underflow_cb_ = underflow_cb; |
| 160 time_cb_ = time_cb; | 174 time_cb_ = time_cb; |
| 161 ended_cb_ = ended_cb; | 175 ended_cb_ = ended_cb; |
| 162 disabled_cb_ = disabled_cb; | 176 disabled_cb_ = disabled_cb; |
| 163 error_cb_ = error_cb; | 177 error_cb_ = error_cb; |
| 164 | 178 |
| 165 scoped_ptr<AudioDecoderList> decoder_list(new AudioDecoderList(decoders)); | 179 scoped_ptr<AudioDecoderList> decoder_list(new AudioDecoderList(decoders)); |
| 166 InitializeNextDecoder(stream, decoder_list.Pass()); | 180 InitializeNextDecoder(stream, decoder_list.Pass()); |
| 167 } | 181 } |
| 168 | 182 |
| 169 void AudioRendererImpl::InitializeNextDecoder( | 183 void AudioRendererImpl::InitializeNextDecoder( |
| 170 const scoped_refptr<DemuxerStream>& demuxer_stream, | 184 const scoped_refptr<DemuxerStream>& demuxer_stream, |
| 171 scoped_ptr<AudioDecoderList> decoders) { | 185 scoped_ptr<AudioDecoderList> decoders) { |
| 172 lock_.AssertAcquired(); | 186 DCHECK(pipeline_thread_checker_.CalledOnValidThread()); |
| 173 DCHECK(!decoders->empty()); | 187 DCHECK(!decoders->empty()); |
| 174 | 188 |
| 175 scoped_refptr<AudioDecoder> decoder = decoders->front(); | 189 scoped_refptr<AudioDecoder> decoder = decoders->front(); |
| 176 decoders->pop_front(); | 190 decoders->pop_front(); |
| 177 | 191 |
| 178 DCHECK(decoder); | 192 DCHECK(decoder); |
| 179 decoder_ = decoder; | 193 decoder_ = decoder; |
| 180 | |
| 181 base::AutoUnlock auto_unlock(lock_); | |
| 182 decoder->Initialize( | 194 decoder->Initialize( |
| 183 demuxer_stream, | 195 demuxer_stream, BindToLoop(base::MessageLoopProxy::current(), base::Bind( |
| 184 base::Bind(&AudioRendererImpl::OnDecoderInitDone, this, | 196 &AudioRendererImpl::OnDecoderInitDone, this, demuxer_stream, |
| 185 demuxer_stream, | 197 base::Passed(&decoders))), |
| 186 base::Passed(&decoders)), | |
| 187 statistics_cb_); | 198 statistics_cb_); |
| 188 } | 199 } |
| 189 | 200 |
| 190 void AudioRendererImpl::OnDecoderInitDone( | 201 void AudioRendererImpl::OnDecoderInitDone( |
| 191 const scoped_refptr<DemuxerStream>& demuxer_stream, | 202 const scoped_refptr<DemuxerStream>& demuxer_stream, |
| 192 scoped_ptr<AudioDecoderList> decoders, | 203 scoped_ptr<AudioDecoderList> decoders, |
| 193 PipelineStatus status) { | 204 PipelineStatus status) { |
| 194 base::AutoLock auto_lock(lock_); | 205 DCHECK(pipeline_thread_checker_.CalledOnValidThread()); |
| 195 | 206 |
| 196 if (state_ == kStopped) { | 207 if (state_ == kStopped) { |
| 197 DCHECK(stopped_); | 208 DCHECK(!sink_); |
| 198 return; | 209 return; |
| 199 } | 210 } |
| 200 | 211 |
| 201 if (!decoders->empty() && status == DECODER_ERROR_NOT_SUPPORTED) { | 212 if (!decoders->empty() && status == DECODER_ERROR_NOT_SUPPORTED) { |
| 202 InitializeNextDecoder(demuxer_stream, decoders.Pass()); | 213 InitializeNextDecoder(demuxer_stream, decoders.Pass()); |
| 203 return; | 214 return; |
| 204 } | 215 } |
| 205 | 216 |
| 206 if (status != PIPELINE_OK) { | 217 if (status != PIPELINE_OK) { |
| 207 base::ResetAndReturn(&init_cb_).Run(status); | 218 base::ResetAndReturn(&init_cb_).Run(status); |
| 208 return; | 219 return; |
| 209 } | 220 } |
| 210 | 221 |
| 211 // We're all good! Continue initializing the rest of the audio renderer based | 222 // We're all good! Continue initializing the rest of the audio renderer based |
| 212 // on the decoder format. | 223 // on the decoder format. |
| 213 | 224 |
| 214 ChannelLayout channel_layout = decoder_->channel_layout(); | 225 ChannelLayout channel_layout = decoder_->channel_layout(); |
| 215 int channels = ChannelLayoutToChannelCount(channel_layout); | 226 int channels = ChannelLayoutToChannelCount(channel_layout); |
| 216 int bits_per_channel = decoder_->bits_per_channel(); | 227 int bits_per_channel = decoder_->bits_per_channel(); |
| 217 int sample_rate = decoder_->samples_per_second(); | 228 int sample_rate = decoder_->samples_per_second(); |
| 218 // TODO(vrk): Add method to AudioDecoder to compute bytes per frame. | |
| 219 bytes_per_frame_ = channels * bits_per_channel / 8; | |
| 220 | 229 |
| 221 algorithm_.reset(new AudioRendererAlgorithm()); | 230 algorithm_.reset(new AudioRendererAlgorithm()); |
|
Ami GONE FROM CHROMIUM
2012/11/03 00:11:45
doesn't this need to be done under lock?
DaleCurtis
2012/11/03 01:32:47
No since it's not used until Initialize() complete
| |
| 222 if (!algorithm_->ValidateConfig(channels, sample_rate, bits_per_channel)) { | 231 if (!algorithm_->ValidateConfig(channels, sample_rate, bits_per_channel)) { |
| 223 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED); | 232 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED); |
| 224 return; | 233 return; |
| 225 } | 234 } |
| 226 | 235 |
| 227 algorithm_->Initialize( | 236 algorithm_->Initialize( |
| 228 channels, sample_rate, bits_per_channel, 0.0f, | 237 channels, sample_rate, bits_per_channel, 0.0f, |
| 229 base::Bind(&AudioRendererImpl::ScheduleRead_Locked, this)); | 238 base::Bind(&AudioRendererImpl::ScheduleRead_Locked, this)); |
| 230 | 239 |
| 231 int buffer_size = GetHighLatencyOutputBufferSize(sample_rate); | 240 int buffer_size = GetHighLatencyOutputBufferSize(sample_rate); |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 260 // purposes it's okay that this buffer size might lead to jitter since it's | 269 // purposes it's okay that this buffer size might lead to jitter since it's |
| 261 // not a multiple of the hardware buffer size. | 270 // not a multiple of the hardware buffer size. |
| 262 format = AudioParameters::AUDIO_PCM_LOW_LATENCY; | 271 format = AudioParameters::AUDIO_PCM_LOW_LATENCY; |
| 263 buffer_size = 2048; | 272 buffer_size = 2048; |
| 264 } | 273 } |
| 265 #endif | 274 #endif |
| 266 | 275 |
| 267 audio_parameters_ = AudioParameters( | 276 audio_parameters_ = AudioParameters( |
| 268 format, channel_layout, sample_rate, bits_per_channel, buffer_size); | 277 format, channel_layout, sample_rate, bits_per_channel, buffer_size); |
| 269 | 278 |
| 279 state_ = kPaused; | |
| 280 | |
| 270 sink_->Initialize(audio_parameters_, this); | 281 sink_->Initialize(audio_parameters_, this); |
| 271 sink_->Start(); | 282 sink_->Start(); |
| 272 | 283 |
| 273 state_ = kPaused; | |
| 274 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); | 284 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); |
| 275 } | 285 } |
| 276 | 286 |
| 277 void AudioRendererImpl::ResumeAfterUnderflow(bool buffer_more_audio) { | 287 void AudioRendererImpl::ResumeAfterUnderflow(bool buffer_more_audio) { |
| 288 DCHECK(pipeline_thread_checker_.CalledOnValidThread()); | |
| 278 base::AutoLock auto_lock(lock_); | 289 base::AutoLock auto_lock(lock_); |
| 279 if (state_ == kUnderflow) { | 290 if (state_ == kUnderflow) { |
| 280 // The "&& preroll_aborted_" is a hack. If preroll is aborted, then we | 291 // The "&& preroll_aborted_" is a hack. If preroll is aborted, then we |
| 281 // shouldn't even reach the kUnderflow state to begin with. But for now | 292 // shouldn't even reach the kUnderflow state to begin with. But for now |
| 282 // we're just making sure that the audio buffer capacity (i.e. the | 293 // we're just making sure that the audio buffer capacity (i.e. the |
| 283 // number of bytes that need to be buffered for preroll to complete) | 294 // number of bytes that need to be buffered for preroll to complete) |
| 284 // does not increase due to an aborted preroll. | 295 // does not increase due to an aborted preroll. |
| 285 // TODO(vrk): Fix this bug correctly! (crbug.com/151352) | 296 // TODO(vrk): Fix this bug correctly! (crbug.com/151352) |
| 286 if (buffer_more_audio && !preroll_aborted_) | 297 if (buffer_more_audio && !preroll_aborted_) |
| 287 algorithm_->IncreaseQueueCapacity(); | 298 algorithm_->IncreaseQueueCapacity(); |
| 288 | 299 |
| 289 state_ = kRebuffering; | 300 state_ = kRebuffering; |
| 290 } | 301 } |
| 291 } | 302 } |
| 292 | 303 |
| 293 void AudioRendererImpl::SetVolume(float volume) { | 304 void AudioRendererImpl::SetVolume(float volume) { |
| 294 if (stopped_) | 305 DCHECK(pipeline_thread_checker_.CalledOnValidThread()); |
| 295 return; | 306 DCHECK(sink_); |
| 296 sink_->SetVolume(volume); | 307 sink_->SetVolume(volume); |
| 297 } | 308 } |
| 298 | 309 |
| 299 AudioRendererImpl::~AudioRendererImpl() { | 310 AudioRendererImpl::~AudioRendererImpl() { |
| 300 // Stop() should have been called and |algorithm_| should have been destroyed. | 311 // Stop() should have been called and |algorithm_| should have been destroyed. |
| 301 DCHECK(state_ == kUninitialized || state_ == kStopped); | 312 DCHECK(state_ == kUninitialized || state_ == kStopped); |
| 302 DCHECK(!algorithm_.get()); | 313 DCHECK(!algorithm_.get()); |
| 303 } | 314 } |
| 304 | 315 |
| 305 void AudioRendererImpl::DecodedAudioReady(AudioDecoder::Status status, | 316 void AudioRendererImpl::DecodedAudioReady(AudioDecoder::Status status, |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 369 | 380 |
| 370 void AudioRendererImpl::ScheduleRead_Locked() { | 381 void AudioRendererImpl::ScheduleRead_Locked() { |
| 371 lock_.AssertAcquired(); | 382 lock_.AssertAcquired(); |
| 372 if (pending_read_ || state_ == kPaused) | 383 if (pending_read_ || state_ == kPaused) |
| 373 return; | 384 return; |
| 374 pending_read_ = true; | 385 pending_read_ = true; |
| 375 decoder_->Read(base::Bind(&AudioRendererImpl::DecodedAudioReady, this)); | 386 decoder_->Read(base::Bind(&AudioRendererImpl::DecodedAudioReady, this)); |
| 376 } | 387 } |
| 377 | 388 |
| 378 void AudioRendererImpl::SetPlaybackRate(float playback_rate) { | 389 void AudioRendererImpl::SetPlaybackRate(float playback_rate) { |
| 390 DCHECK(pipeline_thread_checker_.CalledOnValidThread()); | |
| 379 DCHECK_LE(0.0f, playback_rate); | 391 DCHECK_LE(0.0f, playback_rate); |
| 392 DCHECK(sink_); | |
| 380 | 393 |
| 381 if (!stopped_) { | 394 // We have two cases here: |
| 382 // We have two cases here: | 395 // Play: current_playback_rate == 0.0 && playback_rate != 0.0 |
| 383 // Play: GetPlaybackRate() == 0.0 && playback_rate != 0.0 | 396 // Pause: current_playback_rate != 0.0 && playback_rate == 0.0 |
| 384 // Pause: GetPlaybackRate() != 0.0 && playback_rate == 0.0 | 397 float current_playback_rate = algorithm_->playback_rate(); |
| 385 if (GetPlaybackRate() == 0.0f && playback_rate != 0.0f) { | 398 if (current_playback_rate == 0.0f && playback_rate != 0.0f) { |
| 386 DoPlay(); | 399 DoPlay(); |
| 387 } else if (GetPlaybackRate() != 0.0f && playback_rate == 0.0f) { | 400 } else if (current_playback_rate != 0.0f && playback_rate == 0.0f) { |
| 388 // Pause is easy, we can always pause. | 401 // Pause is easy, we can always pause. |
| 389 DoPause(); | 402 DoPause(); |
| 390 } | |
| 391 } | 403 } |
| 392 | 404 |
| 393 base::AutoLock auto_lock(lock_); | 405 base::AutoLock auto_lock(lock_); |
| 394 algorithm_->SetPlaybackRate(playback_rate); | 406 algorithm_->SetPlaybackRate(playback_rate); |
| 395 } | 407 } |
| 396 | 408 |
| 397 float AudioRendererImpl::GetPlaybackRate() { | 409 float AudioRendererImpl::GetPlaybackRate() { |
| 398 base::AutoLock auto_lock(lock_); | 410 base::AutoLock auto_lock(lock_); |
| 399 return algorithm_->playback_rate(); | 411 return algorithm_ ? algorithm_->playback_rate() : 0; |
|
Ami GONE FROM CHROMIUM
2012/11/03 00:11:45
How can this fail to be non-NULL?
Is it the case
DaleCurtis
2012/11/03 01:32:47
If Stop() happens on the pipeline thread and destr
| |
| 400 } | 412 } |
| 401 | 413 |
| 402 bool AudioRendererImpl::IsBeforePrerollTime( | 414 bool AudioRendererImpl::IsBeforePrerollTime( |
| 403 const scoped_refptr<Buffer>& buffer) { | 415 const scoped_refptr<Buffer>& buffer) { |
| 404 return (state_ == kPrerolling) && buffer && !buffer->IsEndOfStream() && | 416 return (state_ == kPrerolling) && buffer && !buffer->IsEndOfStream() && |
| 405 (buffer->GetTimestamp() + buffer->GetDuration()) < preroll_timestamp_; | 417 (buffer->GetTimestamp() + buffer->GetDuration()) < preroll_timestamp_; |
| 406 } | 418 } |
| 407 | 419 |
| 408 int AudioRendererImpl::Render(AudioBus* audio_bus, | 420 int AudioRendererImpl::Render(AudioBus* audio_bus, |
| 409 int audio_delay_milliseconds) { | 421 int audio_delay_milliseconds) { |
| 410 if (stopped_ || GetPlaybackRate() == 0.0f) { | 422 float playback_rate = GetPlaybackRate(); |
| 423 if (playback_rate == 0.0f) { | |
|
Ami GONE FROM CHROMIUM
2012/11/03 00:11:45
Part of the point of my previous comment on this c
DaleCurtis
2012/11/03 01:32:47
WHY U SO DIFFICULT! Fixed, don't blame me as the p
| |
| 411 // Output silence if stopped. | 424 // Output silence if stopped. |
| 412 audio_bus->Zero(); | 425 audio_bus->Zero(); |
| 413 return 0; | 426 return 0; |
| 414 } | 427 } |
| 415 | 428 |
| 416 // Adjust the playback delay. | 429 // Adjust the playback delay. |
| 417 base::TimeDelta request_delay = | 430 base::TimeDelta request_delay = |
| 418 base::TimeDelta::FromMilliseconds(audio_delay_milliseconds); | 431 base::TimeDelta::FromMilliseconds(audio_delay_milliseconds); |
| 419 | 432 |
| 420 // Finally we need to adjust the delay according to playback rate. | 433 // Finally we need to adjust the delay according to playback rate. |
| 421 if (GetPlaybackRate() != 1.0f) { | 434 if (playback_rate != 1.0f) { |
| 422 request_delay = base::TimeDelta::FromMicroseconds( | 435 request_delay = base::TimeDelta::FromMicroseconds(static_cast<int64>(ceil( |
| 423 static_cast<int64>(ceil(request_delay.InMicroseconds() * | 436 request_delay.InMicroseconds() * playback_rate))); |
| 424 GetPlaybackRate()))); | |
| 425 } | 437 } |
| 426 | 438 |
| 427 int bytes_per_frame = audio_parameters_.GetBytesPerFrame(); | 439 int bytes_per_frame = audio_parameters_.GetBytesPerFrame(); |
| 428 | 440 |
| 429 const int buf_size = audio_bus->frames() * bytes_per_frame; | 441 const int buf_size = audio_bus->frames() * bytes_per_frame; |
| 430 scoped_array<uint8> buf(new uint8[buf_size]); | 442 scoped_array<uint8> buf(new uint8[buf_size]); |
| 431 | 443 |
| 432 int frames_filled = FillBuffer(buf.get(), audio_bus->frames(), request_delay); | 444 int frames_filled = FillBuffer(buf.get(), audio_bus->frames(), request_delay); |
| 433 int bytes_filled = frames_filled * bytes_per_frame; | 445 int bytes_filled = frames_filled * bytes_per_frame; |
| 434 DCHECK_LE(bytes_filled, buf_size); | 446 DCHECK_LE(bytes_filled, buf_size); |
| 435 UpdateEarliestEndTime(bytes_filled, request_delay, base::Time::Now()); | 447 UpdateEarliestEndTime( |
| 448 bytes_filled, playback_rate, request_delay, base::Time::Now()); | |
| 436 | 449 |
| 437 // Deinterleave audio data into the output bus. | 450 // Deinterleave audio data into the output bus. |
| 438 audio_bus->FromInterleaved( | 451 audio_bus->FromInterleaved( |
| 439 buf.get(), frames_filled, audio_parameters_.bits_per_sample() / 8); | 452 buf.get(), frames_filled, audio_parameters_.bits_per_sample() / 8); |
| 440 | 453 |
| 441 return frames_filled; | 454 return frames_filled; |
| 442 } | 455 } |
| 443 | 456 |
| 444 uint32 AudioRendererImpl::FillBuffer(uint8* dest, | 457 uint32 AudioRendererImpl::FillBuffer(uint8* dest, |
| 445 uint32 requested_frames, | 458 uint32 requested_frames, |
| 446 const base::TimeDelta& playback_delay) { | 459 const base::TimeDelta& playback_delay) { |
| 447 base::TimeDelta current_time = kNoTimestamp(); | 460 base::TimeDelta current_time = kNoTimestamp(); |
| 448 base::TimeDelta max_time = kNoTimestamp(); | 461 base::TimeDelta max_time = kNoTimestamp(); |
| 449 | 462 |
| 450 size_t frames_written = 0; | 463 size_t frames_written = 0; |
| 451 base::Closure underflow_cb; | 464 base::Closure underflow_cb; |
| 452 { | 465 { |
| 453 base::AutoLock auto_lock(lock_); | 466 base::AutoLock auto_lock(lock_); |
| 467 // Ensure Stop() hasn't destroyed our |algorithm_| on the pipeline thread. | |
| 468 if (!algorithm_) | |
| 469 return 0; | |
| 454 | 470 |
| 455 if (state_ == kRebuffering && algorithm_->IsQueueFull()) | 471 if (state_ == kRebuffering && algorithm_->IsQueueFull()) |
| 456 state_ = kPlaying; | 472 state_ = kPlaying; |
| 457 | 473 |
| 458 // Mute audio by returning 0 when not playing. | 474 // Mute audio by returning 0 when not playing. |
| 459 if (state_ != kPlaying) { | 475 if (state_ != kPlaying) { |
| 460 // TODO(scherkus): To keep the audio hardware busy we write at most 8k of | 476 // TODO(scherkus): To keep the audio hardware busy we write at most 8k of |
| 461 // zeros. This gets around the tricky situation of pausing and resuming | 477 // zeros. This gets around the tricky situation of pausing and resuming |
| 462 // the audio IPC layer in Chrome. Ideally, we should return zero and then | 478 // the audio IPC layer in Chrome. Ideally, we should return zero and then |
| 463 // the subclass can restart the conversation. | 479 // the subclass can restart the conversation. |
| 464 // | 480 // |
| 465 // This should get handled by the subclass http://crbug.com/106600 | 481 // This should get handled by the subclass http://crbug.com/106600 |
| 466 const uint32 kZeroLength = 8192; | 482 const uint32 kZeroLength = 8192; |
| 467 size_t zeros_to_write = | 483 size_t zeros_to_write = std::min( |
| 468 std::min(kZeroLength, requested_frames * bytes_per_frame_); | 484 kZeroLength, requested_frames * audio_parameters_.GetBytesPerFrame()); |
| 469 memset(dest, 0, zeros_to_write); | 485 memset(dest, 0, zeros_to_write); |
| 470 return zeros_to_write / bytes_per_frame_; | 486 return zeros_to_write / audio_parameters_.GetBytesPerFrame(); |
| 471 } | 487 } |
| 472 | 488 |
| 473 // We use the following conditions to determine end of playback: | 489 // We use the following conditions to determine end of playback: |
| 474 // 1) Algorithm can not fill the audio callback buffer | 490 // 1) Algorithm can not fill the audio callback buffer |
| 475 // 2) We received an end of stream buffer | 491 // 2) We received an end of stream buffer |
| 476 // 3) We haven't already signalled that we've ended | 492 // 3) We haven't already signalled that we've ended |
| 477 // 4) Our estimated earliest end time has expired | 493 // 4) Our estimated earliest end time has expired |
| 478 // | 494 // |
| 479 // TODO(enal): we should replace (4) with a check that the browser has no | 495 // TODO(enal): we should replace (4) with a check that the browser has no |
| 480 // more audio data or at least use a delayed callback. | 496 // more audio data or at least use a delayed callback. |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 539 time_cb_.Run(current_time, max_time); | 555 time_cb_.Run(current_time, max_time); |
| 540 } | 556 } |
| 541 | 557 |
| 542 if (!underflow_cb.is_null()) | 558 if (!underflow_cb.is_null()) |
| 543 underflow_cb.Run(); | 559 underflow_cb.Run(); |
| 544 | 560 |
| 545 return frames_written; | 561 return frames_written; |
| 546 } | 562 } |
| 547 | 563 |
| 548 void AudioRendererImpl::UpdateEarliestEndTime(int bytes_filled, | 564 void AudioRendererImpl::UpdateEarliestEndTime(int bytes_filled, |
| 565 float playback_rate, | |
| 549 base::TimeDelta request_delay, | 566 base::TimeDelta request_delay, |
| 550 base::Time time_now) { | 567 base::Time time_now) { |
| 551 if (bytes_filled != 0) { | 568 if (bytes_filled != 0) { |
| 552 base::TimeDelta predicted_play_time = ConvertToDuration(bytes_filled); | 569 base::TimeDelta predicted_play_time = ConvertToDuration(bytes_filled); |
| 553 float playback_rate = GetPlaybackRate(); | |
| 554 if (playback_rate != 1.0f) { | 570 if (playback_rate != 1.0f) { |
| 555 predicted_play_time = base::TimeDelta::FromMicroseconds( | 571 predicted_play_time = base::TimeDelta::FromMicroseconds( |
| 556 static_cast<int64>(ceil(predicted_play_time.InMicroseconds() * | 572 static_cast<int64>(ceil(predicted_play_time.InMicroseconds() * |
| 557 playback_rate))); | 573 playback_rate))); |
| 558 } | 574 } |
| 559 earliest_end_time_ = | 575 |
| 560 std::max(earliest_end_time_, | 576 base::AutoLock auto_lock(lock_); |
| 561 time_now + request_delay + predicted_play_time); | 577 earliest_end_time_ = std::max( |
| 578 earliest_end_time_, time_now + request_delay + predicted_play_time); | |
| 562 } | 579 } |
| 563 } | 580 } |
| 564 | 581 |
| 565 base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) { | 582 base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) { |
| 566 int bytes_per_second = audio_parameters_.GetBytesPerSecond(); | 583 int bytes_per_second = audio_parameters_.GetBytesPerSecond(); |
| 567 CHECK(bytes_per_second); | 584 CHECK(bytes_per_second); |
| 568 return base::TimeDelta::FromMicroseconds( | 585 return base::TimeDelta::FromMicroseconds( |
| 569 base::Time::kMicrosecondsPerSecond * bytes / bytes_per_second); | 586 base::Time::kMicrosecondsPerSecond * bytes / bytes_per_second); |
| 570 } | 587 } |
| 571 | 588 |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 598 case kUnderflow: | 615 case kUnderflow: |
| 599 case kRebuffering: | 616 case kRebuffering: |
| 600 case kStopped: | 617 case kStopped: |
| 601 if (status != PIPELINE_OK) | 618 if (status != PIPELINE_OK) |
| 602 error_cb_.Run(status); | 619 error_cb_.Run(status); |
| 603 return; | 620 return; |
| 604 } | 621 } |
| 605 } | 622 } |
| 606 | 623 |
| 607 } // namespace media | 624 } // namespace media |
| OLD | NEW |