~binli/ubuntu/vivid/pulseaudio/fix-atoi

« back to all changes in this revision

Viewing changes to src/modules/module-rescue-streams.c

  • Committer: Bin Li
  • Date: 2016-07-05 03:39:49 UTC
  • Revision ID: bin.li@canonical.com-20160705033949-rz4p9x4hbi2danxk
first version based on pulseaudio_6.0-0ubuntu9.27

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***
 
2
  This file is part of PulseAudio.
 
3
 
 
4
  Copyright 2006 Lennart Poettering
 
5
 
 
6
  PulseAudio is free software; you can redistribute it and/or modify
 
7
  it under the terms of the GNU Lesser General Public License as published
 
8
  by the Free Software Foundation; either version 2.1 of the License,
 
9
  or (at your option) any later version.
 
10
 
 
11
  PulseAudio is distributed in the hope that it will be useful, but
 
12
  WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 
14
  General Public License for more details.
 
15
 
 
16
  You should have received a copy of the GNU Lesser General Public License
 
17
  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
 
18
***/
 
19
 
 
20
#ifdef HAVE_CONFIG_H
 
21
#include <config.h>
 
22
#endif
 
23
 
 
24
#include <pulse/xmalloc.h>
 
25
 
 
26
#include <pulsecore/core.h>
 
27
#include <pulsecore/sink-input.h>
 
28
#include <pulsecore/source-output.h>
 
29
#include <pulsecore/modargs.h>
 
30
#include <pulsecore/log.h>
 
31
#include <pulsecore/namereg.h>
 
32
#include <pulsecore/core-util.h>
 
33
 
 
34
#include "module-rescue-streams-symdef.h"
 
35
 
 
36
PA_MODULE_AUTHOR("Lennart Poettering");
 
37
PA_MODULE_DESCRIPTION("When a sink/source is removed, try to move its streams to the default sink/source");
 
38
PA_MODULE_VERSION(PACKAGE_VERSION);
 
39
PA_MODULE_LOAD_ONCE(true);
 
40
 
 
41
static const char* const valid_modargs[] = {
 
42
    NULL,
 
43
};
 
44
 
 
45
struct userdata {
 
46
    pa_hook_slot
 
47
        *sink_unlink_slot,
 
48
        *source_unlink_slot,
 
49
        *sink_input_move_fail_slot,
 
50
        *source_output_move_fail_slot;
 
51
};
 
52
 
 
53
static pa_source* find_source_from_port(pa_core *c, pa_device_port *port) {
 
54
    pa_source *target;
 
55
    uint32_t idx;
 
56
    void *state;
 
57
    pa_device_port *p;
 
58
 
 
59
    if (!port)
 
60
        return NULL;
 
61
 
 
62
    PA_IDXSET_FOREACH(target, c->sources, idx)
 
63
        PA_HASHMAP_FOREACH(p, target->ports, state)
 
64
            if (port == p)
 
65
                return target;
 
66
 
 
67
    return NULL;
 
68
}
 
69
 
 
70
static pa_sink* find_sink_from_port(pa_core *c, pa_device_port *port) {
 
71
    pa_sink *target;
 
72
    uint32_t idx;
 
73
    void *state;
 
74
    pa_device_port *p;
 
75
 
 
76
    if (!port)
 
77
        return NULL;
 
78
 
 
79
    PA_IDXSET_FOREACH(target, c->sinks, idx)
 
80
        PA_HASHMAP_FOREACH(p, target->ports, state)
 
81
            if (port == p)
 
82
                return target;
 
83
 
 
84
    return NULL;
 
85
}
 
86
 
 
87
static void build_group_ports(pa_hashmap *g_ports, pa_hashmap *s_ports) {
 
88
    void *state;
 
89
    pa_device_port *p;
 
90
 
 
91
    if (!g_ports || !s_ports)
 
92
        return;
 
93
 
 
94
    PA_HASHMAP_FOREACH(p, s_ports, state)
 
95
        pa_hashmap_put(g_ports, p, p);
 
96
}
 
