| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 // The AlsaPcmOutputStream object's internal state is accessed by two threads: | 7 // The AlsaPcmOutputStream object's internal state is accessed by two threads: |
| 8 // | 8 // |
| 9 // client thread - creates the object and calls the public APIs. | 9 // client thread - creates the object and calls the public APIs. |
| 10 // message loop thread - executes all the internal tasks including querying | 10 // message loop thread - executes all the internal tasks including querying |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 225 AudioManagerLinux* manager, | 225 AudioManagerLinux* manager, |
| 226 MessageLoop* message_loop) | 226 MessageLoop* message_loop) |
| 227 : shared_data_(MessageLoop::current()), | 227 : shared_data_(MessageLoop::current()), |
| 228 requested_device_name_(device_name), | 228 requested_device_name_(device_name), |
| 229 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample)), | 229 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample)), |
| 230 channels_(params.channels), | 230 channels_(params.channels), |
| 231 sample_rate_(params.sample_rate), | 231 sample_rate_(params.sample_rate), |
| 232 bytes_per_sample_(params.bits_per_sample / 8), | 232 bytes_per_sample_(params.bits_per_sample / 8), |
| 233 bytes_per_frame_(channels_ * params.bits_per_sample / 8), | 233 bytes_per_frame_(channels_ * params.bits_per_sample / 8), |
| 234 should_downmix_(false), | 234 should_downmix_(false), |
| 235 latency_micros_(0), | 235 packet_size_(params.GetPacketSize()), |
| 236 micros_per_packet_(0), | 236 micros_per_packet_(FramesToMicros( |
| 237 params.samples_per_packet, sample_rate_)), |
| 238 latency_micros_(std::max(AlsaPcmOutputStream::kMinLatencyMicros, |
| 239 micros_per_packet_ * 2)), |
| 237 bytes_per_output_frame_(bytes_per_frame_), | 240 bytes_per_output_frame_(bytes_per_frame_), |
| 238 alsa_buffer_frames_(0), | 241 alsa_buffer_frames_(0), |
| 239 stop_stream_(false), | 242 stop_stream_(false), |
| 240 wrapper_(wrapper), | 243 wrapper_(wrapper), |
| 241 manager_(manager), | 244 manager_(manager), |
| 242 playback_handle_(NULL), | 245 playback_handle_(NULL), |
| 243 frames_per_packet_(0), | 246 frames_per_packet_(packet_size_ / bytes_per_frame_), |
| 244 client_thread_loop_(MessageLoop::current()), | 247 client_thread_loop_(MessageLoop::current()), |
| 245 message_loop_(message_loop) { | 248 message_loop_(message_loop) { |
| 246 | 249 |
| 247 // Sanity check input values. | 250 // Sanity check input values. |
| 248 if ((params.sample_rate > kAlsaMaxSampleRate) || (params.sample_rate <= 0)) { | 251 if ((params.sample_rate > kAlsaMaxSampleRate) || (params.sample_rate <= 0)) { |
| 249 LOG(WARNING) << "Unsupported audio frequency."; | 252 LOG(WARNING) << "Unsupported audio frequency."; |
| 250 shared_data_.TransitionTo(kInError); | 253 shared_data_.TransitionTo(kInError); |
| 251 } | 254 } |
| 252 | 255 |
| 253 if (AudioParameters::AUDIO_PCM_LINEAR != params.format) { | 256 if (AudioParameters::AUDIO_PCM_LINEAR != params.format) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 264 AlsaPcmOutputStream::~AlsaPcmOutputStream() { | 267 AlsaPcmOutputStream::~AlsaPcmOutputStream() { |
| 265 InternalState state = shared_data_.state(); | 268 InternalState state = shared_data_.state(); |
| 266 DCHECK(state == kCreated || state == kIsClosed || state == kInError); | 269 DCHECK(state == kCreated || state == kIsClosed || state == kInError); |
| 267 | 270 |
| 268 // TODO(ajwong): Ensure that CloseTask has been called and the | 271 // TODO(ajwong): Ensure that CloseTask has been called and the |
| 269 // playback handle released by DCHECKing that playback_handle_ is NULL. | 272 // playback handle released by DCHECKing that playback_handle_ is NULL. |
| 270 // Currently, because of Bug 18217, there is a race condition on destruction | 273 // Currently, because of Bug 18217, there is a race condition on destruction |
| 271 // where the stream is not always stopped and closed, causing this to fail. | 274 // where the stream is not always stopped and closed, causing this to fail. |
| 272 } | 275 } |
| 273 | 276 |
| 274 bool AlsaPcmOutputStream::Open(uint32 packet_size) { | 277 bool AlsaPcmOutputStream::Open() { |
| 275 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 278 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); |
| 276 | 279 |
| 277 DCHECK_EQ(0U, packet_size % bytes_per_frame_) | |
| 278 << "Buffers should end on a frame boundary. Frame size: " | |
| 279 << bytes_per_frame_; | |
| 280 | |
| 281 if (shared_data_.state() == kInError) { | 280 if (shared_data_.state() == kInError) { |
| 282 return false; | 281 return false; |
| 283 } | 282 } |
| 284 | 283 |
| 285 if (!shared_data_.CanTransitionTo(kIsOpened)) { | 284 if (!shared_data_.CanTransitionTo(kIsOpened)) { |
| 286 NOTREACHED() << "Invalid state: " << shared_data_.state(); | 285 NOTREACHED() << "Invalid state: " << shared_data_.state(); |
| 287 return false; | 286 return false; |
| 288 } | 287 } |
| 289 | 288 |
| 290 // We do not need to check if the transition was successful because | 289 // We do not need to check if the transition was successful because |
| 291 // CanTransitionTo() was checked above, and it is assumed that this | 290 // CanTransitionTo() was checked above, and it is assumed that this |
| 292 // object's public API is only called on one thread so the state cannot | 291 // object's public API is only called on one thread so the state cannot |
| 293 // transition out from under us. | 292 // transition out from under us. |
| 294 shared_data_.TransitionTo(kIsOpened); | 293 shared_data_.TransitionTo(kIsOpened); |
| 295 message_loop_->PostTask( | 294 message_loop_->PostTask( |
| 296 FROM_HERE, | 295 FROM_HERE, |
| 297 NewRunnableMethod(this, &AlsaPcmOutputStream::OpenTask, packet_size)); | 296 NewRunnableMethod(this, &AlsaPcmOutputStream::OpenTask)); |
| 298 | 297 |
| 299 return true; | 298 return true; |
| 300 } | 299 } |
| 301 | 300 |
| 302 void AlsaPcmOutputStream::Close() { | 301 void AlsaPcmOutputStream::Close() { |
| 303 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 302 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); |
| 304 | 303 |
| 305 // Sanity check that the transition occurs correctly. It is safe to | 304 // Sanity check that the transition occurs correctly. It is safe to |
| 306 // continue anyways because all operations for closing are idempotent. | 305 // continue anyways because all operations for closing are idempotent. |
| 307 if (shared_data_.TransitionTo(kIsClosed) != kIsClosed) { | 306 if (shared_data_.TransitionTo(kIsClosed) != kIsClosed) { |
| 308 NOTREACHED() << "Unable to transition Closed."; | 307 NOTREACHED() << "Unable to transition Closed."; |
| 309 } | 308 } |
| 310 | 309 |
| 311 // Signal our successful close, and disassociate the source callback. | |
| 312 shared_data_.OnClose(this); | |
| 313 shared_data_.set_source_callback(NULL); | |
| 314 | |
| 315 message_loop_->PostTask( | 310 message_loop_->PostTask( |
| 316 FROM_HERE, | 311 FROM_HERE, |
| 317 NewRunnableMethod(this, &AlsaPcmOutputStream::CloseTask)); | 312 NewRunnableMethod(this, &AlsaPcmOutputStream::CloseTask)); |
| 318 | 313 |
| 319 // Signal to the manager that we're closed and can be removed. Since | 314 // Signal to the manager that we're closed and can be removed. Since |
| 320 // we just posted a CloseTask to the message loop, we won't be deleted | 315 // we just posted a CloseTask to the message loop, we won't be deleted |
| 321 // immediately, but it will happen soon afterwards. | 316 // immediately, but it will happen soon afterwards. |
| 322 manager()->ReleaseOutputStream(this); | 317 manager()->ReleaseOutputStream(this); |
| 323 } | 318 } |
| 324 | 319 |
| 325 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) { | 320 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) { |
| 326 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 321 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); |
| 327 | 322 |
| 328 CHECK(callback); | 323 CHECK(callback); |
| 329 | 324 |
| 330 shared_data_.set_source_callback(callback); | 325 shared_data_.set_source_callback(callback); |
| 331 | 326 |
| 332 // Only post the task if we can enter the playing state. | 327 // Only post the task if we can enter the playing state. |
| 333 if (shared_data_.TransitionTo(kIsPlaying) == kIsPlaying) { | 328 if (shared_data_.TransitionTo(kIsPlaying) == kIsPlaying) { |
| 334 message_loop_->PostTask( | 329 message_loop_->PostTask( |
| 335 FROM_HERE, | 330 FROM_HERE, |
| 336 NewRunnableMethod(this, &AlsaPcmOutputStream::StartTask)); | 331 NewRunnableMethod(this, &AlsaPcmOutputStream::StartTask)); |
| 337 } | 332 } |
| 338 } | 333 } |
| 339 | 334 |
| 340 void AlsaPcmOutputStream::Stop() { | 335 void AlsaPcmOutputStream::Stop() { |
| 341 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 336 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); |
| 342 | 337 |
| 338 // Reset the callback, so that it is not called anymore. |
| 339 shared_data_.set_source_callback(NULL); |
| 340 |
| 343 shared_data_.TransitionTo(kIsStopped); | 341 shared_data_.TransitionTo(kIsStopped); |
| 344 } | 342 } |
| 345 | 343 |
| 346 void AlsaPcmOutputStream::SetVolume(double volume) { | 344 void AlsaPcmOutputStream::SetVolume(double volume) { |
| 347 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 345 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); |
| 348 | 346 |
| 349 shared_data_.set_volume(static_cast<float>(volume)); | 347 shared_data_.set_volume(static_cast<float>(volume)); |
| 350 } | 348 } |
| 351 | 349 |
| 352 void AlsaPcmOutputStream::GetVolume(double* volume) { | 350 void AlsaPcmOutputStream::GetVolume(double* volume) { |
| 353 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 351 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); |
| 354 | 352 |
| 355 *volume = shared_data_.volume(); | 353 *volume = shared_data_.volume(); |
| 356 } | 354 } |
| 357 | 355 |
| 358 void AlsaPcmOutputStream::OpenTask(uint32 packet_size) { | 356 void AlsaPcmOutputStream::OpenTask() { |
| 359 DCHECK_EQ(message_loop_, MessageLoop::current()); | 357 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 360 | 358 |
| 361 // Initialize the configuration variables. | |
| 362 packet_size_ = packet_size; | |
| 363 frames_per_packet_ = packet_size_ / bytes_per_frame_; | |
| 364 | |
| 365 // Try to open the device. | 359 // Try to open the device. |
| 366 micros_per_packet_ = | |
| 367 FramesToMicros(packet_size / bytes_per_frame_, sample_rate_); | |
| 368 latency_micros_ = std::max(AlsaPcmOutputStream::kMinLatencyMicros, | |
| 369 micros_per_packet_ * 2); | |
| 370 if (requested_device_name_ == kAutoSelectDevice) { | 360 if (requested_device_name_ == kAutoSelectDevice) { |
| 371 playback_handle_ = AutoSelectDevice(latency_micros_); | 361 playback_handle_ = AutoSelectDevice(latency_micros_); |
| 372 if (playback_handle_) | 362 if (playback_handle_) |
| 373 VLOG(1) << "Auto-selected device: " << device_name_; | 363 VLOG(1) << "Auto-selected device: " << device_name_; |
| 374 } else { | 364 } else { |
| 375 device_name_ = requested_device_name_; | 365 device_name_ = requested_device_name_; |
| 376 playback_handle_ = alsa_util::OpenPlaybackDevice(wrapper_, | 366 playback_handle_ = alsa_util::OpenPlaybackDevice(wrapper_, |
| 377 device_name_.c_str(), | 367 device_name_.c_str(), |
| 378 channels_, sample_rate_, | 368 channels_, sample_rate_, |
| 379 pcm_format_, | 369 pcm_format_, |
| (...skipping 533 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 913 AudioOutputStream* stream, uint8* dest, uint32 max_size, | 903 AudioOutputStream* stream, uint8* dest, uint32 max_size, |
| 914 AudioBuffersState buffers_state) { | 904 AudioBuffersState buffers_state) { |
| 915 AutoLock l(lock_); | 905 AutoLock l(lock_); |
| 916 if (source_callback_) { | 906 if (source_callback_) { |
| 917 return source_callback_->OnMoreData(stream, dest, max_size, buffers_state); | 907 return source_callback_->OnMoreData(stream, dest, max_size, buffers_state); |
| 918 } | 908 } |
| 919 | 909 |
| 920 return 0; | 910 return 0; |
| 921 } | 911 } |
| 922 | 912 |
| 923 void AlsaPcmOutputStream::SharedData::OnClose(AudioOutputStream* stream) { | |
| 924 AutoLock l(lock_); | |
| 925 if (source_callback_) { | |
| 926 source_callback_->OnClose(stream); | |
| 927 } | |
| 928 } | |
| 929 | |
| 930 void AlsaPcmOutputStream::SharedData::OnError(AudioOutputStream* stream, | 913 void AlsaPcmOutputStream::SharedData::OnError(AudioOutputStream* stream, |
| 931 int code) { | 914 int code) { |
| 932 AutoLock l(lock_); | 915 AutoLock l(lock_); |
| 933 if (source_callback_) { | 916 if (source_callback_) { |
| 934 source_callback_->OnError(stream, code); | 917 source_callback_->OnError(stream, code); |
| 935 } | 918 } |
| 936 } | 919 } |
| 937 | 920 |
| 938 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 921 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
| 939 // release ownership of the currently registered callback. | 922 // release ownership of the currently registered callback. |
| 940 void AlsaPcmOutputStream::SharedData::set_source_callback( | 923 void AlsaPcmOutputStream::SharedData::set_source_callback( |
| 941 AudioSourceCallback* callback) { | 924 AudioSourceCallback* callback) { |
| 942 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); | 925 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); |
| 943 AutoLock l(lock_); | 926 AutoLock l(lock_); |
| 944 source_callback_ = callback; | 927 source_callback_ = callback; |
| 945 } | 928 } |
| OLD | NEW |