pulse_input.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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_input.h"
6
7#include <pulse/pulseaudio.h>
8
9#include "base/logging.h"
10#include "media/audio/pulse/audio_manager_pulse.h"
11#include "media/audio/pulse/pulse_util.h"
12
13namespace media {
14
15using pulse::AutoPulseLock;
16using pulse::WaitForOperationCompletion;
17
18// Number of blocks of buffers used in the |fifo_|.
19const int kNumberOfBlocksBufferInFifo = 2;
20
21PulseAudioInputStream::PulseAudioInputStream(AudioManagerPulse* audio_manager,
22                                             const std::string& device_name,
23                                             const AudioParameters& params,
24                                             pa_threaded_mainloop* mainloop,
25                                             pa_context* context)
26    : audio_manager_(audio_manager),
27      callback_(NULL),
28      device_name_(device_name),
29      params_(params),
30      channels_(0),
31      volume_(0.0),
32      stream_started_(false),
33      fifo_(params.channels(),
34            params.frames_per_buffer(),
35            kNumberOfBlocksBufferInFifo),
36      pa_mainloop_(mainloop),
37      pa_context_(context),
38      handle_(NULL),
39      context_state_changed_(false) {
40  DCHECK(mainloop);
41  DCHECK(context);
42  CHECK(params_.IsValid());
43}
44
45PulseAudioInputStream::~PulseAudioInputStream() {
46  // All internal structures should already have been freed in Close(),
47  // which calls AudioManagerPulse::Release which deletes this object.
48  DCHECK(!handle_);
49}
50
51bool PulseAudioInputStream::Open() {
52  DCHECK(thread_checker_.CalledOnValidThread());
53  AutoPulseLock auto_lock(pa_mainloop_);
54  if (!pulse::CreateInputStream(pa_mainloop_, pa_context_, &handle_, params_,
55                                device_name_, &StreamNotifyCallback, this)) {
56    return false;
57  }
58
59  DCHECK(handle_);
60
61  return true;
62}
63
64void PulseAudioInputStream::Start(AudioInputCallback* callback) {
65  DCHECK(thread_checker_.CalledOnValidThread());
66  DCHECK(callback);
67  DCHECK(handle_);
68
69  // AGC needs to be started out of the lock.
70  StartAgc();
71
72  AutoPulseLock auto_lock(pa_mainloop_);
73
74  if (stream_started_)
75    return;
76
77  // Clean up the old buffer.
78  pa_stream_drop(handle_);
79  fifo_.Clear();
80
81  // Start the streaming.
82  callback_ = callback;
83  pa_stream_set_read_callback(handle_, &ReadCallback, this);
84  pa_stream_readable_size(handle_);
85  stream_started_ = true;
86
87  pa_operation* operation = pa_stream_cork(handle_, 0, NULL, NULL);
88  WaitForOperationCompletion(pa_mainloop_, operation);
89}
90
91void PulseAudioInputStream::Stop() {
92  DCHECK(thread_checker_.CalledOnValidThread());
93  AutoPulseLock auto_lock(pa_mainloop_);
94  if (!stream_started_)
95    return;
96
97  StopAgc();
98
99  // Set the flag to false to stop filling new data to soundcard.
100  stream_started_ = false;
101
102  pa_operation* operation = pa_stream_flush(handle_,
103                                            &pulse::StreamSuccessCallback,
104                                            pa_mainloop_);
105  WaitForOperationCompletion(pa_mainloop_, operation);
106
107  // Stop the stream.
108  pa_stream_set_read_callback(handle_, NULL, NULL);
109  operation = pa_stream_cork(handle_, 1, &pulse::StreamSuccessCallback,
110                             pa_mainloop_);
111  WaitForOperationCompletion(pa_mainloop_, operation);
112  callback_ = NULL;
113}
114
115void PulseAudioInputStream::Close() {
116  DCHECK(thread_checker_.CalledOnValidThread());
117  {
118    AutoPulseLock auto_lock(pa_mainloop_);
119    if (handle_) {
120      // Disable all the callbacks before disconnecting.
121      pa_stream_set_state_callback(handle_, NULL, NULL);
122      pa_operation* operation = pa_stream_flush(
123          handle_, &pulse::StreamSuccessCallback, pa_mainloop_);
124      WaitForOperationCompletion(pa_mainloop_, operation);
125
126      if (pa_stream_get_state(handle_) != PA_STREAM_UNCONNECTED)
127        pa_stream_disconnect(handle_);
128
129      // Release PulseAudio structures.
130      pa_stream_unref(handle_);
131      handle_ = NULL;
132    }
133  }
134
135  // Signal to the manager that we're closed and can be removed.
136  // This should be the last call in the function as it deletes "this".
137  audio_manager_->ReleaseInputStream(this);
138}
139
140double PulseAudioInputStream::GetMaxVolume() {
141  return static_cast<double>(PA_VOLUME_NORM);
142}
143
144void PulseAudioInputStream::SetVolume(double volume) {
145  AutoPulseLock auto_lock(pa_mainloop_);
146  if (!handle_)
147    return;
148
149  size_t index = pa_stream_get_device_index(handle_);
150  pa_operation* operation = NULL;
151  if (!channels_) {
152    // Get the number of channels for the source only when the |channels_| is 0.
153    // We are assuming the stream source is not changed on the fly here.
154    operation = pa_context_get_source_info_by_index(
155        pa_context_, index, &VolumeCallback, this);
156    WaitForOperationCompletion(pa_mainloop_, operation);
157    if (!channels_) {
158      DLOG(WARNING) << "Failed to get the number of channels for the source";
159      return;
160    }
161  }
162
163  pa_cvolume pa_volume;
164  pa_cvolume_set(&pa_volume, channels_, volume);
165  operation = pa_context_set_source_volume_by_index(
166      pa_context_, index, &pa_volume, NULL, NULL);
167
168  // Don't need to wait for this task to complete.
169  pa_operation_unref(operation);
170}
171
172double PulseAudioInputStream::GetVolume() {
173  if (pa_threaded_mainloop_in_thread(pa_mainloop_)) {
174    // When being called by the pulse thread, GetVolume() is asynchronous and
175    // called under AutoPulseLock.
176    if (!handle_)
177      return 0.0;
178
179    size_t index = pa_stream_get_device_index(handle_);
180    pa_operation* operation = pa_context_get_source_info_by_index(
181        pa_context_, index, &VolumeCallback, this);
182    // Do not wait for the operation since we can't block the pulse thread.
183    pa_operation_unref(operation);
184
185    // Return zero and the callback will asynchronously update the |volume_|.
186    return 0.0;
187  } else {
188    // Called by other thread, put an AutoPulseLock and wait for the operation.
189    AutoPulseLock auto_lock(pa_mainloop_);
190    if (!handle_)
191      return 0.0;
192
193    size_t index = pa_stream_get_device_index(handle_);
194    pa_operation* operation = pa_context_get_source_info_by_index(
195        pa_context_, index, &VolumeCallback, this);
196    WaitForOperationCompletion(pa_mainloop_, operation);
197
198    return volume_;
199  }
200}
201
202// static, used by pa_stream_set_read_callback.
203void PulseAudioInputStream::ReadCallback(pa_stream* handle,
204                                         size_t length,
205                                         void* user_data) {
206  PulseAudioInputStream* stream =
207      reinterpret_cast<PulseAudioInputStream*>(user_data);
208
209  stream->ReadData();
210}
211
212// static, used by pa_context_get_source_info_by_index.
213void PulseAudioInputStream::VolumeCallback(pa_context* context,
214                                           const pa_source_info* info,
215                                           int error, void* user_data) {
216  PulseAudioInputStream* stream =
217      reinterpret_cast<PulseAudioInputStream*>(user_data);
218
219  if (error) {
220    pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
221    return;
222  }
223
224  if (stream->channels_ != info->channel_map.channels)
225    stream->channels_ = info->channel_map.channels;
226
227  pa_volume_t volume = PA_VOLUME_MUTED;  // Minimum possible value.
228  // Use the max volume of any channel as the volume.
229  for (int i = 0; i < stream->channels_; ++i) {
230    if (volume < info->volume.values[i])
231      volume = info->volume.values[i];
232  }
233
234  // It is safe to access |volume_| here since VolumeCallback() is running
235  // under PulseLock.
236  stream->volume_ = static_cast<double>(volume);
237}
238
239// static, used by pa_stream_set_state_callback.
240void PulseAudioInputStream::StreamNotifyCallback(pa_stream* s,
241                                                 void* user_data) {
242  PulseAudioInputStream* stream =
243      reinterpret_cast<PulseAudioInputStream*>(user_data);
244  if (s && stream->callback_ &&
245      pa_stream_get_state(s) == PA_STREAM_FAILED) {
246    stream->callback_->OnError(stream);
247  }
248
249  pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
250}
251
252void PulseAudioInputStream::ReadData() {
253  uint32 hardware_delay = pulse::GetHardwareLatencyInBytes(
254      handle_, params_.sample_rate(), params_.GetBytesPerFrame());
255
256  // Update the AGC volume level once every second. Note that,
257  // |volume| is also updated each time SetVolume() is called
258  // through IPC by the render-side AGC.
259  // We disregard the |normalized_volume| from GetAgcVolume()
260  // and use the value calculated by |volume_|.
261  double normalized_volume = 0.0;
262  GetAgcVolume(&normalized_volume);
263  normalized_volume = volume_ / GetMaxVolume();
264
265  do {
266    size_t length = 0;
267    const void* data = NULL;
268    pa_stream_peek(handle_, &data, &length);
269    if (!data || length == 0)
270      break;
271
272    const int number_of_frames = length / params_.GetBytesPerFrame();
273    if (number_of_frames > fifo_.GetUnfilledFrames()) {
274      // Dynamically increase capacity to the FIFO to handle larger buffer got
275      // from Pulse.
276      const int increase_blocks_of_buffer = static_cast<int>(
277          (number_of_frames - fifo_.GetUnfilledFrames()) /
278              params_.frames_per_buffer()) + 1;
279      fifo_.IncreaseCapacity(increase_blocks_of_buffer);
280    }
281
282    fifo_.Push(data, number_of_frames, params_.bits_per_sample() / 8);
283
284    // Checks if we still have data.
285    pa_stream_drop(handle_);
286  } while (pa_stream_readable_size(handle_) > 0);
287
288  while (fifo_.available_blocks()) {
289    const AudioBus* audio_bus = fifo_.Consume();
290
291    // Compensate the audio delay caused by the FIFO.
292    hardware_delay += fifo_.GetAvailableFrames() * params_.GetBytesPerFrame();
293    callback_->OnData(this, audio_bus, hardware_delay, normalized_volume);
294
295    // Sleep 5ms to wait until render consumes the data in order to avoid
296    // back to back OnData() method.
297    if (fifo_.available_blocks())
298      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(5));
299  }
300
301  pa_threaded_mainloop_signal(pa_mainloop_, 0);
302}
303
304}  // namespace media
305