97
 
 
98
static pa_sink* find_evacuation_sink(pa_core *c, pa_sink_input *i, pa_sink *skip) {
 
99
    pa_sink *target, *def, *fb_sink = NULL;
 
100
    uint32_t idx;
 
101
    pa_hashmap *all_ports;
 
102
    pa_device_port *best_port;
 
103
 
 
104
    pa_assert(c);
 
105
    pa_assert(i);
 
106
 
 
107
    def = pa_namereg_get_default_sink(c, skip);
 
108
 
 
109
    if (def && pa_sink_input_may_move_to(i, def))
 
110
        return def;
 
111
 
 
112
    all_ports = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
113
 
 
114
    PA_IDXSET_FOREACH(target, c->sinks, idx) {
 
115
        if (target == def)
 
116
            continue;
 
117
 
 
118
        if (target == skip)
 
119
            continue;
 
120
 
 
121
        if (!PA_SINK_IS_LINKED(pa_sink_get_state(target)))
 
122
            continue;
 
123
 
 
124
        if (!pa_sink_input_may_move_to(i, target))
 
125
            continue;
 
126
 
 
127
        if (!fb_sink)
 
128
            fb_sink = target;
 
129
 
 
130
        build_group_ports(all_ports, target->ports);
 
131
    }
 
132
 
 
133
    best_port = pa_device_port_find_best(all_ports);
 
134
 
 
135
    pa_hashmap_free(all_ports);
 
136
 
 
137
    if (best_port)
 
138
        target = find_sink_from_port(c, best_port);
 
139
    else
 
140
        target = fb_sink;
 
141
 
 
142
    if (!target)
 
143
        pa_log_debug("No evacuation sink found.");
 
144
 
 
145
    return target;
 
146
}
 
147
 
 
148
static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
 
149
    pa_sink_input *i;
 
150
    uint32_t idx;
 
151
 
 
152
    pa_assert(c);
 
153
    pa_assert(sink);
 
154
 
 
155
    /* There's no point in doing anything if the core is shut down anyway */
 
156
    if (c->state == PA_CORE_SHUTDOWN)
 
157
        return PA_HOOK_OK;
 
158
 
 
159
    if (pa_idxset_size(sink->inputs) <= 0) {
 
160
        pa_log_debug("No sink inputs to move away.");
 
161
        return PA_HOOK_OK;
 
162
    }
 
163
 
 
164
    PA_IDXSET_FOREACH(i, sink->inputs, idx) {
 
165
        pa_sink *target;
 
166
 
 
167
        if (!(target = find_evacuation_sink(c, i, sink)))
 
168
            continue;
 
169
 
 
170
        if (pa_sink_input_move_to(i, target, false) < 0)
 
171
            pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index,
 
172
                        pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
 
173
        else
 
174
            pa_log_info("Successfully moved sink input %u \"%s\" to %s.", i->index,
 
175
                        pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
 
176
    }
 
177
 
 
178
    return PA_HOOK_OK;
 
179
}
 
180
 
 
181
static pa_hook_result_t sink_input_move_fail_hook_callback(pa_core *c, pa_sink_input *i, void *userdata) {
 
182
    pa_sink *target;
 
183
 
 
184
    pa_assert(c);
 
185
    pa_assert(i);
 
186
 
 
187
    /* There's no point in doing anything if the core is shut down anyway */
 
188
    if (c->state == PA_CORE_SHUTDOWN)
 
189
        return PA_HOOK_OK;
 
190
 
 
191
    if (!(target = find_evacuation_sink(c, i, NULL)))
 
192
        return PA_HOOK_OK;
 
193
 
 
194
    if (pa_sink_input_finish_move(i, target, false) < 0) {
 
195
        pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index,
 
196
                        pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
 
197
        return PA_HOOK_OK;
 
198
 
 
199
    } else {
 
200
        pa_log_info("Successfully moved sink input %u \"%s\" to %s.", i->index,
 
201
                    pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
 
202
        return PA_HOOK_STOP;
 
203
    }
 
204
}
 
205
 
 
206
static pa_source* find_evacuation_source(pa_core *c, pa_source_output *o, pa_source *skip) {
 
207
    pa_source *target, *def, *fb_source = NULL;
 
208
    uint32_t idx;
 
209
    pa_hashmap *all_ports;
 
210
    pa_device_port *best_port;
 
211
 
 
212
    pa_assert(c);
 
213
    pa_assert(o);
 
214
 
 
215
    def = pa_namereg_get_default_source(c);
 
216
 
 
217
    if (def && def != skip && pa_source_output_may_move_to(o, def))
 
218
        return def;
 
219
 
 
220
    all_ports = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
221
 
 
222
    PA_IDXSET_FOREACH(target, c->sources, idx) {
 
223
        if (target == def)
 
224
            continue;
 
225
 
 
226
        if (target == skip)
 
227
            continue;
 
228
 
 
229
        if (skip && !target->monitor_of != !skip->monitor_of)
 
230
            continue;
 
231
 
 
232
        if (!PA_SOURCE_IS_LINKED(pa_source_get_state(target)))
 
233
            continue;
 
234
 
 
235
        if (!pa_source_output_may_move_to(o, target))
 
236
            continue;
 
237
 
 
238
        if (!fb_source)
 
239
            fb_source = target;
 
240
 
 
241
        build_group_ports(all_ports, target->ports);
 
242
    }
 
243
 
 
244
    best_port = pa_device_port_find_best(all_ports);
 
245
 
 
246
    pa_hashmap_free(all_ports);
 
247
 
 
248
    if (best_port)
 
249
        target = find_source_from_port(c, best_port);
 
250
    else
 
251
        target = fb_source;
 
252
 
 
253
    if (!target)
 
254
        pa_log_debug("No evacuation source found.");
 
255
 
 
256
    return target;
 
257
}
 
258
 
 
259
static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, void* userdata) {
 
260
    pa_source_output *o;
 
261
    uint32_t idx;
 
262
 
 
263
    pa_assert(c);
 
264
    pa_assert(source);
 
265
 
 
266
    /* There's no point in doing anything if the core is shut down anyway */
 
267
    if (c->state == PA_CORE_SHUTDOWN)
 
268
        return PA_HOOK_OK;
 
269
 
 
270
    if (pa_idxset_size(source->outputs) <= 0) {
 
271
        pa_log_debug("No source outputs to move away.");
 
272
        return PA_HOOK_OK;
 
273
    }
 
274
 
 
275
    PA_IDXSET_FOREACH(o, source->outputs, idx) {
 
276
        pa_source *target;
 
277
 
 
278
        if (!(target = find_evacuation_source(c, o, source)))
 
279
            continue;
 
280
 
 
281
        if (pa_source_output_move_to(o, target, false) < 0)
 
282
            pa_log_info("Failed to move source output %u \"%s\" to %s.", o->index,
 
283
                        pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), target->name);
 
284
        else
 
285
            pa_log_info("Successfully moved source output %u \"%s\" to %s.", o->index,
 
286
                        pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), target->name);
 
