~ubuntu-branches/debian/sid/mumble/sid

« back to all changes in this revision

Viewing changes to .pc/24-fix-pulseaudio-segfault-pt2.diff/src/mumble/PulseAudio.cpp

  • Committer: Package Import Robot
  • Author(s): Christopher Knadle
  • Date: 2014-08-28 16:23:17 UTC
  • mfrom: (1.4.14)
  • Revision ID: package-import@ubuntu.com-20140828162317-nakwb6ie31slej4t
Tags: 1.2.8-1
* New upstream stable release from 2014-08-09
* debian/control:
    - Remove uploader Thorvald Natvig <thorvald@debian.org> due to
      inactivity; thanks very much for your prior contributions.
* debian/patches:
    - Remove 23-fix-pulseaudio-segfault-pt1.diff
             24-fix-pulseaudio-segfault-pt2.diff
      Both patches incorporated upstream in 1.2.8
    - Add 25-make-logfiles-readable-by-adm.diff to make mumble-server log
      files readable by group adm.  Closes: #759287
      Thanks to Jan Braun <janbraun@gmx.net> for reporting the bug and
      submitting a patch.
* debian/mumble-server.postinst:
    - Add check for existance of mumble-server group entry before creation
      of group and user.  Closes: #758833
      Thanks to William Martin <william.martin@power-lan.com> for reporting
      the bug and discussing a fix.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (C) 2005-2011, Thorvald Natvig <thorvald@natvig.com>
2
 
 
3
 
   All rights reserved.
4
 
 
5
 
   Redistribution and use in source and binary forms, with or without
6
 
   modification, are permitted provided that the following conditions
7
 
   are met:
8
 
 
9
 
   - Redistributions of source code must retain the above copyright notice,
10
 
     this list of conditions and the following disclaimer.
11
 
   - Redistributions in binary form must reproduce the above copyright notice,
12
 
     this list of conditions and the following disclaimer in the documentation
13
 
     and/or other materials provided with the distribution.
14
 
   - Neither the name of the Mumble Developers nor the names of its
15
 
     contributors may be used to endorse or promote products derived from this
16
 
     software without specific prior written permission.
17
 
 
18
 
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
 
   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
 
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
 
   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
22
 
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
 
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
 
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25
 
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
 
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27
 
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
 
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 
*/
30
 
 
31
 
#include "mumble_pch.hpp"
32
 
 
33
 
#include "PulseAudio.h"
34
 
 
35
 
#include <sys/soundcard.h>
36
 
#include <fcntl.h>
37
 
#include <errno.h>
38
 
#include <sys/ioctl.h>
39
 
 
40
 
#include "Global.h"
41
 
#include "MainWindow.h"
42
 
#include "Timer.h"
43
 
#include "User.h"
44
 
 
45
 
 
46
 
static const char *mumble_sink_input = "Mumble Speakers";
47
 
static const char *mumble_echo = "Mumble Speakers (Echo)";
48
 
 
49
 
static PulseAudioSystem *pasys = NULL;
50
 
 
51
 
#define NBLOCKS 8
52
 
 
53
 
class PulseAudioInputRegistrar : public AudioInputRegistrar {
54
 
        public:
55
 
                PulseAudioInputRegistrar();
56
 
                virtual AudioInput *create();
57
 
                virtual const QList<audioDevice> getDeviceChoices();
58
 
                virtual void setDeviceChoice(const QVariant &, Settings &);
59
 
                virtual bool canEcho(const QString &) const;
60
 
};
61
 
 
62
 
 
63
 
class PulseAudioOutputRegistrar : public AudioOutputRegistrar {
64
 
        public:
65
 
                PulseAudioOutputRegistrar();
66
 
                virtual AudioOutput *create();
67
 
                virtual const QList<audioDevice> getDeviceChoices();
68
 
                virtual void setDeviceChoice(const QVariant &, Settings &);
69
 
                bool canMuteOthers() const;
70
 
};
71
 
 
72
 
class PulseAudioInit : public DeferInit {
73
 
        public:
74
 
                PulseAudioInputRegistrar *airPulseAudio;
75
 
                PulseAudioOutputRegistrar *aorPulseAudio;
76
 
                void initialize() {
77
 
                        pasys = new PulseAudioSystem();
78
 
                        pasys->qmWait.lock();
79
 
                        pasys->qwcWait.wait(&pasys->qmWait, 1000);
80
 
                        pasys->qmWait.unlock();
81
 
                        if (pasys->bPulseIsGood) {
82
 
                                airPulseAudio = new PulseAudioInputRegistrar();
83
 
                                aorPulseAudio = new PulseAudioOutputRegistrar();
84
 
                        } else {
85
 
                                airPulseAudio = NULL;
86
 
                                aorPulseAudio = NULL;
87
 
                                delete pasys;
88
 
                                pasys = NULL;
89
 
                        }
90
 
                };
91
 
                void destroy() {
92
 
                        delete airPulseAudio;
93
 
                        delete aorPulseAudio;
94
 
                        delete pasys;
95
 
                        pasys = NULL;
96
 
                };
97
 
};
98
 
 
99
 
static PulseAudioInit pulseinit;
100
 
 
101
 
PulseAudioSystem::PulseAudioSystem() {
102
 
        pasInput = pasOutput = pasSpeaker = NULL;
103
 
        bSourceDone=bSinkDone=bServerDone = false;
104
 
        iDelayCache = 0;
105
 
        bPositionalCache = false;
106
 
        bAttenuating = false;
107
 
        iRemainingOperations = 0;
108
 
        bPulseIsGood = false;
109
 
 
110
 
        pam = pa_threaded_mainloop_new();
111
 
        pa_mainloop_api *api = pa_threaded_mainloop_get_api(pam);
112
 
 
113
 
        pa_proplist *proplist;
114
 
 
115
 
        proplist = pa_proplist_new();
116
 
        pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "Mumble");
