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

« back to all changes in this revision

Viewing changes to .pc/23-fix-pulseaudio-segfault-pt1.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;
459
 
        pa_stream_peek(s, &data, &length);
460
 
 
461
 
        AudioInputPtr ai = g.ai;
462
 
        PulseAudioInput *pai = dynamic_cast<PulseAudioInput *>(ai.get());
463
 
        if (! pai) {
464
 
                pa_stream_drop(s);
465
 
                pas->wakeup();
466
 
                return;
467
 
        }
468
 
 
469
 
        const pa_sample_spec *pss = pa_stream_get_sample_spec(s);
470
 
 
471
 
        if (s == pas->pasInput) {
472
 
                if (!pa_sample_spec_equal(pss, &pai->pssMic)) {
473
 
                        pai->pssMic = *pss;
474
 
                        pai->iMicFreq = pss->rate;
475
 
                        pai->iMicChannels = pss->channels;
476
 
                        if (pss->format == PA_SAMPLE_FLOAT32NE)
477
 
                                pai->eMicFormat = PulseAudioInput::SampleFloat;
478
 
                        else
479
 
                                pai->eMicFormat = PulseAudioInput::SampleShort;
480
 
                        pai->initializeMixer();
481
 
                }
482
 
                pai->addMic(data, length / pai->iMicSampleSize);
483
 
        } else if (s == pas->pasSpeaker) {
484
 
                if (!pa_sample_spec_equal(pss, &pai->pssEcho)) {
485
 
                        pai->pssEcho = *pss;
486
 
                        pai->iEchoFreq = pss->rate;
487
 
                        pai->iEchoChannels = pss->channels;
488
 
                        if (pss->format == PA_SAMPLE_FLOAT32NE)
489
 
                                pai->eEchoFormat = PulseAudioInput::SampleFloat;
490
 
                        else
491
 
                                pai->eEchoFormat = PulseAudioInput::SampleShort;
492
 
                        pai->initializeMixer();
493
 
                }
494
 
                pai->addEcho(data, length / pai->iEchoSampleSize);
495
 
        }
496
 
 
497
 
        pa_stream_drop(s);
498
 
}
499
 
 
500
 
