Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(923)

Side by Side Diff: media/audio/linux/alsa_output.cc

Issue 9255017: Add thread safety to AudioManagerBase to protect access to the audio thread member variable. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix style issue Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/audio/audio_output_proxy_unittest.cc ('k') | media/audio/linux/alsa_output_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « media/audio/audio_output_proxy_unittest.cc ('k') | media/audio/linux/alsa_output_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698