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/single_thread_task_runner.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 const std::string& device_id, 43 AudioManagerBase* manager) 44 : params_(params), 45 device_id_(device_id), 46 manager_(manager), 47 pa_context_(NULL), 48 pa_mainloop_(NULL), 49 pa_stream_(NULL), 50 volume_(1.0f), 51 source_callback_(NULL) { 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(thread_checker_.CalledOnValidThread()); 66 return pulse::CreateOutputStream(&pa_mainloop_, &pa_context_, &pa_stream_, 67 params_, device_id_, &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(thread_checker_.CalledOnValidThread()); 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 // NOTE: |bytes_to_fill| may be larger than |requested_bytes| now, this is 128 // okay since pa_stream_begin_write() is the authoritative source on how 129 // much can be written. 130 131 int frames_filled = 0; 132 if (source_callback_) { 133 const uint32 hardware_delay = pulse::GetHardwareLatencyInBytes( 134 pa_stream_, params_.sample_rate(), params_.GetBytesPerFrame()); 135 frames_filled = source_callback_->OnMoreData( 136 audio_bus_.get(), AudioBuffersState(0, hardware_delay)); 137 138 // Zero any unfilled data so it plays back as silence. 139 if (frames_filled < audio_bus_->frames()) { 140 audio_bus_->ZeroFramesPartial( 141 frames_filled, audio_bus_->frames() - frames_filled); 142 } 143 144 // Note: If this ever changes to output raw float the data must be clipped 145 // and sanitized since it may come from an untrusted source such as NaCl. 146 audio_bus_->Scale(volume_); 147 audio_bus_->ToInterleaved( 148 audio_bus_->frames(), params_.bits_per_sample() / 8, buffer); 149 } else { 150 memset(buffer, 0, bytes_to_fill); 151 } 152 153 if (pa_stream_write(pa_stream_, buffer, bytes_to_fill, NULL, 0LL, 154 PA_SEEK_RELATIVE) < 0) { 155 if (source_callback_) { 156 source_callback_->OnError(this); 157 } 158 } 159 160 // NOTE: As mentioned above, |bytes_remaining| may be negative after this. 161 bytes_remaining -= bytes_to_fill; 162 163 // Despite telling Pulse to only request certain buffer sizes, it will not 164 // always obey. In these cases we need to avoid back to back reads from 165 // the renderer as it won't have time to complete the request. 166 // 167 // We can't defer the callback as Pulse will never call us again until we've 168 // satisfied writing the requested number of bytes. 169 // 170 // TODO(dalecurtis): It might be worth choosing the sleep duration based on 171 // the hardware latency return above. Watch http://crbug.com/366433 to see 172 // if a more complicated wait process is necessary. We may also need to see 173 // if a PostDelayedTask should be used here to avoid blocking the PulseAudio 174 // command thread. 175 if (source_callback_ && bytes_remaining > 0) 176 base::PlatformThread::Sleep(params_.GetBufferDuration() / 4); 177 } 178} 179 180void PulseAudioOutputStream::Start(AudioSourceCallback* callback) { 181 DCHECK(thread_checker_.CalledOnValidThread()); 182 CHECK(callback); 183 CHECK(pa_stream_); 184 185 AutoPulseLock auto_lock(pa_mainloop_); 186 187 // Ensure the context and stream are ready. 188 if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY && 189 pa_stream_get_state(pa_stream_) != PA_STREAM_READY) { 190 callback->OnError(this); 191 return; 192 } 193 194 source_callback_ = callback; 195 196 // Uncork (resume) the stream. 197 pa_operation* operation = pa_stream_cork( 198 pa_stream_, 0, &pulse::StreamSuccessCallback, pa_mainloop_); 199 WaitForOperationCompletion(pa_mainloop_, operation); 200} 201 202void PulseAudioOutputStream::Stop() { 203 DCHECK(thread_checker_.CalledOnValidThread()); 204 205 // Cork (pause) the stream. Waiting for the main loop lock will ensure 206 // outstanding callbacks have completed. 207 AutoPulseLock auto_lock(pa_mainloop_); 208 209 // Set |source_callback_| to NULL so all FulfillWriteRequest() calls which may 210 // occur while waiting on the flush and cork exit immediately. 211 source_callback_ = NULL; 212 213 // Flush the stream prior to cork, doing so after will cause hangs. Write 214 // callbacks are suspended while inside pa_threaded_mainloop_lock() so this 215 // is all thread safe. 216 pa_operation* operation = pa_stream_flush( 217 pa_stream_, &pulse::StreamSuccessCallback, pa_mainloop_); 218 WaitForOperationCompletion(pa_mainloop_, operation); 219 220 operation = pa_stream_cork(pa_stream_, 1, &pulse::StreamSuccessCallback, 221 pa_mainloop_); 222 WaitForOperationCompletion(pa_mainloop_, operation); 223} 224 225void PulseAudioOutputStream::SetVolume(double volume) { 226 DCHECK(thread_checker_.CalledOnValidThread()); 227 228 volume_ = static_cast<float>(volume); 229} 230 231void PulseAudioOutputStream::GetVolume(double* volume) { 232 DCHECK(thread_checker_.CalledOnValidThread()); 233 234 *volume = volume_; 235} 236 237} // namespace media 238