pulse_input.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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_stream_flush(handle_, NULL, NULL);
123
124      if (pa_stream_get_state(handle_) != PA_STREAM_UNCONNECTED)
125        pa_stream_disconnect(handle_);
126
127      // Release PulseAudio structures.
128      pa_stream_unref(handle_);
129      handle_ = NULL;
130    }
131  }
132
133  // Signal to the manager that we're closed and can be removed.
134  // This should be the last call in the function as it deletes "this".
135  audio_manager_->ReleaseInputStream(this);
136}
137
138double PulseAudioInputStream::GetMaxVolume() {
139  return static_cast<double>(PA_VOLUME_NORM);
140}
141
142void PulseAudioInputStream::SetVolume(double volume) {
143  AutoPulseLock auto_lock(pa_mainloop_);
144  if (!handle_)
145    return;
146
147  size_t index = pa_stream_get_device_index(handle_);
148  pa_operation* operation = NULL;
149  if (!channels_) {
150    // Get the number of channels for the source only when the |channels_| is 0.
151    // We are assuming the stream source is not changed on the fly here.
152    operation = pa_context_get_source_info_by_index(
153        pa_context_, index, &VolumeCallback, this);
154    WaitForOperationCompletion(pa_mainloop_, operation);
155    if (!channels_) {
156      DLOG(WARNING) << "Failed to get the number of channels for the source";
157      return;
158    }
159  }
160
161  pa_cvolume pa_volume;
162  pa_cvolume_set(&pa_volume, channels_, volume);
163  operation = pa_context_set_source_volume_by_index(
164      pa_context_, index, &pa_volume, NULL, NULL);
165
166  // Don't need to wait for this task to complete.
167  pa_operation_unref(operation);
168}
169
170double PulseAudioInputStream::GetVolume() {
171  if (pa_threaded_mainloop_in_thread(pa_mainloop_)) {
172    // When being called by the pulse thread, GetVolume() is asynchronous and
173    // called under AutoPulseLock.
174    if (!handle_)
175      return 0.0;
176
177    size_t index = pa_stream_get_device_index(handle_);
178    pa_operation* operation = pa_context_get_source_info_by_index(
179        pa_context_, index, &VolumeCallback, this);
180    // Do not wait for the operation since we can't block the pulse thread.
181    pa_operation_unref(operation);
182
183    // Return zero and the callback will asynchronously update the |volume_|.
184    return 0.0;
185  } else {
186    // Called by other thread, put an AutoPulseLock and wait for the operation.
187    AutoPulseLock auto_lock(pa_mainloop_);
188    if (!handle_)
189      return 0.0;
190
191    size_t index = pa_stream_get_device_index(handle_);
192    pa_operation* operation = pa_context_get_source_info_by_index(
193        pa_context_, index, &VolumeCallback, this);
194    WaitForOperationCompletion(pa_mainloop_, operation);
195
196    return volume_;
197  }
198}
199
200// static, used by pa_stream_set_read_callback.
201void PulseAudioInputStream::ReadCallback(pa_stream* handle,
202                                         size_t length,
203                                         void* user_data) {
204  PulseAudioInputStream* stream =
205      reinterpret_cast<PulseAudioInputStream*>(user_data);
206
207  stream->ReadData();
208}
209
210// static, used by pa_context_get_source_info_by_index.
211void PulseAudioInputStream::VolumeCallback(pa_context* context,
212                                           const pa_source_info* info,
213                                           int error, void* user_data) {
214  PulseAudioInputStream* stream =
215      reinterpret_cast<PulseAudioInputStream*>(user_data);
216
217  if (error) {
218    pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
219    return;
220  }
221
222  if (stream->channels_ != info->channel_map.channels)
223    stream->channels_ = info->channel_map.channels;
224
225  pa_volume_t volume = PA_VOLUME_MUTED;  // Minimum possible value.
226  // Use the max volume of any channel as the volume.
227  for (int i = 0; i < stream->channels_; ++i) {
228    if (volume < info->volume.values[i])
229      volume = info->volume.values[i];
230  }
231
232  // It is safe to access |volume_| here since VolumeCallback() is running
233  // under PulseLock.
234  stream->volume_ = static_cast<double>(volume);
235}
236
237// static, used by pa_stream_set_state_callback.
238void PulseAudioInputStream::StreamNotifyCallback(pa_stream* s,
239                                                 void* user_data) {
240  PulseAudioInputStream* stream =
241      reinterpret_cast<PulseAudioInputStream*>(user_data);
242  if (s && stream->callback_ &&
243      pa_stream_get_state(s) == PA_STREAM_FAILED) {
244    stream->callback_->OnError(stream);
245  }
246
247  pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
248}
249
250void PulseAudioInputStream::ReadData() {
251  uint32 hardware_delay = pulse::GetHardwareLatencyInBytes(
252      handle_, params_.sample_rate(), params_.GetBytesPerFrame());
253
254  // Update the AGC volume level once every second. Note that,
255  // |volume| is also updated each time SetVolume() is called
256  // through IPC by the render-side AGC.
257  // We disregard the |normalized_volume| from GetAgcVolume()
258  // and use the value calculated by |volume_|.
259  double normalized_volume = 0.0;
260  GetAgcVolume(&normalized_volume);
261  normalized_volume = volume_ / GetMaxVolume();
262
263  do {
264    size_t length = 0;
265    const void* data = NULL;
266    pa_stream_peek(handle_, &data, &length);
267    if (!data || length == 0)
268      break;
269
270    const int number_of_frames = length / params_.GetBytesPerFrame();
271    fifo_.Push(data, number_of_frames, params_.bits_per_sample() / 8);
272
273    // Checks if we still have data.
274    pa_stream_drop(handle_);
275  } while (pa_stream_readable_size(handle_) > 0);
276
277  while (fifo_.available_blocks()) {
278    const AudioBus* audio_bus = fifo_.Consume();
279
280    // Compensate the audio delay caused by the FIFO.
281    hardware_delay += fifo_.GetAvailableFrames() * params_.GetBytesPerFrame();
282    callback_->OnData(this, audio_bus, hardware_delay, normalized_volume);
283
284    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(5));
285  }
286
287  pa_threaded_mainloop_signal(pa_mainloop_, 0);
288}
289
290}  // namespace media
291