| 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 // 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 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 199 alsa_buffer_frames_(0), | 199 alsa_buffer_frames_(0), |
| 200 stop_stream_(false), | 200 stop_stream_(false), |
| 201 wrapper_(wrapper), | 201 wrapper_(wrapper), |
| 202 manager_(manager), | 202 manager_(manager), |
| 203 playback_handle_(NULL), | 203 playback_handle_(NULL), |
| 204 frames_per_packet_(packet_size_ / bytes_per_frame_), | 204 frames_per_packet_(packet_size_ / bytes_per_frame_), |
| 205 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), | 205 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), |
| 206 state_(kCreated), | 206 state_(kCreated), |
| 207 volume_(1.0f), | 207 volume_(1.0f), |
| 208 source_callback_(NULL) { | 208 source_callback_(NULL) { |
| 209 DCHECK_EQ(MessageLoop::current(), manager_->GetMessageLoop()); | 209 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 210 | 210 |
| 211 // Sanity check input values. | 211 // Sanity check input values. |
| 212 if ((params.sample_rate > kAlsaMaxSampleRate) || (params.sample_rate <= 0)) { | 212 if ((params.sample_rate > kAlsaMaxSampleRate) || (params.sample_rate <= 0)) { |
| 213 LOG(WARNING) << "Unsupported audio frequency."; | 213 LOG(WARNING) << "Unsupported audio frequency."; |
| 214 TransitionTo(kInError); | 214 TransitionTo(kInError); |
| 215 } | 215 } |
| 216 | 216 |
| 217 if (AudioParameters::AUDIO_PCM_LINEAR != params.format && | 217 if (AudioParameters::AUDIO_PCM_LINEAR != params.format && |
| 218 AudioParameters::AUDIO_PCM_LOW_LATENCY != params.format) { | 218 AudioParameters::AUDIO_PCM_LOW_LATENCY != params.format) { |
| 219 LOG(WARNING) << "Unsupported audio format"; | 219 LOG(WARNING) << "Unsupported audio format"; |
| 220 TransitionTo(kInError); | 220 TransitionTo(kInError); |
| 221 } | 221 } |
| 222 | 222 |
| 223 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { | 223 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { |
| 224 LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample; | 224 LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample; |
| 225 TransitionTo(kInError); | 225 TransitionTo(kInError); |
| 226 } | 226 } |
| 227 } | 227 } |
| 228 | 228 |
| 229 AlsaPcmOutputStream::~AlsaPcmOutputStream() { | 229 AlsaPcmOutputStream::~AlsaPcmOutputStream() { |
| 230 InternalState current_state = state(); | 230 InternalState current_state = state(); |
| 231 DCHECK(current_state == kCreated || | 231 DCHECK(current_state == kCreated || |
| 232 current_state == kIsClosed || | 232 current_state == kIsClosed || |
| 233 current_state == kInError); | 233 current_state == kInError); |
| 234 DCHECK(!playback_handle_); | 234 DCHECK(!playback_handle_); |
| 235 } | 235 } |
| 236 | 236 |
| 237 bool AlsaPcmOutputStream::Open() { | 237 bool AlsaPcmOutputStream::Open() { |
| 238 DCHECK_EQ(MessageLoop::current(), manager_->GetMessageLoop()); | 238 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 239 | 239 |
| 240 if (state() == kInError) | 240 if (state() == kInError) |
| 241 return false; | 241 return false; |
| 242 | 242 |
| 243 if (!CanTransitionTo(kIsOpened)) { | 243 if (!CanTransitionTo(kIsOpened)) { |
| 244 NOTREACHED() << "Invalid state: " << state(); | 244 NOTREACHED() << "Invalid state: " << state(); |
| 245 return false; | 245 return false; |
| 246 } | 246 } |
| 247 | 247 |
| 248 // We do not need to check if the transition was successful because | 248 // 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... |
| 286 alsa_buffer_frames_ = frames_per_packet_ * 2; | 286 alsa_buffer_frames_ = frames_per_packet_ * 2; |
| 287 } else { | 287 } else { |
| 288 alsa_buffer_frames_ = buffer_size; | 288 alsa_buffer_frames_ = buffer_size; |
| 289 } | 289 } |
| 290 } | 290 } |
| 291 | 291 |
| 292 return true; | 292 return true; |
| 293 } | 293 } |
| 294 | 294 |
| 295 void AlsaPcmOutputStream::Close() { | 295 void AlsaPcmOutputStream::Close() { |
| 296 DCHECK_EQ(MessageLoop::current(), manager_->GetMessageLoop()); | 296 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 297 | 297 |
| 298 // Sanity check that the transition occurs correctly. It is safe to | 298 // Sanity check that the transition occurs correctly. It is safe to |
| 299 // continue anyways because all operations for closing are idempotent. | 299 // continue anyways because all operations for closing are idempotent. |
| 300 if (TransitionTo(kIsClosed) != kIsClosed) { | 300 if (TransitionTo(kIsClosed) != kIsClosed) { |
| 301 NOTREACHED() << "Unable to transition Closed."; | 301 NOTREACHED() << "Unable to transition Closed."; |
| 302 } else { | 302 } else { |
| 303 // Shutdown the audio device. | 303 // Shutdown the audio device. |
| 304 if (playback_handle_ && | 304 if (playback_handle_ && |
| 305 alsa_util::CloseDevice(wrapper_, playback_handle_) < 0) { | 305 alsa_util::CloseDevice(wrapper_, playback_handle_) < 0) { |
| 306 LOG(WARNING) << "Unable to close audio device. Leaking handle."; | 306 LOG(WARNING) << "Unable to close audio device. Leaking handle."; |
| 307 } | 307 } |
| 308 playback_handle_ = NULL; | 308 playback_handle_ = NULL; |
| 309 | 309 |
| 310 // Release the buffer. | 310 // Release the buffer. |
| 311 buffer_.reset(); | 311 buffer_.reset(); |
| 312 | 312 |
| 313 // Signal anything that might already be scheduled to stop. | 313 // Signal anything that might already be scheduled to stop. |
| 314 stop_stream_ = true; // Not necessary in production, but unit tests | 314 stop_stream_ = true; // Not necessary in production, but unit tests |
| 315 // uses the flag to verify that stream was closed. | 315 // uses the flag to verify that stream was closed. |
| 316 weak_factory_.InvalidateWeakPtrs(); | 316 weak_factory_.InvalidateWeakPtrs(); |
| 317 | 317 |
| 318 // Signal to the manager that we're closed and can be removed. | 318 // Signal to the manager that we're closed and can be removed. |
| 319 // Should be last call in the method as it deletes "this". | 319 // Should be last call in the method as it deletes "this". |
| 320 manager_->ReleaseOutputStream(this); | 320 manager_->ReleaseOutputStream(this); |
| 321 } | 321 } |
| 322 } | 322 } |
| 323 | 323 |
| 324 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) { | 324 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) { |
| 325 DCHECK_EQ(MessageLoop::current(), manager_->GetMessageLoop()); | 325 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 326 | 326 |
| 327 CHECK(callback); | 327 CHECK(callback); |
| 328 | 328 |
| 329 if (stop_stream_) | 329 if (stop_stream_) |
| 330 return; | 330 return; |
| 331 | 331 |
| 332 set_source_callback(callback); | 332 set_source_callback(callback); |
| 333 | 333 |
| 334 // Only post the task if we can enter the playing state. | 334 // Only post the task if we can enter the playing state. |
| 335 if (TransitionTo(kIsPlaying) == kIsPlaying) { | 335 if (TransitionTo(kIsPlaying) == kIsPlaying) { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 354 stop_stream_ = true; | 354 stop_stream_ = true; |
| 355 } | 355 } |
| 356 } | 356 } |
| 357 | 357 |
| 358 if (!stop_stream_) | 358 if (!stop_stream_) |
| 359 ScheduleNextWrite(false); | 359 ScheduleNextWrite(false); |
| 360 } | 360 } |
| 361 } | 361 } |
| 362 | 362 |
| 363 void AlsaPcmOutputStream::Stop() { | 363 void AlsaPcmOutputStream::Stop() { |
| 364 DCHECK_EQ(MessageLoop::current(), manager_->GetMessageLoop()); | 364 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 365 | 365 |
| 366 // Reset the callback, so that it is not called anymore. | 366 // Reset the callback, so that it is not called anymore. |
| 367 set_source_callback(NULL); | 367 set_source_callback(NULL); |
| 368 | 368 |
| 369 TransitionTo(kIsStopped); | 369 TransitionTo(kIsStopped); |
| 370 } | 370 } |
| 371 | 371 |
| 372 void AlsaPcmOutputStream::SetVolume(double volume) { | 372 void AlsaPcmOutputStream::SetVolume(double volume) { |
| 373 DCHECK_EQ(MessageLoop::current(), manager_->GetMessageLoop()); | 373 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 374 | 374 |
| 375 volume_ = static_cast<float>(volume); | 375 volume_ = static_cast<float>(volume); |
| 376 } | 376 } |
| 377 | 377 |
| 378 void AlsaPcmOutputStream::GetVolume(double* volume) { | 378 void AlsaPcmOutputStream::GetVolume(double* volume) { |
| 379 DCHECK_EQ(MessageLoop::current(), manager_->GetMessageLoop()); | 379 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 380 | 380 |
| 381 *volume = volume_; | 381 *volume = volume_; |
| 382 } | 382 } |
| 383 | 383 |
| 384 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { | 384 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { |
| 385 DCHECK_EQ(MessageLoop::current(), manager_->GetMessageLoop()); | 385 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 386 | 386 |
| 387 // If stopped, simulate a 0-length packet. | 387 // If stopped, simulate a 0-length packet. |
| 388 if (stop_stream_) { | 388 if (stop_stream_) { |
| 389 buffer_->Clear(); | 389 buffer_->Clear(); |
| 390 *source_exhausted = true; | 390 *source_exhausted = true; |
| 391 return; | 391 return; |
| 392 } | 392 } |
| 393 | 393 |
| 394 *source_exhausted = false; | 394 *source_exhausted = false; |
| 395 | 395 |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 466 packet->SetDataSize(packet_size); | 466 packet->SetDataSize(packet_size); |
| 467 // Add the packet to the buffer. | 467 // Add the packet to the buffer. |
| 468 buffer_->Append(packet); | 468 buffer_->Append(packet); |
| 469 } else { | 469 } else { |
| 470 *source_exhausted = true; | 470 *source_exhausted = true; |
| 471 } | 471 } |
| 472 } | 472 } |
| 473 } | 473 } |
| 474 | 474 |
| 475 void AlsaPcmOutputStream::WritePacket() { | 475 void AlsaPcmOutputStream::WritePacket() { |
| 476 DCHECK_EQ(MessageLoop::current(), manager_->GetMessageLoop()); | 476 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 477 | 477 |
| 478 // If the device is in error, just eat the bytes. | 478 // If the device is in error, just eat the bytes. |
| 479 if (stop_stream_) { | 479 if (stop_stream_) { |
| 480 buffer_->Clear(); | 480 buffer_->Clear(); |
| 481 return; | 481 return; |
| 482 } | 482 } |
| 483 | 483 |
| 484 if (state() == kIsStopped) | 484 if (state() == kIsStopped) |
| 485 return; | 485 return; |
| 486 | 486 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 530 // This ensures that shorter sounds will still play. | 530 // This ensures that shorter sounds will still play. |
| 531 if (playback_handle_ && | 531 if (playback_handle_ && |
| 532 (wrapper_->PcmState(playback_handle_) == SND_PCM_STATE_PREPARED) && | 532 (wrapper_->PcmState(playback_handle_) == SND_PCM_STATE_PREPARED) && |
| 533 GetCurrentDelay() > 0) { | 533 GetCurrentDelay() > 0) { |
| 534 wrapper_->PcmStart(playback_handle_); | 534 wrapper_->PcmStart(playback_handle_); |
| 535 } | 535 } |
| 536 } | 536 } |
| 537 } | 537 } |
| 538 | 538 |
| 539 void AlsaPcmOutputStream::WriteTask() { | 539 void AlsaPcmOutputStream::WriteTask() { |
| 540 DCHECK_EQ(MessageLoop::current(), manager_->GetMessageLoop()); | 540 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 541 | 541 |
| 542 if (stop_stream_) | 542 if (stop_stream_) |
| 543 return; | 543 return; |
| 544 | 544 |
| 545 if (state() == kIsStopped) | 545 if (state() == kIsStopped) |
| 546 return; | 546 return; |
| 547 | 547 |
| 548 bool source_exhausted; | 548 bool source_exhausted; |
| 549 BufferPacket(&source_exhausted); | 549 BufferPacket(&source_exhausted); |
| 550 WritePacket(); | 550 WritePacket(); |
| 551 | 551 |
| 552 ScheduleNextWrite(source_exhausted); | 552 ScheduleNextWrite(source_exhausted); |
| 553 } | 553 } |
| 554 | 554 |
| 555 void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) { | 555 void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) { |
| 556 DCHECK_EQ(MessageLoop::current(), manager_->GetMessageLoop()); | 556 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 557 | 557 |
| 558 if (stop_stream_) | 558 if (stop_stream_) |
| 559 return; | 559 return; |
| 560 | 560 |
| 561 uint32 frames_avail_wanted = alsa_buffer_frames_ / 2; | 561 uint32 frames_avail_wanted = alsa_buffer_frames_ / 2; |
| 562 uint32 available_frames = GetAvailableFrames(); | 562 uint32 available_frames = GetAvailableFrames(); |
| 563 uint32 frames_in_buffer = buffer_->forward_bytes() / bytes_per_output_frame_; | 563 uint32 frames_in_buffer = buffer_->forward_bytes() / bytes_per_output_frame_; |
| 564 | 564 |
| 565 // Next write is initially scheduled for the moment when half of a packet | 565 // Next write is initially scheduled for the moment when half of a packet |
| 566 // has been played out. | 566 // has been played out. |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 600 if (state() == kIsPlaying) { | 600 if (state() == kIsPlaying) { |
| 601 if (next_fill_time_ms == 0) { | 601 if (next_fill_time_ms == 0) { |
| 602 manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind( | 602 manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind( |
| 603 &AlsaPcmOutputStream::WriteTask, weak_factory_.GetWeakPtr())); | 603 &AlsaPcmOutputStream::WriteTask, weak_factory_.GetWeakPtr())); |
| 604 } else { | 604 } else { |
| 605 // TODO(ajwong): Measure the reliability of the delay interval. Use | 605 // TODO(ajwong): Measure the reliability of the delay interval. Use |
| 606 // base/metrics/histogram.h. | 606 // base/metrics/histogram.h. |
| 607 manager_->GetMessageLoop()->PostDelayedTask(FROM_HERE, | 607 manager_->GetMessageLoop()->PostDelayedTask(FROM_HERE, |
| 608 base::Bind(&AlsaPcmOutputStream::WriteTask, | 608 base::Bind(&AlsaPcmOutputStream::WriteTask, |
| 609 weak_factory_.GetWeakPtr()), | 609 weak_factory_.GetWeakPtr()), |
| 610 base::TimeDelta::FromMilliseconds(next_fill_time_ms)); | 610 next_fill_time_ms); |
| 611 } | 611 } |
| 612 } | 612 } |
| 613 } | 613 } |
| 614 | 614 |
| 615 uint32 AlsaPcmOutputStream::FramesToMicros(uint32 frames, uint32 sample_rate) { | 615 uint32 AlsaPcmOutputStream::FramesToMicros(uint32 frames, uint32 sample_rate) { |
| 616 return frames * base::Time::kMicrosecondsPerSecond / sample_rate; | 616 return frames * base::Time::kMicrosecondsPerSecond / sample_rate; |
| 617 } | 617 } |
| 618 | 618 |
| 619 uint32 AlsaPcmOutputStream::FramesToMillis(uint32 frames, uint32 sample_rate) { | 619 uint32 AlsaPcmOutputStream::FramesToMillis(uint32 frames, uint32 sample_rate) { |
| 620 return frames * base::Time::kMillisecondsPerSecond / sample_rate; | 620 return frames * base::Time::kMillisecondsPerSecond / sample_rate; |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 688 | 688 |
| 689 // snd_pcm_delay() may not work in the beginning of the stream. In this case | 689 // snd_pcm_delay() may not work in the beginning of the stream. In this case |
| 690 // return delay of data we know currently is in the ALSA's buffer. | 690 // return delay of data we know currently is in the ALSA's buffer. |
| 691 if (delay < 0) | 691 if (delay < 0) |
| 692 delay = alsa_buffer_frames_ - GetAvailableFrames(); | 692 delay = alsa_buffer_frames_ - GetAvailableFrames(); |
| 693 | 693 |
| 694 return delay; | 694 return delay; |
| 695 } | 695 } |
| 696 | 696 |
| 697 snd_pcm_sframes_t AlsaPcmOutputStream::GetAvailableFrames() { | 697 snd_pcm_sframes_t AlsaPcmOutputStream::GetAvailableFrames() { |
| 698 DCHECK_EQ(MessageLoop::current(), manager_->GetMessageLoop()); | 698 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 699 | 699 |
| 700 if (stop_stream_) | 700 if (stop_stream_) |
| 701 return 0; | 701 return 0; |
| 702 | 702 |
| 703 // Find the number of frames queued in the sound device. | 703 // Find the number of frames queued in the sound device. |
| 704 snd_pcm_sframes_t available_frames = | 704 snd_pcm_sframes_t available_frames = |
| 705 wrapper_->PcmAvailUpdate(playback_handle_); | 705 wrapper_->PcmAvailUpdate(playback_handle_); |
| 706 if (available_frames < 0) { | 706 if (available_frames < 0) { |
| 707 available_frames = wrapper_->PcmRecover(playback_handle_, | 707 available_frames = wrapper_->PcmRecover(playback_handle_, |
| 708 available_frames, | 708 available_frames, |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 802 return to == kIsClosed || to == kInError; | 802 return to == kIsClosed || to == kInError; |
| 803 | 803 |
| 804 case kIsClosed: | 804 case kIsClosed: |
| 805 default: | 805 default: |
| 806 return false; | 806 return false; |
| 807 } | 807 } |
| 808 } | 808 } |
| 809 | 809 |
| 810 AlsaPcmOutputStream::InternalState | 810 AlsaPcmOutputStream::InternalState |
| 811 AlsaPcmOutputStream::TransitionTo(InternalState to) { | 811 AlsaPcmOutputStream::TransitionTo(InternalState to) { |
| 812 DCHECK_EQ(MessageLoop::current(), manager_->GetMessageLoop()); | 812 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 813 | 813 |
| 814 if (!CanTransitionTo(to)) { | 814 if (!CanTransitionTo(to)) { |
| 815 NOTREACHED() << "Cannot transition from: " << state_ << " to: " << to; | 815 NOTREACHED() << "Cannot transition from: " << state_ << " to: " << to; |
| 816 state_ = kInError; | 816 state_ = kInError; |
| 817 } else { | 817 } else { |
| 818 state_ = to; | 818 state_ = to; |
| 819 } | 819 } |
| 820 return state_; | 820 return state_; |
| 821 } | 821 } |
| 822 | 822 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 836 } | 836 } |
| 837 | 837 |
| 838 void AlsaPcmOutputStream::RunErrorCallback(int code) { | 838 void AlsaPcmOutputStream::RunErrorCallback(int code) { |
| 839 if (source_callback_) | 839 if (source_callback_) |
| 840 source_callback_->OnError(this, code); | 840 source_callback_->OnError(this, code); |
| 841 } | 841 } |
| 842 | 842 |
| 843 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 843 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
| 844 // release ownership of the currently registered callback. | 844 // release ownership of the currently registered callback. |
| 845 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { | 845 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { |
| 846 DCHECK_EQ(MessageLoop::current(), manager_->GetMessageLoop()); | 846 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 847 source_callback_ = callback; | 847 source_callback_ = callback; |
| 848 } | 848 } |
| OLD | NEW |