117
 
        pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "net.sourceforge.mumble.mumble");
118
 
        pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "mumble");
119
 
        pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "game");
120
 
 
121
 
        pacContext = pa_context_new_with_proplist(api, NULL, proplist);
122
 
        pa_proplist_free(proplist);
123
 
 
124
 
        pa_context_set_subscribe_callback(pacContext, subscribe_callback, this);
125
 
 
126
 
        pa_context_set_state_callback(pacContext, context_state_callback, this);
127
 
        pa_context_connect(pacContext, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
128
 
 
129
 
        pade = api->defer_new(api, defer_event_callback, this);
130
 
        api->defer_enable(pade, false);
131
 
 
132
 
        pa_threaded_mainloop_start(pam);
133
 
 
134
 
        bRunning = true;
135
 
}
136
 
 
137
 
PulseAudioSystem::~PulseAudioSystem() {
138
 
        bRunning = false;
139
 
        if (bAttenuating) {
140
 
                qmWait.lock();
141
 
                bAttenuating = false;
142
 
                setVolumes();
143
 
                bool success = qwcWait.wait(&qmWait, 1000);
144
 
                if (! success) {
145
 
                        qWarning("PulseAudio: Shutdown timeout when attempting to restore volumes.");
146
 
                }
147
 
                qmWait.unlock();
148
 
        }
149
 
        pa_threaded_mainloop_stop(pam);
150
 
        pa_context_disconnect(pacContext);
151
 
        pa_context_unref(pacContext);
152
 
        pa_threaded_mainloop_free(pam);
153
 
}
154
 
 
155
 
void PulseAudioSystem::wakeup() {
156
 
        pa_mainloop_api *api = pa_threaded_mainloop_get_api(pam);
157
 
        api->defer_enable(pade, true);
158
 
}
159
 
 
160
 
void PulseAudioSystem::wakeup_lock() {
161
 
        pa_threaded_mainloop_lock(pam);
162
 
        pa_mainloop_api *api = pa_threaded_mainloop_get_api(pam);
163
 
        api->defer_enable(pade, true);
164
 
        pa_threaded_mainloop_unlock(pam);
165
 
}
166
 
 
167
 
void PulseAudioSystem::defer_event_callback(pa_mainloop_api *a, pa_defer_event *e, void *userdata) {
168
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
169
 
        pas->eventCallback(a, e);
170
 
}
171
 
 
172
 