void PulseAudioSystem::write_callback(pa_stream *s, size_t bytes, void *userdata) {
501
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
502
 
        Q_ASSERT(s == pas->pasOutput);
503
 
 
504
 
        AudioOutputPtr ao = g.ao;
505
 
        PulseAudioOutput *pao = dynamic_cast<PulseAudioOutput *>(ao.get());
506
 
 
507
 
        unsigned char buffer[bytes];
508
 
 
509
 
        if (! pao) {
510
 
                // Transitioning, but most likely transitions back, so just zero.
511
 
                memset(buffer, 0, bytes);
512
 
                pa_stream_write(s, buffer, bytes, NULL, 0, PA_SEEK_RELATIVE);
513
 
                pas->wakeup();
514
 
                return;
515
 
        }
516
 
 
517
 
        const pa_sample_spec *pss = pa_stream_get_sample_spec(s);
518
 
        const pa_channel_map *pcm = pa_stream_get_channel_map(pas->pasOutput);
519
 
        if (!pa_sample_spec_equal(pss, &pao->pss) || !pa_channel_map_equal(pcm, &pao->pcm)) {
520
 
                pao->pss = *pss;
521
 
                pao->pcm = *pcm;
522
 
                if (pss->format == PA_SAMPLE_FLOAT32NE)
523
 
                        pao->eSampleFormat = PulseAudioOutput::SampleFloat;
524
 
                else
525
 
                        pao->eSampleFormat = PulseAudioOutput::SampleShort;
526
 
                pao->iMixerFreq = pss->rate;
527
 
                pao->iChannels = pss->channels;
528
 
                unsigned int chanmasks[pss->channels];
529
 
                for (int i=0;i<pss->channels;++i) {
530
 
                        unsigned int cm = 0;
531
 
                        switch (pcm->map[i]) {
532
 
                                case PA_CHANNEL_POSITION_LEFT:
533
 
                                        cm = SPEAKER_FRONT_LEFT;
534
 
                                        break;
535
 
                                case PA_CHANNEL_POSITION_RIGHT:
536
 
                                        cm = SPEAKER_FRONT_RIGHT;
537
 
                                        break;
538
 
                                case PA_CHANNEL_POSITION_CENTER:
539
 
                                        cm = SPEAKER_FRONT_CENTER;
540
 
                                        break;
541
 
                                case PA_CHANNEL_POSITION_REAR_LEFT:
542
 
                                        cm = SPEAKER_BACK_LEFT;
543
 
                                        break;
544
 
                                case PA_CHANNEL_POSITION_REAR_RIGHT:
545
 
                                        cm = SPEAKER_BACK_RIGHT;
546
 
                                        break;
547
 
                                case PA_CHANNEL_POSITION_REAR_CENTER:
548
 
                                        cm = SPEAKER_BACK_CENTER;
549
 
                                        break;
550
 
                                case PA_CHANNEL_POSITION_LFE:
551
 
                                        cm = SPEAKER_LOW_FREQUENCY;
552
 
                                        break;
553
 
                                case PA_CHANNEL_POSITION_SIDE_LEFT:
554
 
                                        cm = SPEAKER_SIDE_LEFT;
555
 
                                        break;
556
 
                                case PA_CHANNEL_POSITION_SIDE_RIGHT:
557
 
                                        cm = SPEAKER_SIDE_RIGHT;
558
 
                                        break;
559
 
                                case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
560
 
                                        cm = SPEAKER_FRONT_LEFT_OF_CENTER;
561
 
                                        break;
562
 
                                case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
563
 
                                        cm = SPEAKER_FRONT_RIGHT_OF_CENTER;
564
 
                                        break;
565
 
                                default:
566
 
                                        cm = 0;
567
 
                                        break;
568
 
                        }
569
 
                        chanmasks[i] = cm;
570
 
                }
571
 
                pao->initializeMixer(chanmasks);
572
 
        }
573
 
 
574
 
        const unsigned int iSampleSize = pao->iSampleSize;
575
 
        const unsigned int samples = bytes / iSampleSize;
576
 
        bool oldAttenuation = pas->bAttenuating;
577
 
 
578
 
        // do we have some mixed output?
579
 
        if (pao->mix(buffer, samples)) {
580
 
                // attenuate if instructed to or it's in settings
581
 
                pas->bAttenuating = (g.bAttenuateOthers || g.s.bAttenuateOthers);
582
 
 
583
 
        } else {
584
 
                memset(buffer, 0, bytes);
585
 
 
586
 
                // attenuate if intructed to (self-activated)
587
 
                pas->bAttenuating = g.bAttenuateOthers;
588
 
        }
589
 
 
590
 
        // if the attenuation state has changed
591
 
        if (oldAttenuation != pas->bAttenuating) {
592
 
                pas->setVolumes();
593
 
        }
594
 
 
595
 
        pa_stream_write(s, buffer, iSampleSize * samples, NULL, 0, PA_SEEK_RELATIVE);
596
 
}
597
 
 
598
 
void PulseAudioSystem::volume_sink_input_list_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) {
599
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
600
 
 
601
 
        if (eol == 0) {
602
 
                // ensure we're not attenuating ourselves!
603
 
                if (strcmp(i->name, mumble_sink_input) != 0) {
604
 
                        // create a new entry
605
 
                        PulseAttenuation patt;
606
 
                        patt.index = i->index;
607
 
                        patt.name = QLatin1String(i->name);
608
 
                        patt.stream_restore_id = QLatin1String(pa_proplist_gets(i->proplist, "module-stream-restore.id"));
609
 
                        patt.normal_volume = i->volume;
610
 
 
611
 
                        // calculate the attenuated volume
612
 
                        pa_volume_t adj = static_cast<pa_volume_t>(PA_VOLUME_NORM * g.s.fOtherVolume);
613
 
                        pa_sw_cvolume_multiply_scalar(&patt.attenuated_volume, &i->volume, adj);
614
 
 
615
 
                        // set it on the sink input
616
 
                        pa_operation_unref(pa_context_set_sink_input_volume(c, i->index, &patt.attenuated_volume, NULL, NULL));
617
 
 
618
 
                        // store it
619
 
                        pas->qhVolumes[i->index] = patt;
620
 
                }
621
 
 
622
 
        } else if (eol < 0) {
623
 
                qWarning("PulseAudio: Sink input introspection error.");
624
 
        }
625
 
}
626
 
 
627
 
