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/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 frames_filled = source_callback_->OnMoreData( 133 audio_bus_.get(), AudioBuffersState(0, hardware_delay)); 134 } 135 136 // Zero any unfilled data so it plays back as silence. 137 if (frames_filled < audio_bus_->frames()) { 138 audio_bus_->ZeroFramesPartial( 139 frames_filled, audio_bus_->frames() - frames_filled); 140 } 141 142 // Note: If this ever changes to output raw float the data must be clipped 143 // and sanitized since it may come from an untrusted source such as NaCl. 144 audio_bus_->Scale(volume_); 145 audio_bus_->ToInterleaved( 146 audio_bus_->frames(), params_.bits_per_sample() / 8, buffer); 147 148 if (pa_stream_write(pa_stream_, buffer, bytes_to_fill, NULL, 0LL, 149 PA_SEEK_RELATIVE) < 0) { 150 if (source_callback_) { 151 source_callback_->OnError(this); 152 } 153 } 154 155 bytes_remaining -= bytes_to_fill; 156 } 157} 158 159void PulseAudioOutputStream::Start(AudioSourceCallback* callback) { 160 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 161 CHECK(callback); 162 CHECK(pa_stream_); 163 164 AutoPulseLock auto_lock(pa_mainloop_); 165 166 // Ensure the context and stream are ready. 167 if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY && 168 pa_stream_get_state(pa_stream_) != PA_STREAM_READY) { 169 callback->OnError(this); 170 return; 171 } 172 173 source_callback_ = callback; 174 175 // Uncork (resume) the stream. 176 pa_operation* operation = pa_stream_cork( 177 pa_stream_, 0, &pulse::StreamSuccessCallback, pa_mainloop_); 178 WaitForOperationCompletion(pa_mainloop_, operation); 179} 180 181void PulseAudioOutputStream::Stop() { 182 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 183 184 // Cork (pause) the stream. Waiting for the main loop lock will ensure 185 // outstanding callbacks have completed. 186 AutoPulseLock auto_lock(pa_mainloop_); 187 188 // Set |source_callback_| to NULL so all FulfillWriteRequest() calls which may 189 // occur while waiting on the flush and cork exit immediately. 190 source_callback_ = NULL; 191 192 // Flush the stream prior to cork, doing so after will cause hangs. Write 193 // callbacks are suspended while inside pa_threaded_mainloop_lock() so this 194 // is all thread safe. 195 pa_operation* operation = pa_stream_flush( 196 pa_stream_, &pulse::StreamSuccessCallback, pa_mainloop_); 197 WaitForOperationCompletion(pa_mainloop_, operation); 198 199 operation = pa_stream_cork(pa_stream_, 1, &pulse::StreamSuccessCallback, 200 pa_mainloop_); 201 WaitForOperationCompletion(pa_mainloop_, operation); 202} 203 204void PulseAudioOutputStream::SetVolume(double volume) { 205 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 206 207 volume_ = static_cast<float>(volume); 208} 209 210void PulseAudioOutputStream::GetVolume(double* volume) { 211 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); 212 213 *volume = volume_; 214} 215 216} // namespace media 217