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