OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 // THREAD SAFETY | 5 // THREAD SAFETY |
6 // | 6 // |
7 // AlsaPcmOutputStream object is *not* thread-safe and should only be used | 7 // AlsaPcmOutputStream object is *not* thread-safe and should only be used |
8 // from the audio thread. We DCHECK on this assumption whenever we can. | 8 // from the audio thread. We DCHECK on this assumption whenever we can. |
9 // | 9 // |
10 // SEMANTICS OF Close() | 10 // SEMANTICS OF Close() |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 TransitionTo(kInError); | 179 TransitionTo(kInError); |
180 } | 180 } |
181 | 181 |
182 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { | 182 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { |
183 LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample(); | 183 LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample(); |
184 TransitionTo(kInError); | 184 TransitionTo(kInError); |
185 } | 185 } |
186 } | 186 } |
187 | 187 |
188 AlsaPcmOutputStream::~AlsaPcmOutputStream() { | 188 AlsaPcmOutputStream::~AlsaPcmOutputStream() { |
| 189 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
189 InternalState current_state = state(); | 190 InternalState current_state = state(); |
190 DCHECK(current_state == kCreated || | 191 DCHECK(current_state == kCreated || |
191 current_state == kIsClosed || | 192 current_state == kIsClosed || |
192 current_state == kInError); | 193 current_state == kInError); |
193 DCHECK(!playback_handle_); | 194 DCHECK(!playback_handle_); |
194 } | 195 } |
195 | 196 |
196 bool AlsaPcmOutputStream::Open() { | 197 bool AlsaPcmOutputStream::Open() { |
197 DCHECK(CalledOnValidThread()); | 198 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
198 | 199 |
199 if (state() == kInError) | 200 if (state() == kInError) |
200 return false; | 201 return false; |
201 | 202 |
202 if (!CanTransitionTo(kIsOpened)) { | 203 if (!CanTransitionTo(kIsOpened)) { |
203 NOTREACHED() << "Invalid state: " << state(); | 204 NOTREACHED() << "Invalid state: " << state(); |
204 return false; | 205 return false; |
205 } | 206 } |
206 | 207 |
207 // We do not need to check if the transition was successful because | 208 // We do not need to check if the transition was successful because |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
245 // Buffer size is at least twice of packet size. | 246 // Buffer size is at least twice of packet size. |
246 alsa_buffer_frames_ = frames_per_packet_ * 2; | 247 alsa_buffer_frames_ = frames_per_packet_ * 2; |
247 } else { | 248 } else { |
248 alsa_buffer_frames_ = buffer_size; | 249 alsa_buffer_frames_ = buffer_size; |
249 } | 250 } |
250 | 251 |
251 return true; | 252 return true; |
252 } | 253 } |
253 | 254 |
254 void AlsaPcmOutputStream::Close() { | 255 void AlsaPcmOutputStream::Close() { |
255 DCHECK(CalledOnValidThread()); | 256 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
256 | 257 |
257 if (state() != kIsClosed) | 258 if (state() != kIsClosed) |
258 TransitionTo(kIsClosed); | 259 TransitionTo(kIsClosed); |
259 | 260 |
260 // Shutdown the audio device. | 261 // Shutdown the audio device. |
261 if (playback_handle_) { | 262 if (playback_handle_) { |
262 if (alsa_util::CloseDevice(wrapper_, playback_handle_) < 0) { | 263 if (alsa_util::CloseDevice(wrapper_, playback_handle_) < 0) { |
263 LOG(WARNING) << "Unable to close audio device. Leaking handle."; | 264 LOG(WARNING) << "Unable to close audio device. Leaking handle."; |
264 } | 265 } |
265 playback_handle_ = NULL; | 266 playback_handle_ = NULL; |
266 | 267 |
267 // Release the buffer. | 268 // Release the buffer. |
268 buffer_.reset(); | 269 buffer_.reset(); |
269 | 270 |
270 // Signal anything that might already be scheduled to stop. | 271 // Signal anything that might already be scheduled to stop. |
271 stop_stream_ = true; // Not necessary in production, but unit tests | 272 stop_stream_ = true; // Not necessary in production, but unit tests |
272 // uses the flag to verify that stream was closed. | 273 // uses the flag to verify that stream was closed. |
273 } | 274 } |
274 | 275 |
275 weak_factory_.InvalidateWeakPtrs(); | 276 weak_factory_.InvalidateWeakPtrs(); |
276 | 277 |
277 // Signal to the manager that we're closed and can be removed. | 278 // Signal to the manager that we're closed and can be removed. |
278 // Should be last call in the method as it deletes "this". | 279 // Should be last call in the method as it deletes "this". |
279 manager_->ReleaseOutputStream(this); | 280 manager_->ReleaseOutputStream(this); |
280 } | 281 } |
281 | 282 |
282 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) { | 283 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) { |
283 DCHECK(CalledOnValidThread()); | 284 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
284 | 285 |
285 CHECK(callback); | 286 CHECK(callback); |
286 | 287 |
287 if (stop_stream_) | 288 if (stop_stream_) |
288 return; | 289 return; |
289 | 290 |
290 // Only post the task if we can enter the playing state. | 291 // Only post the task if we can enter the playing state. |
291 if (TransitionTo(kIsPlaying) != kIsPlaying) | 292 if (TransitionTo(kIsPlaying) != kIsPlaying) |
292 return; | 293 return; |
293 | 294 |
(...skipping 28 matching lines...) Expand all Loading... |
322 memset(silent_packet->writable_data(), 0, silent_packet->data_size()); | 323 memset(silent_packet->writable_data(), 0, silent_packet->data_size()); |
323 buffer_->Append(silent_packet); | 324 buffer_->Append(silent_packet); |
324 WritePacket(); | 325 WritePacket(); |
325 | 326 |
326 // Start the callback chain. | 327 // Start the callback chain. |
327 set_source_callback(callback); | 328 set_source_callback(callback); |
328 WriteTask(); | 329 WriteTask(); |
329 } | 330 } |
330 | 331 |
331 void AlsaPcmOutputStream::Stop() { | 332 void AlsaPcmOutputStream::Stop() { |
332 DCHECK(CalledOnValidThread()); | 333 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
333 | 334 |
334 // Reset the callback, so that it is not called anymore. | 335 // Reset the callback, so that it is not called anymore. |
335 set_source_callback(NULL); | 336 set_source_callback(NULL); |
336 weak_factory_.InvalidateWeakPtrs(); | 337 weak_factory_.InvalidateWeakPtrs(); |
337 | 338 |
338 TransitionTo(kIsStopped); | 339 TransitionTo(kIsStopped); |
339 } | 340 } |
340 | 341 |
341 void AlsaPcmOutputStream::SetVolume(double volume) { | 342 void AlsaPcmOutputStream::SetVolume(double volume) { |
342 DCHECK(CalledOnValidThread()); | 343 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
343 | 344 |
344 volume_ = static_cast<float>(volume); | 345 volume_ = static_cast<float>(volume); |
345 } | 346 } |
346 | 347 |
347 void AlsaPcmOutputStream::GetVolume(double* volume) { | 348 void AlsaPcmOutputStream::GetVolume(double* volume) { |
348 DCHECK(CalledOnValidThread()); | 349 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
349 | 350 |
350 *volume = volume_; | 351 *volume = volume_; |
351 } | 352 } |
352 | 353 |
353 void AlsaPcmOutputStream::SetTickClockForTesting( | 354 void AlsaPcmOutputStream::SetTickClockForTesting( |
354 std::unique_ptr<base::TickClock> tick_clock) { | 355 std::unique_ptr<base::TickClock> tick_clock) { |
355 DCHECK(tick_clock); | 356 DCHECK(tick_clock); |
356 tick_clock_ = std::move(tick_clock); | 357 tick_clock_ = std::move(tick_clock); |
357 } | 358 } |
358 | 359 |
359 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { | 360 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { |
360 DCHECK(CalledOnValidThread()); | 361 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
361 | 362 |
362 // If stopped, simulate a 0-length packet. | 363 // If stopped, simulate a 0-length packet. |
363 if (stop_stream_) { | 364 if (stop_stream_) { |
364 buffer_->Clear(); | 365 buffer_->Clear(); |
365 *source_exhausted = true; | 366 *source_exhausted = true; |
366 return; | 367 return; |
367 } | 368 } |
368 | 369 |
369 *source_exhausted = false; | 370 *source_exhausted = false; |
370 | 371 |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
424 packet->set_data_size(packet_size); | 425 packet->set_data_size(packet_size); |
425 // Add the packet to the buffer. | 426 // Add the packet to the buffer. |
426 buffer_->Append(packet); | 427 buffer_->Append(packet); |
427 } else { | 428 } else { |
428 *source_exhausted = true; | 429 *source_exhausted = true; |
429 } | 430 } |
430 } | 431 } |
431 } | 432 } |
432 | 433 |
433 void AlsaPcmOutputStream::WritePacket() { | 434 void AlsaPcmOutputStream::WritePacket() { |
434 DCHECK(CalledOnValidThread()); | 435 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
435 | 436 |
436 // If the device is in error, just eat the bytes. | 437 // If the device is in error, just eat the bytes. |
437 if (stop_stream_) { | 438 if (stop_stream_) { |
438 buffer_->Clear(); | 439 buffer_->Clear(); |
439 return; | 440 return; |
440 } | 441 } |
441 | 442 |
442 if (state() != kIsPlaying) | 443 if (state() != kIsPlaying) |
443 return; | 444 return; |
444 | 445 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
483 // This ensures that shorter sounds will still play. | 484 // This ensures that shorter sounds will still play. |
484 if (playback_handle_ && | 485 if (playback_handle_ && |
485 (wrapper_->PcmState(playback_handle_) == SND_PCM_STATE_PREPARED) && | 486 (wrapper_->PcmState(playback_handle_) == SND_PCM_STATE_PREPARED) && |
486 GetCurrentDelay() > 0) { | 487 GetCurrentDelay() > 0) { |
487 wrapper_->PcmStart(playback_handle_); | 488 wrapper_->PcmStart(playback_handle_); |
488 } | 489 } |
489 } | 490 } |
490 } | 491 } |
491 | 492 |
492 void AlsaPcmOutputStream::WriteTask() { | 493 void AlsaPcmOutputStream::WriteTask() { |
493 DCHECK(CalledOnValidThread()); | 494 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
494 | 495 |
495 if (stop_stream_) | 496 if (stop_stream_) |
496 return; | 497 return; |
497 | 498 |
498 if (state() == kIsStopped) | 499 if (state() == kIsStopped) |
499 return; | 500 return; |
500 | 501 |
501 bool source_exhausted; | 502 bool source_exhausted; |
502 BufferPacket(&source_exhausted); | 503 BufferPacket(&source_exhausted); |
503 WritePacket(); | 504 WritePacket(); |
504 | 505 |
505 ScheduleNextWrite(source_exhausted); | 506 ScheduleNextWrite(source_exhausted); |
506 } | 507 } |
507 | 508 |
508 void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) { | 509 void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) { |
509 DCHECK(CalledOnValidThread()); | 510 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
510 | 511 |
511 if (stop_stream_ || state() != kIsPlaying) | 512 if (stop_stream_ || state() != kIsPlaying) |
512 return; | 513 return; |
513 | 514 |
514 const uint32_t kTargetFramesAvailable = alsa_buffer_frames_ / 2; | 515 const uint32_t kTargetFramesAvailable = alsa_buffer_frames_ / 2; |
515 uint32_t available_frames = GetAvailableFrames(); | 516 uint32_t available_frames = GetAvailableFrames(); |
516 | 517 |
517 base::TimeDelta next_fill_time; | 518 base::TimeDelta next_fill_time; |
518 if (buffer_->forward_bytes() && available_frames) { | 519 if (buffer_->forward_bytes() && available_frames) { |
519 // If we've got data available and ALSA has room, deliver it immediately. | 520 // If we've got data available and ALSA has room, deliver it immediately. |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
626 } | 627 } |
627 | 628 |
628 if (delay < 0) { | 629 if (delay < 0) { |
629 delay = 0; | 630 delay = 0; |
630 } | 631 } |
631 | 632 |
632 return delay; | 633 return delay; |
633 } | 634 } |
634 | 635 |
635 snd_pcm_sframes_t AlsaPcmOutputStream::GetAvailableFrames() { | 636 snd_pcm_sframes_t AlsaPcmOutputStream::GetAvailableFrames() { |
636 DCHECK(CalledOnValidThread()); | 637 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
637 | 638 |
638 if (stop_stream_) | 639 if (stop_stream_) |
639 return 0; | 640 return 0; |
640 | 641 |
641 // Find the number of frames queued in the sound device. | 642 // Find the number of frames queued in the sound device. |
642 snd_pcm_sframes_t available_frames = | 643 snd_pcm_sframes_t available_frames = |
643 wrapper_->PcmAvailUpdate(playback_handle_); | 644 wrapper_->PcmAvailUpdate(playback_handle_); |
644 if (available_frames < 0) { | 645 if (available_frames < 0) { |
645 available_frames = wrapper_->PcmRecover(playback_handle_, | 646 available_frames = wrapper_->PcmRecover(playback_handle_, |
646 available_frames, | 647 available_frames, |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
759 return to == kIsClosed || to == kInError; | 760 return to == kIsClosed || to == kInError; |
760 | 761 |
761 case kIsClosed: | 762 case kIsClosed: |
762 default: | 763 default: |
763 return false; | 764 return false; |
764 } | 765 } |
765 } | 766 } |
766 | 767 |
767 AlsaPcmOutputStream::InternalState | 768 AlsaPcmOutputStream::InternalState |
768 AlsaPcmOutputStream::TransitionTo(InternalState to) { | 769 AlsaPcmOutputStream::TransitionTo(InternalState to) { |
769 DCHECK(CalledOnValidThread()); | 770 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
770 | 771 |
771 if (!CanTransitionTo(to)) { | 772 if (!CanTransitionTo(to)) { |
772 NOTREACHED() << "Cannot transition from: " << state_ << " to: " << to; | 773 NOTREACHED() << "Cannot transition from: " << state_ << " to: " << to; |
773 state_ = kInError; | 774 state_ = kInError; |
774 } else { | 775 } else { |
775 state_ = to; | 776 state_ = to; |
776 } | 777 } |
777 return state_; | 778 return state_; |
778 } | 779 } |
779 | 780 |
(...skipping 13 matching lines...) Expand all Loading... |
793 } | 794 } |
794 | 795 |
795 void AlsaPcmOutputStream::RunErrorCallback(int code) { | 796 void AlsaPcmOutputStream::RunErrorCallback(int code) { |
796 if (source_callback_) | 797 if (source_callback_) |
797 source_callback_->OnError(); | 798 source_callback_->OnError(); |
798 } | 799 } |
799 | 800 |
800 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 801 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
801 // release ownership of the currently registered callback. | 802 // release ownership of the currently registered callback. |
802 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { | 803 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { |
803 DCHECK(CalledOnValidThread()); | 804 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
804 source_callback_ = callback; | 805 source_callback_ = callback; |
805 } | 806 } |
806 | 807 |
807 } // namespace media | 808 } // namespace media |
OLD | NEW |