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 |