pulse_output.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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 3// found in the LICENSE file. 4 5#include "media/audio/pulse/pulse_output.h" 6 7#include <pulse/pulseaudio.h> 8 9#include "base/message_loop.h" 10#include "media/audio/audio_manager_base.h" 11#include "media/audio/audio_parameters.h" 12#include "media/audio/pulse/pulse_util.h" 13 14namespace media { 15 16using pulse::AutoPulseLock; 17using pulse::WaitForOperationCompletion; 18 19// static, pa_stream_notify_cb 20void PulseAudioOutputStream::StreamNotifyCallback(pa_stream* s, void* p_this) { 21 PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this); 22 23 // Forward unexpected failures to the AudioSourceCallback if available. All 24 // these variables are only modified under pa_threaded_mainloop_lock() so this 25 // should be thread safe. 26 if (s && stream->source_callback_ && 27 pa_stream_get_state(s) == PA_STREAM_FAILED) { 28 stream->source_callback_->OnError(stream); 29 } 30 31 pa_threaded_mainloop_signal(stream->pa_mainloop_, 0); 32} 33 34// static, pa_stream_request_cb_t 35void PulseAudioOutputStream::StreamRequestCallback(pa_stream* s, size_t len, 36 void* p_this) { 37 // Fulfill write request; must always result in a pa_stream_write() call. 38 static_cast<PulseAudioOutputStream*>(p_this)->FulfillWriteRequest(len); 39} 40 41PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params, 42 AudioManagerBase* manager) 43 : params_(params), 44 manager_(manager), 45 pa_context_(NULL), 46 pa_mainloop_(NULL), 47 pa_stream_(NULL), 48 volume_(1.0f), 49 source_callback_(NULL) { 50 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 51 52 CHECK(params_.IsValid()); 53 audio_bus_ = AudioBus::Create(params_); 54} 55 56PulseAudioOutputStream::~PulseAudioOutputStream() { 57 // All internal structures should already have been freed in Close(), which 58 // calls AudioManagerBase::ReleaseOutputStream() which deletes this object. 59 DCHECK(!pa_stream_); 60 DCHECK(!pa_context_); 61 DCHECK(!pa_mainloop_); 62} 63 64bool PulseAudioOutputStream::Open() { 65 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 66 return pulse::CreateOutputStream(&pa_mainloop_, &pa_context_, &pa_stream_, 67 params_, &StreamNotifyCallback, 68 &StreamRequestCallback, this); 69} 70 71void PulseAudioOutputStream::Reset() { 72 if (!pa_mainloop_) { 73 DCHECK(!pa_stream_); 74 DCHECK(!pa_context_); 75 return; 76 } 77 78 { 79 AutoPulseLock auto_lock(pa_mainloop_); 80 81 // Close the stream. 82 if (pa_stream_) { 83 // Ensure all samples are played out before shutdown. 84 pa_operation* operation = pa_stream_flush( 85 pa_stream_, &pulse::StreamSuccessCallback, pa_mainloop_); 86 WaitForOperationCompletion(pa_mainloop_, operation); 87 88 // Release PulseAudio structures. 89 pa_stream_disconnect(pa_stream_); 90 pa_stream_set_write_callback(pa_stream_, NULL, NULL); 91 pa_stream_set_state_callback(pa_stream_, NULL, NULL); 92 pa_stream_unref(pa_stream_); 93 pa_stream_ = NULL; 94 } 95 96 if (pa_context_) { 97 pa_context_disconnect(pa_context_); 98 pa_context_set_state_callback(pa_context_, NULL, NULL); 99 pa_context_unref(pa_context_); 100 pa_context_ = NULL; 101 } 102 } 103 104 pa_threaded_mainloop_stop(pa_mainloop_); 105 pa_threaded_mainloop_free(pa_mainloop_); 106 pa_mainloop_ = NULL; 107} 108 109void PulseAudioOutputStream::Close() { 110 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 111 112 Reset(); 113 114 // Signal to the manager that we're closed and can be removed. 115 // This should be the last call in the function as it deletes "this". 116 manager_->ReleaseOutputStream(this); 117} 118 119void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) { 120 int bytes_remaining = requested_bytes; 121 while (bytes_remaining > 0) { 122 void* buffer = NULL; 123 size_t bytes_to_fill = params_.GetBytesPerBuffer(); 124 CHECK_GE(pa_stream_begin_write(pa_stream_, &buffer, &bytes_to_fill), 0); 125 CHECK_EQ(bytes_to_fill, static_cast<size_t>(params_.GetBytesPerBuffer())); 126 127 int frames_filled = 0; 128 if (source_callback_) { 129 uint32 hardware_delay = pulse::GetHardwareLatencyInBytes( 130 pa_stream_, params_.sample_rate(), 131 params_.GetBytesPerFrame()); 132 source_callback_->WaitTillDataReady(); 133 frames_filled = source_callback_->OnMoreData( 134 audio_bus_.get(), AudioBuffersState(0, hardware_delay)); 135 } 136 137 // Zero any unfilled data so it plays back as silence. 138 if (frames_filled < audio_bus_->frames()) { 139 audio_bus_->ZeroFramesPartial( 140 frames_filled, audio_bus_->frames() - frames_filled); 141 } 142 143 // Note: If this ever changes to output raw float the data must be clipped 144 // and sanitized since it may come from an untrusted source such as NaCl. 145 audio_bus_->Scale(volume_); 146 audio_bus_->ToInterleaved( 147 audio_bus_->frames(), params_.bits_per_sample() / 8, buffer); 148 149 if (pa_stream_write(pa_stream_, buffer, bytes_to_fill, NULL, 0LL, 150 PA_SEEK_RELATIVE) < 0) { 151 if (source_callback_) { 152 source_callback_->OnError(this); 153 } 154 } 155 156 bytes_remaining -= bytes_to_fill; 157 } 158} 159 160void PulseAudioOutputStream::Start(AudioSourceCallback* callback) { 161 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 162 CHECK(callback); 163 CHECK(pa_stream_); 164 165 AutoPulseLock auto_lock(pa_mainloop_); 166 167 // Ensure the context and stream are ready. 168 if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY && 169 pa_stream_get_state(pa_stream_) != PA_STREAM_READY) { 170 callback->OnError(this); 171 return; 172 } 173 174 source_callback_ = callback; 175 176 // Uncork (resume) the stream. 177 pa_operation* operation = pa_stream_cork( 178 pa_stream_, 0, &pulse::StreamSuccessCallback, pa_mainloop_); 179 WaitForOperationCompletion(pa_mainloop_, operation); 180} 181 182void PulseAudioOutputStream::Stop() { 183 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 184 185 // Cork (pause) the stream. Waiting for the main loop lock will ensure 186 // outstanding callbacks have completed. 187 AutoPulseLock auto_lock(pa_mainloop_); 188 189 // Set |source_callback_| to NULL so all FulfillWriteRequest() calls which may 190 // occur while waiting on the flush and cork exit immediately. 191 source_callback_ = NULL; 192 193 // Flush the stream prior to cork, doing so after will cause hangs. Write 194 // callbacks are suspended while inside pa_threaded_mainloop_lock() so this 195 // is all thread safe. 196 pa_operation* operation = pa_stream_flush( 197 pa_stream_, &pulse::StreamSuccessCallback, pa_mainloop_); 198 WaitForOperationCompletion(pa_mainloop_, operation); 199 200 operation = pa_stream_cork(pa_stream_, 1, &pulse::StreamSuccessCallback, 201 pa_mainloop_); 202 WaitForOperationCompletion(pa_mainloop_, operation); 203} 204 205void PulseAudioOutputStream::SetVolume(double volume) { 206 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 207 208 volume_ = static_cast<float>(volume); 209} 210 211void PulseAudioOutputStream::GetVolume(double* volume) { 212 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 213 214 *volume = volume_; 215} 216 217} // namespace media 218