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 "content/renderer/media/track_audio_renderer.h" | 5 #include "content/renderer/media/track_audio_renderer.h" |
6 | 6 |
7 #include "base/location.h" | 7 #include "base/location.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
10 #include "base/synchronization/lock.h" | 10 #include "base/synchronization/lock.h" |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 output_device_id_(device_id), | 130 output_device_id_(device_id), |
131 security_origin_(security_origin), | 131 security_origin_(security_origin), |
132 volume_(0.0), | 132 volume_(0.0), |
133 sink_started_(false) { | 133 sink_started_(false) { |
134 DCHECK(MediaStreamAudioTrack::From(audio_track_)); | 134 DCHECK(MediaStreamAudioTrack::From(audio_track_)); |
135 DVLOG(1) << "TrackAudioRenderer::TrackAudioRenderer()"; | 135 DVLOG(1) << "TrackAudioRenderer::TrackAudioRenderer()"; |
136 } | 136 } |
137 | 137 |
138 TrackAudioRenderer::~TrackAudioRenderer() { | 138 TrackAudioRenderer::~TrackAudioRenderer() { |
139 DCHECK(task_runner_->BelongsToCurrentThread()); | 139 DCHECK(task_runner_->BelongsToCurrentThread()); |
140 DCHECK(!sink_.get()); | 140 DCHECK(!sink_); |
141 DVLOG(1) << "TrackAudioRenderer::~TrackAudioRenderer()"; | 141 DVLOG(1) << "TrackAudioRenderer::~TrackAudioRenderer()"; |
142 } | 142 } |
143 | 143 |
144 void TrackAudioRenderer::Start() { | 144 void TrackAudioRenderer::Start() { |
145 DVLOG(1) << "TrackAudioRenderer::Start()"; | 145 DVLOG(1) << "TrackAudioRenderer::Start()"; |
146 DCHECK(task_runner_->BelongsToCurrentThread()); | 146 DCHECK(task_runner_->BelongsToCurrentThread()); |
147 DCHECK_EQ(playing_, false); | 147 DCHECK_EQ(playing_, false); |
148 | 148 |
149 // We get audio data from |audio_track_|... | 149 // We get audio data from |audio_track_|... |
150 MediaStreamAudioSink::AddToAudioTrack(this, audio_track_); | 150 MediaStreamAudioSink::AddToAudioTrack(this, audio_track_); |
151 // ...and |sink_| will get audio data from us. | 151 // ...and |sink_| will get audio data from us. |
152 DCHECK(!sink_.get()); | 152 DCHECK(!sink_); |
153 sink_ = AudioDeviceFactory::NewAudioRendererSink( | 153 sink_ = AudioDeviceFactory::NewAudioRendererSink( |
154 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, | 154 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, |
155 session_id_, output_device_id_, security_origin_); | 155 session_id_, output_device_id_, security_origin_); |
156 | 156 |
157 base::AutoLock auto_lock(thread_lock_); | 157 base::AutoLock auto_lock(thread_lock_); |
158 prior_elapsed_render_time_ = base::TimeDelta(); | 158 prior_elapsed_render_time_ = base::TimeDelta(); |
159 num_samples_rendered_ = 0; | 159 num_samples_rendered_ = 0; |
160 } | 160 } |
161 | 161 |
162 void TrackAudioRenderer::Stop() { | 162 void TrackAudioRenderer::Stop() { |
163 DVLOG(1) << "TrackAudioRenderer::Stop()"; | 163 DVLOG(1) << "TrackAudioRenderer::Stop()"; |
164 DCHECK(task_runner_->BelongsToCurrentThread()); | 164 DCHECK(task_runner_->BelongsToCurrentThread()); |
165 | 165 |
166 Pause(); | 166 Pause(); |
167 | 167 |
168 // Stop the output audio stream, i.e, stop asking for data to render. | 168 // Stop the output audio stream, i.e, stop asking for data to render. |
169 // It is safer to call Stop() on the |sink_| to clean up the resources even | 169 // It is safer to call Stop() on the |sink_| to clean up the resources even |
170 // when the |sink_| is never started. | 170 // when the |sink_| is never started. |
171 if (sink_.get()) { | 171 if (sink_) { |
172 sink_->Stop(); | 172 sink_->Stop(); |
173 sink_ = NULL; | 173 sink_ = NULL; |
174 } | 174 } |
175 | 175 |
176 if (!sink_started_ && IsLocalRenderer()) { | 176 if (!sink_started_ && IsLocalRenderer()) { |
177 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates", | 177 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates", |
178 kSinkNeverStarted, kSinkStatesMax); | 178 kSinkNeverStarted, kSinkStatesMax); |
179 } | 179 } |
180 sink_started_ = false; | 180 sink_started_ = false; |
181 | 181 |
182 // Ensure that the capturer stops feeding us with captured audio. | 182 // Ensure that the capturer stops feeding us with captured audio. |
183 MediaStreamAudioSink::RemoveFromAudioTrack(this, audio_track_); | 183 MediaStreamAudioSink::RemoveFromAudioTrack(this, audio_track_); |
184 } | 184 } |
185 | 185 |
186 void TrackAudioRenderer::Play() { | 186 void TrackAudioRenderer::Play() { |
187 DVLOG(1) << "TrackAudioRenderer::Play()"; | 187 DVLOG(1) << "TrackAudioRenderer::Play()"; |
188 DCHECK(task_runner_->BelongsToCurrentThread()); | 188 DCHECK(task_runner_->BelongsToCurrentThread()); |
189 | 189 |
190 if (!sink_.get()) | 190 if (!sink_) |
191 return; | 191 return; |
192 | 192 |
193 playing_ = true; | 193 playing_ = true; |
194 | 194 |
195 MaybeStartSink(); | 195 MaybeStartSink(); |
196 } | 196 } |
197 | 197 |
198 void TrackAudioRenderer::Pause() { | 198 void TrackAudioRenderer::Pause() { |
199 DVLOG(1) << "TrackAudioRenderer::Pause()"; | 199 DVLOG(1) << "TrackAudioRenderer::Pause()"; |
200 DCHECK(task_runner_->BelongsToCurrentThread()); | 200 DCHECK(task_runner_->BelongsToCurrentThread()); |
201 | 201 |
202 if (!sink_.get()) | 202 if (!sink_) |
203 return; | 203 return; |
204 | 204 |
205 playing_ = false; | 205 playing_ = false; |
206 | 206 |
207 base::AutoLock auto_lock(thread_lock_); | 207 base::AutoLock auto_lock(thread_lock_); |
208 HaltAudioFlowWhileLockHeld(); | 208 HaltAudioFlowWhileLockHeld(); |
209 } | 209 } |
210 | 210 |
211 void TrackAudioRenderer::SetVolume(float volume) { | 211 void TrackAudioRenderer::SetVolume(float volume) { |
212 DVLOG(1) << "TrackAudioRenderer::SetVolume(" << volume << ")"; | 212 DVLOG(1) << "TrackAudioRenderer::SetVolume(" << volume << ")"; |
213 DCHECK(task_runner_->BelongsToCurrentThread()); | 213 DCHECK(task_runner_->BelongsToCurrentThread()); |
214 | 214 |
215 // Cache the volume. Whenever |sink_| is re-created, call SetVolume() with | 215 // Cache the volume. Whenever |sink_| is re-created, call SetVolume() with |
216 // this cached volume. | 216 // this cached volume. |
217 volume_ = volume; | 217 volume_ = volume; |
218 if (sink_.get()) | 218 if (sink_) |
219 sink_->SetVolume(volume); | 219 sink_->SetVolume(volume); |
220 } | 220 } |
221 | 221 |
222 media::OutputDevice* TrackAudioRenderer::GetOutputDevice() { | 222 media::OutputDevice* TrackAudioRenderer::GetOutputDevice() { |
223 DCHECK(task_runner_->BelongsToCurrentThread()); | 223 DCHECK(task_runner_->BelongsToCurrentThread()); |
224 return this; | 224 return sink_ ? sink_->GetOutputDevice() : nullptr; |
225 } | 225 } |
226 | 226 |
227 base::TimeDelta TrackAudioRenderer::GetCurrentRenderTime() const { | 227 base::TimeDelta TrackAudioRenderer::GetCurrentRenderTime() const { |
228 DCHECK(task_runner_->BelongsToCurrentThread()); | 228 DCHECK(task_runner_->BelongsToCurrentThread()); |
229 base::AutoLock auto_lock(thread_lock_); | 229 base::AutoLock auto_lock(thread_lock_); |
230 if (source_params_.IsValid()) { | 230 if (source_params_.IsValid()) { |
231 return ComputeTotalElapsedRenderTime(prior_elapsed_render_time_, | 231 return ComputeTotalElapsedRenderTime(prior_elapsed_render_time_, |
232 num_samples_rendered_, | 232 num_samples_rendered_, |
233 source_params_.sample_rate()); | 233 source_params_.sample_rate()); |
234 } | 234 } |
(...skipping 14 matching lines...) Expand all Loading... |
249 | 249 |
250 { | 250 { |
251 base::AutoLock auto_lock(thread_lock_); | 251 base::AutoLock auto_lock(thread_lock_); |
252 HaltAudioFlowWhileLockHeld(); | 252 HaltAudioFlowWhileLockHeld(); |
253 } | 253 } |
254 | 254 |
255 scoped_refptr<media::AudioRendererSink> new_sink = | 255 scoped_refptr<media::AudioRendererSink> new_sink = |
256 AudioDeviceFactory::NewAudioRendererSink( | 256 AudioDeviceFactory::NewAudioRendererSink( |
257 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, | 257 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, |
258 session_id_, device_id, security_origin); | 258 session_id_, device_id, security_origin); |
| 259 |
| 260 media::OutputDevice* device = new_sink->GetOutputDevice(); |
259 media::OutputDeviceStatus new_sink_status = | 261 media::OutputDeviceStatus new_sink_status = |
260 new_sink->GetOutputDevice()->GetDeviceStatus(); | 262 device ? device->GetDeviceStatus() |
| 263 : media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL; |
261 if (new_sink_status != media::OUTPUT_DEVICE_STATUS_OK) { | 264 if (new_sink_status != media::OUTPUT_DEVICE_STATUS_OK) { |
262 callback.Run(new_sink_status); | 265 callback.Run(new_sink_status); |
263 return; | 266 return; |
264 } | 267 } |
265 | 268 |
266 output_device_id_ = device_id; | 269 output_device_id_ = device_id; |
267 security_origin_ = security_origin; | 270 security_origin_ = security_origin; |
268 bool was_sink_started = sink_started_; | 271 bool was_sink_started = sink_started_; |
269 | 272 |
270 if (sink_.get()) | 273 if (sink_) |
271 sink_->Stop(); | 274 sink_->Stop(); |
272 | 275 |
273 sink_started_ = false; | 276 sink_started_ = false; |
274 sink_ = new_sink; | 277 sink_ = new_sink; |
275 if (was_sink_started) | 278 if (was_sink_started) |
276 MaybeStartSink(); | 279 MaybeStartSink(); |
277 | 280 |
278 callback.Run(media::OUTPUT_DEVICE_STATUS_OK); | 281 callback.Run(media::OUTPUT_DEVICE_STATUS_OK); |
279 } | 282 } |
280 | 283 |
281 media::AudioParameters TrackAudioRenderer::GetOutputParameters() { | |
282 DCHECK(task_runner_->BelongsToCurrentThread()); | |
283 if (!sink_ || !source_params_.IsValid()) | |
284 return media::AudioParameters(); | |
285 | |
286 // Output parameters consist of the same channel layout and sample rate as the | |
287 // source, but having the buffer duration preferred by the hardware. | |
288 const media::AudioParameters& preferred_params = | |
289 sink_->GetOutputDevice()->GetOutputParameters(); | |
290 return media::AudioParameters( | |
291 preferred_params.format(), source_params_.channel_layout(), | |
292 source_params_.sample_rate(), source_params_.bits_per_sample(), | |
293 preferred_params.frames_per_buffer() * source_params_.sample_rate() / | |
294 preferred_params.sample_rate()); | |
295 } | |
296 | |
297 media::OutputDeviceStatus TrackAudioRenderer::GetDeviceStatus() { | |
298 DCHECK(task_runner_->BelongsToCurrentThread()); | |
299 if (!sink_.get()) | |
300 return media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL; | |
301 | |
302 return sink_->GetOutputDevice()->GetDeviceStatus(); | |
303 } | |
304 | |
305 void TrackAudioRenderer::MaybeStartSink() { | 284 void TrackAudioRenderer::MaybeStartSink() { |
306 DCHECK(task_runner_->BelongsToCurrentThread()); | 285 DCHECK(task_runner_->BelongsToCurrentThread()); |
307 DVLOG(1) << "TrackAudioRenderer::MaybeStartSink()"; | 286 DVLOG(1) << "TrackAudioRenderer::MaybeStartSink()"; |
308 | 287 |
309 if (!sink_.get() || !source_params_.IsValid() || !playing_) | 288 if (!(sink_ && source_params_.IsValid() && playing_)) |
310 return; | 289 return; |
311 | 290 |
312 // Re-create the AudioShifter to drop old audio data and reset to a starting | 291 // Re-create the AudioShifter to drop old audio data and reset to a starting |
313 // state. MaybeStartSink() is always called in a situation where either the | 292 // state. MaybeStartSink() is always called in a situation where either the |
314 // source or sink has changed somehow and so all of AudioShifter's internal | 293 // source or sink has changed somehow and so all of AudioShifter's internal |
315 // time-sync state is invalid. | 294 // time-sync state is invalid. |
316 CreateAudioShifter(); | 295 CreateAudioShifter(); |
317 | 296 |
318 if (sink_started_ || | 297 media::OutputDevice* device = sink_->GetOutputDevice(); |
319 sink_->GetOutputDevice()->GetDeviceStatus() != | 298 if (sink_started_ || !device || |
320 media::OUTPUT_DEVICE_STATUS_OK) { | 299 device->GetDeviceStatus() != media::OUTPUT_DEVICE_STATUS_OK) { |
321 return; | 300 return; |
322 } | 301 } |
323 | 302 |
| 303 // Output parameters consist of the same channel layout and sample rate as the |
| 304 // source, but having the buffer duration preferred by the hardware. |
| 305 const media::AudioParameters& preferred_params = |
| 306 device->GetOutputParameters(); |
| 307 media::AudioParameters sink_params( |
| 308 preferred_params.format(), source_params_.channel_layout(), |
| 309 source_params_.sample_rate(), source_params_.bits_per_sample(), |
| 310 preferred_params.frames_per_buffer() * source_params_.sample_rate() / |
| 311 preferred_params.sample_rate()); |
324 DVLOG(1) << ("TrackAudioRenderer::MaybeStartSink() -- Starting sink. " | 312 DVLOG(1) << ("TrackAudioRenderer::MaybeStartSink() -- Starting sink. " |
325 "source_params_={") | 313 "source_params_={") |
326 << source_params_.AsHumanReadableString() << "}, sink parameters={" | 314 << source_params_.AsHumanReadableString() << "}, sink parameters={" |
327 << GetOutputParameters().AsHumanReadableString() << '}'; | 315 << sink_params.AsHumanReadableString() << '}'; |
328 sink_->Initialize(GetOutputParameters(), this); | 316 sink_->Initialize(sink_params, this); |
329 sink_->Start(); | 317 sink_->Start(); |
330 sink_->SetVolume(volume_); | 318 sink_->SetVolume(volume_); |
331 sink_->Play(); // Not all the sinks play on start. | 319 sink_->Play(); // Not all the sinks play on start. |
332 sink_started_ = true; | 320 sink_started_ = true; |
333 if (IsLocalRenderer()) { | 321 if (IsLocalRenderer()) { |
334 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates", kSinkStarted, | 322 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates", kSinkStarted, |
335 kSinkStatesMax); | 323 kSinkStatesMax); |
336 } | 324 } |
337 } | 325 } |
338 | 326 |
339 void TrackAudioRenderer::ReconfigureSink(const media::AudioParameters& params) { | 327 void TrackAudioRenderer::ReconfigureSink(const media::AudioParameters& params) { |
340 DCHECK(task_runner_->BelongsToCurrentThread()); | 328 DCHECK(task_runner_->BelongsToCurrentThread()); |
341 | 329 |
342 DVLOG(1) << "TrackAudioRenderer::ReconfigureSink()"; | 330 DVLOG(1) << "TrackAudioRenderer::ReconfigureSink()"; |
343 | 331 |
344 if (source_params_.Equals(params)) | 332 if (source_params_.Equals(params)) |
345 return; | 333 return; |
346 source_params_ = params; | 334 source_params_ = params; |
347 | 335 |
348 if (!sink_.get()) | 336 if (!sink_) |
349 return; // TrackAudioRenderer has not yet been started. | 337 return; // TrackAudioRenderer has not yet been started. |
350 | 338 |
351 // Stop |sink_| and re-create a new one to be initialized with different audio | 339 // Stop |sink_| and re-create a new one to be initialized with different audio |
352 // parameters. Then, invoke MaybeStartSink() to restart everything again. | 340 // parameters. Then, invoke MaybeStartSink() to restart everything again. |
353 sink_->Stop(); | 341 sink_->Stop(); |
354 sink_started_ = false; | 342 sink_started_ = false; |
355 sink_ = AudioDeviceFactory::NewAudioRendererSink( | 343 sink_ = AudioDeviceFactory::NewAudioRendererSink( |
356 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, | 344 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, |
357 session_id_, output_device_id_, security_origin_); | 345 session_id_, output_device_id_, security_origin_); |
358 MaybeStartSink(); | 346 MaybeStartSink(); |
(...skipping 29 matching lines...) Expand all Loading... |
388 if (source_params_.IsValid()) { | 376 if (source_params_.IsValid()) { |
389 prior_elapsed_render_time_ = | 377 prior_elapsed_render_time_ = |
390 ComputeTotalElapsedRenderTime(prior_elapsed_render_time_, | 378 ComputeTotalElapsedRenderTime(prior_elapsed_render_time_, |
391 num_samples_rendered_, | 379 num_samples_rendered_, |
392 source_params_.sample_rate()); | 380 source_params_.sample_rate()); |
393 num_samples_rendered_ = 0; | 381 num_samples_rendered_ = 0; |
394 } | 382 } |
395 } | 383 } |
396 | 384 |
397 } // namespace content | 385 } // namespace content |
OLD | NEW |