OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2010 The WebRTC Project Authors. All rights reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 #include "webrtc/sound/pulseaudiosoundsystem.h" | |
12 | |
13 #ifdef HAVE_LIBPULSE | |
14 | |
15 #include <algorithm> | |
16 #include <string> | |
17 | |
18 #include "webrtc/base/arraysize.h" | |
19 #include "webrtc/base/common.h" | |
20 #include "webrtc/base/fileutils.h" // for GetApplicationName() | |
21 #include "webrtc/base/logging.h" | |
22 #include "webrtc/base/timeutils.h" | |
23 #include "webrtc/base/worker.h" | |
24 #include "webrtc/sound/sounddevicelocator.h" | |
25 #include "webrtc/sound/soundinputstreaminterface.h" | |
26 #include "webrtc/sound/soundoutputstreaminterface.h" | |
27 | |
28 namespace rtc { | |
29 | |
30 // First PulseAudio protocol version that supports PA_STREAM_ADJUST_LATENCY. | |
31 static const uint32_t kAdjustLatencyProtocolVersion = 13; | |
32 | |
33 // Lookup table from the rtc format enum in soundsysteminterface.h to | |
34 // Pulse's enums. | |
35 static const pa_sample_format_t kCricketFormatToPulseFormatTable[] = { | |
36 // The order here must match the order in soundsysteminterface.h | |
37 PA_SAMPLE_S16LE, | |
38 }; | |
39 | |
40 // Some timing constants for optimal operation. See | |
41 // https://tango.0pointer.de/pipermail/pulseaudio-discuss/2008-January/001170.ht
ml | |
42 // for a good explanation of some of the factors that go into this. | |
43 | |
44 // Playback. | |
45 | |
46 // For playback, there is a round-trip delay to fill the server-side playback | |
47 // buffer, so setting too low of a latency is a buffer underflow risk. We will | |
48 // automatically increase the latency if a buffer underflow does occur, but we | |
49 // also enforce a sane minimum at start-up time. Anything lower would be | |
50 // virtually guaranteed to underflow at least once, so there's no point in | |
51 // allowing lower latencies. | |
52 static const int kPlaybackLatencyMinimumMsecs = 20; | |
53 // Every time a playback stream underflows, we will reconfigure it with target | |
54 // latency that is greater by this amount. | |
55 static const int kPlaybackLatencyIncrementMsecs = 20; | |
56 // We also need to configure a suitable request size. Too small and we'd burn | |
57 // CPU from the overhead of transfering small amounts of data at once. Too large | |
58 // and the amount of data remaining in the buffer right before refilling it | |
59 // would be a buffer underflow risk. We set it to half of the buffer size. | |
60 static const int kPlaybackRequestFactor = 2; | |
61 | |
62 // Capture. | |
63 | |
64 // For capture, low latency is not a buffer overflow risk, but it makes us burn | |
65 // CPU from the overhead of transfering small amounts of data at once, so we set | |
66 // a recommended value that we use for the kLowLatency constant (but if the user | |
67 // explicitly requests something lower then we will honour it). | |
68 // 1ms takes about 6-7% CPU. 5ms takes about 5%. 10ms takes about 4.x%. | |
69 static const int kLowCaptureLatencyMsecs = 10; | |
70 // There is a round-trip delay to ack the data to the server, so the | |
71 // server-side buffer needs extra space to prevent buffer overflow. 20ms is | |
72 // sufficient, but there is no penalty to making it bigger, so we make it huge. | |
73 // (750ms is libpulse's default value for the _total_ buffer size in the | |
74 // kNoLatencyRequirements case.) | |
75 static const int kCaptureBufferExtraMsecs = 750; | |
76 | |
77 static void FillPlaybackBufferAttr(int latency, | |
78 pa_buffer_attr *attr) { | |
79 attr->maxlength = latency; | |
80 attr->tlength = latency; | |
81 attr->minreq = latency / kPlaybackRequestFactor; | |
82 attr->prebuf = attr->tlength - attr->minreq; | |
83 LOG(LS_VERBOSE) << "Configuring latency = " << attr->tlength << ", minreq = " | |
84 << attr->minreq << ", minfill = " << attr->prebuf; | |
85 } | |
86 | |
87 static pa_volume_t CricketVolumeToPulseVolume(int volume) { | |
88 // PA's volume space goes from 0% at PA_VOLUME_MUTED (value 0) to 100% at | |
89 // PA_VOLUME_NORM (value 0x10000). It can also go beyond 100% up to | |
90 // PA_VOLUME_MAX (value UINT32_MAX-1), but using that is probably unwise. | |
91 // We just linearly map the 0-255 scale of SoundSystemInterface onto | |
92 // PA_VOLUME_MUTED-PA_VOLUME_NORM. If the programmer exceeds kMaxVolume then | |
93 // they can access the over-100% features of PA. | |
94 return PA_VOLUME_MUTED + (PA_VOLUME_NORM - PA_VOLUME_MUTED) * | |
95 volume / SoundSystemInterface::kMaxVolume; | |
96 } | |
97 | |
98 static int PulseVolumeToCricketVolume(pa_volume_t pa_volume) { | |
99 return SoundSystemInterface::kMinVolume + | |
100 (SoundSystemInterface::kMaxVolume - SoundSystemInterface::kMinVolume) * | |
101 pa_volume / PA_VOLUME_NORM; | |
102 } | |
103 | |
104 static pa_volume_t MaxChannelVolume(pa_cvolume *channel_volumes) { | |
105 pa_volume_t pa_volume = PA_VOLUME_MUTED; // Minimum possible value. | |
106 for (int i = 0; i < channel_volumes->channels; ++i) { | |
107 if (pa_volume < channel_volumes->values[i]) { | |
108 pa_volume = channel_volumes->values[i]; | |
109 } | |
110 } | |
111 return pa_volume; | |
112 } | |
113 | |
114 class PulseAudioDeviceLocator : public SoundDeviceLocator { | |
115 public: | |
116 PulseAudioDeviceLocator(const std::string &name, | |
117 const std::string &device_name) | |
118 : SoundDeviceLocator(name, device_name) { | |
119 } | |
120 | |
121 virtual SoundDeviceLocator *Copy() const { | |
122 return new PulseAudioDeviceLocator(*this); | |
123 } | |
124 }; | |
125 | |
126 // Functionality that is common to both PulseAudioInputStream and | |
127 // PulseAudioOutputStream. | |
128 class PulseAudioStream { | |
129 public: | |
130 PulseAudioStream(PulseAudioSoundSystem *pulse, pa_stream *stream, int flags) | |
131 : pulse_(pulse), stream_(stream), flags_(flags) { | |
132 } | |
133 | |
134 ~PulseAudioStream() { | |
135 // Close() should have been called during the containing class's destructor. | |
136 ASSERT(stream_ == NULL); | |
137 } | |
138 | |
139 // Must be called with the lock held. | |
140 bool Close() { | |
141 if (!IsClosed()) { | |
142 // Unset this here so that we don't get a TERMINATED callback. | |
143 symbol_table()->pa_stream_set_state_callback()(stream_, NULL, NULL); | |
144 if (symbol_table()->pa_stream_disconnect()(stream_) != 0) { | |
145 LOG(LS_ERROR) << "Can't disconnect stream"; | |
146 // Continue and return true anyways. | |
147 } | |
148 symbol_table()->pa_stream_unref()(stream_); | |
149 stream_ = NULL; | |
150 } | |
151 return true; | |
152 } | |
153 | |
154 // Must be called with the lock held. | |
155 int LatencyUsecs() { | |
156 if (!(flags_ & SoundSystemInterface::FLAG_REPORT_LATENCY)) { | |
157 return 0; | |
158 } | |
159 | |
160 pa_usec_t latency; | |
161 int negative; | |
162 Lock(); | |
163 int re = symbol_table()->pa_stream_get_latency()(stream_, &latency, | |
164 &negative); | |
165 Unlock(); | |
166 if (re != 0) { | |
167 LOG(LS_ERROR) << "Can't query latency"; | |
168 // We'd rather continue playout/capture with an incorrect delay than stop | |
169 // it altogether, so return a valid value. | |
170 return 0; | |
171 } | |
172 if (negative) { | |
173 // The delay can be negative for monitoring streams if the captured | |
174 // samples haven't been played yet. In such a case, "latency" contains the | |
175 // magnitude, so we must negate it to get the real value. | |
176 return -latency; | |
177 } else { | |
178 return latency; | |
179 } | |
180 } | |
181 | |
182 PulseAudioSoundSystem *pulse() { | |
183 return pulse_; | |
184 } | |
185 | |
186 PulseAudioSymbolTable *symbol_table() { | |
187 return &pulse()->symbol_table_; | |
188 } | |
189 | |
190 pa_stream *stream() { | |
191 ASSERT(stream_ != NULL); | |
192 return stream_; | |
193 } | |
194 | |
195 bool IsClosed() { | |
196 return stream_ == NULL; | |
197 } | |
198 | |
199 void Lock() { | |
200 pulse()->Lock(); | |
201 } | |
202 | |
203 void Unlock() { | |
204 pulse()->Unlock(); | |
205 } | |
206 | |
207 private: | |
208 PulseAudioSoundSystem *pulse_; | |
209 pa_stream *stream_; | |
210 int flags_; | |
211 | |
212 RTC_DISALLOW_COPY_AND_ASSIGN(PulseAudioStream); | |
213 }; | |
214 | |
215 // Implementation of an input stream. See soundinputstreaminterface.h regarding | |
216 // thread-safety. | |
217 class PulseAudioInputStream : | |
218 public SoundInputStreamInterface, | |
219 private rtc::Worker { | |
220 public: | |
221 PulseAudioInputStream(PulseAudioSoundSystem *pulse, | |
222 pa_stream *stream, | |
223 int flags) | |
224 : stream_(pulse, stream, flags), | |
225 temp_sample_data_(NULL), | |
226 temp_sample_data_size_(0) { | |
227 // This callback seems to never be issued, but let's set it anyways. | |
228 symbol_table()->pa_stream_set_overflow_callback()(stream, &OverflowCallback, | |
229 NULL); | |
230 } | |
231 | |
232 virtual ~PulseAudioInputStream() { | |
233 bool success = Close(); | |
234 // We need that to live. | |
235 VERIFY(success); | |
236 } | |
237 | |
238 virtual bool StartReading() { | |
239 return StartWork(); | |
240 } | |
241 | |
242 virtual bool StopReading() { | |
243 return StopWork(); | |
244 } | |
245 | |
246 virtual bool GetVolume(int *volume) { | |
247 bool ret = false; | |
248 | |
249 Lock(); | |
250 | |
251 // Unlike output streams, input streams have no concept of a stream volume, | |
252 // only a device volume. So we have to retrieve the volume of the device | |
253 // itself. | |
254 | |
255 pa_cvolume channel_volumes; | |
256 | |
257 GetVolumeCallbackData data; | |
258 data.instance = this; | |
259 data.channel_volumes = &channel_volumes; | |
260 | |
261 pa_operation *op = symbol_table()->pa_context_get_source_info_by_index()( | |
262 stream_.pulse()->context_, | |
263 symbol_table()->pa_stream_get_device_index()(stream_.stream()), | |
264 &GetVolumeCallbackThunk, | |
265 &data); | |
266 if (!stream_.pulse()->FinishOperation(op)) { | |
267 goto done; | |
268 } | |
269 | |
270 if (data.channel_volumes) { | |
271 // This pointer was never unset by the callback, so we must have received | |
272 // an empty list of infos. This probably never happens, but we code for it | |
273 // anyway. | |
274 LOG(LS_ERROR) << "Did not receive GetVolumeCallback"; | |
275 goto done; | |
276 } | |
277 | |
278 // We now have the volume for each channel. Each channel could have a | |
279 // different volume if, e.g., the user went and changed the volumes in the | |
280 // PA UI. To get a single volume for SoundSystemInterface we just take the | |
281 // maximum. Ideally we'd do so with pa_cvolume_max, but it doesn't exist in | |
282 // Hardy, so we do it manually. | |
283 pa_volume_t pa_volume; | |
284 pa_volume = MaxChannelVolume(&channel_volumes); | |
285 // Now map onto the SoundSystemInterface range. | |
286 *volume = PulseVolumeToCricketVolume(pa_volume); | |
287 | |
288 ret = true; | |
289 done: | |
290 Unlock(); | |
291 return ret; | |
292 } | |
293 | |
294 virtual bool SetVolume(int volume) { | |
295 bool ret = false; | |
296 pa_volume_t pa_volume = CricketVolumeToPulseVolume(volume); | |
297 | |
298 Lock(); | |
299 | |
300 // Unlike output streams, input streams have no concept of a stream volume, | |
301 // only a device volume. So we have to change the volume of the device | |
302 // itself. | |
303 | |
304 // The device may have a different number of channels than the stream and | |
305 // their mapping may be different, so we don't want to use the channel count | |
306 // from our sample spec. We could use PA_CHANNELS_MAX to cover our bases, | |
307 // and the server allows that even if the device's channel count is lower, | |
308 // but some buggy PA clients don't like that (the pavucontrol on Hardy dies | |
309 // in an assert if the channel count is different). So instead we look up | |
310 // the actual number of channels that the device has. | |
311 | |
312 uint8_t channels; | |
313 | |
314 GetSourceChannelCountCallbackData data; | |
315 data.instance = this; | |
316 data.channels = &channels; | |
317 | |
318 uint32_t device_index = symbol_table()->pa_stream_get_device_index()( | |
319 stream_.stream()); | |
320 | |
321 pa_operation *op = symbol_table()->pa_context_get_source_info_by_index()( | |
322 stream_.pulse()->context_, | |
323 device_index, | |
324 &GetSourceChannelCountCallbackThunk, | |
325 &data); | |
326 if (!stream_.pulse()->FinishOperation(op)) { | |
327 goto done; | |
328 } | |
329 | |
330 if (data.channels) { | |
331 // This pointer was never unset by the callback, so we must have received | |
332 // an empty list of infos. This probably never happens, but we code for it | |
333 // anyway. | |
334 LOG(LS_ERROR) << "Did not receive GetSourceChannelCountCallback"; | |
335 goto done; | |
336 } | |
337 | |
338 pa_cvolume channel_volumes; | |
339 symbol_table()->pa_cvolume_set()(&channel_volumes, channels, pa_volume); | |
340 | |
341 op = symbol_table()->pa_context_set_source_volume_by_index()( | |
342 stream_.pulse()->context_, | |
343 device_index, | |
344 &channel_volumes, | |
345 // This callback merely logs errors. | |
346 &SetVolumeCallback, | |
347 NULL); | |
348 if (!op) { | |
349 LOG(LS_ERROR) << "pa_context_set_source_volume_by_index()"; | |
350 goto done; | |
351 } | |
352 // Don't need to wait for this to complete. | |
353 symbol_table()->pa_operation_unref()(op); | |
354 | |
355 ret = true; | |
356 done: | |
357 Unlock(); | |
358 return ret; | |
359 } | |
360 | |
361 virtual bool Close() { | |
362 if (!StopReading()) { | |
363 return false; | |
364 } | |
365 bool ret = true; | |
366 if (!stream_.IsClosed()) { | |
367 Lock(); | |
368 ret = stream_.Close(); | |
369 Unlock(); | |
370 } | |
371 return ret; | |
372 } | |
373 | |
374 virtual int LatencyUsecs() { | |
375 return stream_.LatencyUsecs(); | |
376 } | |
377 | |
378 private: | |
379 struct GetVolumeCallbackData { | |
380 PulseAudioInputStream* instance; | |
381 pa_cvolume* channel_volumes; | |
382 }; | |
383 | |
384 struct GetSourceChannelCountCallbackData { | |
385 PulseAudioInputStream* instance; | |
386 uint8_t* channels; | |
387 }; | |
388 | |
389 void Lock() { | |
390 stream_.Lock(); | |
391 } | |
392 | |
393 void Unlock() { | |
394 stream_.Unlock(); | |
395 } | |
396 | |
397 PulseAudioSymbolTable *symbol_table() { | |
398 return stream_.symbol_table(); | |
399 } | |
400 | |
401 void EnableReadCallback() { | |
402 symbol_table()->pa_stream_set_read_callback()( | |
403 stream_.stream(), | |
404 &ReadCallbackThunk, | |
405 this); | |
406 } | |
407 | |
408 void DisableReadCallback() { | |
409 symbol_table()->pa_stream_set_read_callback()( | |
410 stream_.stream(), | |
411 NULL, | |
412 NULL); | |
413 } | |
414 | |
415 static void ReadCallbackThunk(pa_stream *unused1, | |
416 size_t unused2, | |
417 void *userdata) { | |
418 PulseAudioInputStream *instance = | |
419 static_cast<PulseAudioInputStream *>(userdata); | |
420 instance->OnReadCallback(); | |
421 } | |
422 | |
423 void OnReadCallback() { | |
424 // We get the data pointer and size now in order to save one Lock/Unlock | |
425 // on OnMessage. | |
426 if (symbol_table()->pa_stream_peek()(stream_.stream(), | |
427 &temp_sample_data_, | |
428 &temp_sample_data_size_) != 0) { | |
429 LOG(LS_ERROR) << "Can't read data!"; | |
430 return; | |
431 } | |
432 // Since we consume the data asynchronously on a different thread, we have | |
433 // to temporarily disable the read callback or else Pulse will call it | |
434 // continuously until we consume the data. We re-enable it below. | |
435 DisableReadCallback(); | |
436 HaveWork(); | |
437 } | |
438 | |
439 // Inherited from Worker. | |
440 virtual void OnStart() { | |
441 Lock(); | |
442 EnableReadCallback(); | |
443 Unlock(); | |
444 } | |
445 | |
446 // Inherited from Worker. | |
447 virtual void OnHaveWork() { | |
448 ASSERT(temp_sample_data_ && temp_sample_data_size_); | |
449 SignalSamplesRead(temp_sample_data_, | |
450 temp_sample_data_size_, | |
451 this); | |
452 temp_sample_data_ = NULL; | |
453 temp_sample_data_size_ = 0; | |
454 | |
455 Lock(); | |
456 for (;;) { | |
457 // Ack the last thing we read. | |
458 if (symbol_table()->pa_stream_drop()(stream_.stream()) != 0) { | |
459 LOG(LS_ERROR) << "Can't ack read data"; | |
460 } | |
461 | |
462 if (symbol_table()->pa_stream_readable_size()(stream_.stream()) <= 0) { | |
463 // Then that was all the data. | |
464 break; | |
465 } | |
466 | |
467 // Else more data. | |
468 const void *sample_data; | |
469 size_t sample_data_size; | |
470 if (symbol_table()->pa_stream_peek()(stream_.stream(), | |
471 &sample_data, | |
472 &sample_data_size) != 0) { | |
473 LOG(LS_ERROR) << "Can't read data!"; | |
474 break; | |
475 } | |
476 | |
477 // Drop lock for sigslot dispatch, which could take a while. | |
478 Unlock(); | |
479 SignalSamplesRead(sample_data, sample_data_size, this); | |
480 Lock(); | |
481 | |
482 // Return to top of loop for the ack and the check for more data. | |
483 } | |
484 EnableReadCallback(); | |
485 Unlock(); | |
486 } | |
487 | |
488 // Inherited from Worker. | |
489 virtual void OnStop() { | |
490 Lock(); | |
491 DisableReadCallback(); | |
492 Unlock(); | |
493 } | |
494 | |
495 static void OverflowCallback(pa_stream *stream, | |
496 void *userdata) { | |
497 LOG(LS_WARNING) << "Buffer overflow on capture stream " << stream; | |
498 } | |
499 | |
500 static void GetVolumeCallbackThunk(pa_context *unused, | |
501 const pa_source_info *info, | |
502 int eol, | |
503 void *userdata) { | |
504 GetVolumeCallbackData *data = | |
505 static_cast<GetVolumeCallbackData *>(userdata); | |
506 data->instance->OnGetVolumeCallback(info, eol, &data->channel_volumes); | |
507 } | |
508 | |
509 void OnGetVolumeCallback(const pa_source_info *info, | |
510 int eol, | |
511 pa_cvolume **channel_volumes) { | |
512 if (eol) { | |
513 // List is over. Wake GetVolume(). | |
514 stream_.pulse()->Signal(); | |
515 return; | |
516 } | |
517 | |
518 if (*channel_volumes) { | |
519 **channel_volumes = info->volume; | |
520 // Unset the pointer so that we know that we have have already copied the | |
521 // volume. | |
522 *channel_volumes = NULL; | |
523 } else { | |
524 // We have received an additional callback after the first one, which | |
525 // doesn't make sense for a single source. This probably never happens, | |
526 // but we code for it anyway. | |
527 LOG(LS_WARNING) << "Ignoring extra GetVolumeCallback"; | |
528 } | |
529 } | |
530 | |
531 static void GetSourceChannelCountCallbackThunk(pa_context *unused, | |
532 const pa_source_info *info, | |
533 int eol, | |
534 void *userdata) { | |
535 GetSourceChannelCountCallbackData *data = | |
536 static_cast<GetSourceChannelCountCallbackData *>(userdata); | |
537 data->instance->OnGetSourceChannelCountCallback(info, eol, &data->channels); | |
538 } | |
539 | |
540 void OnGetSourceChannelCountCallback(const pa_source_info *info, | |
541 int eol, | |
542 uint8_t **channels) { | |
543 if (eol) { | |
544 // List is over. Wake SetVolume(). | |
545 stream_.pulse()->Signal(); | |
546 return; | |
547 } | |
548 | |
549 if (*channels) { | |
550 **channels = info->channel_map.channels; | |
551 // Unset the pointer so that we know that we have have already copied the | |
552 // channel count. | |
553 *channels = NULL; | |
554 } else { | |
555 // We have received an additional callback after the first one, which | |
556 // doesn't make sense for a single source. This probably never happens, | |
557 // but we code for it anyway. | |
558 LOG(LS_WARNING) << "Ignoring extra GetSourceChannelCountCallback"; | |
559 } | |
560 } | |
561 | |
562 static void SetVolumeCallback(pa_context *unused1, | |
563 int success, | |
564 void *unused2) { | |
565 if (!success) { | |
566 LOG(LS_ERROR) << "Failed to change capture volume"; | |
567 } | |
568 } | |
569 | |
570 PulseAudioStream stream_; | |
571 // Temporary storage for passing data between threads. | |
572 const void *temp_sample_data_; | |
573 size_t temp_sample_data_size_; | |
574 | |
575 RTC_DISALLOW_COPY_AND_ASSIGN(PulseAudioInputStream); | |
576 }; | |
577 | |
578 // Implementation of an output stream. See soundoutputstreaminterface.h | |
579 // regarding thread-safety. | |
580 class PulseAudioOutputStream : | |
581 public SoundOutputStreamInterface, | |
582 private rtc::Worker { | |
583 public: | |
584 PulseAudioOutputStream(PulseAudioSoundSystem *pulse, | |
585 pa_stream *stream, | |
586 int flags, | |
587 int latency) | |
588 : stream_(pulse, stream, flags), | |
589 configured_latency_(latency), | |
590 temp_buffer_space_(0) { | |
591 symbol_table()->pa_stream_set_underflow_callback()(stream, | |
592 &UnderflowCallbackThunk, | |
593 this); | |
594 } | |
595 | |
596 virtual ~PulseAudioOutputStream() { | |
597 bool success = Close(); | |
598 // We need that to live. | |
599 VERIFY(success); | |
600 } | |
601 | |
602 virtual bool EnableBufferMonitoring() { | |
603 return StartWork(); | |
604 } | |
605 | |
606 virtual bool DisableBufferMonitoring() { | |
607 return StopWork(); | |
608 } | |
609 | |
610 virtual bool WriteSamples(const void *sample_data, | |
611 size_t size) { | |
612 bool ret = true; | |
613 Lock(); | |
614 if (symbol_table()->pa_stream_write()(stream_.stream(), | |
615 sample_data, | |
616 size, | |
617 NULL, | |
618 0, | |
619 PA_SEEK_RELATIVE) != 0) { | |
620 LOG(LS_ERROR) << "Unable to write"; | |
621 ret = false; | |
622 } | |
623 Unlock(); | |
624 return ret; | |
625 } | |
626 | |
627 virtual bool GetVolume(int *volume) { | |
628 bool ret = false; | |
629 | |
630 Lock(); | |
631 | |
632 pa_cvolume channel_volumes; | |
633 | |
634 GetVolumeCallbackData data; | |
635 data.instance = this; | |
636 data.channel_volumes = &channel_volumes; | |
637 | |
638 pa_operation *op = symbol_table()->pa_context_get_sink_input_info()( | |
639 stream_.pulse()->context_, | |
640 symbol_table()->pa_stream_get_index()(stream_.stream()), | |
641 &GetVolumeCallbackThunk, | |
642 &data); | |
643 if (!stream_.pulse()->FinishOperation(op)) { | |
644 goto done; | |
645 } | |
646 | |
647 if (data.channel_volumes) { | |
648 // This pointer was never unset by the callback, so we must have received | |
649 // an empty list of infos. This probably never happens, but we code for it | |
650 // anyway. | |
651 LOG(LS_ERROR) << "Did not receive GetVolumeCallback"; | |
652 goto done; | |
653 } | |
654 | |
655 // We now have the volume for each channel. Each channel could have a | |
656 // different volume if, e.g., the user went and changed the volumes in the | |
657 // PA UI. To get a single volume for SoundSystemInterface we just take the | |
658 // maximum. Ideally we'd do so with pa_cvolume_max, but it doesn't exist in | |
659 // Hardy, so we do it manually. | |
660 pa_volume_t pa_volume; | |
661 pa_volume = MaxChannelVolume(&channel_volumes); | |
662 // Now map onto the SoundSystemInterface range. | |
663 *volume = PulseVolumeToCricketVolume(pa_volume); | |
664 | |
665 ret = true; | |
666 done: | |
667 Unlock(); | |
668 return ret; | |
669 } | |
670 | |
671 virtual bool SetVolume(int volume) { | |
672 bool ret = false; | |
673 pa_volume_t pa_volume = CricketVolumeToPulseVolume(volume); | |
674 | |
675 Lock(); | |
676 | |
677 const pa_sample_spec *spec = symbol_table()->pa_stream_get_sample_spec()( | |
678 stream_.stream()); | |
679 if (!spec) { | |
680 LOG(LS_ERROR) << "pa_stream_get_sample_spec()"; | |
681 goto done; | |
682 } | |
683 | |
684 pa_cvolume channel_volumes; | |
685 symbol_table()->pa_cvolume_set()(&channel_volumes, spec->channels, | |
686 pa_volume); | |
687 | |
688 pa_operation *op; | |
689 op = symbol_table()->pa_context_set_sink_input_volume()( | |
690 stream_.pulse()->context_, | |
691 symbol_table()->pa_stream_get_index()(stream_.stream()), | |
692 &channel_volumes, | |
693 // This callback merely logs errors. | |
694 &SetVolumeCallback, | |
695 NULL); | |
696 if (!op) { | |
697 LOG(LS_ERROR) << "pa_context_set_sink_input_volume()"; | |
698 goto done; | |
699 } | |
700 // Don't need to wait for this to complete. | |
701 symbol_table()->pa_operation_unref()(op); | |
702 | |
703 ret = true; | |
704 done: | |
705 Unlock(); | |
706 return ret; | |
707 } | |
708 | |
709 virtual bool Close() { | |
710 if (!DisableBufferMonitoring()) { | |
711 return false; | |
712 } | |
713 bool ret = true; | |
714 if (!stream_.IsClosed()) { | |
715 Lock(); | |
716 symbol_table()->pa_stream_set_underflow_callback()(stream_.stream(), | |
717 NULL, | |
718 NULL); | |
719 ret = stream_.Close(); | |
720 Unlock(); | |
721 } | |
722 return ret; | |
723 } | |
724 | |
725 virtual int LatencyUsecs() { | |
726 return stream_.LatencyUsecs(); | |
727 } | |
728 | |
729 #if 0 | |
730 // TODO(henrika): Versions 0.9.16 and later of Pulse have a new API for | |
731 // zero-copy writes, but Hardy is not new enough to have that so we can't | |
732 // rely on it. Perhaps auto-detect if it's present or not and use it if we | |
733 // can? | |
734 | |
735 virtual bool GetWriteBuffer(void **buffer, size_t *size) { | |
736 bool ret = true; | |
737 Lock(); | |
738 if (symbol_table()->pa_stream_begin_write()(stream_.stream(), buffer, size) | |
739 != 0) { | |
740 LOG(LS_ERROR) << "Can't get write buffer"; | |
741 ret = false; | |
742 } | |
743 Unlock(); | |
744 return ret; | |
745 } | |
746 | |
747 // Releases the caller's hold on the write buffer. "written" must be the | |
748 // amount of data that was written. | |
749 virtual bool ReleaseWriteBuffer(void *buffer, size_t written) { | |
750 bool ret = true; | |
751 Lock(); | |
752 if (written == 0) { | |
753 if (symbol_table()->pa_stream_cancel_write()(stream_.stream()) != 0) { | |
754 LOG(LS_ERROR) << "Can't cancel write"; | |
755 ret = false; | |
756 } | |
757 } else { | |
758 if (symbol_table()->pa_stream_write()(stream_.stream(), | |
759 buffer, | |
760 written, | |
761 NULL, | |
762 0, | |
763 PA_SEEK_RELATIVE) != 0) { | |
764 LOG(LS_ERROR) << "Unable to write"; | |
765 ret = false; | |
766 } | |
767 } | |
768 Unlock(); | |
769 return ret; | |
770 } | |
771 #endif | |
772 | |
773 private: | |
774 struct GetVolumeCallbackData { | |
775 PulseAudioOutputStream* instance; | |
776 pa_cvolume* channel_volumes; | |
777 }; | |
778 | |
779 void Lock() { | |
780 stream_.Lock(); | |
781 } | |
782 | |
783 void Unlock() { | |
784 stream_.Unlock(); | |
785 } | |
786 | |
787 PulseAudioSymbolTable *symbol_table() { | |
788 return stream_.symbol_table(); | |
789 } | |
790 | |
791 void EnableWriteCallback() { | |
792 pa_stream_state_t state = symbol_table()->pa_stream_get_state()( | |
793 stream_.stream()); | |
794 if (state == PA_STREAM_READY) { | |
795 // May already have available space. Must check. | |
796 temp_buffer_space_ = symbol_table()->pa_stream_writable_size()( | |
797 stream_.stream()); | |
798 if (temp_buffer_space_ > 0) { | |
799 // Yup, there is already space available, so if we register a write | |
800 // callback then it will not receive any event. So dispatch one ourself | |
801 // instead. | |
802 HaveWork(); | |
803 return; | |
804 } | |
805 } | |
806 symbol_table()->pa_stream_set_write_callback()( | |
807 stream_.stream(), | |
808 &WriteCallbackThunk, | |
809 this); | |
810 } | |
811 | |
812 void DisableWriteCallback() { | |
813 symbol_table()->pa_stream_set_write_callback()( | |
814 stream_.stream(), | |
815 NULL, | |
816 NULL); | |
817 } | |
818 | |
819 static void WriteCallbackThunk(pa_stream *unused, | |
820 size_t buffer_space, | |
821 void *userdata) { | |
822 PulseAudioOutputStream *instance = | |
823 static_cast<PulseAudioOutputStream *>(userdata); | |
824 instance->OnWriteCallback(buffer_space); | |
825 } | |
826 | |
827 void OnWriteCallback(size_t buffer_space) { | |
828 temp_buffer_space_ = buffer_space; | |
829 // Since we write the data asynchronously on a different thread, we have | |
830 // to temporarily disable the write callback or else Pulse will call it | |
831 // continuously until we write the data. We re-enable it below. | |
832 DisableWriteCallback(); | |
833 HaveWork(); | |
834 } | |
835 | |
836 // Inherited from Worker. | |
837 virtual void OnStart() { | |
838 Lock(); | |
839 EnableWriteCallback(); | |
840 Unlock(); | |
841 } | |
842 | |
843 // Inherited from Worker. | |
844 virtual void OnHaveWork() { | |
845 ASSERT(temp_buffer_space_ > 0); | |
846 | |
847 SignalBufferSpace(temp_buffer_space_, this); | |
848 | |
849 temp_buffer_space_ = 0; | |
850 Lock(); | |
851 EnableWriteCallback(); | |
852 Unlock(); | |
853 } | |
854 | |
855 // Inherited from Worker. | |
856 virtual void OnStop() { | |
857 Lock(); | |
858 DisableWriteCallback(); | |
859 Unlock(); | |
860 } | |
861 | |
862 static void UnderflowCallbackThunk(pa_stream *unused, | |
863 void *userdata) { | |
864 PulseAudioOutputStream *instance = | |
865 static_cast<PulseAudioOutputStream *>(userdata); | |
866 instance->OnUnderflowCallback(); | |
867 } | |
868 | |
869 void OnUnderflowCallback() { | |
870 LOG(LS_WARNING) << "Buffer underflow on playback stream " | |
871 << stream_.stream(); | |
872 | |
873 if (configured_latency_ == SoundSystemInterface::kNoLatencyRequirements) { | |
874 // We didn't configure a pa_buffer_attr before, so switching to one now | |
875 // would be questionable. | |
876 return; | |
877 } | |
878 | |
879 // Otherwise reconfigure the stream with a higher target latency. | |
880 | |
881 const pa_sample_spec *spec = symbol_table()->pa_stream_get_sample_spec()( | |
882 stream_.stream()); | |
883 if (!spec) { | |
884 LOG(LS_ERROR) << "pa_stream_get_sample_spec()"; | |
885 return; | |
886 } | |
887 | |
888 size_t bytes_per_sec = symbol_table()->pa_bytes_per_second()(spec); | |
889 | |
890 int new_latency = configured_latency_ + | |
891 bytes_per_sec * kPlaybackLatencyIncrementMsecs / | |
892 rtc::kNumMicrosecsPerSec; | |
893 | |
894 pa_buffer_attr new_attr = {0}; | |
895 FillPlaybackBufferAttr(new_latency, &new_attr); | |
896 | |
897 pa_operation *op = symbol_table()->pa_stream_set_buffer_attr()( | |
898 stream_.stream(), | |
899 &new_attr, | |
900 // No callback. | |
901 NULL, | |
902 NULL); | |
903 if (!op) { | |
904 LOG(LS_ERROR) << "pa_stream_set_buffer_attr()"; | |
905 return; | |
906 } | |
907 // Don't need to wait for this to complete. | |
908 symbol_table()->pa_operation_unref()(op); | |
909 | |
910 // Save the new latency in case we underflow again. | |
911 configured_latency_ = new_latency; | |
912 } | |
913 | |
914 static void GetVolumeCallbackThunk(pa_context *unused, | |
915 const pa_sink_input_info *info, | |
916 int eol, | |
917 void *userdata) { | |
918 GetVolumeCallbackData *data = | |
919 static_cast<GetVolumeCallbackData *>(userdata); | |
920 data->instance->OnGetVolumeCallback(info, eol, &data->channel_volumes); | |
921 } | |
922 | |
923 void OnGetVolumeCallback(const pa_sink_input_info *info, | |
924 int eol, | |
925 pa_cvolume **channel_volumes) { | |
926 if (eol) { | |
927 // List is over. Wake GetVolume(). | |
928 stream_.pulse()->Signal(); | |
929 return; | |
930 } | |
931 | |
932 if (*channel_volumes) { | |
933 **channel_volumes = info->volume; | |
934 // Unset the pointer so that we know that we have have already copied the | |
935 // volume. | |
936 *channel_volumes = NULL; | |
937 } else { | |
938 // We have received an additional callback after the first one, which | |
939 // doesn't make sense for a single sink input. This probably never | |
940 // happens, but we code for it anyway. | |
941 LOG(LS_WARNING) << "Ignoring extra GetVolumeCallback"; | |
942 } | |
943 } | |
944 | |
945 static void SetVolumeCallback(pa_context *unused1, | |
946 int success, | |
947 void *unused2) { | |
948 if (!success) { | |
949 LOG(LS_ERROR) << "Failed to change playback volume"; | |
950 } | |
951 } | |
952 | |
953 PulseAudioStream stream_; | |
954 int configured_latency_; | |
955 // Temporary storage for passing data between threads. | |
956 size_t temp_buffer_space_; | |
957 | |
958 RTC_DISALLOW_COPY_AND_ASSIGN(PulseAudioOutputStream); | |
959 }; | |
960 | |
961 PulseAudioSoundSystem::PulseAudioSoundSystem() | |
962 : mainloop_(NULL), context_(NULL) { | |
963 } | |
964 | |
965 PulseAudioSoundSystem::~PulseAudioSoundSystem() { | |
966 Terminate(); | |
967 } | |
968 | |
969 bool PulseAudioSoundSystem::Init() { | |
970 if (IsInitialized()) { | |
971 return true; | |
972 } | |
973 | |
974 // Load libpulse. | |
975 if (!symbol_table_.Load()) { | |
976 // Most likely the Pulse library and sound server are not installed on | |
977 // this system. | |
978 LOG(LS_WARNING) << "Failed to load symbol table"; | |
979 return false; | |
980 } | |
981 | |
982 // Now create and start the Pulse event thread. | |
983 mainloop_ = symbol_table_.pa_threaded_mainloop_new()(); | |
984 if (!mainloop_) { | |
985 LOG(LS_ERROR) << "Can't create mainloop"; | |
986 goto fail0; | |
987 } | |
988 | |
989 if (symbol_table_.pa_threaded_mainloop_start()(mainloop_) != 0) { | |
990 LOG(LS_ERROR) << "Can't start mainloop"; | |
991 goto fail1; | |
992 } | |
993 | |
994 Lock(); | |
995 context_ = CreateNewConnection(); | |
996 Unlock(); | |
997 | |
998 if (!context_) { | |
999 goto fail2; | |
1000 } | |
1001 | |
1002 // Otherwise we're now ready! | |
1003 return true; | |
1004 | |
1005 fail2: | |
1006 symbol_table_.pa_threaded_mainloop_stop()(mainloop_); | |
1007 fail1: | |
1008 symbol_table_.pa_threaded_mainloop_free()(mainloop_); | |
1009 mainloop_ = NULL; | |
1010 fail0: | |
1011 return false; | |
1012 } | |
1013 | |
1014 void PulseAudioSoundSystem::Terminate() { | |
1015 if (!IsInitialized()) { | |
1016 return; | |
1017 } | |
1018 | |
1019 Lock(); | |
1020 symbol_table_.pa_context_disconnect()(context_); | |
1021 symbol_table_.pa_context_unref()(context_); | |
1022 Unlock(); | |
1023 context_ = NULL; | |
1024 symbol_table_.pa_threaded_mainloop_stop()(mainloop_); | |
1025 symbol_table_.pa_threaded_mainloop_free()(mainloop_); | |
1026 mainloop_ = NULL; | |
1027 | |
1028 // We do not unload the symbol table because we may need it again soon if | |
1029 // Init() is called again. | |
1030 } | |
1031 | |
1032 bool PulseAudioSoundSystem::EnumeratePlaybackDevices( | |
1033 SoundDeviceLocatorList *devices) { | |
1034 return EnumerateDevices<pa_sink_info>( | |
1035 devices, | |
1036 symbol_table_.pa_context_get_sink_info_list(), | |
1037 &EnumeratePlaybackDevicesCallbackThunk); | |
1038 } | |
1039 | |
1040 bool PulseAudioSoundSystem::EnumerateCaptureDevices( | |
1041 SoundDeviceLocatorList *devices) { | |
1042 return EnumerateDevices<pa_source_info>( | |
1043 devices, | |
1044 symbol_table_.pa_context_get_source_info_list(), | |
1045 &EnumerateCaptureDevicesCallbackThunk); | |
1046 } | |
1047 | |
1048 bool PulseAudioSoundSystem::GetDefaultPlaybackDevice( | |
1049 SoundDeviceLocator **device) { | |
1050 return GetDefaultDevice<&pa_server_info::default_sink_name>(device); | |
1051 } | |
1052 | |
1053 bool PulseAudioSoundSystem::GetDefaultCaptureDevice( | |
1054 SoundDeviceLocator **device) { | |
1055 return GetDefaultDevice<&pa_server_info::default_source_name>(device); | |
1056 } | |
1057 | |
1058 SoundOutputStreamInterface *PulseAudioSoundSystem::OpenPlaybackDevice( | |
1059 const SoundDeviceLocator *device, | |
1060 const OpenParams ¶ms) { | |
1061 return OpenDevice<SoundOutputStreamInterface>( | |
1062 device, | |
1063 params, | |
1064 "Playback", | |
1065 &PulseAudioSoundSystem::ConnectOutputStream); | |
1066 } | |
1067 | |
1068 SoundInputStreamInterface *PulseAudioSoundSystem::OpenCaptureDevice( | |
1069 const SoundDeviceLocator *device, | |
1070 const OpenParams ¶ms) { | |
1071 return OpenDevice<SoundInputStreamInterface>( | |
1072 device, | |
1073 params, | |
1074 "Capture", | |
1075 &PulseAudioSoundSystem::ConnectInputStream); | |
1076 } | |
1077 | |
1078 const char *PulseAudioSoundSystem::GetName() const { | |
1079 return "PulseAudio"; | |
1080 } | |
1081 | |
1082 inline bool PulseAudioSoundSystem::IsInitialized() { | |
1083 return mainloop_ != NULL; | |
1084 } | |
1085 | |
1086 struct ConnectToPulseCallbackData { | |
1087 PulseAudioSoundSystem *instance; | |
1088 bool connect_done; | |
1089 }; | |
1090 | |
1091 void PulseAudioSoundSystem::ConnectToPulseCallbackThunk( | |
1092 pa_context *context, void *userdata) { | |
1093 ConnectToPulseCallbackData *data = | |
1094 static_cast<ConnectToPulseCallbackData *>(userdata); | |
1095 data->instance->OnConnectToPulseCallback(context, &data->connect_done); | |
1096 } | |
1097 | |
1098 void PulseAudioSoundSystem::OnConnectToPulseCallback( | |
1099 pa_context *context, bool *connect_done) { | |
1100 pa_context_state_t state = symbol_table_.pa_context_get_state()(context); | |
1101 if (state == PA_CONTEXT_READY || | |
1102 state == PA_CONTEXT_FAILED || | |
1103 state == PA_CONTEXT_TERMINATED) { | |
1104 // Connection process has reached a terminal state. Wake ConnectToPulse(). | |
1105 *connect_done = true; | |
1106 Signal(); | |
1107 } | |
1108 } | |
1109 | |
1110 // Must be called with the lock held. | |
1111 bool PulseAudioSoundSystem::ConnectToPulse(pa_context *context) { | |
1112 bool ret = true; | |
1113 ConnectToPulseCallbackData data; | |
1114 // Have to put this up here to satisfy the compiler. | |
1115 pa_context_state_t state; | |
1116 | |
1117 data.instance = this; | |
1118 data.connect_done = false; | |
1119 | |
1120 symbol_table_.pa_context_set_state_callback()(context, | |
1121 &ConnectToPulseCallbackThunk, | |
1122 &data); | |
1123 | |
1124 // Connect to PulseAudio sound server. | |
1125 if (symbol_table_.pa_context_connect()( | |
1126 context, | |
1127 NULL, // Default server | |
1128 PA_CONTEXT_NOAUTOSPAWN, | |
1129 NULL) != 0) { // No special fork handling needed | |
1130 LOG(LS_ERROR) << "Can't start connection to PulseAudio sound server"; | |
1131 ret = false; | |
1132 goto done; | |
1133 } | |
1134 | |
1135 // Wait for the connection state machine to reach a terminal state. | |
1136 do { | |
1137 Wait(); | |
1138 } while (!data.connect_done); | |
1139 | |
1140 // Now check to see what final state we reached. | |
1141 state = symbol_table_.pa_context_get_state()(context); | |
1142 | |
1143 if (state != PA_CONTEXT_READY) { | |
1144 if (state == PA_CONTEXT_FAILED) { | |
1145 LOG(LS_ERROR) << "Failed to connect to PulseAudio sound server"; | |
1146 } else if (state == PA_CONTEXT_TERMINATED) { | |
1147 LOG(LS_ERROR) << "PulseAudio connection terminated early"; | |
1148 } else { | |
1149 // Shouldn't happen, because we only signal on one of those three states. | |
1150 LOG(LS_ERROR) << "Unknown problem connecting to PulseAudio"; | |
1151 } | |
1152 ret = false; | |
1153 } | |
1154 | |
1155 done: | |
1156 // We unset our callback for safety just in case the state might somehow | |
1157 // change later, because the pointer to "data" will be invalid after return | |
1158 // from this function. | |
1159 symbol_table_.pa_context_set_state_callback()(context, NULL, NULL); | |
1160 return ret; | |
1161 } | |
1162 | |
1163 // Must be called with the lock held. | |
1164 pa_context *PulseAudioSoundSystem::CreateNewConnection() { | |
1165 // Create connection context. | |
1166 std::string app_name; | |
1167 // TODO(henrika): Pulse etiquette says this name should be localized. Do | |
1168 // we care? | |
1169 rtc::Filesystem::GetApplicationName(&app_name); | |
1170 pa_context *context = symbol_table_.pa_context_new()( | |
1171 symbol_table_.pa_threaded_mainloop_get_api()(mainloop_), | |
1172 app_name.c_str()); | |
1173 if (!context) { | |
1174 LOG(LS_ERROR) << "Can't create context"; | |
1175 goto fail0; | |
1176 } | |
1177 | |
1178 // Now connect. | |
1179 if (!ConnectToPulse(context)) { | |
1180 goto fail1; | |
1181 } | |
1182 | |
1183 // Otherwise the connection succeeded and is ready. | |
1184 return context; | |
1185 | |
1186 fail1: | |
1187 symbol_table_.pa_context_unref()(context); | |
1188 fail0: | |
1189 return NULL; | |
1190 } | |
1191 | |
1192 struct EnumerateDevicesCallbackData { | |
1193 PulseAudioSoundSystem *instance; | |
1194 SoundSystemInterface::SoundDeviceLocatorList *devices; | |
1195 }; | |
1196 | |
1197 void PulseAudioSoundSystem::EnumeratePlaybackDevicesCallbackThunk( | |
1198 pa_context *unused, | |
1199 const pa_sink_info *info, | |
1200 int eol, | |
1201 void *userdata) { | |
1202 EnumerateDevicesCallbackData *data = | |
1203 static_cast<EnumerateDevicesCallbackData *>(userdata); | |
1204 data->instance->OnEnumeratePlaybackDevicesCallback(data->devices, info, eol); | |
1205 } | |
1206 | |
1207 void PulseAudioSoundSystem::EnumerateCaptureDevicesCallbackThunk( | |
1208 pa_context *unused, | |
1209 const pa_source_info *info, | |
1210 int eol, | |
1211 void *userdata) { | |
1212 EnumerateDevicesCallbackData *data = | |
1213 static_cast<EnumerateDevicesCallbackData *>(userdata); | |
1214 data->instance->OnEnumerateCaptureDevicesCallback(data->devices, info, eol); | |
1215 } | |
1216 | |
1217 void PulseAudioSoundSystem::OnEnumeratePlaybackDevicesCallback( | |
1218 SoundDeviceLocatorList *devices, | |
1219 const pa_sink_info *info, | |
1220 int eol) { | |
1221 if (eol) { | |
1222 // List is over. Wake EnumerateDevices(). | |
1223 Signal(); | |
1224 return; | |
1225 } | |
1226 | |
1227 // Else this is the next device. | |
1228 devices->push_back( | |
1229 new PulseAudioDeviceLocator(info->description, info->name)); | |
1230 } | |
1231 | |
1232 void PulseAudioSoundSystem::OnEnumerateCaptureDevicesCallback( | |
1233 SoundDeviceLocatorList *devices, | |
1234 const pa_source_info *info, | |
1235 int eol) { | |
1236 if (eol) { | |
1237 // List is over. Wake EnumerateDevices(). | |
1238 Signal(); | |
1239 return; | |
1240 } | |
1241 | |
1242 if (info->monitor_of_sink != PA_INVALID_INDEX) { | |
1243 // We don't want to list monitor sources, since they are almost certainly | |
1244 // not what the user wants for voice conferencing. | |
1245 return; | |
1246 } | |
1247 | |
1248 // Else this is the next device. | |
1249 devices->push_back( | |
1250 new PulseAudioDeviceLocator(info->description, info->name)); | |
1251 } | |
1252 | |
1253 template <typename InfoStruct> | |
1254 bool PulseAudioSoundSystem::EnumerateDevices( | |
1255 SoundDeviceLocatorList *devices, | |
1256 pa_operation *(*enumerate_fn)( | |
1257 pa_context *c, | |
1258 void (*callback_fn)( | |
1259 pa_context *c, | |
1260 const InfoStruct *i, | |
1261 int eol, | |
1262 void *userdata), | |
1263 void *userdata), | |
1264 void (*callback_fn)( | |
1265 pa_context *c, | |
1266 const InfoStruct *i, | |
1267 int eol, | |
1268 void *userdata)) { | |
1269 ClearSoundDeviceLocatorList(devices); | |
1270 if (!IsInitialized()) { | |
1271 return false; | |
1272 } | |
1273 | |
1274 EnumerateDevicesCallbackData data; | |
1275 data.instance = this; | |
1276 data.devices = devices; | |
1277 | |
1278 Lock(); | |
1279 pa_operation *op = (*enumerate_fn)( | |
1280 context_, | |
1281 callback_fn, | |
1282 &data); | |
1283 bool ret = FinishOperation(op); | |
1284 Unlock(); | |
1285 return ret; | |
1286 } | |
1287 | |
1288 struct GetDefaultDeviceCallbackData { | |
1289 PulseAudioSoundSystem *instance; | |
1290 SoundDeviceLocator **device; | |
1291 }; | |
1292 | |
1293 template <const char *(pa_server_info::*field)> | |
1294 void PulseAudioSoundSystem::GetDefaultDeviceCallbackThunk( | |
1295 pa_context *unused, | |
1296 const pa_server_info *info, | |
1297 void *userdata) { | |
1298 GetDefaultDeviceCallbackData *data = | |
1299 static_cast<GetDefaultDeviceCallbackData *>(userdata); | |
1300 data->instance->OnGetDefaultDeviceCallback<field>(info, data->device); | |
1301 } | |
1302 | |
1303 template <const char *(pa_server_info::*field)> | |
1304 void PulseAudioSoundSystem::OnGetDefaultDeviceCallback( | |
1305 const pa_server_info *info, | |
1306 SoundDeviceLocator **device) { | |
1307 if (info) { | |
1308 const char *dev = info->*field; | |
1309 if (dev) { | |
1310 *device = new PulseAudioDeviceLocator("Default device", dev); | |
1311 } | |
1312 } | |
1313 Signal(); | |
1314 } | |
1315 | |
1316 template <const char *(pa_server_info::*field)> | |
1317 bool PulseAudioSoundSystem::GetDefaultDevice(SoundDeviceLocator **device) { | |
1318 if (!IsInitialized()) { | |
1319 return false; | |
1320 } | |
1321 bool ret; | |
1322 *device = NULL; | |
1323 GetDefaultDeviceCallbackData data; | |
1324 data.instance = this; | |
1325 data.device = device; | |
1326 Lock(); | |
1327 pa_operation *op = symbol_table_.pa_context_get_server_info()( | |
1328 context_, | |
1329 &GetDefaultDeviceCallbackThunk<field>, | |
1330 &data); | |
1331 ret = FinishOperation(op); | |
1332 Unlock(); | |
1333 return ret && (*device != NULL); | |
1334 } | |
1335 | |
1336 void PulseAudioSoundSystem::StreamStateChangedCallbackThunk( | |
1337 pa_stream *stream, | |
1338 void *userdata) { | |
1339 PulseAudioSoundSystem *instance = | |
1340 static_cast<PulseAudioSoundSystem *>(userdata); | |
1341 instance->OnStreamStateChangedCallback(stream); | |
1342 } | |
1343 | |
1344 void PulseAudioSoundSystem::OnStreamStateChangedCallback(pa_stream *stream) { | |
1345 pa_stream_state_t state = symbol_table_.pa_stream_get_state()(stream); | |
1346 if (state == PA_STREAM_READY) { | |
1347 LOG(LS_INFO) << "Pulse stream " << stream << " ready"; | |
1348 } else if (state == PA_STREAM_FAILED || | |
1349 state == PA_STREAM_TERMINATED || | |
1350 state == PA_STREAM_UNCONNECTED) { | |
1351 LOG(LS_ERROR) << "Pulse stream " << stream << " failed to connect: " | |
1352 << LastError(); | |
1353 } | |
1354 } | |
1355 | |
1356 template <typename StreamInterface> | |
1357 StreamInterface *PulseAudioSoundSystem::OpenDevice( | |
1358 const SoundDeviceLocator *device, | |
1359 const OpenParams ¶ms, | |
1360 const char *stream_name, | |
1361 StreamInterface *(PulseAudioSoundSystem::*connect_fn)( | |
1362 pa_stream *stream, | |
1363 const char *dev, | |
1364 int flags, | |
1365 pa_stream_flags_t pa_flags, | |
1366 int latency, | |
1367 const pa_sample_spec &spec)) { | |
1368 if (!IsInitialized()) { | |
1369 return NULL; | |
1370 } | |
1371 | |
1372 const char *dev = static_cast<const PulseAudioDeviceLocator *>(device)-> | |
1373 device_name().c_str(); | |
1374 | |
1375 StreamInterface *stream_interface = NULL; | |
1376 | |
1377 ASSERT(params.format < arraysize(kCricketFormatToPulseFormatTable)); | |
1378 | |
1379 pa_sample_spec spec; | |
1380 spec.format = kCricketFormatToPulseFormatTable[params.format]; | |
1381 spec.rate = params.freq; | |
1382 spec.channels = params.channels; | |
1383 | |
1384 int pa_flags = 0; | |
1385 if (params.flags & FLAG_REPORT_LATENCY) { | |
1386 pa_flags |= PA_STREAM_INTERPOLATE_TIMING | | |
1387 PA_STREAM_AUTO_TIMING_UPDATE; | |
1388 } | |
1389 | |
1390 if (params.latency != kNoLatencyRequirements) { | |
1391 // If configuring a specific latency then we want to specify | |
1392 // PA_STREAM_ADJUST_LATENCY to make the server adjust parameters | |
1393 // automatically to reach that target latency. However, that flag doesn't | |
1394 // exist in Ubuntu 8.04 and many people still use that, so we have to check | |
1395 // the protocol version of libpulse. | |
1396 if (symbol_table_.pa_context_get_protocol_version()(context_) >= | |
1397 kAdjustLatencyProtocolVersion) { | |
1398 pa_flags |= PA_STREAM_ADJUST_LATENCY; | |
1399 } | |
1400 } | |
1401 | |
1402 Lock(); | |
1403 | |
1404 pa_stream *stream = symbol_table_.pa_stream_new()(context_, stream_name, | |
1405 &spec, NULL); | |
1406 if (!stream) { | |
1407 LOG(LS_ERROR) << "Can't create pa_stream"; | |
1408 goto done; | |
1409 } | |
1410 | |
1411 // Set a state callback to log errors. | |
1412 symbol_table_.pa_stream_set_state_callback()(stream, | |
1413 &StreamStateChangedCallbackThunk, | |
1414 this); | |
1415 | |
1416 stream_interface = (this->*connect_fn)( | |
1417 stream, | |
1418 dev, | |
1419 params.flags, | |
1420 static_cast<pa_stream_flags_t>(pa_flags), | |
1421 params.latency, | |
1422 spec); | |
1423 if (!stream_interface) { | |
1424 LOG(LS_ERROR) << "Can't connect stream to " << dev; | |
1425 symbol_table_.pa_stream_unref()(stream); | |
1426 } | |
1427 | |
1428 done: | |
1429 Unlock(); | |
1430 return stream_interface; | |
1431 } | |
1432 | |
1433 // Must be called with the lock held. | |
1434 SoundOutputStreamInterface *PulseAudioSoundSystem::ConnectOutputStream( | |
1435 pa_stream *stream, | |
1436 const char *dev, | |
1437 int flags, | |
1438 pa_stream_flags_t pa_flags, | |
1439 int latency, | |
1440 const pa_sample_spec &spec) { | |
1441 pa_buffer_attr attr = {0}; | |
1442 pa_buffer_attr *pattr = NULL; | |
1443 if (latency != kNoLatencyRequirements) { | |
1444 // kLowLatency is 0, so we treat it the same as a request for zero latency. | |
1445 ssize_t bytes_per_sec = symbol_table_.pa_bytes_per_second()(&spec); | |
1446 latency = std::max( | |
1447 latency, static_cast<int>(bytes_per_sec * kPlaybackLatencyMinimumMsecs / | |
1448 rtc::kNumMicrosecsPerSec)); | |
1449 FillPlaybackBufferAttr(latency, &attr); | |
1450 pattr = &attr; | |
1451 } | |
1452 if (symbol_table_.pa_stream_connect_playback()( | |
1453 stream, | |
1454 dev, | |
1455 pattr, | |
1456 pa_flags, | |
1457 // Let server choose volume | |
1458 NULL, | |
1459 // Not synchronized to any other playout | |
1460 NULL) != 0) { | |
1461 return NULL; | |
1462 } | |
1463 return new PulseAudioOutputStream(this, stream, flags, latency); | |
1464 } | |
1465 | |
1466 // Must be called with the lock held. | |
1467 SoundInputStreamInterface *PulseAudioSoundSystem::ConnectInputStream( | |
1468 pa_stream *stream, | |
1469 const char *dev, | |
1470 int flags, | |
1471 pa_stream_flags_t pa_flags, | |
1472 int latency, | |
1473 const pa_sample_spec &spec) { | |
1474 pa_buffer_attr attr = {0}; | |
1475 pa_buffer_attr *pattr = NULL; | |
1476 if (latency != kNoLatencyRequirements) { | |
1477 size_t bytes_per_sec = symbol_table_.pa_bytes_per_second()(&spec); | |
1478 if (latency == kLowLatency) { | |
1479 latency = bytes_per_sec * kLowCaptureLatencyMsecs / | |
1480 rtc::kNumMicrosecsPerSec; | |
1481 } | |
1482 // Note: fragsize specifies a maximum transfer size, not a minimum, so it is | |
1483 // not possible to force a high latency setting, only a low one. | |
1484 attr.fragsize = latency; | |
1485 attr.maxlength = latency + bytes_per_sec * kCaptureBufferExtraMsecs / | |
1486 rtc::kNumMicrosecsPerSec; | |
1487 LOG(LS_VERBOSE) << "Configuring latency = " << attr.fragsize | |
1488 << ", maxlength = " << attr.maxlength; | |
1489 pattr = &attr; | |
1490 } | |
1491 if (symbol_table_.pa_stream_connect_record()(stream, | |
1492 dev, | |
1493 pattr, | |
1494 pa_flags) != 0) { | |
1495 return NULL; | |
1496 } | |
1497 return new PulseAudioInputStream(this, stream, flags); | |
1498 } | |
1499 | |
1500 // Must be called with the lock held. | |
1501 bool PulseAudioSoundSystem::FinishOperation(pa_operation *op) { | |
1502 if (!op) { | |
1503 LOG(LS_ERROR) << "Failed to start operation"; | |
1504 return false; | |
1505 } | |
1506 | |
1507 do { | |
1508 Wait(); | |
1509 } while (symbol_table_.pa_operation_get_state()(op) == PA_OPERATION_RUNNING); | |
1510 | |
1511 symbol_table_.pa_operation_unref()(op); | |
1512 | |
1513 return true; | |
1514 } | |
1515 | |
1516 inline void PulseAudioSoundSystem::Lock() { | |
1517 symbol_table_.pa_threaded_mainloop_lock()(mainloop_); | |
1518 } | |
1519 | |
1520 inline void PulseAudioSoundSystem::Unlock() { | |
1521 symbol_table_.pa_threaded_mainloop_unlock()(mainloop_); | |
1522 } | |
1523 | |
1524 // Must be called with the lock held. | |
1525 inline void PulseAudioSoundSystem::Wait() { | |
1526 symbol_table_.pa_threaded_mainloop_wait()(mainloop_); | |
1527 } | |
1528 | |
1529 // Must be called with the lock held. | |
1530 inline void PulseAudioSoundSystem::Signal() { | |
1531 symbol_table_.pa_threaded_mainloop_signal()(mainloop_, 0); | |
1532 } | |
1533 | |
1534 // Must be called with the lock held. | |
1535 const char *PulseAudioSoundSystem::LastError() { | |
1536 return symbol_table_.pa_strerror()(symbol_table_.pa_context_errno()( | |
1537 context_)); | |
1538 } | |
1539 | |
1540 } // namespace rtc | |
1541 | |
1542 #endif // HAVE_LIBPULSE | |
OLD | NEW |