void PulseAudioSystem::restore_sink_input_list_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) {
628
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
629
 
 
630
 
        if (eol == 0) {
631
 
                // if we were tracking this specific sink previously
632
 
                if (pas->qhVolumes.contains(i->index)) {
633
 
                        // and if it has the attenuated volume we applied to it
634
 
                        if (pa_cvolume_equal(&i->volume, &pas->qhVolumes[i->index].attenuated_volume) != 0) {
635
 
                                // mark it as matched
636
 
                                pas->qlMatchedSinks.append(i->index);
637
 
 
638
 
                                // reset the volume to normal
639
 
                                pas->iRemainingOperations++;
640
 
                                pa_operation_unref(pa_context_set_sink_input_volume(c, i->index, &pas->qhVolumes[i->index].normal_volume, restore_volume_success_callback, pas));
641
 
                        }
642
 
 
643
 
                // otherwise, save for matching at the end of iteration
644
 
                } else {
645
 
                        QString restore_id = QLatin1String(pa_proplist_gets(i->proplist, "module-stream-restore.id"));
646
 
                        PulseAttenuation patt;
647
 
                        patt.index = i->index;
648
 
                        patt.normal_volume = i->volume;
649
 
                        pas->qhUnmatchedSinks[restore_id] = patt;
650
 
                }
651
 
 
652
 
        } else if (eol < 0) {
653
 
                qWarning("PulseAudio: Sink input introspection error.");
654
 
 
655
 
        } else {
656
 
                // build a list of missing streams by iterating our active list
657
 
                QHash<uint32_t, PulseAttenuation>::const_iterator it;
658
 
                for (it = pas->qhVolumes.constBegin(); it != pas->qhVolumes.constEnd(); ++it) {
659
 
                        // skip if previously matched
660
 
                        if (pas->qlMatchedSinks.contains(it.key())) {
661
 
                                continue;
662
 
                        }
663
 
 
664
 
                        // check if the restore id matches. the only case where this would
665
 
                        // happen is if the application was reopened during attenuation.
666
 
                        if (pas->qhUnmatchedSinks.contains(it.value().stream_restore_id)) {
667
 
                                PulseAttenuation active_sink = pas->qhUnmatchedSinks[it.value().stream_restore_id];
668
 
                                // if the volume wasn't changed from our attenuation
669
 
                                if (pa_cvolume_equal(&active_sink.normal_volume, &it.value().attenuated_volume) != 0) {
670
 
                                        // reset the volume to normal
671
 
                                        pas->iRemainingOperations++;
672
 
                                        pa_operation_unref(pa_context_set_sink_input_volume(c, active_sink.index, &it.value().normal_volume, restore_volume_success_callback, pas));
673
 
                                }
674
 
                                continue;
675
 
                        }
676
 
 
677
 
                        // at this point, we don't know what happened to the sink. add
678
 
                        // it to a list to check the stream restore database for.
679
 
                        pas->qhMissingSinks[it.value().stream_restore_id] = it.value();
680
 
                }
681
 
 
682
 
                // clean up
683
 
                pas->qlMatchedSinks.clear();
684
 
                pas->qhUnmatchedSinks.clear();
685
 
                pas->qhVolumes.clear();
686
 
 
687
 
                // if we had missing sinks, check the stream restore database
688
 
                // to see if we can find and update them.
689
 
                if (pas->qhMissingSinks.count() > 0) {
690
 
                        pas->iRemainingOperations++;
691
 
                        pa_operation_unref(pa_ext_stream_restore_read(c, stream_restore_read_callback, pas));
692
 
                }
693
 
 
694
 
                // trigger the volume completion callback;
695
 
                // necessary so that shutdown actions are called
696
 
                restore_volume_success_callback(c, 1, pas);
697
 
        }
698
 
}
699
 
 
700
 
void PulseAudioSystem::stream_restore_read_callback(pa_context *c, const pa_ext_stream_restore_info *i, int eol, void *userdata) {
701
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
702
 
 
703
 
        if (eol == 0) {
704
 
                QString name = QLatin1String(i->name);
705
 
 
706
 
                // were we looking for this restoration?
707
 
                if (pas->qhMissingSinks.contains(name)) {
708
 
                        // make sure it still has the volume we gave it
709
 
                        if (pa_cvolume_equal(&pas->qhMissingSinks[name].attenuated_volume, &i->volume) != 0) {
710
 
                                // update the stream restore record
711
 
                                pa_ext_stream_restore_info restore = *i;
712
 
                                restore.volume = pas->qhMissingSinks[name].normal_volume;
713
 
                                pas->iRemainingOperations++;
714
 
                                pa_operation_unref(pa_ext_stream_restore_write(c, PA_UPDATE_REPLACE, &restore, 1, 1, restore_volume_success_callback, pas));
715
 
                        }
716
 
 
717
 
                        pas->qhMissingSinks.remove(name);
718
 
                }
719
 
 
720
 
        } else if (eol < 0) {
721
 
                qWarning("PulseAudio: Couldn't read stream restore database.");
722
 
                pas->qhMissingSinks.clear();
723
 
 
724
 
        } else {
725
 
                // verify missing list is empty
726
 
                if (pas->qhMissingSinks.count() > 0) {
727
 
                        qWarning("PulseAudio: Failed to match %d stream(s).", pas->qhMissingSinks.count());
728
 
                        pas->qhMissingSinks.clear();
729
 
                }
730
 
 
731
 
                // trigger the volume completion callback;
732
 
                // necessary so that shutdown actions are called
733
 
                restore_volume_success_callback(c, 1, pas);
734
 
        }
735
 
}
736
 
 
737
 