void PulseAudioSystem::eventCallback(pa_mainloop_api *api, pa_defer_event *) {
173
 
        api->defer_enable(pade, false);
174
 
 
175
 
        if (! bSourceDone || ! bSinkDone || ! bServerDone)
176
 
                return;
177
 
 
178
 
        AudioInputPtr ai = g.ai;
179
 
        AudioOutputPtr ao = g.ao;
180
 
        AudioInput *raw_ai = ai.get();
181
 
        AudioOutput *raw_ao = ao.get();
182
 
        PulseAudioInput *pai = dynamic_cast<PulseAudioInput *>(raw_ai);
183
 
        PulseAudioOutput *pao = dynamic_cast<PulseAudioOutput *>(raw_ao);
184
 
 
185
 
        if (raw_ao) {
186
 
                QString odev = g.s.qsPulseAudioOutput.isEmpty() ? qsDefaultOutput : g.s.qsPulseAudioOutput;
187
 
                pa_stream_state ost = pasOutput ? pa_stream_get_state(pasOutput) : PA_STREAM_TERMINATED;
188
 
                bool do_stop = false;
189
 
                bool do_start = false;
190
 
 
191
 
                if (! pao && (ost == PA_STREAM_READY)) {
192
 
                        do_stop = true;
193
 
                } else if (pao) {
194
 
                        switch (ost) {
195
 
                                case PA_STREAM_TERMINATED: {
196
 
                                                if (pasOutput)
197
 
                                                        pa_stream_unref(pasOutput);
198
 
 
199
 
                                                pa_sample_spec pss = qhSpecMap.value(odev);
200
 
                                                pa_channel_map pcm = qhChanMap.value(odev);
201
 
                                                if ((pss.format != PA_SAMPLE_FLOAT32NE) && (pss.format != PA_SAMPLE_S16NE))
202
 
                                                        pss.format = PA_SAMPLE_FLOAT32NE;
203
 
                                                if (pss.rate == 0)
204
 
                                                        pss.rate = SAMPLE_RATE;
205
 
                                                if ((pss.channels == 0) || (! g.s.doPositionalAudio()))
206
 
                                                        pss.channels = 1;
207
 
 
208
 
                                                pasOutput = pa_stream_new(pacContext, mumble_sink_input, &pss, (pss.channels == 1) ? NULL : &pcm);
209
 
                                                pa_stream_set_state_callback(pasOutput, stream_callback, this);
210
 
                                                pa_stream_set_write_callback(pasOutput, write_callback, this);
211
 
                                        }
212
 
                                case PA_STREAM_UNCONNECTED:
213
 
                                        do_start = true;
214
 
                                        break;
215
 
                                case PA_STREAM_READY: {
216
 
                                                if (g.s.iOutputDelay != iDelayCache) {
217
 
                                                        do_stop = true;
218
 
                                                } else if (g.s.doPositionalAudio() != bPositionalCache) {
219
 
                                                        do_stop = true;
220
 
                                                } else if (odev != qsOutputCache) {
221
 
                                                        do_stop = true;
222
 
                                                }
223
 
                                                break;
224
 
                                        }
225
 
                                default:
226
 
                                        break;
227
 
                        }
228
 
                }
229
 
                if (do_stop) {
230
 
                        qWarning("PulseAudio: Stopping output");
231
 
                        pa_stream_disconnect(pasOutput);
232
 
                } else if (do_start) {
233
 
                        qWarning("PulseAudio: Starting output: %s", qPrintable(odev));
234
 
                        pa_buffer_attr buff;
235
 
                        const pa_sample_spec *pss = pa_stream_get_sample_spec(pasOutput);
236
 
                        const unsigned int iBlockLen = ((pao->iFrameSize * pss->rate) / SAMPLE_RATE) * pss->channels * ((pss->format == PA_SAMPLE_FLOAT32NE) ? sizeof(float) : sizeof(short));
237
 
                        buff.tlength = iBlockLen * (g.s.iOutputDelay+1);
238
 
                        buff.minreq = iBlockLen;
239
 
                        buff.maxlength = -1;
240
 
                        buff.prebuf = -1;
241
 
                        buff.fragsize = iBlockLen;
242
 
 
243
 
                        iDelayCache = g.s.iOutputDelay;
244
 
                        bPositionalCache = g.s.doPositionalAudio();
245
 
                        qsOutputCache = odev;
246
 
 
247
 
                        pa_stream_connect_playback(pasOutput, qPrintable(odev), &buff, PA_STREAM_ADJUST_LATENCY, NULL, NULL);
248
 
                }
249
 
        }
250
 
 
251
 
        if (raw_ai) {
252
 
                QString idev = g.s.qsPulseAudioInput.isEmpty() ? qsDefaultInput : g.s.qsPulseAudioInput;
253
 
                pa_stream_state ist = pasInput ? pa_stream_get_state(pasInput) : PA_STREAM_TERMINATED;
254
 
                bool do_stop = false;
255
 
                bool do_start = false;
256
 
 
257
 
                if (! pai && (ist == PA_STREAM_READY)) {
258
 
                        do_stop = true;
259
 
                } else if (pai) {
260
 
                        switch (ist) {
261
 
                                case PA_STREAM_TERMINATED: {
262
 
                                                if (pasInput)
263
 
                                                        pa_stream_unref(pasInput);
264
 
 
265
 
                                                pa_sample_spec pss = qhSpecMap.value(idev);
266
 
                                                if ((pss.format != PA_SAMPLE_FLOAT32NE) && (pss.format != PA_SAMPLE_S16NE))
267
 
                                                        pss.format = PA_SAMPLE_FLOAT32NE;
268
 
                                                if (pss.rate == 0)
269
 
                                                        pss.rate = SAMPLE_RATE;
270
 
                                                pss.channels = 1;
271
 
 
272
 
                                                pasInput = pa_stream_new(pacContext, "Microphone", &pss, NULL);
273
 
                                                pa_stream_set_state_callback(pasInput, stream_callback, this);
274
 
                                                pa_stream_set_read_callback(pasInput, read_callback, this);
275
 
                                        }
276
 
                                case PA_STREAM_UNCONNECTED:
277
 
                                        do_start = true;
278
 
                                        break;
279
 
                                case PA_STREAM_READY: {
280
 
                                                if (idev != qsInputCache) {
281
 
                                                        do_stop = true;
282
 
                                                }
283
 
                                                break;
284
 
                                        }
285
 
                                default:
286
 
                                        break;
287
 
                        }
288
 
                }
289
 
                if (do_stop) {
290
 
                        qWarning("PulseAudio: Stopping input");
291
 
                        pa_stream_disconnect(pasInput);
292
 
                } else if (do_start) {
293
 
                        qWarning("PulseAudio: Starting input %s",qPrintable(idev));
294
 
                        pa_buffer_attr buff;
295
 
                        const pa_sample_spec *pss = pa_stream_get_sample_spec(pasInput);
296
 
                        const unsigned int iBlockLen = ((pai->iFrameSize * pss->rate) / SAMPLE_RATE) * pss->channels * ((pss->format == PA_SAMPLE_FLOAT32NE) ? sizeof(float) : sizeof(short));
297
 
                        buff.tlength = iBlockLen;
298
 
                        buff.minreq = iBlockLen;
299
 
                        buff.maxlength = -1;
300
 
                        buff.prebuf = -1;
301
 
                        buff.fragsize = iBlockLen;
302
 
 
303
 
                        qsInputCache = idev;
304
 
 
305
 
                        pa_stream_connect_record(pasInput, qPrintable(idev), &buff, PA_STREAM_ADJUST_LATENCY);
306
 
                }
307
 
        }
308
 
 
309
 
        if (raw_ai) {
310
 
                QString odev = g.s.qsPulseAudioOutput.isEmpty() ? qsDefaultOutput : g.s.qsPulseAudioOutput;
311
 
                QString edev = qhEchoMap.value(odev);
312
 
                pa_stream_state est = pasSpeaker ? pa_stream_get_state(pasSpeaker) : PA_STREAM_TERMINATED;
313
 
                bool do_stop = false;
314
 
                bool do_start = false;
315
 
 
316
 
                if ((! pai || ! g.s.doEcho()) && (est == PA_STREAM_READY)) {
317
 
                        do_stop = true;
318
 
                } else if (pai && g.s.doEcho()) {
319
 
                        switch (est) {
320
 
                                case PA_STREAM_TERMINATED: {
321
 
                                                if (pasSpeaker)
322
 
                                                        pa_stream_unref(pasSpeaker);
323
 
 
324
 
                                                pa_sample_spec pss = qhSpecMap.value(edev);
325
 
                                                pa_channel_map pcm = qhChanMap.value(edev);
326
 
                                                if ((pss.format != PA_SAMPLE_FLOAT32NE) && (pss.format != PA_SAMPLE_S16NE))
327
 
                                                        pss.format = PA_SAMPLE_FLOAT32NE;
328
 
                                                if (pss.rate == 0)
329
 
                                                        pss.rate = SAMPLE_RATE;
330
 
                                                if ((pss.channels == 0) || (! g.s.bEchoMulti))
331
 
                                                        pss.channels = 1;
332
 
 
333
 
                                                pasSpeaker = pa_stream_new(pacContext, mumble_echo, &pss, (pss.channels == 1) ? NULL : &pcm);
334
 
                                                pa_stream_set_state_callback(pasSpeaker, stream_callback, this);
335
 
                                                pa_stream_set_read_callback(pasSpeaker, read_callback, this);
336
 
                                        }
337
 
                                case PA_STREAM_UNCONNECTED:
338
 
                                        do_start = true;
339
 
                                        break;
340
 
                                case PA_STREAM_READY: {
341
 
                                                if (g.s.bEchoMulti != bEchoMultiCache) {
342
 
                                                        do_stop = true;
343
 
                                                } else if (edev != qsEchoCache) {
344
 
                                                        do_stop = true;
345
 
                                                }
346
 
                                                break;
347
 
                                        }
348
 
                                default:
349
 
                                        break;
350
 
                        }
351
 
                }
352
 
                if (do_stop) {
353
 
                        qWarning("PulseAudio: Stopping echo");
354
 
                        pa_stream_disconnect(pasSpeaker);
355
 
                } else if (do_start) {
356
 
                        qWarning("PulseAudio: Starting echo: %s", qPrintable(edev));
357
 
                        pa_buffer_attr buff;
358
 
                        const pa_sample_spec *pss = pa_stream_get_sample_spec(pasSpeaker);
359
 
                        const unsigned int iBlockLen = ((pai->iFrameSize * pss->rate) / SAMPLE_RATE) * pss->channels * ((pss->format == PA_SAMPLE_FLOAT32NE) ? sizeof(float) : sizeof(short));
360
 
                        buff.tlength = iBlockLen;
361
 
                        buff.minreq = iBlockLen;
362
 
                        buff.maxlength = -1;
363
 
                        buff.prebuf = -1;
364
 
                        buff.fragsize = iBlockLen;
365
 
 
366
 
                        bEchoMultiCache = g.s.bEchoMulti;
367
 
                        qsEchoCache = edev;
368
 
 
369
 
                        pa_stream_connect_record(pasSpeaker, qPrintable(edev), &buff, PA_STREAM_ADJUST_LATENCY);
370
 
                }
371
 
        }
372
 
}
373
 
 
374
 
