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