void PulseAudioSystem::restore_volume_success_callback(pa_context *c, int success, void *userdata) {
738
 
        PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
739
 
 
740
 
        pas->iRemainingOperations--;
741
 
 
742
 
        // if there are no more pending volume adjustments and we're shutting down,
743
 
        // let the main thread know
744
 
        if (! pas->bRunning && pas->iRemainingOperations == 0) {
745
 
                pas->qwcWait.wakeAll();
746
 
        }
747
 
}
748
 
 
749
 
void PulseAudioSystem::query() {
750
 
        bSourceDone=bSinkDone=bServerDone = false;
751
 
        qhInput.clear();
752
 
        qhOutput.clear();
753
 
        qhEchoMap.clear();
754
 
        qhSpecMap.clear();
755
 
        qhChanMap.clear();
756
 
        qhInput.insert(QString(), tr("Default Input"));
757
 
        qhOutput.insert(QString(), tr("Default Output"));
758
 
        pa_operation_unref(pa_context_get_server_info(pacContext, server_callback, this));
759
 
        pa_operation_unref(pa_context_get_sink_info_list(pacContext, sink_callback, this));
760
 
        pa_operation_unref(pa_context_get_source_info_list(pacContext, source_callback, this));
761
 
        wakeup();
762
 
}
763
 
 
764
 
void PulseAudioSystem::setVolumes() {
765
 
        // set attenuation state and volumes
766
 
        if (bAttenuating) {
767
 
                // ensure the volume map is empty, otherwise it may be dangerous to change
768
 
                if (qhVolumes.empty()) {
769
 
                        // set the new per-application volumes and store the old ones
770
 
                        pa_operation_unref(pa_context_get_sink_input_info_list(pacContext, volume_sink_input_list_callback, this));
771
 
                }
772
 
        // clear attenuation state and restore normal volumes
773
 
        } else {
774
 
                iRemainingOperations++;
775
 
                pa_operation_unref(pa_context_get_sink_input_info_list(pacContext, restore_sink_input_list_callback, this));
776
 
        }
777
 
}
778
 
 
779
 
void PulseAudioSystem::contextCallback(pa_context *c) {
780
 
        Q_ASSERT(c == pacContext);
781
 
        switch (pa_context_get_state(c)) {
782
 
                case PA_CONTEXT_READY:
783
 
                        bPulseIsGood = true;
784
 
                        pa_operation_unref(pa_context_subscribe(pacContext, PA_SUBSCRIPTION_MASK_SOURCE, NULL, this));
785
 
                        pa_operation_unref(pa_context_subscribe(pacContext, PA_SUBSCRIPTION_MASK_SINK, NULL, this));
786
 
                        query();
787
 
                        break;
788
 
                case PA_CONTEXT_TERMINATED:
789
 
                        qWarning("PulseAudio: Forcibly disconnected from PulseAudio");
790
 
                        break;
791
 
                case PA_CONTEXT_FAILED:
792
 
                        qWarning("PulseAudio: Connection failure: %s", pa_strerror(pa_context_errno(c)));
793
 
                        break;
794
 
                default:
795
 
                        return;
796
 
        }
797
 
        qmWait.lock();
798
 
        qwcWait.wakeAll();
799
 
        qmWait.unlock();
800
 
}
801
 
 
802
 
PulseAudioInputRegistrar::PulseAudioInputRegistrar() : AudioInputRegistrar(QLatin1String("PulseAudio"), 10) {
803
 
}
804
 
 
805
 
AudioInput *PulseAudioInputRegistrar::create() {
806
 
        return new PulseAudioInput();
807
 
}
808
 
 
809
 