void PulseAudioSystem::context_state_callback(pa_context *c, void *userdata) {
375
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
376
 
        pas->contextCallback(c);
377
 
}
378
 
 
379
 
void PulseAudioSystem::subscribe_callback(pa_context *, pa_subscription_event_type evt, unsigned int, void *userdata) {
380
 
        switch (evt & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
381
 
                case PA_SUBSCRIPTION_EVENT_NEW:
382
 
                case PA_SUBSCRIPTION_EVENT_REMOVE:
383
 
                        break;
384
 
                default:
385
 
                        return;
386
 
        }
387
 
        switch (evt & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
388
 
                case PA_SUBSCRIPTION_EVENT_SINK:
389
 
                case PA_SUBSCRIPTION_EVENT_SOURCE:
390
 
                        break;
391
 
                default:
392
 
                        return;
393
 
        }
394
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
395
 
        qWarning("PulseAudio: Sinks or inputs changed (inserted or removed sound card)");
396
 
        pas->query();
397
 
}
398
 
 
399
 
void PulseAudioSystem::sink_callback(pa_context *, const pa_sink_info *i, int eol, void *userdata) {
400
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
401
 
        if (!i || eol) {
402
 
                pas->bSinkDone = true;
403
 
                pas->wakeup();
404
 
                return;
405
 
        }
406
 
 
407
 
        const QString name = QLatin1String(i->name);
408
 
 
409
 
        pas->qhSpecMap.insert(name, i->sample_spec);
410
 
        pas->qhChanMap.insert(name, i->channel_map);
411
 
        pas->qhOutput.insert(name, QLatin1String(i->description));
412
 
        pas->qhEchoMap.insert(name, QLatin1String(i->monitor_source_name));
413
 
}
414
 
 
415
 
void PulseAudioSystem::source_callback(pa_context *, const pa_source_info *i, int eol, void *userdata) {
416
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
417
 
        if (!i || eol) {
418
 
                pas->bSourceDone = true;
419
 
                pas->wakeup();
420
 
                return;
421
 
        }
422
 
 
423
 
        const QString name = QLatin1String(i->name);
424
 
 
425
 
        pas->qhSpecMap.insert(name, i->sample_spec);
426
 
        pas->qhChanMap.insert(name, i->channel_map);
427
 
 
428
 
        if (i->monitor_of_sink == PA_INVALID_INDEX)
429
 
                pas->qhInput.insert(QLatin1String(i->name), QLatin1String(i->description));
430
 
}
431
 
 
432
 
void PulseAudioSystem::server_callback(pa_context *, const pa_server_info *i, void *userdata) {
433
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
434
 
 
435
 
        pas->qsDefaultInput = QLatin1String(i->default_source_name);
436
 
        pas->qsDefaultOutput = QLatin1String(i->default_sink_name);
437
 
 
438
 
        pas->bServerDone = true;
439
 
        pas->wakeup();
440
 
}
441
 
 
442
 
