Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(994)

Side by Side Diff: media/audio/pulse/pulse_input.cc

Issue 10952024: Adding pulseaudio input support to chrome (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: moved the pulse code to AudioManagerPulse, and addressed Dale's comments. Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "base/logging.h"
8 #include "base/message_loop.h"
9 #include "media/audio/audio_manager_base.h"
10 #include "media/audio/pulse/pulse_util.h"
11 #include "media/base/seekable_buffer.h"
12
13 namespace media {
14
15 PulseAudioInputStream::PulseAudioInputStream(AudioManagerBase* audio_manager,
16 const std::string& device_name,
17 const AudioParameters& params,
18 pa_threaded_mainloop* mainloop,
19 pa_context* context)
20 : audio_manager_(audio_manager),
21 callback_(NULL),
22 device_name_(device_name),
23 params_(params),
24 channels_(0),
25 volume_(0.0),
26 stream_started_(false),
27 pa_mainloop_(mainloop),
28 pa_context_(context),
29 handle_(NULL) {
30 DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread());
31 DCHECK(mainloop);
32 DCHECK(context);
33 }
34
35 PulseAudioInputStream::~PulseAudioInputStream() {
36 // All internal structures should already have been freed in Close(),
37 // which calls AudioManagerPulse::Release which deletes this object.
38 DCHECK(!handle_);
39 }
40
41 bool PulseAudioInputStream::Open() {
42 DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread());
43 AutoPulseLock auto_lock(pa_mainloop_);
44
45 // Set sample specifications.
46 pa_sample_spec pa_sample_specifications;
47 pa_sample_specifications.format = BitsToPASampleFormat(
48 params_.bits_per_sample());
49 pa_sample_specifications.rate = params_.sample_rate();
50 pa_sample_specifications.channels = params_.channels();
51
52 // Get channel mapping and open recording stream.
53 pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap(
54 params_.channel_layout());
55 pa_channel_map* map = (source_channel_map.channels != 0)?
56 &source_channel_map : NULL;
57
58 // Create a new recording stream.
59 handle_ = pa_stream_new(pa_context_, "RecordStream",
60 &pa_sample_specifications, map);
61 if (!handle_) {
62 DLOG(ERROR) << "Open: failed to create PA stream";
63 return false;
64 }
65
66 pa_stream_set_state_callback(handle_, &StreamNotifyCallback, this);
67 pa_stream_set_read_callback(handle_, &ReadCallback, this);
68 pa_stream_readable_size(handle_);
69
70 // Set server-side capture buffer metrics. Detailed documentation on what
71 // values should be chosen can be found at
72 // freedesktop.org/software/pulseaudio/doxygen/structpa__buffer__attr.html.
73 pa_buffer_attr buffer_attributes;
74 const unsigned int buffer_size = params_.GetBytesPerBuffer();
75 buffer_attributes.maxlength = static_cast<uint32_t>(-1);
76 buffer_attributes.tlength = buffer_size;
77 buffer_attributes.minreq = buffer_size;
78 buffer_attributes.prebuf = static_cast<uint32_t>(-1);
79 buffer_attributes.fragsize = buffer_size;
80 int flags = PA_STREAM_AUTO_TIMING_UPDATE |
81 PA_STREAM_INTERPOLATE_TIMING |
82 PA_STREAM_ADJUST_LATENCY |
83 PA_STREAM_START_CORKED;
84 int err = pa_stream_connect_record(
85 handle_,
86 device_name_ == AudioManagerBase::kDefaultDeviceId ?
87 NULL : device_name_.c_str(),
88 &buffer_attributes,
89 static_cast<pa_stream_flags_t>(flags));
90 if (err) {
91 DLOG(ERROR) << "pa_stream_connect_playback FAILED " << err;
92 return false;
93 }
94
95 // Wait for the stream to be ready.
96 while (true) {
97 pa_stream_state_t stream_state = pa_stream_get_state(handle_);
98 if(!PA_STREAM_IS_GOOD(stream_state)) {
99 DLOG(ERROR) << "Invalid PulseAudio stream state";
100 return false;
101 }
102
103 if (stream_state == PA_STREAM_READY)
104 break;
105 pa_threaded_mainloop_wait(pa_mainloop_);
106 }
107
108 pa_stream_set_read_callback(handle_, &ReadCallback, this);
109 pa_stream_readable_size(handle_);
110
111 buffer_.reset(new media::SeekableBuffer(0, 2 * params_.GetBytesPerBuffer()));
112 audio_data_buffer_.reset(new uint8[params_.GetBytesPerBuffer()]);
113 return true;
114 }
115
116 void PulseAudioInputStream::Start(AudioInputCallback* callback) {
117 DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread());
118 DCHECK(callback);
119 DCHECK(handle_);
120 AutoPulseLock auto_lock(pa_mainloop_);
121
122 if (stream_started_)
123 return;
124
125 // Clean up the old buffer.
126 pa_stream_drop(handle_);
127
128 // Start the streaming.
129 stream_started_ = true;
130 callback_ = callback;
131
132 pa_operation* operation = pa_stream_cork(handle_, 0, NULL, NULL);
133 WaitForOperationCompletion(pa_mainloop_, operation);
134 }
135
136 void PulseAudioInputStream::Stop() {
137 DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread());
138 AutoPulseLock auto_lock(pa_mainloop_);
139 if (!stream_started_)
140 return;
141
142 // Set the flag to false to stop filling new data to soundcard.
143 stream_started_ = false;
144
145 pa_operation* operation = pa_stream_flush(
146 handle_, &StreamSuccessCallback, pa_mainloop_);
147 WaitForOperationCompletion(pa_mainloop_, operation);
148
149 // Stop the stream.
150 pa_stream_set_read_callback(handle_, NULL, NULL);
151 operation = pa_stream_cork(handle_, 1, &StreamSuccessCallback, pa_mainloop_);
152 WaitForOperationCompletion(pa_mainloop_, operation);
153 }
154
155 void PulseAudioInputStream::Close() {
156 DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread());
157 {
158 AutoPulseLock auto_lock(pa_mainloop_);
159 if (handle_) {
160 // Disable all the callbacks before disconnecting.
161 pa_stream_set_state_callback(handle_, NULL, NULL);
162 pa_stream_flush(handle_, NULL, NULL);
163
164 if (pa_stream_get_state(handle_) != PA_STREAM_UNCONNECTED)
165 pa_stream_disconnect(handle_);
166
167 // Release PulseAudio structures.
168 pa_stream_unref(handle_);
169 handle_ = NULL;
170 }
171 }
172
173 if (callback_)
174 callback_->OnClose(this);
175
176 // Signal to the manager that we're closed and can be removed.
177 // This should be the last call in the function as it deletes "this".
178 audio_manager_->ReleaseInputStream(this);
179 }
180
181 double PulseAudioInputStream::GetMaxVolume() {
182 return static_cast<double>(PA_VOLUME_NORM);
183 }
184
185 void PulseAudioInputStream::SetVolume(double volume) {
186 AutoPulseLock auto_lock(pa_mainloop_);
187 if (!handle_)
188 return;
189
190 size_t index = pa_stream_get_device_index(handle_);
191 pa_operation* operation = NULL;
192 if (!channels_) {
193 // Get the number of channels for the source only when the |channels_| is 0.
194 // We are assuming the stream source is not changed on the fly here.
195 operation = pa_context_get_source_info_by_index(
196 pa_context_, index, &VolumeCallback, this);
197 WaitForOperationCompletion(pa_mainloop_, operation);
198 if (!channels_) {
199 DLOG(WARNING) << "Failed to get the number of channels for the source";
200 return;
201 }
202 }
203
204 pa_cvolume pa_volume;
205 pa_cvolume_set(&pa_volume, channels_, volume);
206 operation = pa_context_set_source_volume_by_index(
207 pa_context_, index, &pa_volume, NULL, NULL);
208
209 // Don't need to wait for this task to complete.
210 pa_operation_unref(operation);
211 }
212
213 double PulseAudioInputStream::GetVolume() {
214 AutoPulseLock auto_lock(pa_mainloop_);
215 if (!handle_)
216 return 0.0;
217
218 size_t index = pa_stream_get_device_index(handle_);
219 pa_operation* operation = pa_context_get_source_info_by_index(
220 pa_context_, index, &VolumeCallback, this);
221 WaitForOperationCompletion(pa_mainloop_, operation);
222
223 return volume_;
224 }
225
226 // static, used by pa_stream_set_read_callback.
227 void PulseAudioInputStream::ReadCallback(pa_stream* handle,
228 size_t length,
229 void* user_data) {
230 PulseAudioInputStream* stream =
231 reinterpret_cast<PulseAudioInputStream*>(user_data);
232
233 stream->ReadData();
234 }
235
236 // static, used by pa_context_get_source_info_by_index.
237 void PulseAudioInputStream::VolumeCallback(pa_context* context,
238 const pa_source_info* info,
239 int error, void* user_data) {
240 PulseAudioInputStream* stream =
241 reinterpret_cast<PulseAudioInputStream*>(user_data);
242
243 if (error) {
244 pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
245 return;
246 }
247
248 if (stream->channels_ != info->channel_map.channels)
249 stream->channels_ = info->channel_map.channels;
250
251 pa_volume_t volume = PA_VOLUME_MUTED; // Minimum possible value.
252 // Use the max volume of any channel as the volume.
253 for (int i = 0; i < stream->channels_; ++i) {
254 if (volume < info->volume.values[i])
255 volume = info->volume.values[i];
256 }
257
258 stream->volume_ = static_cast<double>(volume);
259 }
260
261 // static, used by pa_stream_set_state_callback.
262 void PulseAudioInputStream::StreamNotifyCallback(pa_stream* stream,
263 void* user_data) {
264 PulseAudioInputStream* pulse_stream =
265 reinterpret_cast<PulseAudioInputStream*>(user_data);
266 if (stream && pulse_stream->callback_ &&
267 pa_stream_get_state(stream) == PA_STREAM_FAILED) {
268 pulse_stream->callback_->OnError(
269 pulse_stream, pa_context_errno(pulse_stream->pa_context_));
270 }
271
272 pa_threaded_mainloop_signal(pulse_stream->pa_mainloop_, 0);
273 }
274
275 void PulseAudioInputStream::ReadData() {
276 uint32 hardware_delay = GetHardwareLatencyInBytes(
277 handle_, params_.sample_rate(), params_.GetBytesPerFrame());
278
279 // Update the AGC volume level once every second. Note that,
280 // |volume| is also updated each time SetVolume() is called
281 // through IPC by the render-side AGC.
282 double normalized_volume = 0.0;
283 QueryAgcVolume(&normalized_volume);
284
285 while (true) {
286 size_t length = 0;
287 const void* data = NULL;
288 pa_stream_peek(handle_, &data, &length);
289 if (!data || length == 0)
290 break;
291
292 buffer_->Append(reinterpret_cast<const uint8*>(data), length);
293
294 // Checks if we still have data.
295 pa_stream_drop(handle_);
296 if (pa_stream_readable_size(handle_) <= 0)
297 break;
298 }
299
300 int packet_size = params_.GetBytesPerBuffer();
301 while (buffer_->forward_bytes() >= packet_size) {
302 buffer_->Read(audio_data_buffer_.get(), packet_size);
303 callback_->OnData(this, audio_data_buffer_.get(), packet_size,
304 hardware_delay, normalized_volume);
305
306 if (buffer_->forward_bytes() < packet_size)
307 break;
308
309 // TODO(xians): improve the code by implementing a WaitTillDataReady on the
310 // input side.
311 DLOG(WARNING) << "OnData is being called consecutively, sleep 2ms to "
312 << "wait until render consumes the data";
313 base::PlatformThread::Sleep(
314 base::TimeDelta::FromMilliseconds(2));
315 }
316
317 pa_threaded_mainloop_signal(pa_mainloop_, 0);
318 }
319
320 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698