pulse_output.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek// Use of this source code is governed by a BSD-style license that can be 3f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek// found in the LICENSE file. 4f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek 5f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek#include "media/audio/pulse/pulse_output.h" 6f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek 7f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek#include <pulse/pulseaudio.h> 8f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek 9f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek#include "base/single_thread_task_runner.h" 10f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek#include "media/audio/audio_manager_base.h" 11f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek#include "media/audio/audio_parameters.h" 12f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek#include "media/audio/pulse/pulse_util.h" 13f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek 14f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimeknamespace media { 15f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek 16f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimekusing pulse::AutoPulseLock; 17f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimekusing pulse::WaitForOperationCompletion; 18f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek 19f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek// static, pa_stream_notify_cb 20f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimekvoid PulseAudioOutputStream::StreamNotifyCallback(pa_stream* s, void* p_this) { 21f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this); 227a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen 237a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen // Forward unexpected failures to the AudioSourceCallback if available. All 247a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen // these variables are only modified under pa_threaded_mainloop_lock() so this 257a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen // should be thread safe. 267a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen if (s && stream->source_callback_ && 277a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen pa_stream_get_state(s) == PA_STREAM_FAILED) { 287a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen stream->source_callback_->OnError(stream); 297a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen } 307a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen 3176c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen pa_threaded_mainloop_signal(stream->pa_mainloop_, 0); 3276c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen} 337a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen 347a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen// static, pa_stream_request_cb_t 35f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimekvoid PulseAudioOutputStream::StreamRequestCallback(pa_stream* s, size_t len, 36f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek void* p_this) { 37f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek // Fulfill write request; must always result in a pa_stream_write() call. 38f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek static_cast<PulseAudioOutputStream*>(p_this)->FulfillWriteRequest(len); 39f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek} 40f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek 4176c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel BenzaquenPulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params, 42f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek const std::string& device_id, 437a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen AudioManagerBase* manager) 44f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek : params_(params), 4576c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen device_id_(device_id), 4676c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen manager_(manager), 47f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek pa_context_(NULL), 48f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek pa_mainloop_(NULL), 49f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek pa_stream_(NULL), 50f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek volume_(1.0f), 51f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek source_callback_(NULL) { 527a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); 53f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek 54f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek CHECK(params_.IsValid()); 55f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek audio_bus_ = AudioBus::Create(params_); 5676c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen} 5776c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen 5876c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel BenzaquenPulseAudioOutputStream::~PulseAudioOutputStream() { 59f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek // All internal structures should already have been freed in Close(), which 6076c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen // calls AudioManagerBase::ReleaseOutputStream() which deletes this object. 61f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek DCHECK(!pa_stream_); 62f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek DCHECK(!pa_context_); 6376c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen DCHECK(!pa_mainloop_); 6476c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen} 6576c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen 66f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimekbool PulseAudioOutputStream::Open() { 6776c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); 68f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek return pulse::CreateOutputStream(&pa_mainloop_, &pa_context_, &pa_stream_, 6976c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen params_, device_id_, &StreamNotifyCallback, 7076c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen &StreamRequestCallback, this); 7176c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen} 7276c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen 7376c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquenvoid PulseAudioOutputStream::Reset() { 7476c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen if (!pa_mainloop_) { 75f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek DCHECK(!pa_stream_); 76f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek DCHECK(!pa_context_); 77f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek return; 78f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek } 79f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek 80f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek { 817a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen AutoPulseLock auto_lock(pa_mainloop_); 82f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek 8376c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen // Close the stream. 84f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek if (pa_stream_) { 85f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek // Ensure all samples are played out before shutdown. 867a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen pa_operation* operation = pa_stream_flush( 87f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek pa_stream_, &pulse::StreamSuccessCallback, pa_mainloop_); 88f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek WaitForOperationCompletion(pa_mainloop_, operation); 8976c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen 9076c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen // Release PulseAudio structures. 9176c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen pa_stream_disconnect(pa_stream_); 92f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek pa_stream_set_write_callback(pa_stream_, NULL, NULL); 937a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen pa_stream_set_state_callback(pa_stream_, NULL, NULL); 947a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen pa_stream_unref(pa_stream_); 957a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen pa_stream_ = NULL; 967a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen } 977a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen 987a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen if (pa_context_) { 99f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek pa_context_disconnect(pa_context_); 1007a337af9e8bc752a2d3b227e4058ed2baf7a19d1Samuel Benzaquen pa_context_set_state_callback(pa_context_, NULL, NULL); 101f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek pa_context_unref(pa_context_); 102f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek pa_context_ = NULL; 10376c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen } 104f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek } 105f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek 106f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek pa_threaded_mainloop_stop(pa_mainloop_); 10776c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen pa_threaded_mainloop_free(pa_mainloop_); 10876c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen pa_mainloop_ = NULL; 10976c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen} 11076c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen 11176c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquenvoid PulseAudioOutputStream::Close() { 11276c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); 11376c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen 11476c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen Reset(); 11576c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen 11676c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen // Signal to the manager that we're closed and can be removed. 11776c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen // This should be the last call in the function as it deletes "this". 11876c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen manager_->ReleaseOutputStream(this); 11976c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen} 120f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek 121f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimekvoid PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) { 12276c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen int bytes_remaining = requested_bytes; 12376c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen while (bytes_remaining > 0) { 12476c2f92c4b4ab7e02857661a05e53ba4b501d87aSamuel Benzaquen void* buffer = NULL; 125f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek size_t bytes_to_fill = params_.GetBytesPerBuffer(); 126f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek CHECK_GE(pa_stream_begin_write(pa_stream_, &buffer, &bytes_to_fill), 0); 127f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek CHECK_EQ(bytes_to_fill, static_cast<size_t>(params_.GetBytesPerBuffer())); 128f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek 129f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek int frames_filled = 0; 130f7f295f321fd434e1e542844a71f538a56f2f8fbManuel Klimek if (source_callback_) { 131 uint32 hardware_delay = pulse::GetHardwareLatencyInBytes( 132 pa_stream_, params_.sample_rate(), 133 params_.GetBytesPerFrame()); 134 frames_filled = source_callback_->OnMoreData( 135 audio_bus_.get(), AudioBuffersState(0, hardware_delay)); 136 } 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 150 if (pa_stream_write(pa_stream_, buffer, bytes_to_fill, NULL, 0LL, 151 PA_SEEK_RELATIVE) < 0) { 152 if (source_callback_) { 153 source_callback_->OnError(this); 154 } 155 } 156 157 bytes_remaining -= bytes_to_fill; 158 } 159} 160 161void PulseAudioOutputStream::Start(AudioSourceCallback* callback) { 162 DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); 163 CHECK(callback); 164 CHECK(pa_stream_); 165 166 AutoPulseLock auto_lock(pa_mainloop_); 167 168 // Ensure the context and stream are ready. 169 if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY && 170 pa_stream_get_state(pa_stream_) != PA_STREAM_READY) { 171 callback->OnError(this); 172 return; 173 } 174 175 source_callback_ = callback; 176 177 // Uncork (resume) the stream. 178 pa_operation* operation = pa_stream_cork( 179 pa_stream_, 0, &pulse::StreamSuccessCallback, pa_mainloop_); 180 WaitForOperationCompletion(pa_mainloop_, operation); 181} 182 183void PulseAudioOutputStream::Stop() { 184 DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); 185 186 // Cork (pause) the stream. Waiting for the main loop lock will ensure 187 // outstanding callbacks have completed. 188 AutoPulseLock auto_lock(pa_mainloop_); 189 190 // Set |source_callback_| to NULL so all FulfillWriteRequest() calls which may 191 // occur while waiting on the flush and cork exit immediately. 192 source_callback_ = NULL; 193 194 // Flush the stream prior to cork, doing so after will cause hangs. Write 195 // callbacks are suspended while inside pa_threaded_mainloop_lock() so this 196 // is all thread safe. 197 pa_operation* operation = pa_stream_flush( 198 pa_stream_, &pulse::StreamSuccessCallback, pa_mainloop_); 199 WaitForOperationCompletion(pa_mainloop_, operation); 200 201 operation = pa_stream_cork(pa_stream_, 1, &pulse::StreamSuccessCallback, 202 pa_mainloop_); 203 WaitForOperationCompletion(pa_mainloop_, operation); 204} 205 206void PulseAudioOutputStream::SetVolume(double volume) { 207 DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); 208 209 volume_ = static_cast<float>(volume); 210} 211 212void PulseAudioOutputStream::GetVolume(double* volume) { 213 DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); 214 215 *volume = volume_; 216} 217 218} // namespace media 219