void PulseAudioSystem::stream_callback(pa_stream *s, void *userdata) {
443
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
444
 
        switch (pa_stream_get_state(s)) {
445
 
                case PA_STREAM_FAILED:
446
 
                        qWarning("PulseAudio: Stream error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
447
 
                        break;
448
 
                default:
449
 
                        break;
450
 
        }
451
 
        pas->wakeup();
452
 
}
453
 
 
454
 
void PulseAudioSystem::read_callback(pa_stream *s, size_t bytes, void *userdata) {
455
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
456
 
 
457
 
        size_t length = bytes;
458
 
        const void *data = NULL;
459
 
        pa_stream_peek(s, &data, &length);
460
 
        if (data == NULL && length > 0) {
461
 
                qWarning("PulseAudio: pa_stream_peek reports no data at current read index.");
462
 
        } else if (data == NULL && length == 0) {
463
 
                qWarning("PulseAudio: pa_stream_peek reports empty memblockq.");
464
 
        } else if (data == NULL || length == 0) {
465
 
                qWarning("PulseAudio: invalid pa_stream_peek state encountered.");
466
 
                return;
467
 
        }
468
 
 
469
 
        AudioInputPtr ai = g.ai;
470
 
        PulseAudioInput *pai = dynamic_cast<PulseAudioInput *>(ai.get());
471
 
        if (! pai) {
472
 
                pa_stream_drop(s);
473
 
                pas->wakeup();
474
 
                return;
475
 
        }
476
 
 
477
 
        const pa_sample_spec *pss = pa_stream_get_sample_spec(s);
478
 
 
479
 
        if (s == pas->pasInput) {
480
 
                if (!pa_sample_spec_equal(pss, &pai->pssMic)) {
481
 
                        pai->pssMic = *pss;
482
 
                        pai->iMicFreq = pss->rate;
483
 
                        pai->iMicChannels = pss->channels;
484
 
                        if (pss->format == PA_SAMPLE_FLOAT32NE)
485
 
                                pai->eMicFormat = PulseAudioInput::SampleFloat;
486
 
                        else
487
 
                                pai->eMicFormat = PulseAudioInput::SampleShort;
488
 
                        pai->initializeMixer();
489
 
                }
490
 
                if (data != NULL) {
491
 
                        pai->addMic(data, length / pai->iMicSampleSize);
492
 
                }
493
 
        } else if (s == pas->pasSpeaker) {
494
 
                if (!pa_sample_spec_equal(pss, &pai->pssEcho)) {
495
 
                        pai->pssEcho = *pss;
496
 
                        pai->iEchoFreq = pss->rate;
497
 
                        pai->iEchoChannels = pss->channels;
498
 
                        if (pss->format == PA_SAMPLE_FLOAT32NE)
499
 
                                pai->eEchoFormat = PulseAudioInput::SampleFloat;
500
 
                        else
501
 
                                pai->eEchoFormat = PulseAudioInput::SampleShort;
502
 
                        pai->initializeMixer();
503
 
                }
504
 
                if (data != NULL) {
505
 
                        pai->addEcho(data, length / pai->iEchoSampleSize);
506
 
                }
507
 
        }
508
 
 
509
 
        pa_stream_drop(s);
510
 
}
511
 
 
512
 
void PulseAudioSystem::write_callback(pa_stream *s, size_t bytes, void *userdata) {
513
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
514
 
        Q_ASSERT(s == pas->pasOutput);
515
 
 
516
 
        AudioOutputPtr ao = g.ao;
517
 
        PulseAudioOutput *pao = dynamic_cast<PulseAudioOutput *>(ao.get());
518
 
 
519
 
        unsigned char buffer[bytes];
520
 
 
521
 
        if (! pao) {
522
 
                // Transitioning, but most likely transitions back, so just zero.
523
 
                memset(buffer, 0, bytes);
524
 
                pa_stream_write(s, buffer, bytes, NULL, 0, PA_SEEK_RELATIVE);
525
 
                pas->wakeup();
526
 
                return;
527
 
        }
528
 
 
529
 
        const pa_sample_spec *pss = pa_stream_get_sample_spec(s);
530
 
        const pa_channel_map *pcm = pa_stream_get_channel_map(pas->pasOutput);
531
 
        if (!pa_sample_spec_equal(pss, &pao->pss) || !pa_channel_map_equal(pcm, &pao->pcm)) {
532
 
                pao->pss = *pss;
533
 
                pao->pcm = *pcm;
534
 
                if (pss->format == PA_SAMPLE_FLOAT32NE)
535
 
                        pao->eSampleFormat = PulseAudioOutput::SampleFloat;
536
 
                else
537
 
                        pao->eSampleFormat = PulseAudioOutput::SampleShort;
538
 
                pao->iMixerFreq = pss->rate;
539
 
                pao->iChannels = pss->channels;
540
 
                unsigned int chanmasks[pss->channels];
541
 
                for (int i=0;i<pss->channels;++i) {
542
 
                        unsigned int cm = 0;
543
 
                        switch (pcm->map[i]) {
544
 
                                case PA_CHANNEL_POSITION_LEFT:
545
 
                                        cm = SPEAKER_FRONT_LEFT;
546
 
                                        break;
547
 
                                case PA_CHANNEL_POSITION_RIGHT:
548
 
                                        cm = SPEAKER_FRONT_RIGHT;
549
 
                                        break;
550
 
                                case PA_CHANNEL_POSITION_CENTER:
551
 
                                        cm = SPEAKER_FRONT_CENTER;
552
 
                                        break;
553
 
                                case PA_CHANNEL_POSITION_REAR_LEFT:
554
 
                                        cm = SPEAKER_BACK_LEFT;
555
 
                                        break;
556
 
                                case PA_CHANNEL_POSITION_REAR_RIGHT:
557
 
                                        cm = SPEAKER_BACK_RIGHT;
558
 
                                        break;
559
 
                                case PA_CHANNEL_POSITION_REAR_CENTER:
560
 
                                        cm = SPEAKER_BACK_CENTER;
561
 
                                        break;
562
 
                                case PA_CHANNEL_POSITION_LFE:
563
 
                                        cm = SPEAKER_LOW_FREQUENCY;
564
 
                                        break;
565
 
                                case PA_CHANNEL_POSITION_SIDE_LEFT:
566
 
                                        cm = SPEAKER_SIDE_LEFT;
567
 
                                        break;
568
 
                                case PA_CHANNEL_POSITION_SIDE_RIGHT:
569
 
                                        cm = SPEAKER_SIDE_RIGHT;
570
 
                                        break;
571
 
                                case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
572
 
                                        cm = SPEAKER_FRONT_LEFT_OF_CENTER;
573
 
                                        break;
574
 
                                case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
575
 
                                        cm = SPEAKER_FRONT_RIGHT_OF_CENTER;
576
 
                                        break;
577
 
                                default:
578
 
                                        cm = 0;
579
 
                                        break;
580
 
                        }
581
 
                        chanmasks[i] = cm;
582
 
                }
583
 
                pao->initializeMixer(chanmasks);
584
 
        }
585
 
 
586
 
        const unsigned int iSampleSize = pao->iSampleSize;
587
 
        const unsigned int samples = bytes / iSampleSize;
588
 
        bool oldAttenuation = pas->bAttenuating;
589
 
 
590
 
        // do we have some mixed output?
591
 
        if (pao->mix(buffer, samples)) {
592
 
                // attenuate if instructed to or it's in settings
593
 
                pas->bAttenuating = (g.bAttenuateOthers || g.s.bAttenuateOthers);
594
 
 
595
 
        } else {
596
 
                memset(buffer, 0, bytes);
597
 
 
598
 
                // attenuate if intructed to (self-activated)
599
 
                pas->bAttenuating = g.bAttenuateOthers;
600
 
        }
601
 
 
602
 
        // if the attenuation state has changed
603
 
        if (oldAttenuation != pas->bAttenuating) {
604
 
                pas->setVolumes();
605
 
        }
606
 
 
607
 
        pa_stream_write(s, buffer, iSampleSize * samples, NULL, 0, PA_SEEK_RELATIVE);
608
 
}
609
 
 
610
 
void PulseAudioSystem::volume_sink_input_list_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) {
611
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
612
 
 
613
 
        if (eol == 0) {
614
 
                // ensure we're not attenuating ourselves!
615
 
                if (strcmp(i->name, mumble_sink_input) != 0) {
616
 
                        // create a new entry
617
 
                        PulseAttenuation patt;
618
 
                        patt.index = i->index;
619
 
                        patt.name = QLatin1String(i->name);
620
 
                        patt.stream_restore_id = QLatin1String(pa_proplist_gets(i->proplist, "module-stream-restore.id"));
621
 
                        patt.normal_volume = i->volume;
622
 
 
623
 
                        // calculate the attenuated volume
624
 
                        pa_volume_t adj = static_cast<pa_volume_t>(PA_VOLUME_NORM * g.s.fOtherVolume);
625
 
                        pa_sw_cvolume_multiply_scalar(&patt.attenuated_volume, &i->volume, adj);
626
 
 
627
 
                        // set it on the sink input
628
 
                        pa_operation_unref(pa_context_set_sink_input_volume(c, i->index, &patt.attenuated_volume, NULL, NULL));
629
 
 
630
 
                        // store it
631
 
                        pas->qhVolumes[i->index] = patt;
632
 
                }
633
 
 
634
 
        } else if (eol < 0) {
635
 
                qWarning("PulseAudio: Sink input introspection error.");
636
 
        }
637
 
}
638
 
 
639
 