287
    }
 
288
 
 
289
    return PA_HOOK_OK;
 
290
}
 
291
 
 
292
static pa_hook_result_t source_output_move_fail_hook_callback(pa_core *c, pa_source_output *i, void *userdata) {
 
293
    pa_source *target;
 
294
 
 
295
    pa_assert(c);
 
296
    pa_assert(i);
 
297
 
 
298
    /* There's no point in doing anything if the core is shut down anyway */
 
299
    if (c->state == PA_CORE_SHUTDOWN)
 
300
        return PA_HOOK_OK;
 
301
 
 
302
    if (!(target = find_evacuation_source(c, i, NULL)))
 
303
        return PA_HOOK_OK;
 
304
 
 
305
    if (pa_source_output_finish_move(i, target, false) < 0) {
 
306
        pa_log_info("Failed to move source input %u \"%s\" to %s.", i->index,
 
307
                        pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
 
308
        return PA_HOOK_OK;
 
309
 
 
310
    } else {
 
311
        pa_log_info("Successfully moved source input %u \"%s\" to %s.", i->index,
 
312
                    pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
 
313
        return PA_HOOK_STOP;
 
314
    }
 
315
}
 
316
 
 
317
int pa__init(pa_module*m) {
 
318
    pa_modargs *ma;
 
319
    struct userdata *u;
 
320
 
 
321
    pa_assert(m);
 
322
 
 
323
    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
 
324
        pa_log("Failed to parse module arguments");
 
325
        return -1;
 
326
    }
 
327
 
 
328
    m->userdata = u = pa_xnew(struct userdata, 1);
 
329
 
 
330
    /* A little bit later than module-stream-restore, module-intended-roles... */
 
331
    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_unlink_hook_callback, u);
 
332
    u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) source_unlink_hook_callback, u);
 
333
 
 
334
    u->sink_input_move_fail_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_input_move_fail_hook_callback, u);
 
335
    u->source_output_move_fail_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], PA_HOOK_LATE+20, (pa_hook_cb_t) source_output_move_fail_hook_callback, u);
 
336
 
 
337
    pa_modargs_free(ma);
 
338
    return 0;
 
339
}
 
340
 
 
341
void pa__done(pa_module*m) {
 
342
    struct userdata *u;
 
343
 
 
344
    pa_assert(m);
 
345
 
 
346
    if (!(u = m->userdata))
 
347
        return;
 
348
 
 
349
    if (u->sink_unlink_slot)
 
350
        pa_hook_slot_free(u->sink_unlink_slot);
 
351
    if (u->source_unlink_slot)
 
352
        pa_hook_slot_free(u->source_unlink_slot);
 
353
 
 
354
    if (u->sink_input_move_fail_slot)
 
355
        pa_hook_slot_free(u->sink_input_move_fail_slot);
 
356
    if (u->source_output_move_fail_slot)
 
357
        pa_hook_slot_free(u->source_output_move_fail_slot);
 
358
 
 
359
    pa_xfree(u);
 
360
}