const QList<audioDevice> PulseAudioInputRegistrar::getDeviceChoices() {
810
 
        QList<audioDevice> qlReturn;
811
 
 
812
 
        QStringList qlInputDevs = pasys->qhInput.keys();
813
 
        qSort(qlInputDevs);
814
 
 
815
 
        if (qlInputDevs.contains(g.s.qsPulseAudioInput)) {
816
 
                qlInputDevs.removeAll(g.s.qsPulseAudioInput);
817
 
                qlInputDevs.prepend(g.s.qsPulseAudioInput);
818
 
        }
819
 
 
820
 
        foreach(const QString &dev, qlInputDevs) {
821
 
                qlReturn << audioDevice(pasys->qhInput.value(dev), dev);
822
 
        }
823
 
 
824
 
        return qlReturn;
825
 
}
826
 
 
827
 
void PulseAudioInputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) {
828
 
        s.qsPulseAudioInput = choice.toString();
829
 
}
830
 
 
831
 
bool PulseAudioInputRegistrar::canEcho(const QString &osys) const {
832
 
        return (osys == name);
833
 
}
834
 
 
835
 
PulseAudioOutputRegistrar::PulseAudioOutputRegistrar() : AudioOutputRegistrar(QLatin1String("PulseAudio"), 10) {
836
 
}
837
 
 
838
 
AudioOutput *PulseAudioOutputRegistrar::create() {
839
 
        return new PulseAudioOutput();
840
 
}
841
 
 
842
 
const QList<audioDevice> PulseAudioOutputRegistrar::getDeviceChoices() {
843
 
        QList<audioDevice> qlReturn;
844
 
 
845
 
        QStringList qlOutputDevs = pasys->qhOutput.keys();
846
 
        qSort(qlOutputDevs);
847
 
 
848
 
        if (qlOutputDevs.contains(g.s.qsPulseAudioOutput)) {
849
 
                qlOutputDevs.removeAll(g.s.qsPulseAudioOutput);
850
 
                qlOutputDevs.prepend(g.s.qsPulseAudioOutput);
851
 
        }
852
 
 
853
 
        foreach(const QString &dev, qlOutputDevs) {
854
 
                qlReturn << audioDevice(pasys->qhOutput.value(dev), dev);
855
 
        }
856
 
 
857
 
        return qlReturn;
858
 
}
859
 
 
860
 
void PulseAudioOutputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) {
861
 
        s.qsPulseAudioOutput = choice.toString();
862
 
}
863
 
 
864
 
bool PulseAudioOutputRegistrar::canMuteOthers() const {
865
 
        return true;
866
 
}
867
 
 
868
 
PulseAudioInput::PulseAudioInput() {
869
 
        memset(&pssMic, 0, sizeof(pssMic));
870
 
        memset(&pssEcho, 0, sizeof(pssEcho));
871
 
        bRunning = true;
872
 
        if (pasys)
873
 
                pasys->wakeup_lock();
874
 
}
875
 
 
876
 
PulseAudioInput::~PulseAudioInput() {
877
 
        bRunning = false;
878
 
        qmMutex.lock();
879
 
        qwcWait.wakeAll();
880
 
        qmMutex.unlock();
881
 
        wait();
882
 
        if (pasys)
883
 
                pasys->wakeup_lock();
884
 
}
885
 
 
886
 
void PulseAudioInput::run() {
887
 
        qmMutex.lock();
888
 
        while (bRunning)
889
 
                qwcWait.wait(&qmMutex);
890
 
        qmMutex.unlock();
891
 
}
892
 
 
893
 
PulseAudioOutput::PulseAudioOutput() {
894
 
        memset(&pss, 0, sizeof(pss));
895
 
        memset(&pcm, 0, sizeof(pcm));
896
 
        bRunning = true;
897
 
        if (pasys)
898
 
                pasys->wakeup_lock();
899
 
}
900
 
 
901
 
PulseAudioOutput::~PulseAudioOutput() {
902
 
        bRunning = false;
903
 
        qmMutex.lock();
904
 
        qwcWait.wakeAll();
905
 
        qmMutex.unlock();
906
 
        wait();
907
 
        if (pasys)
908
 
                pasys->wakeup_lock();
909
 
}
910
 
 
911
 
void PulseAudioOutput::run() {
912
 
        qmMutex.lock();
913
 
        while (bRunning)
914
 
                qwcWait.wait(&qmMutex);
915
 
        qmMutex.unlock();
916
 
}