void PulseAudioSystem::restore_sink_input_list_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) {
640
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
641
 
 
642
 
        if (eol == 0) {
643
 
                // if we were tracking this specific sink previously
644
 
                if (pas->qhVolumes.contains(i->index)) {
645
 
                        // and if it has the attenuated volume we applied to it
646
 
                        if (pa_cvolume_equal(&i->volume, &pas->qhVolumes[i->index].attenuated_volume) != 0) {
647
 
                                // mark it as matched
648
 
                                pas->qlMatchedSinks.append(i->index);
649
 
 
650
 
                                // reset the volume to normal
651
 
                                pas->iRemainingOperations++;
652
 
                                pa_operation_unref(pa_context_set_sink_input_volume(c, i->index, &pas->qhVolumes[i->index].normal_volume, restore_volume_success_callback, pas));
653
 
                        }
654
 
 
655
 
                // otherwise, save for matching at the end of iteration
656
 
                } else {
657
 
                        QString restore_id = QLatin1String(pa_proplist_gets(i->proplist, "module-stream-restore.id"));
658
 
                        PulseAttenuation patt;
659
 
                        patt.index = i->index;
660
 
                        patt.normal_volume = i->volume;
661
 
                        pas->qhUnmatchedSinks[restore_id] = patt;
662
 
                }
663
 
 
664
 
        } else if (eol < 0) {
665
 
                qWarning("PulseAudio: Sink input introspection error.");
666
 
 
667
 
        } else {
668
 
                // build a list of missing streams by iterating our active list
669
 
                QHash<uint32_t, PulseAttenuation>::const_iterator it;
670
 
                for (it = pas->qhVolumes.constBegin(); it != pas->qhVolumes.constEnd(); ++it) {
671
 
                        // skip if previously matched
672
 
                        if (pas->qlMatchedSinks.contains(it.key())) {
673
 
                                continue;
674
 
                        }
675
 
 
676
 
                        // check if the restore id matches. the only case where this would
677
 
                        // happen is if the application was reopened during attenuation.
678
 
                        if (pas->qhUnmatchedSinks.contains(it.value().stream_restore_id)) {
679
 
                                PulseAttenuation active_sink = pas->qhUnmatchedSinks[it.value().stream_restore_id];
680
 
                                // if the volume wasn't changed from our attenuation
681
 
                                if (pa_cvolume_equal(&active_sink.normal_volume, &it.value().attenuated_volume) != 0) {
682
 
                                        // reset the volume to normal
683
 
                                        pas->iRemainingOperations++;
684
 
                                        pa_operation_unref(pa_context_set_sink_input_volume(c, active_sink.index, &it.value().normal_volume, restore_volume_success_callback, pas));
685
 
                                }
686
 
                                continue;
687
 
                        }
688
 
 
689
 
                        // at this point, we don't know what happened to the sink. add
690
 
                        // it to a list to check the stream restore database for.
691
 
                        pas->qhMissingSinks[it.value().stream_restore_id] = it.value();
692
 
                }
693
 
 
694
 
                // clean up
695
 
                pas->qlMatchedSinks.clear();
696
 
                pas->qhUnmatchedSinks.clear();
697
 
                pas->qhVolumes.clear();
698
 
 
699
 
                // if we had missing sinks, check the stream restore database
700
 
                // to see if we can find and update them.
701
 
                if (pas->qhMissingSinks.count() > 0) {
702
 
                        pas->iRemainingOperations++;
703
 
                        pa_operation_unref(pa_ext_stream_restore_read(c, stream_restore_read_callback, pas));
704
 
                }
705
 
 
706
 
                // trigger the volume completion callback;
707
 
                // necessary so that shutdown actions are called
708
 
                restore_volume_success_callback(c, 1, pas);
709
 
        }
710
 
}
711
 
 
712
 
