| 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 |