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 |