void PulseAudioSystem::stream_restore_read_callback(pa_context *c, const pa_ext_stream_restore_info *i, int eol, void *userdata) {
713
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
714
 
 
715
 
        if (eol == 0) {
716
 
                QString name = QLatin1String(i->name);
717
 
 
718
 
                // were we looking for this restoration?
719
 
                if (pas->qhMissingSinks.contains(name)) {
720
 
                        // make sure it still has the volume we gave it
721
 
                        if (pa_cvolume_equal(&pas->qhMissingSinks[name].attenuated_volume, &i->volume) != 0) {
722
 
                                // update the stream restore record
723
 
                                pa_ext_stream_restore_info restore = *i;
724
 
                                restore.volume = pas->qhMissingSinks[name].normal_volume;
725
 
                                pas->iRemainingOperations++;
726
 
                                pa_operation_unref(pa_ext_stream_restore_write(c, PA_UPDATE_REPLACE, &restore, 1, 1, restore_volume_success_callback, pas));
727
 
                        }
728
 
 
729
 
                        pas->qhMissingSinks.remove(name);
730
 
                }
731
 
 
732
 
        } else if (eol < 0) {
733
 
                qWarning("PulseAudio: Couldn't read stream restore database.");
734
 
                pas->qhMissingSinks.clear();
735
 
 
736
 
        } else {
737
 
                // verify missing list is empty
738
 
                if (pas->qhMissingSinks.count() > 0) {
739
 
                        qWarning("PulseAudio: Failed to match %d stream(s).", pas->qhMissingSinks.count());
740
 
                        pas->qhMissingSinks.clear();
741
 
                }
742
 
 
743
 
                // trigger the volume completion callback;
744
 
                // necessary so that shutdown actions are called
745
 
                restore_volume_success_callback(c, 1, pas);
746
 
        }
747
 
}
748
 
 
749
 
void PulseAudioSystem::restore_volume_success_callback(pa_context *c, int success, void *userdata) {
750
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
751
 
 
752
 
        pas->iRemainingOperations--;
753
 
 
754
 
        // if there are no more pending volume adjustments and we're shutting down,
755
 
        // let the main thread know
756
 
        if (! pas->bRunning && pas->iRemainingOperations == 0) {
757
 
                pas->qwcWait.wakeAll();
758
 
        }
759
 
}
760
 
 
761
 
void PulseAudioSystem::query() {
762
 
        bSourceDone=bSinkDone=bServerDone = false;
763
 
        qhInput.clear();
764
 
        qhOutput.clear();
765
 
        qhEchoMap.clear();
766
 
        qhSpecMap.clear();
767
 
        qhChanMap.clear();
768
 
        qhInput.insert(QString(), tr("Default Input"));
769
 
        qhOutput.insert(QString(), tr("Default Output"));
770
 
        pa_operation_unref(pa_context_get_server_info(pacContext, server_callback, this));
771
 
        pa_operation_unref(pa_context_get_sink_info_list(pacContext, sink_callback, this));
772
 
        pa_operation_unref(pa_context_get_source_info_list(pacContext, source_callback, this));
773
 
        wakeup();
774
 
}
775
 
 
776
 
void PulseAudioSystem::setVolumes() {
777
 
        // set attenuation state and volumes
778
 
        if (bAttenuating) {
779
 
                // ensure the volume map is empty, otherwise it may be dangerous to change
780
 
                if (qhVolumes.empty()) {
781
 
                        // set the new per-application volumes and store the old ones
782
 
                        pa_operation_unref(pa_context_get_sink_input_info_list(pacContext, volume_sink_input_list_callback, this));
783
 
                }
784
 
        // clear attenuation state and restore normal volumes
785
 
        } else {
786
 
                iRemainingOperations++;
787
 
                pa_operation_unref(pa_context_get_sink_input_info_list(pacContext, restore_sink_input_list_callback, this));
788
 
        }
789
 
}
790
 
 
791
 
