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