void PulseAudioSystem::contextCallback(pa_context *c) {
792
 
        Q_ASSERT(c == pacContext);
793
 
        switch (pa_context_get_state(c)) {
794
 
                case PA_CONTEXT_READY:
795
 
                        bPulseIsGood = true;
796
 
                        pa_operation_unref(pa_context_subscribe(pacContext, PA_SUBSCRIPTION_MASK_SOURCE, NULL, this));
797
 
                        pa_operation_unref(pa_context_subscribe(pacContext, PA_SUBSCRIPTION_MASK_SINK, NULL, this));
798
 
                        query();
799
 
                        break;
800
 
                case PA_CONTEXT_TERMINATED:
801
 
                        qWarning("PulseAudio: Forcibly disconnected from PulseAudio");
802
 
                        break;
803
 
                case PA_CONTEXT_FAILED:
804
 
                        qWarning("PulseAudio: Connection failure: %s", pa_strerror(pa_context_errno(c)));
805
 
                        break;
806
 
                default:
807
 
                        return;
808
 
        }
809
 
        qmWait.lock();
810
 
        qwcWait.wakeAll();
811
 
        qmWait.unlock();
812
 
}
813
 
 
814
 
PulseAudioInputRegistrar::PulseAudioInputRegistrar() : AudioInputRegistrar(QLatin1String("PulseAudio"), 10) {
815
 
}
816
 
 
817
 
AudioInput *PulseAudioInputRegistrar::create() {
818
 
        return new PulseAudioInput();
819
 
}
820
 
 
821
 
const QList<audioDevice> PulseAudioInputRegistrar::getDeviceChoices() {
822
 
        QList<audioDevice> qlReturn;
823
 
 
824
 
        QStringList qlInputDevs = pasys->qhInput.keys();
825
 
        qSort(qlInputDevs);
826
 
 
827
 
        if (qlInputDevs.contains(g.s.qsPulseAudioInput)) {
828
 
                qlInputDevs.removeAll(g.s.qsPulseAudioInput);
829
 
                qlInputDevs.prepend(g.s.qsPulseAudioInput);
830
 
        }
831
 
 
832
 
        foreach(const QString &dev, qlInputDevs) {
833
 
                qlReturn << audioDevice(pasys->qhInput.value(dev), dev);
834
 
        }
835
 
 
836
 
        return qlReturn;
837
 
}
838
 
 
839
 
void PulseAudioInputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) {
840
 
        s.qsPulseAudioInput = choice.toString();
841
 
}
842
 
 
843
 
bool PulseAudioInputRegistrar::canEcho(const QString &osys) const {
844
 
        return (osys == name);
845
 
}
846
 
 
847
 
PulseAudioOutputRegistrar::PulseAudioOutputRegistrar() : AudioOutputRegistrar(QLatin1String("PulseAudio"), 10) {
848
 
}
849
 
 
850
 
AudioOutput *PulseAudioOutputRegistrar::create() {
851
 
        return new PulseAudioOutput();
852
 
}
853
 
 
854
 
const QList<audioDevice> PulseAudioOutputRegistrar::getDeviceChoices() {
855
 
        QList<audioDevice> qlReturn;
856
 
 
857
 
        QStringList qlOutputDevs = pasys->qhOutput.keys();
858
 
        qSort(qlOutputDevs);
859
 
 
860
 
        if (qlOutputDevs.contains(g.s.qsPulseAudioOutput)) {
861
 
                qlOutputDevs.removeAll(g.s.qsPulseAudioOutput);
862
 
                qlOutputDevs.prepend(g.s.qsPulseAudioOutput);
863
 
        }
864
 
 
865
 
        foreach(const QString &dev, qlOutputDevs) {
866
 
                qlReturn << audioDevice(pasys->qhOutput.value(dev), dev);
867
 
        }
868
 
 
869
 
        return qlReturn;
870
 
}
871
 
 
872
 
void PulseAudioOutputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) {
873
 
        s.qsPulseAudioOutput = choice.toString();
874
 
}
875
 
 
876
 
bool PulseAudioOutputRegistrar::canMuteOthers() const {
877
 
        return true;
878
 
}
879
 
 
880
 
PulseAudioInput::PulseAudioInput() {
881
 
        memset(&pssMic, 0, sizeof(pssMic));
882
 
        memset(&pssEcho, 0, sizeof(pssEcho));
883
 
        bRunning = true;
884
 
        if (pasys)
885
 
                pasys->wakeup_lock();
886
 
}
887
 
 
888
 
PulseAudioInput::~PulseAudioInput() {
889
 
        bRunning = false;
890
 
        qmMutex.lock();
891
 
        qwcWait.wakeAll();
892
 
        qmMutex.unlock();
893
 
        wait();
894
 
        if (pasys)
895
 
                pasys->wakeup_lock();
896
 
}
897
 
 
898
 
void PulseAudioInput::run() {
899
 
        qmMutex.lock();
900
 
        while (bRunning)
901
 
                qwcWait.wait(&qmMutex);
902
 
        qmMutex.unlock();
903
 
}
904
 
 
905
 
PulseAudioOutput::PulseAudioOutput() {
906
 
        memset(&pss, 0, sizeof(pss));
907
 
        memset(&pcm, 0, sizeof(pcm));
908
 
        bRunning = true;
909
 
        if (pasys)
910
 
                pasys->wakeup_lock();
911
 
}
912
 
 
913
 
PulseAudioOutput::~PulseAudioOutput() {
914
 
        bRunning = false;
915
 
        qmMutex.lock();
916
 
        qwcWait.wakeAll();
917
 
        qmMutex.unlock();
918
 
        wait();
919
 
        if (pasys)
920
 
                pasys->wakeup_lock();
921
 
}
922
 
 
923
 
void PulseAudioOutput::run() {
924
 
        qmMutex.lock();
925
 
        while (bRunning)
926
 
                qwcWait.wait(&qmMutex);
927
 
        qmMutex.unlock();
928
 
}