Merge "ia32: add EFI support by enabling partition type" into tizen
[scm/bb/meta-tizen.git] / recipes-multimedia / pulseaudio / pulseaudio_5.0 / 0103-audio-groups-main-volume-policy-volume-api-Various-f.patch
1 From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
2 Date: Tue, 17 Jun 2014 19:45:45 +0300
3 Subject: audio-groups, main-volume-policy, volume-api: Various fixes
4
5 Sorry, this is a huge unreviewable commit. Contained improvements
6 include at least:
7
8  * Flat volumes are now handled properly. Previously, audio groups
9    controlled the absolute volume of streams if flat volume was in
10    effect, which made no sense.
11  * Audio group volumes are now persistent.
12  * Audio group volumes are applied to new streams before the streams
13    start to play, instead of after, which could cause audible
14    glitches.
15  * When a stream volume is changed by the user, the volume is
16    propagated to the stream's audio group.
17  * Fixed the handling of the "NEG" keyword in the match syntax in
18    module-audio-groups. Previously the "NEG" keyword was parsed, but
19    it had no effect.
20
21 Change-Id: I02bad3d23b3e562c71dbc6af6f3e308089893751
22 ---
23  src/Makefile.am                                    |    2 +-
24  src/map-file                                       |    3 +
25  src/modules/audio-groups/audio-groups.conf.example |   19 +-
26  src/modules/audio-groups/module-audio-groups.c     | 1710 ++++++++++++++------
27  .../main-volume-policy/main-volume-context.c       |  184 +--
28  .../main-volume-policy/main-volume-context.h       |   35 +-
29  .../main-volume-policy/main-volume-policy.c        |   65 +-
30  .../main-volume-policy.conf.example                |    1 -
31  .../main-volume-policy/main-volume-policy.h        |    6 +-
32  .../main-volume-policy/module-main-volume-policy.c |  651 +++++---
33  src/modules/volume-api/audio-group.c               |  288 +---
34  src/modules/volume-api/audio-group.h               |   29 +-
35  src/modules/volume-api/binding.c                   |  386 -----
36  src/modules/volume-api/binding.h                   |  128 --
37  src/modules/volume-api/bvolume.h                   |    3 +
38  src/modules/volume-api/device-creator.c            |  431 +++--
39  src/modules/volume-api/device.c                    |   54 +-
40  src/modules/volume-api/device.h                    |    4 +-
41  src/modules/volume-api/inidb.c                     |  553 +++++++
42  src/modules/volume-api/inidb.h                     |   54 +
43  src/modules/volume-api/module-volume-api.c         |   27 +
44  src/modules/volume-api/mute-control.c              |  241 +--
45  src/modules/volume-api/mute-control.h              |   75 +-
46  src/modules/volume-api/sstream.c                   |  313 ++--
47  src/modules/volume-api/sstream.h                   |   56 +-
48  src/modules/volume-api/stream-creator.c            |  691 ++++----
49  src/modules/volume-api/volume-api.c                |  430 +++--
50  src/modules/volume-api/volume-api.h                |   79 +-
51  src/modules/volume-api/volume-control.c            |  308 ++--
52  src/modules/volume-api/volume-control.h            |   93 +-
53  src/pulse/ext-volume-api.c                         |  100 ++
54  src/pulse/ext-volume-api.h                         |    3 +
55  src/tizen-ivi/audio-groups.conf                    |   23 +-
56  src/tizen-ivi/main-volume-policy.conf              |    1 -
57  34 files changed, 4012 insertions(+), 3034 deletions(-)
58  delete mode 100644 src/modules/volume-api/binding.c
59  delete mode 100644 src/modules/volume-api/binding.h
60  create mode 100644 src/modules/volume-api/inidb.c
61  create mode 100644 src/modules/volume-api/inidb.h
62
63 diff --git a/src/Makefile.am b/src/Makefile.am
64 index 9d17336..b5cf2a8 100644
65 --- a/src/Makefile.am
66 +++ b/src/Makefile.am
67 @@ -1098,10 +1098,10 @@ endif
68  
69  libvolume_api_la_SOURCES = \
70                 modules/volume-api/audio-group.c modules/volume-api/audio-group.h \
71 -               modules/volume-api/binding.c modules/volume-api/binding.h \
72                 modules/volume-api/bvolume.h \
73                 modules/volume-api/device.c modules/volume-api/device.h \
74                 modules/volume-api/device-creator.c modules/volume-api/device-creator.h \
75 +               modules/volume-api/inidb.c modules/volume-api/inidb.h \
76                 modules/volume-api/mute-control.c modules/volume-api/mute-control.h \
77                 modules/volume-api/sstream.c modules/volume-api/sstream.h \
78                 modules/volume-api/stream-creator.c modules/volume-api/stream-creator.h \
79 diff --git a/src/map-file b/src/map-file
80 index 28ea54e..cb31833 100644
81 --- a/src/map-file
82 +++ b/src/map-file
83 @@ -190,13 +190,16 @@ pa_ext_node_manager_set_subscribe_cb;
84  pa_ext_echo_cancel_set_volume;
85  pa_ext_echo_cancel_set_device;
86  pa_ext_volume_api_balance_valid;
87 +pa_ext_volume_api_bvolume_balance_to_string;
88  pa_ext_volume_api_bvolume_copy_balance;
89  pa_ext_volume_api_bvolume_get_left_right_balance;
90  pa_ext_volume_api_bvolume_get_rear_front_balance;
91  pa_ext_volume_api_bvolume_equal;
92  pa_ext_volume_api_bvolume_from_cvolume;
93 +pa_ext_volume_api_bvolume_init;
94  pa_ext_volume_api_bvolume_init_invalid;
95  pa_ext_volume_api_bvolume_init_mono;
96 +pa_ext_volume_api_bvolume_parse_balance;
97  pa_ext_volume_api_bvolume_remap;
98  pa_ext_volume_api_bvolume_reset_balance;
99  pa_ext_volume_api_bvolume_set_left_right_balance;
100 diff --git a/src/modules/audio-groups/audio-groups.conf.example b/src/modules/audio-groups/audio-groups.conf.example
101 index 8acdb76..6aa6989 100644
102 --- a/src/modules/audio-groups/audio-groups.conf.example
103 +++ b/src/modules/audio-groups/audio-groups.conf.example
104 @@ -1,28 +1,29 @@
105  [General]
106 -audio-groups = x-example-call-downlink-audio-group x-example-default-output-audio-group x-example-music-output-audio-group
107 -streams = phone-output music-output default-output
108 +stream-rules = phone-output music-output default-output
109  
110  [AudioGroup x-example-call-downlink-audio-group]
111 -volume-control = create
112 -mute-control = none
113 +volume-control = create:call-downlink-volume-control
114 +mute-control = create:call-downlink-mute-control
115  
116  [AudioGroup x-example-default-output-audio-group]
117 -volume-control = create
118 -mute-control = none
119 +volume-control = create:default-output-volume-control
120 +mute-control = create:call-downlink-mute-control
121  
122  [AudioGroup x-example-music-output-audio-group]
123  volume-control = bind:AudioGroup:x-example-default-output-audio-group
124 +mute-control = bind:AudioGroup:x-example-default-output-audio-group
125  
126 -[Stream phone-output]
127 +[StreamRule phone-output]
128  match = (direction output AND property media.role=phone)
129  audio-group-for-volume = x-example-call-downlink-audio-group
130  audio-group-for-mute = x-example-call-downlink-audio-group
131  
132 -[Stream music-output]
133 +[StreamRule music-output]
134  match = (direction output AND property media.role=music)
135  audio-group-for-volume = x-example-music-output-audio-group
136  audio-group-for-mute = x-example-music-output-audio-group
137  
138 -[Stream default-output]
139 +[StreamRule default-output]
140 +match = (direction output)
141  audio-group-for-volume = x-example-default-output-audio-group
142  audio-group-for-mute = x-example-default-output-audio-group
143 diff --git a/src/modules/audio-groups/module-audio-groups.c b/src/modules/audio-groups/module-audio-groups.c
144 index 2b3a570..e37a24e 100644
145 --- a/src/modules/audio-groups/module-audio-groups.c
146 +++ b/src/modules/audio-groups/module-audio-groups.c
147 @@ -40,15 +40,18 @@
148  
149  #include "module-audio-groups-symdef.h"
150  
151 +#define AUDIOGROUP_START "AudioGroup "
152 +#define STREAM_RULE_START "StreamRule "
153 +#define NONE_KEYWORD "none"
154 +#define CREATE_PREFIX "create:"
155 +#define BIND_PREFIX "bind:"
156 +#define BIND_AUDIO_GROUP_PREFIX BIND_PREFIX "AudioGroup:"
157 +
158  PA_MODULE_AUTHOR("Ismo Puustinen");
159  PA_MODULE_DESCRIPTION("Create audio groups and classify streams to them");
160  PA_MODULE_VERSION(PACKAGE_VERSION);
161  PA_MODULE_LOAD_ONCE(true);
162  
163 -#ifndef AUDIO_GROUP_CONFIG
164 -#define AUDIO_GROUP_CONFIG "audio-groups.conf"
165 -#endif
166 -
167  enum match_direction {
168      match_direction_unknown = 0,
169      match_direction_input,
170 @@ -80,60 +83,103 @@ struct expression {
171      PA_LLIST_HEAD(struct conjunction, conjunctions);
172  };
173  
174 -/* data gathered from settings */
175 +struct group {
176 +    struct userdata *userdata;
177 +    pa_audio_group *audio_group;
178 +    struct control *volume_control;
179 +    struct control *mute_control;
180 +    char *own_volume_control_name;
181 +    char *own_mute_control_name;
182 +    struct group *volume_master;
183 +    struct group *mute_master;
184 +    char *volume_master_name;
185 +    char *mute_master_name;
186 +
187 +    pa_hashmap *volume_slaves; /* struct group -> struct group (hashmap-as-a-set) */
188 +    pa_hashmap *mute_slaves; /* struct group -> struct group (hashmap-as-a-set) */
189 +    pa_hashmap *volume_stream_rules; /* struct stream_rule -> struct stream_rule (hashmap-as-a-set) */
190 +    pa_hashmap *mute_stream_rules; /* struct stream_rule -> struct stream_rule (hashmap-as-a-set) */
191 +
192 +    bool unlinked;
193 +};
194  
195 -enum control_action {
196 -    CONTROL_ACTION_NONE,
197 -    CONTROL_ACTION_CREATE,
198 -    CONTROL_ACTION_BIND,
199 +enum control_type {
200 +    CONTROL_TYPE_VOLUME,
201 +    CONTROL_TYPE_MUTE,
202  };
203  
204 -struct audio_group {
205 +struct control {
206      struct userdata *userdata;
207 -    char *id;
208 -    char *description;
209 -    enum control_action volume_control_action;
210 -    enum control_action mute_control_action;
211 -    pa_binding_target_info *volume_control_target_info;
212 -    pa_binding_target_info *mute_control_target_info;
213 +    enum control_type type;
214 +
215 +    union {
216 +        pa_volume_control *volume_control;
217 +        pa_mute_control *mute_control;
218 +    };
219 +
220 +    /* Controls that are created for streams don't own their pa_volume_control
221 +     * and pa_mute_control objects, because they're owned by the streams. */
222 +    bool own_control;
223  
224 -    /* official audio group */
225 -    pa_audio_group *group;
226 +    /* If non-NULL, then this control mirrors the state of the master
227 +     * control. If someone changes the master state, the state of this control
228 +     * is also updated, and also if someone changes this control's state, the
229 +     * change is applied also to the master. */
230 +    struct control *master;
231  
232 -    struct audio_group_control *volume_control;
233 -    struct audio_group_control *mute_control;
234 +    /* struct control -> struct control (hashmap-as-a-set)
235 +     * Contains the controls that have this control as their master. */
236 +    pa_hashmap *slaves;
237  
238 +    /* Set to true when the master control's state has been copied to this
239 +     * control. */
240 +    bool synced_with_master;
241 +
242 +    bool acquired;
243      bool unlinked;
244  };
245  
246 -struct stream {
247 +struct stream_rule {
248      struct userdata *userdata;
249 -    char *id;
250 +    char *name;
251      enum match_direction direction;
252      char *audio_group_name_for_volume;
253      char *audio_group_name_for_mute;
254 -    pa_audio_group *audio_group_for_volume;
255 -    pa_audio_group *audio_group_for_mute;
256 -    pa_binding_target_info *volume_control_target_info;
257 -    pa_binding_target_info *mute_control_target_info;
258 -    struct expression *rule;
259 -
260 -    bool unlinked;
261 +    struct group *group_for_volume;
262 +    struct group *group_for_mute;
263 +    struct expression *match_expression;
264  };
265  
266  struct userdata {
267 -    pa_hashmap *audio_groups; /* name -> struct audio_group */
268 -    pa_dynarray *streams; /* struct stream */
269 -    pa_hook_slot *new_stream_volume;
270 -    pa_hook_slot *new_stream_mute;
271 -
272 -    pa_volume_api *api;
273 -
274 -    /* The following fields are only used during initialization. */
275 -    pa_hashmap *audio_group_names; /* name -> name (hashmap-as-a-set) */
276 -    pa_hashmap *unused_audio_groups; /* name -> struct audio_group */
277 -    pa_dynarray *stream_names;
278 -    pa_hashmap *unused_streams; /* name -> struct stream */
279 +    pa_volume_api *volume_api;
280 +    pa_hashmap *groups; /* name -> struct group */
281 +    pa_hashmap *stream_rules; /* name -> struct stream_rule */
282 +    pa_dynarray *stream_rules_list; /* struct stream_rule */
283 +
284 +    /* pas_stream -> struct stream_rule
285 +     * When a stream matches with a rule, it's added here. */
286 +    pa_hashmap *rules_by_stream;
287 +
288 +    /* pas_stream -> struct control
289 +     * Contains proxy controls for all relative volume controls of streams. */
290 +    pa_hashmap *stream_volume_controls;
291 +
292 +    /* pas_stream -> struct control
293 +     * Contains proxy controls for all mute controls of streams. */
294 +    pa_hashmap *stream_mute_controls;
295 +
296 +    pa_hook_slot *stream_put_slot;
297 +    pa_hook_slot *stream_unlink_slot;
298 +    pa_hook_slot *volume_control_implementation_initialized_slot;
299 +    pa_hook_slot *mute_control_implementation_initialized_slot;
300 +    pa_hook_slot *volume_control_set_initial_volume_slot;
301 +    pa_hook_slot *mute_control_set_initial_mute_slot;
302 +    pa_hook_slot *volume_control_volume_changed_slot;
303 +    pa_hook_slot *mute_control_mute_changed_slot;
304 +    pa_hook_slot *volume_control_unlink_slot;
305 +    pa_hook_slot *mute_control_unlink_slot;
306 +
307 +    pa_dynarray *stream_rule_names; /* Only used during initialization. */
308  };
309  
310  static const char* const valid_modargs[] = {
311 @@ -141,77 +187,408 @@ static const char* const valid_modargs[] = {
312      NULL
313  };
314  
315 -static void audio_group_unlink(struct audio_group *group);
316 +static void control_free(struct control *control);
317 +static void control_set_master(struct control *control, struct control *master);
318 +static void control_add_slave(struct control *control, struct control *slave);
319 +static void control_remove_slave(struct control *control, struct control *slave);
320  
321 -static void print_literal(struct literal *l);
322 -static void print_conjunction(struct conjunction *c);
323 -static void print_expression(struct expression *e);
324 -static void delete_expression(struct expression *e);
325 +static void group_free(struct group *group);
326 +static void group_set_master(struct group *group, enum control_type type, struct group *master);
327 +static int group_set_master_name(struct group *group, enum control_type type, const char *name);
328 +static void group_disable_control(struct group *group, enum control_type type);
329 +static void group_add_slave(struct group *group, enum control_type type, struct group *slave);
330 +static void group_remove_slave(struct group *group, enum control_type type, struct group *slave);
331  
332 -static struct audio_group *audio_group_new(struct userdata *u, const char *name) {
333 -    struct audio_group *group;
334 +static void stream_rule_set_group(struct stream_rule *rule, enum control_type type, struct group *group);
335 +static void stream_rule_set_group_name(struct stream_rule *rule, enum control_type type, const char *name);
336  
337 -    pa_assert(u);
338 -    pa_assert(name);
339 +static bool literal_match(struct literal *literal, pas_stream *stream);
340  
341 -    group = pa_xnew0(struct audio_group, 1);
342 -    group->userdata = u;
343 -    group->id = pa_xstrdup(name);
344 -    group->description = pa_xstrdup(name);
345 -    group->volume_control_action = CONTROL_ACTION_NONE;
346 -    group->mute_control_action = CONTROL_ACTION_NONE;
347 +static struct expression *expression_new(void);
348 +static void expression_free(struct expression *expression);
349 +
350 +static int volume_control_set_volume_cb(pa_volume_control *volume_control, const pa_bvolume *original_volume,
351 +                                        const pa_bvolume *remapped_volume, bool set_volume, bool set_balance) {
352 +    struct control *control;
353 +    struct control *slave;
354 +    void *state;
355 +
356 +    pa_assert(volume_control);
357 +    pa_assert(original_volume);
358 +    pa_assert(remapped_volume);
359 +
360 +    control = volume_control->userdata;
361 +
362 +    /* There are four cases that need to be considered:
363 +     *
364 +     * 1) The master control is propagating the volume to this control. We need
365 +     * to propagate the volume downstream.
366 +     *
367 +     * 2) This control was just assigned a master control and the volume hasn't
368 +     * yet been synchronized. In this case the volume that is now being set for
369 +     * this control is the master control's volume. We need to propagate the
370 +     * volume downstream.
371 +     *
372 +     * 3) Someone set the volume directly for this control, and this control
373 +     * has a master control. We need to propagate the volume upstream, and wait
374 +     * for another call that will fall under the case 1.
375 +     *
376 +     * 4) Someone set the volume directly for this control, and this control
377 +     * doesn't have a master control. We need to propagate the volume
378 +     * downstream.
379 +     *
380 +     * As we can see, the action is the same in cases 1, 2 and 4. */
381 +
382 +    /* Case 3. */
383 +    if (control->synced_with_master && !control->master->volume_control->set_volume_in_progress) {
384 +        pa_volume_control_set_volume(control->master->volume_control, original_volume, set_volume, set_balance);
385 +        return 0;
386 +    }
387 +
388 +    /* Cases 1, 2 and 4. */
389 +    PA_HASHMAP_FOREACH(slave, control->slaves, state)
390 +        pa_volume_control_set_volume(slave->volume_control, original_volume, set_volume, set_balance);
391  
392 -    return group;
393 +    return 0;
394  }
395  
396 -static int audio_group_put(struct audio_group *group) {
397 -    int r;
398 +static int mute_control_set_mute_cb(pa_mute_control *mute_control, bool mute) {
399 +    struct control *control;
400 +    struct control *slave;
401 +    void *state;
402 +
403 +    pa_assert(mute_control);
404 +
405 +    control = mute_control->userdata;
406 +
407 +    /* There are four cases that need to be considered:
408 +     *
409 +     * 1) The master control is propagating the mute to this control. We need
410 +     * to propagate the mute downstream.
411 +     *
412 +     * 2) This control was just assigned a master control and the mute hasn't
413 +     * yet been synchronized. In this case the mute that is now being set for
414 +     * this control is the master control's mute. We need to propagate the mute
415 +     * downstream.
416 +     *
417 +     * 3) Someone set the mute directly for this control, and this control has
418 +     * a master control. We need to propagate the mute upstream, and wait for
419 +     * another call that will fall under the case 1.
420 +     *
421 +     * 4) Someone set the mute directly for this control, and this control
422 +     * doesn't have a master control. We need to propagate the mute downstream.
423 +     *
424 +     * As we can see, the action is the same in cases 1, 2 and 4. */
425 +
426 +    /* Case 3. */
427 +    if (control->synced_with_master && !control->master->mute_control->set_mute_in_progress) {
428 +        pa_mute_control_set_mute(control->master->mute_control, mute);
429 +        return 0;
430 +    }
431 +
432 +    /* Cases 1, 2 and 4. */
433 +    PA_HASHMAP_FOREACH(slave, control->slaves, state)
434 +        pa_mute_control_set_mute(slave->mute_control, mute);
435 +
436 +    return 0;
437 +}
438 +
439 +static int control_new_for_group(struct group *group, enum control_type type, const char *name, bool persistent, struct control **_r) {
440 +    struct control *control = NULL;
441 +    int r = 0;
442  
443      pa_assert(group);
444 +    pa_assert(name);
445 +    pa_assert(_r);
446 +
447 +    control = pa_xnew0(struct control, 1);
448 +    control->userdata = group->userdata;
449 +    control->type = type;
450 +    control->slaves = pa_hashmap_new(NULL, NULL);
451 +
452 +    switch (type) {
453 +        case CONTROL_TYPE_VOLUME:
454 +            if (persistent)
455 +                control->volume_control = pa_hashmap_get(control->userdata->volume_api->volume_controls, name);
456 +
457 +            if (!control->volume_control) {
458 +                r = pa_volume_control_new(control->userdata->volume_api, name, persistent, &control->volume_control);
459 +                if (r < 0)
460 +                    goto fail;
461 +            }
462  
463 -    r = pa_audio_group_new(group->userdata->api, group->id, group->description, &group->group);
464 -    if (r < 0)
465 -        goto fail;
466 +            pa_volume_control_set_convertible_to_dB(control->volume_control, true);
467 +
468 +            if (persistent) {
469 +                r = pa_volume_control_acquire_for_audio_group(control->volume_control, group->audio_group,
470 +                                                              volume_control_set_volume_cb, control);
471 +                if (r < 0)
472 +                    goto fail;
473 +
474 +                control->acquired = true;
475 +            } else {
476 +                control->volume_control->set_volume = volume_control_set_volume_cb;
477 +                control->volume_control->userdata = control;
478 +            }
479 +            break;
480 +
481 +        case CONTROL_TYPE_MUTE:
482 +            if (persistent)
483 +                control->mute_control = pa_hashmap_get(control->userdata->volume_api->mute_controls, name);
484 +
485 +            if (!control->mute_control) {
486 +                r = pa_mute_control_new(control->userdata->volume_api, name, persistent, &control->mute_control);
487 +                if (r < 0)
488 +                    goto fail;
489 +            }
490 +
491 +            if (persistent) {
492 +                r = pa_mute_control_acquire_for_audio_group(control->mute_control, group->audio_group,
493 +                                                            mute_control_set_mute_cb, control);
494 +                if (r < 0)
495 +                    goto fail;
496 +
497 +                control->acquired = true;
498 +            } else {
499 +                control->mute_control->set_mute = mute_control_set_mute_cb;
500 +                control->mute_control->userdata = control;
501 +            }
502 +            break;
503 +    }
504 +
505 +    control->own_control = true;
506 +
507 +    *_r = control;
508 +    return 0;
509 +
510 +fail:
511 +    if (control)
512 +        control_free(control);
513 +
514 +    return r;
515 +}
516 +
517 +static struct control *control_new_for_stream(struct userdata *u, enum control_type type, pas_stream *stream) {
518 +    struct control *control;
519 +
520 +    pa_assert(u);
521 +    pa_assert(stream);
522  
523 -    switch (group->volume_control_action) {
524 -        case CONTROL_ACTION_NONE:
525 +    control = pa_xnew0(struct control, 1);
526 +    control->userdata = u;
527 +    control->type = type;
528 +
529 +    switch (type) {
530 +        case CONTROL_TYPE_VOLUME:
531 +            control->volume_control = stream->relative_volume_control;
532 +            pa_assert(control->volume_control);
533 +            break;
534 +
535 +        case CONTROL_TYPE_MUTE:
536 +            control->mute_control = stream->mute_control;
537 +            pa_assert(control->mute_control);
538              break;
539 +    }
540  
541 -        case CONTROL_ACTION_CREATE:
542 -            pa_audio_group_set_have_own_volume_control(group->group, true);
543 -            pa_audio_group_set_volume_control(group->group, group->group->own_volume_control);
544 +    return control;
545 +}
546 +
547 +static void control_put(struct control *control) {
548 +    pa_assert(control);
549 +
550 +    switch (control->type) {
551 +        case CONTROL_TYPE_VOLUME:
552 +            if (control->own_control && !control->volume_control->linked)
553 +                pa_volume_control_put(control->volume_control);
554              break;
555  
556 -        case CONTROL_ACTION_BIND:
557 -            pa_audio_group_bind_volume_control(group->group, group->volume_control_target_info);
558 +        case CONTROL_TYPE_MUTE:
559 +            if (control->own_control && !control->mute_control->linked)
560 +                pa_mute_control_put(control->mute_control);
561              break;
562      }
563 +}
564 +
565 +static void control_unlink(struct control *control) {
566 +    pa_assert(control);
567 +
568 +    if (control->unlinked)
569 +        return;
570 +
571 +    control->unlinked = true;
572 +
573 +    if (control->slaves) {
574 +        struct control *slave;
575 +
576 +        while ((slave = pa_hashmap_first(control->slaves)))
577 +            control_set_master(slave, NULL);
578 +    }
579 +
580 +    control_set_master(control, NULL);
581  
582 -    switch (group->mute_control_action) {
583 -        case CONTROL_ACTION_NONE:
584 +    switch (control->type) {
585 +        case CONTROL_TYPE_VOLUME:
586 +            if (control->own_control && control->volume_control && !control->volume_control->persistent)
587 +                pa_volume_control_unlink(control->volume_control);
588              break;
589  
590 -        case CONTROL_ACTION_CREATE:
591 -            pa_audio_group_set_have_own_mute_control(group->group, true);
592 -            pa_audio_group_set_mute_control(group->group, group->group->own_mute_control);
593 +        case CONTROL_TYPE_MUTE:
594 +            if (control->own_control && control->mute_control && !control->mute_control->persistent)
595 +                pa_mute_control_unlink(control->mute_control);
596              break;
597 +    }
598 +}
599 +
600 +static void control_free(struct control *control) {
601 +    pa_assert(control);
602 +
603 +    if (!control->unlinked)
604 +        control_unlink(control);
605 +
606 +    if (control->slaves) {
607 +        pa_assert(pa_hashmap_isempty(control->slaves));
608 +        pa_hashmap_free(control->slaves);
609 +    }
610 +
611 +    switch (control->type) {
612 +        case CONTROL_TYPE_VOLUME:
613 +            if (control->acquired)
614 +                pa_volume_control_release(control->volume_control);
615 +
616 +            if (control->own_control && control->volume_control && !control->volume_control->persistent)
617 +                pa_volume_control_free(control->volume_control);
618 +            break;
619 +
620 +        case CONTROL_TYPE_MUTE:
621 +            if (control->acquired)
622 +                pa_mute_control_release(control->mute_control);
623  
624 -        case CONTROL_ACTION_BIND:
625 -            pa_audio_group_bind_mute_control(group->group, group->mute_control_target_info);
626 +            if (control->own_control && control->mute_control && !control->mute_control->persistent)
627 +                pa_mute_control_free(control->mute_control);
628              break;
629      }
630  
631 -    pa_audio_group_put(group->group);
632 +    pa_xfree(control);
633 +}
634 +
635 +static void control_set_master(struct control *control, struct control *master) {
636 +    struct control *old_master;
637 +
638 +    pa_assert(control);
639 +    pa_assert(!master || master->type == control->type);
640 +
641 +    old_master = control->master;
642 +
643 +    if (master == old_master)
644 +        return;
645 +
646 +    if (old_master) {
647 +        control_remove_slave(old_master, control);
648 +        control->synced_with_master = false;
649 +    }
650 +
651 +    control->master = master;
652 +
653 +    if (master) {
654 +        control_add_slave(master, control);
655 +
656 +        switch (control->type) {
657 +            case CONTROL_TYPE_VOLUME:
658 +                pa_volume_control_set_volume(control->volume_control, &master->volume_control->volume, true, true);
659 +                break;
660 +
661 +            case CONTROL_TYPE_MUTE:
662 +                pa_mute_control_set_mute(control->mute_control, master->mute_control->mute);
663 +                break;
664 +        }
665 +
666 +        control->synced_with_master = true;
667 +    }
668 +}
669 +
670 +static void control_add_slave(struct control *control, struct control *slave) {
671 +    pa_assert(control);
672 +    pa_assert(slave);
673 +
674 +    pa_assert_se(pa_hashmap_put(control->slaves, slave, slave) >= 0);
675 +}
676 +
677 +static void control_remove_slave(struct control *control, struct control *slave) {
678 +    pa_assert(control);
679 +    pa_assert(slave);
680 +
681 +    pa_assert_se(pa_hashmap_remove(control->slaves, slave));
682 +}
683 +
684 +static int group_new(struct userdata *u, const char *name, struct group **_r) {
685 +    struct group *group = NULL;
686 +    int r;
687 +    struct group *slave;
688 +    struct stream_rule *rule;
689 +    void *state;
690 +
691 +    pa_assert(u);
692 +    pa_assert(name);
693 +    pa_assert(_r);
694 +
695 +    group = pa_xnew0(struct group, 1);
696 +    group->userdata = u;
697 +
698 +    r = pa_audio_group_new(u->volume_api, name, &group->audio_group);
699 +    if (r < 0)
700 +        goto fail;
701 +
702 +    group->volume_slaves = pa_hashmap_new(NULL, NULL);
703 +    group->mute_slaves = pa_hashmap_new(NULL, NULL);
704 +    group->volume_stream_rules = pa_hashmap_new(NULL, NULL);
705 +    group->mute_stream_rules = pa_hashmap_new(NULL, NULL);
706 +
707 +    PA_HASHMAP_FOREACH(slave, u->groups, state) {
708 +        if (slave == group)
709 +            continue;
710 +
711 +        if (pa_safe_streq(slave->volume_master_name, group->audio_group->name))
712 +            group_set_master(slave, CONTROL_TYPE_VOLUME, group);
713 +
714 +        if (pa_safe_streq(slave->mute_master_name, group->audio_group->name))
715 +            group_set_master(slave, CONTROL_TYPE_MUTE, group);
716 +    }
717 +
718 +    PA_HASHMAP_FOREACH(rule, u->stream_rules, state) {
719 +        if (pa_safe_streq(rule->audio_group_name_for_volume, group->audio_group->name))
720 +            stream_rule_set_group(rule, CONTROL_TYPE_VOLUME, group);
721  
722 +        if (pa_safe_streq(rule->audio_group_name_for_mute, group->audio_group->name))
723 +            stream_rule_set_group(rule, CONTROL_TYPE_MUTE, group);
724 +    }
725 +
726 +    *_r = group;
727      return 0;
728  
729  fail:
730 -    audio_group_unlink(group);
731 +    if (group)
732 +        group_free(group);
733  
734      return r;
735  }
736  
737 -static void audio_group_unlink(struct audio_group *group) {
738 +static void group_put(struct group *group) {
739 +    pa_assert(group);
740 +
741 +    pa_audio_group_put(group->audio_group);
742 +
743 +    if (group->volume_control)
744 +        control_put(group->volume_control);
745 +
746 +    if (group->mute_control)
747 +        control_put(group->mute_control);
748 +}
749 +
750 +static void group_unlink(struct group *group) {
751 +    struct stream_rule *rule;
752 +    struct group *slave;
753 +    void *state;
754 +
755      pa_assert(group);
756  
757      if (group->unlinked)
758 @@ -219,192 +596,406 @@ static void audio_group_unlink(struct audio_group *group) {
759  
760      group->unlinked = true;
761  
762 -    if (group->group) {
763 -        pa_audio_group_free(group->group);
764 -        group->group = NULL;
765 -    }
766 +    PA_HASHMAP_FOREACH(rule, group->volume_stream_rules, state)
767 +        stream_rule_set_group(rule, CONTROL_TYPE_VOLUME, NULL);
768 +
769 +    PA_HASHMAP_FOREACH(rule, group->mute_stream_rules, state)
770 +        stream_rule_set_group(rule, CONTROL_TYPE_MUTE, NULL);
771 +
772 +    PA_HASHMAP_FOREACH(slave, group->volume_slaves, state)
773 +        group_set_master(slave, CONTROL_TYPE_VOLUME, NULL);
774 +
775 +    PA_HASHMAP_FOREACH(slave, group->mute_slaves, state)
776 +        group_set_master(slave, CONTROL_TYPE_MUTE, NULL);
777 +
778 +    group_disable_control(group, CONTROL_TYPE_MUTE);
779 +    group_disable_control(group, CONTROL_TYPE_VOLUME);
780 +
781 +    if (group->audio_group)
782 +        pa_audio_group_unlink(group->audio_group);
783  }
784  
785 -static void audio_group_free(struct audio_group *group) {
786 +static void group_free(struct group *group) {
787      pa_assert(group);
788  
789 -    if (!group->unlinked)
790 -        audio_group_unlink(group);
791 +    group_unlink(group);
792 +
793 +    if (group->mute_stream_rules) {
794 +        pa_assert(pa_hashmap_isempty(group->mute_stream_rules));
795 +        pa_hashmap_free(group->mute_stream_rules);
796 +    }
797 +
798 +    if (group->volume_stream_rules) {
799 +        pa_assert(pa_hashmap_isempty(group->volume_stream_rules));
800 +        pa_hashmap_free(group->volume_stream_rules);
801 +    }
802 +
803 +    if (group->mute_slaves) {
804 +        pa_assert(pa_hashmap_isempty(group->mute_slaves));
805 +        pa_hashmap_free(group->mute_slaves);
806 +    }
807 +
808 +    if (group->volume_slaves) {
809 +        pa_assert(pa_hashmap_isempty(group->volume_slaves));
810 +        pa_hashmap_free(group->volume_slaves);
811 +    }
812  
813 -    if (group->mute_control_target_info)
814 -        pa_binding_target_info_free(group->mute_control_target_info);
815 +    pa_assert(!group->mute_master_name);
816 +    pa_assert(!group->volume_master_name);
817 +    pa_assert(!group->mute_master);
818 +    pa_assert(!group->volume_master);
819 +    pa_assert(!group->mute_control);
820 +    pa_assert(!group->volume_control);
821  
822 -    if (group->volume_control_target_info)
823 -        pa_binding_target_info_free(group->volume_control_target_info);
824 +    if (group->audio_group)
825 +        pa_audio_group_free(group->audio_group);
826  
827 -    pa_xfree(group->description);
828 -    pa_xfree(group->id);
829      pa_xfree(group);
830  }
831  
832 -static void audio_group_set_description(struct audio_group *group, const char *description) {
833 +static void group_set_own_control_name(struct group *group, enum control_type type, const char *name) {
834 +    struct group *slave;
835 +    void *state;
836 +
837      pa_assert(group);
838 -    pa_assert(description);
839  
840 -    pa_xfree(group->description);
841 -    group->description = pa_xstrdup(description);
842 +    if (name)
843 +        group_set_master_name(group, type, NULL);
844 +
845 +    switch (type) {
846 +        case CONTROL_TYPE_VOLUME:
847 +            if (pa_safe_streq(name, group->own_volume_control_name))
848 +                return;
849 +
850 +            if (group->volume_control) {
851 +                control_free(group->volume_control);
852 +                group->volume_control = NULL;
853 +            }
854 +
855 +            pa_xfree(group->own_volume_control_name);
856 +            group->own_volume_control_name = pa_xstrdup(name);
857 +
858 +            if (name) {
859 +                control_new_for_group(group, CONTROL_TYPE_VOLUME, name, true, &group->volume_control);
860 +
861 +                PA_HASHMAP_FOREACH(slave, group->volume_slaves, state) {
862 +                    if (slave->volume_control)
863 +                        control_set_master(slave->volume_control, group->volume_control);
864 +                }
865 +            }
866 +            break;
867 +
868 +        case CONTROL_TYPE_MUTE:
869 +            if (pa_safe_streq(name, group->own_mute_control_name))
870 +                return;
871 +
872 +            if (group->mute_control) {
873 +                control_free(group->mute_control);
874 +                group->mute_control = NULL;
875 +            }
876 +
877 +            pa_xfree(group->own_mute_control_name);
878 +            group->own_mute_control_name = pa_xstrdup(name);
879 +
880 +            if (name) {
881 +                control_new_for_group(group, CONTROL_TYPE_MUTE, name, true, &group->mute_control);
882 +
883 +                PA_HASHMAP_FOREACH(slave, group->mute_slaves, state) {
884 +                    if (slave->mute_control)
885 +                        control_set_master(slave->mute_control, group->mute_control);
886 +                }
887 +            }
888 +            break;
889 +    }
890  }
891  
892 -static void audio_group_set_volume_control_action(struct audio_group *group, enum control_action action,
893 -                                                  pa_binding_target_info *target_info) {
894 +static void group_set_master(struct group *group, enum control_type type, struct group *master) {
895 +    struct group *old_master;
896 +    struct control *master_control = NULL;
897 +
898      pa_assert(group);
899 -    pa_assert((action == CONTROL_ACTION_BIND) ^ !target_info);
900 +    pa_assert(master != group);
901 +
902 +    switch (type) {
903 +        case CONTROL_TYPE_VOLUME:
904 +            old_master = group->volume_master;
905 +
906 +            if (master == old_master)
907 +                return;
908 +
909 +            if (old_master)
910 +                group_remove_slave(old_master, CONTROL_TYPE_VOLUME, group);
911 +
912 +            group->volume_master = master;
913 +
914 +            if (master)
915 +                group_add_slave(master, CONTROL_TYPE_VOLUME, group);
916 +
917 +            if (group->volume_control) {
918 +                if (master)
919 +                    master_control = master->volume_control;
920  
921 -    group->volume_control_action = action;
922 +                control_set_master(group->volume_control, master_control);
923 +            }
924 +            break;
925 +
926 +        case CONTROL_TYPE_MUTE:
927 +            old_master = group->mute_master;
928 +
929 +            if (master == old_master)
930 +                return;
931  
932 -    if (group->volume_control_target_info)
933 -        pa_binding_target_info_free(group->volume_control_target_info);
934 +            if (old_master)
935 +                group_remove_slave(old_master, CONTROL_TYPE_MUTE, group);
936  
937 -    if (action == CONTROL_ACTION_BIND)
938 -        group->volume_control_target_info = pa_binding_target_info_copy(target_info);
939 -    else
940 -        group->volume_control_target_info = NULL;
941 +            group->mute_master = master;
942 +
943 +            if (master)
944 +                group_add_slave(master, CONTROL_TYPE_MUTE, group);
945 +
946 +            if (group->mute_control) {
947 +                if (master)
948 +                    master_control = master->volume_control;
949 +
950 +                control_set_master(group->volume_control, master_control);
951 +            }
952 +            break;
953 +    }
954  }
955  
956 -static void audio_group_set_mute_control_action(struct audio_group *group, enum control_action action,
957 -                                                pa_binding_target_info *target_info) {
958 +static int group_set_master_name(struct group *group, enum control_type type, const char *name) {
959 +    struct group *slave;
960 +    void *state;
961 +    struct group *master = NULL;
962 +
963      pa_assert(group);
964 -    pa_assert((action == CONTROL_ACTION_BIND) ^ !target_info);
965  
966 -    group->mute_control_action = action;
967 +    if (pa_safe_streq(name, group->audio_group->name)) {
968 +        pa_log("Can't bind audio group control to itself.");
969 +        return -PA_ERR_INVALID;
970 +    }
971  
972 -    if (group->mute_control_target_info)
973 -        pa_binding_target_info_free(group->mute_control_target_info);
974 +    if (name)
975 +        group_set_own_control_name(group, type, NULL);
976  
977 -    if (action == CONTROL_ACTION_BIND)
978 -        group->mute_control_target_info = pa_binding_target_info_copy(target_info);
979 -    else
980 -        group->mute_control_target_info = NULL;
981 -}
982 +    switch (type) {
983 +        case CONTROL_TYPE_VOLUME:
984 +            if (pa_safe_streq(name, group->volume_master_name))
985 +                return 0;
986  
987 -static struct stream *stream_new(struct userdata *u, const char *name) {
988 -    struct stream *stream;
989 +            pa_xfree(group->volume_master_name);
990 +            group->volume_master_name = pa_xstrdup(name);
991  
992 -    pa_assert(u);
993 -    pa_assert(name);
994 +            if (name && !group->volume_control) {
995 +                control_new_for_group(group, CONTROL_TYPE_VOLUME, "audio-group-volume-control", false, &group->volume_control);
996  
997 -    stream = pa_xnew0(struct stream, 1);
998 -    stream->userdata = u;
999 -    stream->id = pa_xstrdup(name);
1000 -    stream->direction = match_direction_unknown;
1001 +                PA_HASHMAP_FOREACH(slave, group->volume_slaves, state) {
1002 +                    if (slave->volume_control)
1003 +                        control_set_master(slave->volume_control, group->volume_control);
1004 +                }
1005  
1006 -    return stream;
1007 -}
1008 +            } else if (!name && group->volume_control) {
1009 +                control_free(group->volume_control);
1010 +                group->volume_control = NULL;
1011 +            }
1012 +            break;
1013  
1014 -static void stream_put(struct stream *stream) {
1015 -    pa_assert(stream);
1016 +        case CONTROL_TYPE_MUTE:
1017 +            if (pa_safe_streq(name, group->mute_master_name))
1018 +                return 0;
1019  
1020 -    if (stream->audio_group_name_for_volume) {
1021 -        stream->audio_group_for_volume = pa_hashmap_get(stream->userdata->audio_groups, stream->audio_group_name_for_volume);
1022 -        if (stream->audio_group_for_volume)
1023 -            stream->volume_control_target_info =
1024 -                    pa_binding_target_info_new(PA_AUDIO_GROUP_BINDING_TARGET_TYPE, stream->audio_group_for_volume->name,
1025 -                                               PA_AUDIO_GROUP_BINDING_TARGET_FIELD_VOLUME_CONTROL);
1026 -        else
1027 -            pa_log("Stream %s refers to undefined audio group %s.", stream->id, stream->audio_group_name_for_volume);
1028 +            pa_xfree(group->mute_master_name);
1029 +            group->mute_master_name = pa_xstrdup(name);
1030 +
1031 +            if (name && !group->mute_control) {
1032 +                control_new_for_group(group, CONTROL_TYPE_MUTE, "audio-group-mute-control", false, &group->mute_control);
1033 +
1034 +                PA_HASHMAP_FOREACH(slave, group->mute_slaves, state) {
1035 +                    if (slave->mute_control)
1036 +                        control_set_master(slave->mute_control, group->mute_control);
1037 +                }
1038 +
1039 +            } else if (!name && group->mute_control) {
1040 +                control_free(group->mute_control);
1041 +                group->mute_control = NULL;
1042 +            }
1043 +            break;
1044      }
1045  
1046 -    if (stream->audio_group_name_for_mute) {
1047 -        stream->audio_group_for_mute = pa_hashmap_get(stream->userdata->audio_groups, stream->audio_group_name_for_mute);
1048 -        if (stream->audio_group_for_mute)
1049 -            stream->mute_control_target_info =
1050 -                    pa_binding_target_info_new(PA_AUDIO_GROUP_BINDING_TARGET_TYPE, stream->audio_group_for_mute->name,
1051 -                                               PA_AUDIO_GROUP_BINDING_TARGET_FIELD_MUTE_CONTROL);
1052 -        else
1053 -            pa_log("Stream %s refers to undefined audio group %s.", stream->id, stream->audio_group_name_for_volume);
1054 +    if (name)
1055 +        master = pa_hashmap_get(group->userdata->groups, name);
1056 +
1057 +    group_set_master(group, type, master);
1058 +
1059 +    return 0;
1060 +}
1061 +
1062 +static void group_disable_control(struct group *group, enum control_type type) {
1063 +    pa_assert(group);
1064 +
1065 +    group_set_own_control_name(group, type, NULL);
1066 +    group_set_master_name(group, type, NULL);
1067 +}
1068 +
1069 +static void group_add_slave(struct group *group, enum control_type type, struct group *slave) {
1070 +    pa_assert(group);
1071 +    pa_assert(slave);
1072 +
1073 +    switch (type) {
1074 +        case CONTROL_TYPE_VOLUME:
1075 +            pa_assert_se(pa_hashmap_put(group->volume_slaves, slave, slave) >= 0);
1076 +            break;
1077 +
1078 +        case CONTROL_TYPE_MUTE:
1079 +            pa_assert_se(pa_hashmap_put(group->mute_slaves, slave, slave) >= 0);
1080 +            break;
1081      }
1082  }
1083  
1084 -static void stream_unlink(struct stream *stream) {
1085 -    pa_assert(stream);
1086 +static void group_remove_slave(struct group *group, enum control_type type, struct group *slave) {
1087 +    pa_assert(group);
1088 +    pa_assert(slave);
1089  
1090 -    if (stream->unlinked)
1091 -        return;
1092 +    switch (type) {
1093 +        case CONTROL_TYPE_VOLUME:
1094 +            pa_assert_se(pa_hashmap_remove(group->volume_slaves, slave));
1095 +            break;
1096  
1097 -    if (stream->mute_control_target_info) {
1098 -        pa_binding_target_info_free(stream->mute_control_target_info);
1099 -        stream->mute_control_target_info = NULL;
1100 +        case CONTROL_TYPE_MUTE:
1101 +            pa_assert_se(pa_hashmap_remove(group->mute_slaves, slave));
1102      }
1103 +}
1104  
1105 -    if (stream->volume_control_target_info) {
1106 -        pa_binding_target_info_free(stream->volume_control_target_info);
1107 -        stream->volume_control_target_info = NULL;
1108 +static void group_add_stream_rule(struct group *group, enum control_type type, struct stream_rule *rule) {
1109 +    pa_assert(group);
1110 +    pa_assert(rule);
1111 +
1112 +    switch (type) {
1113 +        case CONTROL_TYPE_VOLUME:
1114 +            pa_assert_se(pa_hashmap_put(group->volume_stream_rules, rule, rule) >= 0);
1115 +            break;
1116 +
1117 +        case CONTROL_TYPE_MUTE:
1118 +            pa_assert_se(pa_hashmap_put(group->mute_stream_rules, rule, rule) >= 0);
1119 +            break;
1120      }
1121 +}
1122 +
1123 +static void group_remove_stream_rule(struct group *group, enum control_type type, struct stream_rule *rule) {
1124 +    pa_assert(group);
1125 +    pa_assert(rule);
1126  
1127 -    stream->unlinked = true;
1128 +    switch (type) {
1129 +        case CONTROL_TYPE_VOLUME:
1130 +            pa_assert_se(pa_hashmap_remove(group->volume_stream_rules, rule));
1131 +            break;
1132 +
1133 +        case CONTROL_TYPE_MUTE:
1134 +            pa_assert_se(pa_hashmap_remove(group->mute_stream_rules, rule));
1135 +            break;
1136 +    }
1137  }
1138  
1139 -static void stream_free(struct stream *stream) {
1140 -    pa_assert(stream);
1141 +static struct stream_rule *stream_rule_new(struct userdata *u, const char *name) {
1142 +    struct stream_rule *rule;
1143  
1144 -    if (!stream->unlinked)
1145 -        stream_unlink(stream);
1146 +    pa_assert(u);
1147 +    pa_assert(name);
1148  
1149 -    if (stream->rule)
1150 -        delete_expression(stream->rule);
1151 +    rule = pa_xnew0(struct stream_rule, 1);
1152 +    rule->userdata = u;
1153 +    rule->name = pa_xstrdup(name);
1154 +    rule->direction = match_direction_unknown;
1155 +    rule->match_expression = expression_new();
1156  
1157 -    pa_xfree(stream->audio_group_name_for_mute);
1158 -    pa_xfree(stream->audio_group_name_for_volume);
1159 -    pa_xfree(stream->id);
1160 -    pa_xfree(stream);
1161 +    return rule;
1162  }
1163  
1164 -static void stream_set_audio_group_name_for_volume(struct stream *stream, const char *name) {
1165 -    pa_assert(stream);
1166 +static void stream_rule_free(struct stream_rule *rule) {
1167 +    pa_assert(rule);
1168  
1169 -    pa_xfree(stream->audio_group_name_for_volume);
1170 -    stream->audio_group_name_for_volume = pa_xstrdup(name);
1171 +    if (rule->match_expression)
1172 +        expression_free(rule->match_expression);
1173 +
1174 +    stream_rule_set_group_name(rule, CONTROL_TYPE_MUTE, NULL);
1175 +    stream_rule_set_group_name(rule, CONTROL_TYPE_VOLUME, NULL);
1176 +    pa_xfree(rule->name);
1177 +    pa_xfree(rule);
1178  }
1179  
1180 -static void stream_set_audio_group_name_for_mute(struct stream *stream, const char *name) {
1181 -    pa_assert(stream);
1182 +static void stream_rule_set_match_expression(struct stream_rule *rule, struct expression *expression) {
1183 +    pa_assert(rule);
1184 +    pa_assert(expression);
1185  
1186 -    pa_xfree(stream->audio_group_name_for_mute);
1187 -    stream->audio_group_name_for_mute = pa_xstrdup(name);
1188 +    if (rule->match_expression)
1189 +        expression_free(rule->match_expression);
1190 +
1191 +    rule->match_expression = expression;
1192  }
1193  
1194 -/* stream classification */
1195 +static void stream_rule_set_group(struct stream_rule *rule, enum control_type type, struct group *group) {
1196 +    pa_assert(rule);
1197  
1198 -static bool match_predicate(struct literal *l, pas_stream *d) {
1199 +    switch (type) {
1200 +        case CONTROL_TYPE_VOLUME:
1201 +            if (group == rule->group_for_volume)
1202 +                return;
1203  
1204 -    if (l->stream_direction != match_direction_unknown) {
1205 -        /* check the stream direction; _sink inputs_ are always _outputs_ */
1206 +            if (rule->group_for_volume)
1207 +                group_remove_stream_rule(rule->group_for_volume, CONTROL_TYPE_VOLUME, rule);
1208  
1209 -        if ((d->direction == PA_DIRECTION_OUTPUT && l->stream_direction == match_direction_output) ||
1210 -            ((d->direction == PA_DIRECTION_INPUT && l->stream_direction == match_direction_input))) {
1211 -            return true;
1212 -        }
1213 +            rule->group_for_volume = group;
1214 +
1215 +            if (group)
1216 +                group_add_stream_rule(group, CONTROL_TYPE_VOLUME, rule);
1217 +            break;
1218 +
1219 +        case CONTROL_TYPE_MUTE:
1220 +            if (group == rule->group_for_mute)
1221 +                return;
1222 +
1223 +            if (rule->group_for_mute)
1224 +                group_remove_stream_rule(rule->group_for_mute, CONTROL_TYPE_MUTE, rule);
1225 +
1226 +            rule->group_for_mute = group;
1227 +
1228 +            if (group)
1229 +                group_add_stream_rule(group, CONTROL_TYPE_MUTE, rule);
1230 +            break;
1231      }
1232 -    else if (l->property_name && l->property_value) {
1233 -        /* check the property from the property list */
1234 +}
1235  
1236 -        if (pa_proplist_contains(d->proplist, l->property_name)) {
1237 -            const char *prop = pa_proplist_gets(d->proplist, l->property_name);
1238 +static void stream_rule_set_group_name(struct stream_rule *rule, enum control_type type, const char *name) {
1239 +    struct group *group = NULL;
1240  
1241 -            if (prop && strcmp(prop, l->property_value) == 0) {
1242 -                return true;
1243 -            }
1244 -        }
1245 +    pa_assert(rule);
1246 +
1247 +    switch (type) {
1248 +        case CONTROL_TYPE_VOLUME:
1249 +            pa_xfree(rule->audio_group_name_for_volume);
1250 +            rule->audio_group_name_for_volume = pa_xstrdup(name);
1251 +            break;
1252 +
1253 +        case CONTROL_TYPE_MUTE:
1254 +            pa_xfree(rule->audio_group_name_for_mute);
1255 +            rule->audio_group_name_for_mute = pa_xstrdup(name);
1256 +            break;
1257      }
1258  
1259 -    /* no match */
1260 -    return false;
1261 -}
1262 +    if (name)
1263 +        group = pa_hashmap_get(rule->userdata->groups, name);
1264  
1265 -static bool match_rule(struct expression *e, pas_stream *d) {
1266 +    stream_rule_set_group(rule, type, group);
1267 +}
1268  
1269 +static bool stream_rule_match(struct stream_rule *rule, pas_stream *stream) {
1270      struct conjunction *c;
1271  
1272 -    PA_LLIST_FOREACH(c, e->conjunctions) {
1273 +    PA_LLIST_FOREACH(c, rule->match_expression->conjunctions) {
1274          struct literal *l;
1275          bool and_success = true;
1276          PA_LLIST_FOREACH(l, c->literals) {
1277 -            if (!match_predicate(l, d)) {
1278 +            if (!literal_match(l, stream)) {
1279                  /* at least one fail for conjunction */
1280                  and_success = false;
1281                  break;
1282 @@ -421,56 +1012,246 @@ static bool match_rule(struct expression *e, pas_stream *d) {
1283      return false;
1284  }
1285  
1286 -static void classify_stream(struct userdata *u, pas_stream *new_data, bool mute) {
1287 -    /* do the classification here */
1288 +/* stream classification */
1289  
1290 -    struct stream *stream = NULL;
1291 -    unsigned idx;
1292 +static bool literal_match(struct literal *literal, pas_stream *stream) {
1293 +
1294 +    if (literal->stream_direction != match_direction_unknown) {
1295 +        /* check the stream direction; _sink inputs_ are always _outputs_ */
1296  
1297 -    /* go through the stream match definitions in given order */
1298 +        if ((stream->direction == PA_DIRECTION_OUTPUT && literal->stream_direction == match_direction_output) ||
1299 +            (stream->direction == PA_DIRECTION_INPUT && literal->stream_direction == match_direction_input)) {
1300 +            return literal->negation ? false : true;
1301 +        }
1302 +    }
1303 +    else if (literal->property_name && literal->property_value) {
1304 +        /* check the property from the property list */
1305  
1306 -    PA_DYNARRAY_FOREACH(stream, u->streams, idx) {
1307 -        if (stream->rule && match_rule(stream->rule, new_data)) {
1308 -            pa_log_info("stream %s (%s) match with rule %s:", new_data->name, new_data->description, stream->id);
1309 -            print_expression(stream->rule);
1310 +        if (pa_proplist_contains(stream->proplist, literal->property_name)) {
1311 +            const char *prop = pa_proplist_gets(stream->proplist, literal->property_name);
1312  
1313 -            if (mute) {
1314 -                if (new_data->use_default_mute_control && stream->audio_group_for_mute)
1315 -                    pas_stream_bind_mute_control(new_data, stream->mute_control_target_info);
1316 -            } else {
1317 -                if (new_data->use_default_volume_control && stream->audio_group_for_volume)
1318 -                    pas_stream_bind_volume_control(new_data, stream->volume_control_target_info);
1319 -            }
1320 +            if (prop && strcmp(prop, literal->property_value) == 0)
1321 +                return literal->negation ? false : true;
1322 +        }
1323 +    }
1324 +
1325 +    /* no match */
1326 +    return literal->negation ? true : false;
1327 +}
1328 +
1329 +static pa_hook_result_t stream_put_cb(void *hook_data, void *call_data, void *userdata) {
1330 +    struct userdata *u = userdata;
1331 +    pas_stream *stream = call_data;
1332 +    struct stream_rule *rule;
1333 +    unsigned idx;
1334  
1335 -            return;
1336 +    pa_assert(u);
1337 +    pa_assert(stream);
1338 +
1339 +    PA_DYNARRAY_FOREACH(rule, u->stream_rules_list, idx) {
1340 +        if (stream_rule_match(rule, stream)) {
1341 +            pa_hashmap_put(u->rules_by_stream, stream, rule);
1342 +            break;
1343          }
1344      }
1345  
1346 -    /* no matches, don't touch the volumes */
1347 +    return PA_HOOK_OK;
1348 +}
1349 +
1350 +static pa_hook_result_t stream_unlink_cb(void *hook_data, void *call_data, void *userdata) {
1351 +    struct userdata *u = userdata;
1352 +    pas_stream *stream = call_data;
1353 +
1354 +    pa_assert(u);
1355 +    pa_assert(stream);
1356 +
1357 +    pa_hashmap_remove(u->rules_by_stream, stream);
1358 +
1359 +    return PA_HOOK_OK;
1360 +}
1361 +
1362 +static pa_hook_result_t volume_control_implementation_initialized_cb(void *hook_data, void *call_data, void *userdata) {
1363 +    struct userdata *u = userdata;
1364 +    pa_volume_control *volume_control = call_data;
1365 +    struct control *control;
1366 +
1367 +    pa_assert(u);
1368 +    pa_assert(volume_control);
1369 +
1370 +    if (volume_control->purpose != PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME)
1371 +        return PA_HOOK_OK;
1372 +
1373 +    control = control_new_for_stream(u, CONTROL_TYPE_VOLUME, volume_control->owner_stream);
1374 +    control_put(control);
1375 +    pa_assert_se(pa_hashmap_put(u->stream_volume_controls, volume_control->owner_stream, control) >= 0);
1376 +
1377 +    return PA_HOOK_OK;
1378 +}
1379 +
1380 +static pa_hook_result_t mute_control_implementation_initialized_cb(void *hook_data, void *call_data, void *userdata) {
1381 +    struct userdata *u = userdata;
1382 +    pa_mute_control *mute_control = call_data;
1383 +    struct control *control;
1384 +
1385 +    pa_assert(u);
1386 +    pa_assert(mute_control);
1387 +
1388 +    if (mute_control->purpose != PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE)
1389 +        return PA_HOOK_OK;
1390 +
1391 +    control = control_new_for_stream(u, CONTROL_TYPE_MUTE, mute_control->owner_stream);
1392 +    control_put(control);
1393 +    pa_assert_se(pa_hashmap_put(u->stream_mute_controls, mute_control->owner_stream, control) >= 0);
1394 +
1395 +    return PA_HOOK_OK;
1396 +}
1397 +
1398 +static pa_hook_result_t volume_control_set_initial_volume_cb(void *hook_data, void *call_data, void *userdata) {
1399 +    struct userdata *u = userdata;
1400 +    pa_volume_control *volume_control = call_data;
1401 +    struct stream_rule *rule;
1402 +    struct control *control;
1403 +
1404 +    pa_assert(u);
1405 +    pa_assert(volume_control);
1406 +
1407 +    if (volume_control->purpose != PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME)
1408 +        return PA_HOOK_OK;
1409 +
1410 +    rule = pa_hashmap_get(u->rules_by_stream, volume_control->owner_stream);
1411 +    if (!rule)
1412 +        return PA_HOOK_OK;
1413 +
1414 +    if (!rule->group_for_volume)
1415 +        return PA_HOOK_OK;
1416 +
1417 +    if (!rule->group_for_volume->volume_control)
1418 +        return PA_HOOK_OK;
1419 +
1420 +    control = pa_hashmap_get(u->stream_volume_controls, volume_control->owner_stream);
1421 +    pa_assert(control);
1422 +    pa_assert(control->volume_control == volume_control);
1423 +
1424 +    /* This will set the volume for volume_control. */
1425 +    control_set_master(control, rule->group_for_volume->volume_control);
1426 +
1427 +    return PA_HOOK_STOP;
1428 +}
1429 +
1430 +static pa_hook_result_t mute_control_set_initial_mute_cb(void *hook_data, void *call_data, void *userdata) {
1431 +    struct userdata *u = userdata;
1432 +    pa_mute_control *mute_control = call_data;
1433 +    struct stream_rule *rule;
1434 +    struct control *control;
1435 +
1436 +    pa_assert(u);
1437 +    pa_assert(mute_control);
1438 +
1439 +    if (mute_control->purpose != PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE)
1440 +        return PA_HOOK_OK;
1441 +
1442 +    rule = pa_hashmap_get(u->rules_by_stream, mute_control->owner_stream);
1443 +    if (!rule)
1444 +        return PA_HOOK_OK;
1445 +
1446 +    if (!rule->group_for_mute)
1447 +        return PA_HOOK_OK;
1448 +
1449 +    if (!rule->group_for_mute->mute_control)
1450 +        return PA_HOOK_OK;
1451 +
1452 +    control = pa_hashmap_get(u->stream_mute_controls, mute_control->owner_stream);
1453 +    pa_assert(control);
1454 +    pa_assert(control->mute_control == mute_control);
1455 +
1456 +    /* This will set the mute for mute_control. */
1457 +    control_set_master(control, rule->group_for_mute->mute_control);
1458 +
1459 +    return PA_HOOK_STOP;
1460 +}
1461 +
1462 +static pa_hook_result_t volume_control_volume_changed_cb(void *hook_data, void *call_data, void *userdata) {
1463 +    struct userdata *u = userdata;
1464 +    pa_volume_control *volume_control = call_data;
1465 +    struct control *control;
1466 +
1467 +    pa_assert(u);
1468 +    pa_assert(volume_control);
1469 +
1470 +    if (volume_control->purpose != PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME)
1471 +        return PA_HOOK_OK;
1472 +
1473 +    control = pa_hashmap_get(u->stream_volume_controls, volume_control->owner_stream);
1474 +    if (!control)
1475 +        return PA_HOOK_OK;
1476 +
1477 +    if (!control->master)
1478 +        return PA_HOOK_OK;
1479 +
1480 +    pa_volume_control_set_volume(control->master->volume_control, &volume_control->volume, true, true);
1481 +
1482 +    return PA_HOOK_OK;
1483 +}
1484 +
1485 +static pa_hook_result_t mute_control_mute_changed_cb(void *hook_data, void *call_data, void *userdata) {
1486 +    struct userdata *u = userdata;
1487 +    pa_mute_control *mute_control = call_data;
1488 +    struct control *control;
1489 +
1490 +    pa_assert(u);
1491 +    pa_assert(mute_control);
1492 +
1493 +    if (mute_control->purpose != PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE)
1494 +        return PA_HOOK_OK;
1495 +
1496 +    control = pa_hashmap_get(u->stream_mute_controls, mute_control->owner_stream);
1497 +    if (!control)
1498 +        return PA_HOOK_OK;
1499 +
1500 +    if (!control->master)
1501 +        return PA_HOOK_OK;
1502 +
1503 +    pa_mute_control_set_mute(control->master->mute_control, mute_control->mute);
1504 +
1505 +    return PA_HOOK_OK;
1506  }
1507  
1508 -static pa_hook_result_t set_volume_control_cb(
1509 -        void *hook_data,
1510 -        pas_stream *new_data,
1511 -        struct userdata *u) {
1512 +static pa_hook_result_t volume_control_unlink_cb(void *hook_data, void *call_data, void *userdata) {
1513 +    struct userdata *u = userdata;
1514 +    pa_volume_control *volume_control = call_data;
1515 +    struct control *control;
1516  
1517 -    pa_assert(new_data);
1518      pa_assert(u);
1519 +    pa_assert(volume_control);
1520 +
1521 +    if (volume_control->purpose != PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME)
1522 +        return PA_HOOK_OK;
1523  
1524 -    classify_stream(u, new_data, false);
1525 +    control = pa_hashmap_remove(u->stream_volume_controls, volume_control->owner_stream);
1526 +    if (!control)
1527 +        return PA_HOOK_OK;
1528 +
1529 +    control_free(control);
1530  
1531      return PA_HOOK_OK;
1532  }
1533  
1534 -static pa_hook_result_t set_mute_control_cb(
1535 -        void *hook_data,
1536 -        pas_stream *new_data,
1537 -        struct userdata *u) {
1538 +static pa_hook_result_t mute_control_unlink_cb(void *hook_data, void *call_data, void *userdata) {
1539 +    struct userdata *u = userdata;
1540 +    pa_mute_control *mute_control = call_data;
1541 +    struct control *control;
1542  
1543 -    pa_assert(new_data);
1544      pa_assert(u);
1545 +    pa_assert(mute_control);
1546 +
1547 +    if (mute_control->purpose != PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE)
1548 +        return PA_HOOK_OK;
1549  
1550 -    classify_stream(u, new_data, true);
1551 +    control = pa_hashmap_remove(u->stream_mute_controls, mute_control->owner_stream);
1552 +    if (!control)
1553 +        return PA_HOOK_OK;
1554 +
1555 +    control_free(control);
1556  
1557      return PA_HOOK_OK;
1558  }
1559 @@ -520,6 +1301,7 @@ static pa_hook_result_t set_mute_control_cb(
1560      (property application.process.binary=paplay OR (direction input OR direction output))
1561  */
1562  
1563 +#if 0
1564  static void print_literal(struct literal *l) {
1565      if (l->stream_direction != match_direction_unknown) {
1566          pa_log_info("       %sstream direction %s",
1567 @@ -549,6 +1331,7 @@ static void print_expression(struct expression *e) {
1568          print_conjunction(c);
1569      }
1570  }
1571 +#endif
1572  
1573  static void delete_literal(struct literal *l) {
1574  
1575 @@ -573,14 +1356,23 @@ static void delete_conjunction(struct conjunction *c) {
1576      pa_xfree(c);
1577  }
1578  
1579 -static void delete_expression(struct expression *e) {
1580 +static struct expression *expression_new(void) {
1581 +    struct expression *expression;
1582 +
1583 +    expression = pa_xnew0(struct expression, 1);
1584 +
1585 +    return expression;
1586 +}
1587 +
1588 +static void expression_free(struct expression *expression) {
1589      struct conjunction *c;
1590  
1591 -    PA_LLIST_FOREACH(c, e->conjunctions) {
1592 +    pa_assert(expression);
1593 +
1594 +    PA_LLIST_FOREACH(c, expression->conjunctions)
1595          delete_conjunction(c);
1596 -    }
1597  
1598 -    pa_xfree(e);
1599 +    pa_xfree(expression);
1600  }
1601  
1602  enum logic_operator {
1603 @@ -917,26 +1709,21 @@ static bool gather_expression(struct expression *e, struct expression_token *et)
1604      return true;
1605  }
1606  
1607 -static struct expression *parse_rule(const char *rule_string) {
1608 -    char *k, *l;
1609 +static int expression_from_string(const char *str, struct expression **_r) {
1610 +    const char *k;
1611 +    char *l;
1612      struct expression *e = NULL;
1613 -    int len;
1614      char *buf = NULL;
1615      struct expression_token *et = NULL;
1616  
1617 -    if (!rule_string)
1618 -        goto error;
1619 -
1620 -    len = strlen(rule_string);
1621 -
1622 -    buf = (char *) pa_xmalloc0(len);
1623 +    pa_assert(str);
1624 +    pa_assert(_r);
1625  
1626 -    if (!buf)
1627 -        goto error;
1628 +    buf = pa_xmalloc0(strlen(str) + 1);
1629  
1630      /* remove whitespace */
1631  
1632 -    k = (char *) rule_string;
1633 +    k = str;
1634      l = buf;
1635  
1636      while (*k) {
1637 @@ -958,9 +1745,6 @@ static struct expression *parse_rule(const char *rule_string) {
1638  
1639      e = pa_xnew0(struct expression, 1);
1640  
1641 -    if (!e)
1642 -        goto error;
1643 -
1644      PA_LLIST_HEAD_INIT(struct conjunction, e->conjunctions);
1645  
1646      /* gather expressions to actual match format */
1647 @@ -978,30 +1762,15 @@ static struct expression *parse_rule(const char *rule_string) {
1648      delete_expression_token(et);
1649      pa_xfree(buf);
1650  
1651 -    return e;
1652 +    *_r = e;
1653 +    return 0;
1654  
1655  error:
1656      delete_expression_token(et);
1657      pa_xfree(buf);
1658 -    pa_xfree(e);
1659 -    return NULL;
1660 -}
1661 -
1662 -static int parse_audio_groups(pa_config_parser_state *state) {
1663 -    struct userdata *u;
1664 -    char *name;
1665 -    const char *split_state = NULL;
1666 -
1667 -    pa_assert(state);
1668 -
1669 -    u = state->userdata;
1670 -
1671 -    pa_hashmap_remove_all(u->audio_group_names);
1672 +    expression_free(e);
1673  
1674 -    while ((name = pa_split_spaces(state->rvalue, &split_state)))
1675 -        pa_hashmap_put(u->audio_group_names, name, name);
1676 -
1677 -    return 0;
1678 +    return -PA_ERR_INVALID;
1679  }
1680  
1681  static int parse_streams(pa_config_parser_state *state) {
1682 @@ -1013,15 +1782,13 @@ static int parse_streams(pa_config_parser_state *state) {
1683  
1684      u = state->userdata;
1685  
1686 -    pa_dynarray_remove_all(u->stream_names);
1687 -
1688      while ((name = pa_split_spaces(state->rvalue, &split_state))) {
1689          const char *name2;
1690          unsigned idx;
1691          bool duplicate = false;
1692  
1693 -        /* Avoid adding duplicates in u->stream_names. */
1694 -        PA_DYNARRAY_FOREACH(name2, u->stream_names, idx) {
1695 +        /* Avoid adding duplicates in u->stream_rule_names. */
1696 +        PA_DYNARRAY_FOREACH(name2, u->stream_rule_names, idx) {
1697              if (pa_streq(name, name2)) {
1698                  duplicate = true;
1699                  break;
1700 @@ -1033,230 +1800,221 @@ static int parse_streams(pa_config_parser_state *state) {
1701              continue;
1702          }
1703  
1704 -        pa_dynarray_append(u->stream_names, name);
1705 +        pa_dynarray_append(u->stream_rule_names, name);
1706      }
1707  
1708      return 0;
1709  }
1710  
1711 -static int parse_common(pa_config_parser_state *state) {
1712 -#define AUDIOGROUP_START "AudioGroup "
1713 -#define STREAM_START "Stream "
1714 -#define BIND_KEYWORD "bind:"
1715 -#define NONE_KEYWORD "none"
1716 -
1717 -    char *section;
1718 -    struct userdata *u = (struct userdata *) state->userdata;
1719 -    int r;
1720 -    pa_binding_target_info *target_info;
1721 -
1722 +static int parse_group_control(pa_config_parser_state *state, struct group *group, enum control_type type) {
1723      pa_assert(state);
1724 +    pa_assert(group);
1725  
1726 -    section = state->section;
1727 -    if (!section)
1728 -        goto error;
1729 +    if (pa_streq(state->rvalue, NONE_KEYWORD))
1730 +        group_disable_control(group, type);
1731 +
1732 +    else if (pa_startswith(state->rvalue, CREATE_PREFIX))
1733 +        group_set_own_control_name(group, type, state->rvalue + strlen(CREATE_PREFIX));
1734  
1735 -    if (strncmp(section, AUDIOGROUP_START, strlen(AUDIOGROUP_START)) == 0) {
1736 -        char *ag_name = section + strlen(AUDIOGROUP_START);
1737 -        struct audio_group *ag = (struct audio_group *) pa_hashmap_get(u->unused_audio_groups, ag_name);
1738 +    else if (pa_startswith(state->rvalue, BIND_PREFIX)) {
1739 +        if (pa_startswith(state->rvalue, BIND_AUDIO_GROUP_PREFIX)) {
1740 +            int r;
1741  
1742 -        if (!ag) {
1743 -            /* first item for this audio group section, so create the struct */
1744 -            ag = audio_group_new(u, ag_name);
1745 -            pa_hashmap_put(u->unused_audio_groups, ag->id, ag);
1746 +            r = group_set_master_name(group, type, state->rvalue + strlen(BIND_AUDIO_GROUP_PREFIX));
1747 +            if (r < 0) {
1748 +                pa_log("[%s:%u] Failed to set binding target \"%s\".", state->filename, state->lineno, state->rvalue + strlen(BIND_PREFIX));
1749 +                return r;
1750 +            }
1751 +        } else {
1752 +            pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue + strlen(BIND_PREFIX));
1753 +            return -PA_ERR_INVALID;
1754          }
1755 +    } else {
1756 +        pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
1757 +        return -PA_ERR_INVALID;
1758 +    }
1759  
1760 -        if (strcmp(state->lvalue, "description") == 0)
1761 -            audio_group_set_description(ag, state->rvalue);
1762 +    return 0;
1763 +}
1764 +
1765 +static int parse_common(pa_config_parser_state *state) {
1766 +    char *section;
1767 +    struct userdata *u = state->userdata;
1768 +    const char *name;
1769 +    int r;
1770  
1771 -        else if (strcmp(state->lvalue, "volume-control") == 0) {
1772 -            if (pa_streq(state->rvalue, "create"))
1773 -                audio_group_set_volume_control_action(ag, CONTROL_ACTION_CREATE, NULL);
1774 +    pa_assert(state);
1775  
1776 -            else if (pa_streq(state->rvalue, NONE_KEYWORD))
1777 -                audio_group_set_volume_control_action(ag, CONTROL_ACTION_NONE, NULL);
1778 +    section = state->section;
1779 +    if (!section) {
1780 +        pa_log("[%s:%u] Lvalue \"%s\" not expected in the General section.", state->filename, state->lineno, state->lvalue);
1781 +        return -PA_ERR_INVALID;
1782 +    }
1783  
1784 -            else if (pa_startswith(state->rvalue, BIND_KEYWORD)) {
1785 -                r = pa_binding_target_info_new_from_string(state->rvalue, "volume_control", &target_info);
1786 -                if (r < 0) {
1787 -                    pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue);
1788 -                    goto error;
1789 -                }
1790 +    if (pa_startswith(section, AUDIOGROUP_START)) {
1791 +        struct group *group;
1792  
1793 -                audio_group_set_volume_control_action(ag, CONTROL_ACTION_BIND, target_info);
1794 -                pa_binding_target_info_free(target_info);
1795 +        name = section + strlen(AUDIOGROUP_START);
1796  
1797 -            } else {
1798 -                pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
1799 -                goto error;
1800 +        group = pa_hashmap_get(u->groups, name);
1801 +        if (!group) {
1802 +            r = group_new(u, name, &group);
1803 +            if (r < 0) {
1804 +                pa_log("[%s:%u] Failed to create an audio group with name \"%s\".", state->filename, state->lineno, name);
1805 +                return r;
1806              }
1807 +
1808 +            pa_hashmap_put(u->groups, (void *) group->audio_group->name, group);
1809          }
1810 -        else if (strcmp(state->lvalue, "mute-control") == 0) {
1811 -            if (pa_streq(state->rvalue, "create"))
1812 -                audio_group_set_mute_control_action(ag, CONTROL_ACTION_CREATE, NULL);
1813 -
1814 -            else if (pa_streq(state->rvalue, NONE_KEYWORD))
1815 -                audio_group_set_mute_control_action(ag, CONTROL_ACTION_NONE, NULL);
1816 -
1817 -            else if (pa_startswith(state->rvalue, BIND_KEYWORD)) {
1818 -                r = pa_binding_target_info_new_from_string(state->rvalue, "mute_control", &target_info);
1819 -                if (r < 0) {
1820 -                    pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue);
1821 -                    goto error;
1822 -                }
1823  
1824 -                audio_group_set_mute_control_action(ag, CONTROL_ACTION_BIND, target_info);
1825 -                pa_binding_target_info_free(target_info);
1826 +        if (pa_streq(state->lvalue, "description"))
1827 +            pa_audio_group_set_description(group->audio_group, state->rvalue);
1828  
1829 -            } else {
1830 -                pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
1831 -                goto error;
1832 -            }
1833 +        else if (pa_streq(state->lvalue, "volume-control"))
1834 +            return parse_group_control(state, group, CONTROL_TYPE_VOLUME);
1835 +
1836 +        else if (pa_streq(state->lvalue, "mute-control"))
1837 +            return parse_group_control(state, group, CONTROL_TYPE_MUTE);
1838 +
1839 +        else {
1840 +            pa_log("[%s:%u] Lvalue \"%s\" not expected in the AudioGroup section.", state->filename, state->lineno, state->lvalue);
1841 +            return -PA_ERR_INVALID;
1842          }
1843      }
1844 -    else if (strncmp(section, STREAM_START, strlen(STREAM_START)) == 0) {
1845 -        char *stream_name = section + strlen(STREAM_START);
1846 +    else if (pa_startswith(section, STREAM_RULE_START)) {
1847 +        struct stream_rule *rule;
1848  
1849 -        struct stream *stream = (struct stream *) pa_hashmap_get(u->unused_streams, stream_name);
1850 +        name = section + strlen(STREAM_RULE_START);
1851  
1852 -        if (!stream) {
1853 -            /* first item for this stream section, so create the struct */
1854 -            stream = stream_new(u, stream_name);
1855 -            pa_hashmap_put(u->unused_streams, stream->id, stream);
1856 +        rule = pa_hashmap_get(u->stream_rules, name);
1857 +        if (!rule) {
1858 +            rule = stream_rule_new(u, name);
1859 +            pa_hashmap_put(u->stream_rules, rule->name, rule);
1860          }
1861  
1862          if (pa_streq(state->lvalue, "audio-group-for-volume"))
1863 -            stream_set_audio_group_name_for_volume(stream, *state->rvalue ? state->rvalue : NULL);
1864 +            stream_rule_set_group_name(rule, CONTROL_TYPE_VOLUME, state->rvalue);
1865  
1866          else if (pa_streq(state->lvalue, "audio-group-for-mute"))
1867 -            stream_set_audio_group_name_for_mute(stream, *state->rvalue ? state->rvalue : NULL);
1868 +            stream_rule_set_group_name(rule, CONTROL_TYPE_MUTE, state->rvalue);
1869  
1870 -        else if (strcmp(state->lvalue, "match") == 0) {
1871 -            if (!state->rvalue)
1872 -                goto error;
1873 +        else if (pa_streq(state->lvalue, "match")) {
1874 +            struct expression *expression;
1875  
1876 -            stream->rule = parse_rule(state->rvalue);
1877 -
1878 -            if (!stream->rule) {
1879 -                goto error;
1880 +            r = expression_from_string(state->rvalue, &expression);
1881 +            if (r < 0) {
1882 +                pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
1883 +                return r;
1884              }
1885 +
1886 +            stream_rule_set_match_expression(rule, expression);
1887          }
1888      }
1889  
1890      return 0;
1891 -
1892 -error:
1893 -
1894 -    pa_log_error("failed parsing audio group definition file");
1895 -    return -1;
1896 -
1897 -#undef NONE_KEYWORD
1898 -#undef AUDIO_GROUP_KEYWORD
1899 -#undef BIND_KEYWORD
1900 -#undef STREAM_START
1901 -#undef AUDIOGROUP_START
1902  }
1903  
1904 -static void finalize_config(struct userdata *u) {
1905 -    const char *group_name;
1906 +int pa__init(pa_module *module) {
1907 +    pa_modargs *ma = NULL;
1908 +    struct userdata *u;
1909 +    FILE *f;
1910 +    char *fn = NULL;
1911 +    struct group *group;
1912      void *state;
1913 -    struct audio_group *group;
1914 -    const char *stream_name;
1915 +    const char *name;
1916      unsigned idx;
1917 -    struct stream *stream;
1918  
1919 -    pa_assert(u);
1920 -
1921 -    PA_HASHMAP_FOREACH(group_name, u->audio_group_names, state) {
1922 -        int r;
1923 +    pa_assert(module);
1924  
1925 -        group = pa_hashmap_remove(u->unused_audio_groups, group_name);
1926 -        if (!group)
1927 -            group = audio_group_new(u, group_name);
1928 -
1929 -        r = audio_group_put(group);
1930 -        if (r < 0) {
1931 -            pa_log("Failed to create audio group %s.", group_name);
1932 -            audio_group_free(group);
1933 -            continue;
1934 -        }
1935 -
1936 -        pa_assert_se(pa_hashmap_put(u->audio_groups, group->id, group) >= 0);
1937 +    if (!(ma = pa_modargs_new(module->argument, valid_modargs))) {
1938 +        pa_log("Failed to parse module arguments");
1939 +        goto fail;
1940      }
1941  
1942 -    PA_HASHMAP_FOREACH(group, u->unused_audio_groups, state)
1943 -        pa_log_debug("Audio group %s is not used.", group->id);
1944 -
1945 -    pa_hashmap_free(u->unused_audio_groups);
1946 -    u->unused_audio_groups = NULL;
1947 -
1948 -    pa_hashmap_free(u->audio_group_names);
1949 -    u->audio_group_names = NULL;
1950 -
1951 -    PA_DYNARRAY_FOREACH(stream_name, u->stream_names, idx) {
1952 -        stream = pa_hashmap_remove(u->unused_streams, stream_name);
1953 -        if (!stream) {
1954 -            pa_log("Reference to undefined stream %s, ignoring.", stream_name);
1955 -            continue;
1956 -        }
1957 +    u = module->userdata = pa_xnew0(struct userdata, 1);
1958 +    u->volume_api = pa_volume_api_get(module->core);
1959 +    u->groups = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
1960 +                                    (pa_free_cb_t) group_free);
1961 +    u->stream_rules = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
1962 +                                          (pa_free_cb_t) stream_rule_free);
1963 +    u->stream_rules_list = pa_dynarray_new(NULL);
1964 +    u->rules_by_stream = pa_hashmap_new(NULL, NULL);
1965 +    u->stream_volume_controls = pa_hashmap_new_full(NULL, NULL, NULL, (pa_free_cb_t) control_free);
1966 +    u->stream_mute_controls = pa_hashmap_new_full(NULL, NULL, NULL, (pa_free_cb_t) control_free);
1967 +    u->stream_put_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_PUT], PA_HOOK_NORMAL, stream_put_cb,
1968 +                                         u);
1969 +    u->stream_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_UNLINK], PA_HOOK_NORMAL,
1970 +                                            stream_unlink_cb, u);
1971 +    u->volume_control_implementation_initialized_slot =
1972 +            pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_IMPLEMENTATION_INITIALIZED],
1973 +                            PA_HOOK_NORMAL, volume_control_implementation_initialized_cb, u);
1974 +    u->mute_control_implementation_initialized_slot =
1975 +            pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_IMPLEMENTATION_INITIALIZED],
1976 +                            PA_HOOK_NORMAL, mute_control_implementation_initialized_cb, u);
1977 +    u->volume_control_set_initial_volume_slot =
1978 +            pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_SET_INITIAL_VOLUME], PA_HOOK_NORMAL,
1979 +                            volume_control_set_initial_volume_cb, u);
1980 +    u->mute_control_set_initial_mute_slot =
1981 +            pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_SET_INITIAL_MUTE], PA_HOOK_NORMAL,
1982 +                            mute_control_set_initial_mute_cb, u);
1983 +    u->volume_control_volume_changed_slot =
1984 +            pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_VOLUME_CHANGED], PA_HOOK_NORMAL,
1985 +                            volume_control_volume_changed_cb, u);
1986 +    u->mute_control_mute_changed_slot =
1987 +            pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_MUTE_CHANGED], PA_HOOK_NORMAL,
1988 +                            mute_control_mute_changed_cb, u);
1989 +    u->volume_control_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK],
1990 +                                                    PA_HOOK_NORMAL, volume_control_unlink_cb, u);
1991 +    u->mute_control_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK],
1992 +                                                  PA_HOOK_NORMAL, mute_control_unlink_cb, u);
1993 +    u->stream_rule_names = pa_dynarray_new(pa_xfree);
1994 +
1995 +    f = pa_open_config_file(PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "audio-groups.conf", "audio-groups.conf", NULL, &fn);
1996 +    if (f) {
1997 +        pa_config_item config_items[] = {
1998 +            { "stream-rules", parse_streams, NULL, "General" },
1999 +            { NULL, parse_common, NULL, NULL },
2000 +            { NULL, NULL, NULL, NULL },
2001 +        };
2002  
2003 -        stream_put(stream);
2004 -        pa_dynarray_append(u->streams, stream);
2005 +        pa_config_parse(fn, f, config_items, NULL, u);
2006 +        pa_xfree(fn);
2007 +        fn = NULL;
2008 +        fclose(f);
2009 +        f = NULL;
2010      }
2011  
2012 -    PA_HASHMAP_FOREACH(stream, u->unused_streams, state)
2013 -        pa_log_debug("Stream %s is not used.", stream->id);
2014 -
2015 -    pa_hashmap_free(u->unused_streams);
2016 -    u->unused_streams = NULL;
2017 -
2018 -    pa_dynarray_free(u->stream_names);
2019 -    u->stream_names = NULL;
2020 -}
2021 +    PA_HASHMAP_FOREACH(group, u->groups, state)
2022 +        group_put(group);
2023  
2024 -static bool parse_configuration(struct userdata *u, const char *filename) {
2025 -    FILE *f;
2026 -    char *fn = NULL;
2027 +    PA_DYNARRAY_FOREACH(name, u->stream_rule_names, idx) {
2028 +        struct stream_rule *rule;
2029  
2030 -    pa_config_item table[] = {
2031 -        { "audio-groups", parse_audio_groups, NULL, "General" },
2032 -        { "streams", parse_streams, NULL, "General" },
2033 -        { NULL, parse_common, NULL, NULL },
2034 -        { NULL, NULL, NULL, NULL },
2035 -    };
2036 +        rule = pa_hashmap_get(u->stream_rules, name);
2037 +        if (rule)
2038 +            pa_dynarray_append(u->stream_rules_list, rule);
2039 +        else
2040 +            pa_log("Non-existent stream rule \"%s\" referenced, ignoring.", name);
2041 +    }
2042  
2043 -    u->audio_group_names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
2044 -    u->unused_audio_groups = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2045 -                                                 (pa_free_cb_t) audio_group_free);
2046 -    u->stream_names = pa_dynarray_new(pa_xfree);
2047 -    u->unused_streams = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2048 -                                            (pa_free_cb_t) stream_free);
2049 +    pa_dynarray_free(u->stream_rule_names);
2050 +    u->stream_rule_names = NULL;
2051  
2052 -    if (pa_is_path_absolute(filename))
2053 -        f = pa_open_config_file(filename, NULL, NULL, &fn);
2054 -    else {
2055 -        char *sys_conf_file;
2056 +    pa_modargs_free(ma);
2057  
2058 -        sys_conf_file = pa_sprintf_malloc(PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "%s", filename);
2059 -        f = pa_open_config_file(sys_conf_file, filename, NULL, &fn);
2060 -        pa_xfree(sys_conf_file);
2061 -    }
2062 +    return 0;
2063  
2064 -    if (f) {
2065 -        pa_config_parse(fn, f, table, NULL, u);
2066 -        pa_xfree(fn);
2067 -        fn = NULL;
2068 -        fclose(f);
2069 -        f = NULL;
2070 -    }
2071 +fail:
2072 +    pa__done(module);
2073  
2074 -    finalize_config(u);
2075 +    if (ma)
2076 +        pa_modargs_free(ma);
2077  
2078 -    return true;
2079 +    return -1;
2080  }
2081  
2082  void pa__done(pa_module *m) {
2083 -    struct userdata* u;
2084 +    struct userdata *u;
2085  
2086      pa_assert(m);
2087  
2088 @@ -1265,70 +2023,56 @@ void pa__done(pa_module *m) {
2089      if (!u)
2090          return;
2091  
2092 -    if (u->new_stream_volume)
2093 -        pa_hook_slot_free(u->new_stream_volume);
2094 +    if (u->mute_control_unlink_slot)
2095 +        pa_hook_slot_free(u->mute_control_unlink_slot);
2096  
2097 -    if (u->new_stream_mute)
2098 -        pa_hook_slot_free(u->new_stream_mute);
2099 +    if (u->volume_control_unlink_slot)
2100 +        pa_hook_slot_free(u->volume_control_unlink_slot);
2101  
2102 -    if (u->streams)
2103 -        pa_dynarray_free(u->streams);
2104 +    if (u->mute_control_mute_changed_slot)
2105 +        pa_hook_slot_free(u->mute_control_mute_changed_slot);
2106  
2107 -    if (u->audio_groups)
2108 -        pa_hashmap_free(u->audio_groups);
2109 +    if (u->volume_control_volume_changed_slot)
2110 +        pa_hook_slot_free(u->volume_control_volume_changed_slot);
2111  
2112 -    if (u->api)
2113 -        pa_volume_api_unref(u->api);
2114 +    if (u->mute_control_set_initial_mute_slot)
2115 +        pa_hook_slot_free(u->mute_control_set_initial_mute_slot);
2116  
2117 -    pa_xfree(u);
2118 -}
2119 +    if (u->volume_control_set_initial_volume_slot)
2120 +        pa_hook_slot_free(u->volume_control_set_initial_volume_slot);
2121  
2122 -int pa__init(pa_module *m) {
2123 -    pa_modargs *ma = NULL;
2124 -    struct userdata *u;
2125 -    const char *filename;
2126 +    if (u->mute_control_implementation_initialized_slot)
2127 +        pa_hook_slot_free(u->mute_control_implementation_initialized_slot);
2128  
2129 -    pa_assert(m);
2130 +    if (u->volume_control_implementation_initialized_slot)
2131 +        pa_hook_slot_free(u->volume_control_implementation_initialized_slot);
2132  
2133 -    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
2134 -        pa_log("Failed to parse module arguments");
2135 -        goto error;
2136 -    }
2137 +    if (u->stream_unlink_slot)
2138 +        pa_hook_slot_free(u->stream_unlink_slot);
2139  
2140 -    u = m->userdata = pa_xnew0(struct userdata, 1);
2141 +    if (u->stream_put_slot)
2142 +        pa_hook_slot_free(u->stream_put_slot);
2143  
2144 -    if (!u)
2145 -        goto error;
2146 +    if (u->stream_mute_controls)
2147 +        pa_hashmap_free(u->stream_mute_controls);
2148  
2149 -    u->api = pa_volume_api_get(m->core);
2150 +    if (u->stream_volume_controls)
2151 +        pa_hashmap_free(u->stream_volume_controls);
2152  
2153 -    if (!u->api)
2154 -        goto error;
2155 -
2156 -    u->audio_groups = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2157 -                                          (pa_free_cb_t) audio_group_free);
2158 -    u->streams = pa_dynarray_new((pa_free_cb_t) stream_free);
2159 -
2160 -    filename = pa_modargs_get_value(ma, "filename", AUDIO_GROUP_CONFIG);
2161 -
2162 -    if (!parse_configuration(u, filename))
2163 -        goto error;
2164 +    if (u->rules_by_stream)
2165 +        pa_hashmap_free(u->rules_by_stream);
2166  
2167 -    u->new_stream_volume = pa_hook_connect(&u->api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_VOLUME_CONTROL], PA_HOOK_EARLY, (pa_hook_cb_t) set_volume_control_cb, u);
2168 -    u->new_stream_mute = pa_hook_connect(&u->api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_MUTE_CONTROL], PA_HOOK_EARLY, (pa_hook_cb_t) set_mute_control_cb, u);
2169 +    if (u->stream_rules_list)
2170 +        pa_dynarray_free(u->stream_rules_list);
2171  
2172 -    if (!u->new_stream_volume || !u->new_stream_mute)
2173 -        goto error;
2174 +    if (u->stream_rules)
2175 +        pa_hashmap_free(u->stream_rules);
2176  
2177 -    pa_modargs_free(ma);
2178 +    if (u->groups)
2179 +        pa_hashmap_free(u->groups);
2180  
2181 -    return 0;
2182 +    if (u->volume_api)
2183 +        pa_volume_api_unref(u->volume_api);
2184  
2185 -error:
2186 -    pa__done(m);
2187 -
2188 -    if (ma)
2189 -        pa_modargs_free(ma);
2190 -
2191 -    return -1;
2192 +    pa_xfree(u);
2193  }
2194 diff --git a/src/modules/main-volume-policy/main-volume-context.c b/src/modules/main-volume-policy/main-volume-context.c
2195 index 7ac35c6..9b9f9fd 100644
2196 --- a/src/modules/main-volume-policy/main-volume-context.c
2197 +++ b/src/modules/main-volume-policy/main-volume-context.c
2198 @@ -28,32 +28,33 @@
2199  #include <modules/volume-api/mute-control.h>
2200  #include <modules/volume-api/volume-control.h>
2201  
2202 -int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, const char *description,
2203 -                               pa_main_volume_context **context) {
2204 -    pa_main_volume_context *context_local;
2205 +#include <pulsecore/core-util.h>
2206 +
2207 +int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, void *userdata, pa_main_volume_context **_r) {
2208 +    pa_main_volume_context *context;
2209      int r;
2210  
2211      pa_assert(policy);
2212      pa_assert(name);
2213 -    pa_assert(description);
2214 -    pa_assert(context);
2215 +    pa_assert(_r);
2216  
2217 -    context_local = pa_xnew0(struct pa_main_volume_context, 1);
2218 -    context_local->main_volume_policy = policy;
2219 -    context_local->index = pa_main_volume_policy_allocate_main_volume_context_index(policy);
2220 +    context = pa_xnew0(struct pa_main_volume_context, 1);
2221 +    context->main_volume_policy = policy;
2222 +    context->index = pa_main_volume_policy_allocate_main_volume_context_index(policy);
2223  
2224 -    r = pa_main_volume_policy_register_name(policy, name, true, &context_local->name);
2225 +    r = pa_main_volume_policy_register_name(policy, name, true, &context->name);
2226      if (r < 0)
2227          goto fail;
2228  
2229 -    context_local->description = pa_xstrdup(description);
2230 -
2231 -    *context = context_local;
2232 +    context->description = pa_xstrdup(context->name);
2233 +    context->userdata = userdata;
2234  
2235 +    *_r = context;
2236      return 0;
2237  
2238  fail:
2239 -    pa_main_volume_context_free(context_local);
2240 +    if (context)
2241 +        pa_main_volume_context_free(context);
2242  
2243      return r;
2244  }
2245 @@ -62,7 +63,6 @@ void pa_main_volume_context_put(pa_main_volume_context *context) {
2246      pa_assert(context);
2247  
2248      pa_main_volume_policy_add_main_volume_context(context->main_volume_policy, context);
2249 -
2250      context->linked = true;
2251  
2252      pa_log_debug("Created main volume context #%u.", context->index);
2253 @@ -93,40 +93,21 @@ void pa_main_volume_context_unlink(pa_main_volume_context *context) {
2254      pa_log_debug("Unlinking main volume context %s.", context->name);
2255  
2256      if (context->linked)
2257 -        pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK], context);
2258 -
2259 -    if (context->main_input_mute_control_binding) {
2260 -        pa_binding_free(context->main_input_mute_control_binding);
2261 -        context->main_input_mute_control_binding = NULL;
2262 -    }
2263 +        pa_main_volume_policy_remove_main_volume_context(context->main_volume_policy, context);
2264  
2265 -    if (context->main_output_mute_control_binding) {
2266 -        pa_binding_free(context->main_output_mute_control_binding);
2267 -        context->main_output_mute_control_binding = NULL;
2268 -    }
2269 -
2270 -    if (context->main_input_volume_control_binding) {
2271 -        pa_binding_free(context->main_input_volume_control_binding);
2272 -        context->main_input_volume_control_binding = NULL;
2273 -    }
2274 -
2275 -    if (context->main_output_volume_control_binding) {
2276 -        pa_binding_free(context->main_output_volume_control_binding);
2277 -        context->main_output_volume_control_binding = NULL;
2278 -    }
2279 +    pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK], context);
2280  
2281      context->main_input_mute_control = NULL;
2282      context->main_output_mute_control = NULL;
2283      context->main_input_volume_control = NULL;
2284      context->main_output_volume_control = NULL;
2285 -
2286 -    pa_main_volume_policy_remove_main_volume_context(context->main_volume_policy, context);
2287  }
2288  
2289  void pa_main_volume_context_free(pa_main_volume_context *context) {
2290      pa_assert(context);
2291  
2292 -    if (!context->unlinked)
2293 +    /* unlink() expects name to be set. */
2294 +    if (!context->unlinked && context->name)
2295          pa_main_volume_context_unlink(context);
2296  
2297      pa_xfree(context->description);
2298 @@ -137,13 +118,33 @@ void pa_main_volume_context_free(pa_main_volume_context *context) {
2299      pa_xfree(context);
2300  }
2301  
2302 -const char *pa_main_volume_context_get_name(pa_main_volume_context *context) {
2303 +void pa_main_volume_context_set_description(pa_main_volume_context *context, const char *description) {
2304 +    char *old_description;
2305 +
2306      pa_assert(context);
2307 +    pa_assert(description);
2308 +
2309 +    old_description = context->description;
2310 +
2311 +    if (pa_streq(description, old_description))
2312 +        return;
2313  
2314 -    return context->name;
2315 +    context->description = pa_xstrdup(description);
2316 +
2317 +    if (!context->linked || context->unlinked) {
2318 +        pa_xfree(old_description);
2319 +        return;
2320 +    }
2321 +
2322 +    pa_log_debug("Main volume context %s description changed from \"%s\" to \"%s\".", context->name, old_description,
2323 +                 description);
2324 +    pa_xfree(old_description);
2325 +
2326 +    pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_DESCRIPTION_CHANGED],
2327 +                 context);
2328  }
2329  
2330 -static void set_main_output_volume_control_internal(pa_main_volume_context *context, pa_volume_control *control) {
2331 +void pa_main_volume_context_set_main_output_volume_control(pa_main_volume_context *context, pa_volume_control *control) {
2332      pa_volume_control *old_control;
2333  
2334      pa_assert(context);
2335 @@ -158,7 +159,7 @@ static void set_main_output_volume_control_internal(pa_main_volume_context *cont
2336      if (!context->linked || context->unlinked)
2337          return;
2338  
2339 -    pa_log_debug("The main output volume control of main volume context %s changed from %s to %s.", context->name,
2340 +    pa_log_debug("Main volume context %s main output volume control changed from %s to %s.", context->name,
2341                   old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
2342  
2343      pa_hook_fire(&context->main_volume_policy->hooks
2344 @@ -166,24 +167,7 @@ static void set_main_output_volume_control_internal(pa_main_volume_context *cont
2345                   context);
2346  }
2347  
2348 -void pa_main_volume_context_bind_main_output_volume_control(pa_main_volume_context *context,
2349 -                                                            pa_binding_target_info *target_info) {
2350 -    pa_binding_owner_info owner_info = {
2351 -        .userdata = context,
2352 -        .set_value = (pa_binding_set_value_cb_t) set_main_output_volume_control_internal,
2353 -    };
2354 -
2355 -    pa_assert(context);
2356 -    pa_assert(target_info);
2357 -
2358 -    if (context->main_output_volume_control_binding)
2359 -        pa_binding_free(context->main_output_volume_control_binding);
2360 -
2361 -    context->main_output_volume_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
2362 -                                                                 target_info);
2363 -}
2364 -
2365 -static void set_main_input_volume_control_internal(pa_main_volume_context *context, pa_volume_control *control) {
2366 +void pa_main_volume_context_set_main_input_volume_control(pa_main_volume_context *context, pa_volume_control *control) {
2367      pa_volume_control *old_control;
2368  
2369      pa_assert(context);
2370 @@ -198,7 +182,7 @@ static void set_main_input_volume_control_internal(pa_main_volume_context *conte
2371      if (!context->linked || context->unlinked)
2372          return;
2373  
2374 -    pa_log_debug("The main input volume control of main volume context %s changed from %s to %s.", context->name,
2375 +    pa_log_debug("Main volume context %s main input volume control changed from %s to %s.", context->name,
2376                   old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
2377  
2378      pa_hook_fire(&context->main_volume_policy->hooks
2379 @@ -206,24 +190,7 @@ static void set_main_input_volume_control_internal(pa_main_volume_context *conte
2380                   context);
2381  }
2382  
2383 -void pa_main_volume_context_bind_main_input_volume_control(pa_main_volume_context *context,
2384 -                                                           pa_binding_target_info *target_info) {
2385 -    pa_binding_owner_info owner_info = {
2386 -        .userdata = context,
2387 -        .set_value = (pa_binding_set_value_cb_t) set_main_input_volume_control_internal,
2388 -    };
2389 -
2390 -    pa_assert(context);
2391 -    pa_assert(target_info);
2392 -
2393 -    if (context->main_input_volume_control_binding)
2394 -        pa_binding_free(context->main_input_volume_control_binding);
2395 -
2396 -    context->main_input_volume_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
2397 -                                                                target_info);
2398 -}
2399 -
2400 -static void set_main_output_mute_control_internal(pa_main_volume_context *context, pa_mute_control *control) {
2401 +void pa_main_volume_context_set_main_output_mute_control(pa_main_volume_context *context, pa_mute_control *control) {
2402      pa_mute_control *old_control;
2403  
2404      pa_assert(context);
2405 @@ -238,7 +205,7 @@ static void set_main_output_mute_control_internal(pa_main_volume_context *contex
2406      if (!context->linked || context->unlinked)
2407          return;
2408  
2409 -    pa_log_debug("The main output mute control of main volume context %s changed from %s to %s.", context->name,
2410 +    pa_log_debug("Main volume context %s main output mute control changed from %s to %s.", context->name,
2411                   old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
2412  
2413      pa_hook_fire(&context->main_volume_policy->hooks
2414 @@ -246,24 +213,7 @@ static void set_main_output_mute_control_internal(pa_main_volume_context *contex
2415                   context);
2416  }
2417  
2418 -void pa_main_volume_context_bind_main_output_mute_control(pa_main_volume_context *context,
2419 -                                                          pa_binding_target_info *target_info) {
2420 -    pa_binding_owner_info owner_info = {
2421 -        .userdata = context,
2422 -        .set_value = (pa_binding_set_value_cb_t) set_main_output_mute_control_internal,
2423 -    };
2424 -
2425 -    pa_assert(context);
2426 -    pa_assert(target_info);
2427 -
2428 -    if (context->main_output_mute_control_binding)
2429 -        pa_binding_free(context->main_output_mute_control_binding);
2430 -
2431 -    context->main_output_mute_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
2432 -                                                               target_info);
2433 -}
2434 -
2435 -static void set_main_input_mute_control_internal(pa_main_volume_context *context, pa_mute_control *control) {
2436 +void pa_main_volume_context_set_main_input_mute_control(pa_main_volume_context *context, pa_mute_control *control) {
2437      pa_mute_control *old_control;
2438  
2439      pa_assert(context);
2440 @@ -278,48 +228,10 @@ static void set_main_input_mute_control_internal(pa_main_volume_context *context
2441      if (!context->linked || context->unlinked)
2442          return;
2443  
2444 -    pa_log_debug("The main input mute control of main volume context %s changed from %s to %s.", context->name,
2445 +    pa_log_debug("Main volume context %s main input mute control changed from %s to %s.", context->name,
2446                   old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
2447  
2448      pa_hook_fire(&context->main_volume_policy->hooks
2449                       [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_MUTE_CONTROL_CHANGED],
2450                   context);
2451  }
2452 -
2453 -void pa_main_volume_context_bind_main_input_mute_control(pa_main_volume_context *context,
2454 -                                                         pa_binding_target_info *target_info) {
2455 -    pa_binding_owner_info owner_info = {
2456 -        .userdata = context,
2457 -        .set_value = (pa_binding_set_value_cb_t) set_main_input_mute_control_internal,
2458 -    };
2459 -
2460 -    pa_assert(context);
2461 -    pa_assert(target_info);
2462 -
2463 -    if (context->main_input_mute_control_binding)
2464 -        pa_binding_free(context->main_input_mute_control_binding);
2465 -
2466 -    context->main_input_mute_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
2467 -                                                              target_info);
2468 -}
2469 -
2470 -pa_binding_target_type *pa_main_volume_context_create_binding_target_type(pa_main_volume_policy *policy) {
2471 -    pa_binding_target_type *type;
2472 -
2473 -    pa_assert(policy);
2474 -
2475 -    type = pa_binding_target_type_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, policy->main_volume_contexts,
2476 -                                      &policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT],
2477 -                                      &policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK],
2478 -                                      (pa_binding_target_type_get_name_cb_t) pa_main_volume_context_get_name);
2479 -    pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL,
2480 -                                     PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_output_volume_control));
2481 -    pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL,
2482 -                                     PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_input_volume_control));
2483 -    pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL,
2484 -                                     PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_output_mute_control));
2485 -    pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL,
2486 -                                     PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_input_mute_control));
2487 -
2488 -    return type;
2489 -}
2490 diff --git a/src/modules/main-volume-policy/main-volume-context.h b/src/modules/main-volume-policy/main-volume-context.h
2491 index 4a0a6f7..3770168 100644
2492 --- a/src/modules/main-volume-policy/main-volume-context.h
2493 +++ b/src/modules/main-volume-policy/main-volume-context.h
2494 @@ -24,16 +24,8 @@
2495  
2496  #include <modules/main-volume-policy/main-volume-policy.h>
2497  
2498 -#include <modules/volume-api/binding.h>
2499 -
2500  typedef struct pa_main_volume_context pa_main_volume_context;
2501  
2502 -#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE "MainVolumeContext"
2503 -#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL "main_output_volume_control"
2504 -#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL "main_input_volume_control"
2505 -#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL "main_output_mute_control"
2506 -#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL "main_input_mute_control"
2507 -
2508  struct pa_main_volume_context {
2509      pa_main_volume_policy *main_volume_policy;
2510      uint32_t index;
2511 @@ -44,32 +36,21 @@ struct pa_main_volume_context {
2512      pa_mute_control *main_output_mute_control;
2513      pa_mute_control *main_input_mute_control;
2514  
2515 -    pa_binding *main_output_volume_control_binding;
2516 -    pa_binding *main_input_volume_control_binding;
2517 -    pa_binding *main_output_mute_control_binding;
2518 -    pa_binding *main_input_mute_control_binding;
2519 -
2520      bool linked;
2521      bool unlinked;
2522 +
2523 +    void *userdata;
2524  };
2525  
2526 -int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, const char *description,
2527 -                               pa_main_volume_context **context);
2528 +int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, void *userdata, pa_main_volume_context **_r);
2529  void pa_main_volume_context_put(pa_main_volume_context *context);
2530  void pa_main_volume_context_unlink(pa_main_volume_context *context);
2531  void pa_main_volume_context_free(pa_main_volume_context *context);
2532  
2533 -const char *pa_main_volume_context_get_name(pa_main_volume_context *context);
2534 -
2535 -void pa_main_volume_context_bind_main_output_volume_control(pa_main_volume_context *context,
2536 -                                                            pa_binding_target_info *target_info);
2537 -void pa_main_volume_context_bind_main_input_volume_control(pa_main_volume_context *context,
2538 -                                                           pa_binding_target_info *target_info);
2539 -void pa_main_volume_context_bind_main_output_mute_control(pa_main_volume_context *context,
2540 -                                                          pa_binding_target_info *target_info);
2541 -void pa_main_volume_context_bind_main_input_mute_control(pa_main_volume_context *context, pa_binding_target_info *target_info);
2542 -
2543 -/* Called from main-volume-policy.c only. */
2544 -pa_binding_target_type *pa_main_volume_context_create_binding_target_type(pa_main_volume_policy *policy);
2545 +void pa_main_volume_context_set_description(pa_main_volume_context *context, const char *description);
2546 +void pa_main_volume_context_set_main_output_volume_control(pa_main_volume_context *context, pa_volume_control *control);
2547 +void pa_main_volume_context_set_main_input_volume_control(pa_main_volume_context *context, pa_volume_control *control);
2548 +void pa_main_volume_context_set_main_output_mute_control(pa_main_volume_context *context, pa_mute_control *control);
2549 +void pa_main_volume_context_set_main_input_mute_control(pa_main_volume_context *context, pa_mute_control *control);
2550  
2551  #endif
2552 diff --git a/src/modules/main-volume-policy/main-volume-policy.c b/src/modules/main-volume-policy/main-volume-policy.c
2553 index b0b4ede..3c0fccf 100644
2554 --- a/src/modules/main-volume-policy/main-volume-policy.c
2555 +++ b/src/modules/main-volume-policy/main-volume-policy.c
2556 @@ -28,6 +28,7 @@
2557  #include <modules/main-volume-policy/main-volume-context.h>
2558  
2559  #include <pulsecore/core-util.h>
2560 +#include <pulsecore/namereg.h>
2561  #include <pulsecore/shared.h>
2562  
2563  static pa_main_volume_policy *main_volume_policy_new(pa_core *core);
2564 @@ -70,6 +71,46 @@ void pa_main_volume_policy_unref(pa_main_volume_policy *policy) {
2565      }
2566  }
2567  
2568 +static pa_hook_result_t volume_control_unlink_cb(void *hook_data, void *call_data, void *userdata) {
2569 +    pa_main_volume_policy *policy = userdata;
2570 +    pa_volume_control *control = call_data;
2571 +    pa_main_volume_context *context;
2572 +    void *state;
2573 +
2574 +    pa_assert(policy);
2575 +    pa_assert(control);
2576 +
2577 +    PA_HASHMAP_FOREACH(context, policy->main_volume_contexts, state) {
2578 +        if (context->main_output_volume_control == control)
2579 +            pa_main_volume_context_set_main_output_volume_control(context, NULL);
2580 +
2581 +        if (context->main_input_volume_control == control)
2582 +            pa_main_volume_context_set_main_input_volume_control(context, NULL);
2583 +    }
2584 +
2585 +    return PA_HOOK_OK;
2586 +}
2587 +
2588 +static pa_hook_result_t mute_control_unlink_cb(void *hook_data, void *call_data, void *userdata) {
2589 +    pa_main_volume_policy *policy = userdata;
2590 +    pa_mute_control *control = call_data;
2591 +    pa_main_volume_context *context;
2592 +    void *state;
2593 +
2594 +    pa_assert(policy);
2595 +    pa_assert(control);
2596 +
2597 +    PA_HASHMAP_FOREACH(context, policy->main_volume_contexts, state) {
2598 +        if (context->main_output_mute_control == control)
2599 +            pa_main_volume_context_set_main_output_mute_control(context, NULL);
2600 +
2601 +        if (context->main_input_mute_control == control)
2602 +            pa_main_volume_context_set_main_input_mute_control(context, NULL);
2603 +    }
2604 +
2605 +    return PA_HOOK_OK;
2606 +}
2607 +
2608  static pa_main_volume_policy *main_volume_policy_new(pa_core *core) {
2609      pa_main_volume_policy *policy;
2610      unsigned i;
2611 @@ -86,8 +127,10 @@ static pa_main_volume_policy *main_volume_policy_new(pa_core *core) {
2612      for (i = 0; i < PA_MAIN_VOLUME_POLICY_HOOK_MAX; i++)
2613          pa_hook_init(&policy->hooks[i], policy);
2614  
2615 -    policy->main_volume_context_binding_target_type = pa_main_volume_context_create_binding_target_type(policy);
2616 -    pa_volume_api_add_binding_target_type(policy->volume_api, policy->main_volume_context_binding_target_type);
2617 +    policy->volume_control_unlink_slot = pa_hook_connect(&policy->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK],
2618 +                                                         PA_HOOK_NORMAL, volume_control_unlink_cb, policy);
2619 +    policy->mute_control_unlink_slot = pa_hook_connect(&policy->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK],
2620 +                                                       PA_HOOK_NORMAL, mute_control_unlink_cb, policy);
2621  
2622      pa_log_debug("Created a pa_main_volume_policy object.");
2623  
2624 @@ -102,10 +145,11 @@ static void main_volume_policy_free(pa_main_volume_policy *policy) {
2625  
2626      pa_log_debug("Freeing the pa_main_volume_policy object.");
2627  
2628 -    if (policy->main_volume_context_binding_target_type) {
2629 -        pa_volume_api_remove_binding_target_type(policy->volume_api, policy->main_volume_context_binding_target_type);
2630 -        pa_binding_target_type_free(policy->main_volume_context_binding_target_type);
2631 -    }
2632 +    if (policy->mute_control_unlink_slot)
2633 +        pa_hook_slot_free(policy->mute_control_unlink_slot);
2634 +
2635 +    if (policy->volume_control_unlink_slot)
2636 +        pa_hook_slot_free(policy->volume_control_unlink_slot);
2637  
2638      for (i = 0; i < PA_MAIN_VOLUME_POLICY_HOOK_MAX; i++)
2639          pa_hook_done(&policy->hooks[i]);
2640 @@ -134,19 +178,24 @@ int pa_main_volume_policy_register_name(pa_main_volume_policy *policy, const cha
2641      pa_assert(requested_name);
2642      pa_assert(registered_name);
2643  
2644 +    if (!pa_namereg_is_valid_name(requested_name)) {
2645 +        pa_log("Invalid name: \"%s\"", requested_name);
2646 +        return -PA_ERR_INVALID;
2647 +    }
2648 +
2649      n = pa_xstrdup(requested_name);
2650  
2651      if (pa_hashmap_put(policy->names, n, n) < 0) {
2652          unsigned i = 1;
2653  
2654 -        pa_xfree(n);
2655 -
2656          if (fail_if_already_registered) {
2657 +            pa_xfree(n);
2658              pa_log("Name %s already registered.", requested_name);
2659              return -PA_ERR_EXIST;
2660          }
2661  
2662          do {
2663 +            pa_xfree(n);
2664              i++;
2665              n = pa_sprintf_malloc("%s.%u", requested_name, i);
2666          } while (pa_hashmap_put(policy->names, n, n) < 0);
2667 diff --git a/src/modules/main-volume-policy/main-volume-policy.conf.example b/src/modules/main-volume-policy/main-volume-policy.conf.example
2668 index a4a35d3..3fcd267 100644
2669 --- a/src/modules/main-volume-policy/main-volume-policy.conf.example
2670 +++ b/src/modules/main-volume-policy/main-volume-policy.conf.example
2671 @@ -3,7 +3,6 @@ output-volume-model = by-active-main-volume-context
2672  input-volume-model = by-active-main-volume-context
2673  output-mute-model = none
2674  input-mute-model = none
2675 -main-volume-contexts = x-example-call-main-volume-context x-example-default-main-volume-context
2676  
2677  [MainVolumeContext x-example-call-main-volume-context]
2678  description = Call main volume context
2679 diff --git a/src/modules/main-volume-policy/main-volume-policy.h b/src/modules/main-volume-policy/main-volume-policy.h
2680 index 5cd669e..d5f6e02 100644
2681 --- a/src/modules/main-volume-policy/main-volume-policy.h
2682 +++ b/src/modules/main-volume-policy/main-volume-policy.h
2683 @@ -22,7 +22,6 @@
2684    USA.
2685  ***/
2686  
2687 -#include <modules/volume-api/binding.h>
2688  #include <modules/volume-api/volume-api.h>
2689  
2690  #include <pulsecore/core.h>
2691 @@ -35,6 +34,7 @@ typedef struct pa_main_volume_context pa_main_volume_context;
2692  enum {
2693      PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT,
2694      PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK,
2695 +    PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_DESCRIPTION_CHANGED,
2696      PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED,
2697      PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_VOLUME_CONTROL_CHANGED,
2698      PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_MUTE_CONTROL_CHANGED,
2699 @@ -53,7 +53,9 @@ struct pa_main_volume_policy {
2700  
2701      uint32_t next_main_volume_context_index;
2702      pa_hook hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAX];
2703 -    pa_binding_target_type *main_volume_context_binding_target_type;
2704 +
2705 +    pa_hook_slot *volume_control_unlink_slot;
2706 +    pa_hook_slot *mute_control_unlink_slot;
2707  };
2708  
2709  pa_main_volume_policy *pa_main_volume_policy_get(pa_core *core);
2710 diff --git a/src/modules/main-volume-policy/module-main-volume-policy.c b/src/modules/main-volume-policy/module-main-volume-policy.c
2711 index 0a89aa7..1b7693e 100644
2712 --- a/src/modules/main-volume-policy/module-main-volume-policy.c
2713 +++ b/src/modules/main-volume-policy/module-main-volume-policy.c
2714 @@ -27,7 +27,7 @@
2715  
2716  #include <modules/main-volume-policy/main-volume-context.h>
2717  
2718 -#include <modules/volume-api/binding.h>
2719 +#include <modules/volume-api/audio-group.h>
2720  #include <modules/volume-api/volume-api.h>
2721  
2722  #include <pulse/direction.h>
2723 @@ -36,6 +36,9 @@
2724  #include <pulsecore/core-util.h>
2725  #include <pulsecore/i18n.h>
2726  
2727 +#define BIND_PREFIX "bind:"
2728 +#define BIND_AUDIO_GROUP_PREFIX BIND_PREFIX "AudioGroup:"
2729 +
2730  PA_MODULE_AUTHOR("Tanu Kaskinen");
2731  PA_MODULE_DESCRIPTION(_("Main volume and mute policy"));
2732  PA_MODULE_VERSION(PACKAGE_VERSION);
2733 @@ -52,6 +55,7 @@ enum model {
2734  };
2735  
2736  struct userdata {
2737 +    pa_volume_api *volume_api;
2738      pa_main_volume_policy *main_volume_policy;
2739      enum model output_volume_model;
2740      enum model input_volume_model;
2741 @@ -60,26 +64,67 @@ struct userdata {
2742      pa_hashmap *contexts; /* name -> struct context */
2743  
2744      pa_hook_slot *active_main_volume_context_changed_slot;
2745 +    pa_hook_slot *main_volume_context_main_output_volume_control_changed_slot;
2746 +    pa_hook_slot *main_volume_context_main_input_volume_control_changed_slot;
2747 +    pa_hook_slot *main_volume_context_main_output_mute_control_changed_slot;
2748 +    pa_hook_slot *main_volume_context_main_input_mute_control_changed_slot;
2749 +    pa_hook_slot *audio_group_put_slot;
2750 +    pa_hook_slot *audio_group_unlink_slot;
2751 +    pa_hook_slot *audio_group_volume_control_changed_slot;
2752 +    pa_hook_slot *audio_group_mute_control_changed_slot;
2753 +};
2754  
2755 -    /* The following fields are only used during initialization. */
2756 -    pa_hashmap *context_names; /* name -> name (hashmap-as-a-set) */
2757 -    pa_hashmap *unused_contexts; /* name -> struct context */
2758 +struct control_info {
2759 +    /* As appropriate for this control, points to one of
2760 +     *  - pa_main_volume_context.main_output_volume_control
2761 +     *  - pa_main_volume_context.main_input_volume_control
2762 +     *  - pa_main_volume_context.main_output_mute_control
2763 +     *  - pa_main_volume_context.main_input_mute_control */
2764 +    void **control;
2765 +
2766 +    /* As appropriate for this control, points to one of
2767 +     *  - userdata.output_volume_model
2768 +     *  - userdata.input_volume_model
2769 +     *  - userdata.output_mute_model
2770 +     *  - userdata.input_mute_model */
2771 +    enum model *model;
2772 +
2773 +    /* Name of the audio group to which the context volume or mute control is
2774 +     * bound. If the context control is not bound to anything, this is NULL. */
2775 +    char *binding_target_name;
2776 +
2777 +    /* Points to the audio group to which the context volume or mute control is
2778 +     * bound. If the context control is not bound to anything, or it's bound
2779 +     * but the target doesn't currently exist, this is NULL. */
2780 +    pa_audio_group *binding_target;
2781 +
2782 +    /* As appropriate for this control, points to one of
2783 +     *  - pa_main_volume_context_set_main_output_volume_control()
2784 +     *  - pa_main_volume_context_set_main_input_volume_control()
2785 +     *  - pa_main_volume_context_set_main_output_mute_control()
2786 +     *  - pa_main_volume_context_set_main_input_mute_control() */
2787 +    void (*set_control)(pa_main_volume_context *context, void *control);
2788 +
2789 +    /* As appropriate for this control, points to one of
2790 +     *  - pa_volume_api_set_main_output_volume_control()
2791 +     *  - pa_volume_api_set_main_input_volume_control()
2792 +     *  - pa_volume_api_set_main_output_mute_control()
2793 +     *  - pa_volume_api_set_main_input_mute_control() */
2794 +    void (*set_volume_api_control)(pa_volume_api *api, void *control);
2795  };
2796  
2797  struct context {
2798      struct userdata *userdata;
2799 -    char *name;
2800 -    char *description;
2801 -    pa_binding_target_info *main_output_volume_control_target_info;
2802 -    pa_binding_target_info *main_input_volume_control_target_info;
2803 -    pa_binding_target_info *main_output_mute_control_target_info;
2804 -    pa_binding_target_info *main_input_mute_control_target_info;
2805      pa_main_volume_context *main_volume_context;
2806 +    struct control_info output_volume_info;
2807 +    struct control_info input_volume_info;
2808 +    struct control_info output_mute_info;
2809 +    struct control_info input_mute_info;
2810  
2811      bool unlinked;
2812  };
2813  
2814 -static void context_unlink(struct context *context);
2815 +static void context_free(struct context *context);
2816  
2817  static const char *model_to_string(enum model model) {
2818      switch (model) {
2819 @@ -107,56 +152,57 @@ static int model_from_string(const char *str, enum model *model) {
2820      return 0;
2821  }
2822  
2823 -static struct context *context_new(struct userdata *u, const char *name) {
2824 -    struct context *context;
2825 +static int context_new(struct userdata *u, const char *name, struct context **_r) {
2826 +    struct context *context = NULL;
2827 +    int r;
2828  
2829      pa_assert(u);
2830      pa_assert(name);
2831 +    pa_assert(_r);
2832  
2833      context = pa_xnew0(struct context, 1);
2834      context->userdata = u;
2835 -    context->name = pa_xstrdup(name);
2836 -    context->description = pa_xstrdup(name);
2837  
2838 -    return context;
2839 -}
2840 -
2841 -static int context_put(struct context *context) {
2842 -    int r;
2843 -
2844 -    pa_assert(context);
2845 -
2846 -    r = pa_main_volume_context_new(context->userdata->main_volume_policy, context->name, context->description,
2847 -                                   &context->main_volume_context);
2848 +    r = pa_main_volume_context_new(u->main_volume_policy, name, u, &context->main_volume_context);
2849      if (r < 0)
2850          goto fail;
2851  
2852 -    if (context->main_output_volume_control_target_info)
2853 -        pa_main_volume_context_bind_main_output_volume_control(context->main_volume_context,
2854 -                                                               context->main_output_volume_control_target_info);
2855 +    context->output_volume_info.control = (void **) &context->main_volume_context->main_output_volume_control;
2856 +    context->input_volume_info.control = (void **) &context->main_volume_context->main_input_volume_control;
2857 +    context->output_mute_info.control = (void **) &context->main_volume_context->main_output_mute_control;
2858 +    context->input_mute_info.control = (void **) &context->main_volume_context->main_input_mute_control;
2859  
2860 -    if (context->main_input_volume_control_target_info)
2861 -        pa_main_volume_context_bind_main_input_volume_control(context->main_volume_context,
2862 -                                                              context->main_input_volume_control_target_info);
2863 +    context->output_volume_info.model = &u->output_volume_model;
2864 +    context->input_volume_info.model = &u->input_volume_model;
2865 +    context->output_mute_info.model = &u->output_mute_model;
2866 +    context->input_mute_info.model = &u->input_mute_model;
2867  
2868 -    if (context->main_output_mute_control_target_info)
2869 -        pa_main_volume_context_bind_main_output_mute_control(context->main_volume_context,
2870 -                                                             context->main_output_mute_control_target_info);
2871 +    context->output_volume_info.set_control = (void *) pa_main_volume_context_set_main_output_volume_control;
2872 +    context->input_volume_info.set_control = (void *) pa_main_volume_context_set_main_input_volume_control;
2873 +    context->output_mute_info.set_control = (void *) pa_main_volume_context_set_main_output_mute_control;
2874 +    context->input_mute_info.set_control = (void *) pa_main_volume_context_set_main_input_mute_control;
2875  
2876 -    if (context->main_input_mute_control_target_info)
2877 -        pa_main_volume_context_bind_main_input_mute_control(context->main_volume_context,
2878 -                                                            context->main_input_mute_control_target_info);
2879 -
2880 -    pa_main_volume_context_put(context->main_volume_context);
2881 +    context->output_volume_info.set_volume_api_control = (void *) pa_volume_api_set_main_output_volume_control;
2882 +    context->input_volume_info.set_volume_api_control = (void *) pa_volume_api_set_main_input_volume_control;
2883 +    context->output_mute_info.set_volume_api_control = (void *) pa_volume_api_set_main_output_mute_control;
2884 +    context->input_mute_info.set_volume_api_control = (void *) pa_volume_api_set_main_input_mute_control;
2885  
2886 +    *_r = context;
2887      return 0;
2888  
2889  fail:
2890 -    context_unlink(context);
2891 +    if (context)
2892 +        context_free(context);
2893  
2894      return r;
2895  }
2896  
2897 +static void context_put(struct context *context) {
2898 +    pa_assert(context);
2899 +
2900 +    pa_main_volume_context_put(context->main_volume_context);
2901 +}
2902 +
2903  static void context_unlink(struct context *context) {
2904      pa_assert(context);
2905  
2906 @@ -165,10 +211,8 @@ static void context_unlink(struct context *context) {
2907  
2908      context->unlinked = true;
2909  
2910 -    if (context->main_volume_context) {
2911 -        pa_main_volume_context_free(context->main_volume_context);
2912 -        context->main_volume_context = NULL;
2913 -    }
2914 +    if (context->main_volume_context)
2915 +        pa_main_volume_context_unlink(context->main_volume_context);
2916  }
2917  
2918  static void context_free(struct context *context) {
2919 @@ -177,132 +221,290 @@ static void context_free(struct context *context) {
2920      if (!context->unlinked)
2921          context_unlink(context);
2922  
2923 -    if (context->main_input_mute_control_target_info)
2924 -        pa_binding_target_info_free(context->main_input_mute_control_target_info);
2925 -
2926 -    if (context->main_output_mute_control_target_info)
2927 -        pa_binding_target_info_free(context->main_output_mute_control_target_info);
2928 -
2929 -    if (context->main_input_volume_control_target_info)
2930 -        pa_binding_target_info_free(context->main_input_volume_control_target_info);
2931 -
2932 -    if (context->main_output_volume_control_target_info)
2933 -        pa_binding_target_info_free(context->main_output_volume_control_target_info);
2934 +    if (context->main_volume_context)
2935 +        pa_main_volume_context_free(context->main_volume_context);
2936  
2937 -    pa_xfree(context->description);
2938 -    pa_xfree(context->name);
2939      pa_xfree(context);
2940  }
2941  
2942 -static void context_set_description(struct context *context, const char *description) {
2943 -    pa_assert(context);
2944 -    pa_assert(description);
2945 -
2946 -    pa_xfree(context->description);
2947 -    context->description = pa_xstrdup(description);
2948 -}
2949 -
2950 -static void context_set_main_control_target_info(struct context *context, enum control_type type, pa_direction_t direction,
2951 -                                                 pa_binding_target_info *info) {
2952 +static struct control_info *context_get_control_info(struct context *context, enum control_type type,
2953 +                                                     pa_direction_t direction) {
2954      pa_assert(context);
2955  
2956      switch (type) {
2957          case CONTROL_TYPE_VOLUME:
2958 -            if (direction == PA_DIRECTION_OUTPUT) {
2959 -                if (context->main_output_volume_control_target_info)
2960 -                    pa_binding_target_info_free(context->main_output_volume_control_target_info);
2961 -
2962 -                if (info)
2963 -                    context->main_output_volume_control_target_info = pa_binding_target_info_copy(info);
2964 -                else
2965 -                    context->main_output_volume_control_target_info = NULL;
2966 -            } else {
2967 -                if (context->main_input_volume_control_target_info)
2968 -                    pa_binding_target_info_free(context->main_input_volume_control_target_info);
2969 -
2970 -                if (info)
2971 -                    context->main_input_volume_control_target_info = pa_binding_target_info_copy(info);
2972 -                else
2973 -                    context->main_input_volume_control_target_info = NULL;
2974 +            switch (direction) {
2975 +                case PA_DIRECTION_OUTPUT:
2976 +                    return &context->output_volume_info;
2977 +
2978 +                case PA_DIRECTION_INPUT:
2979 +                    return &context->input_volume_info;
2980              }
2981              break;
2982  
2983          case CONTROL_TYPE_MUTE:
2984 -            if (direction == PA_DIRECTION_OUTPUT) {
2985 -                if (context->main_output_mute_control_target_info)
2986 -                    pa_binding_target_info_free(context->main_output_mute_control_target_info);
2987 -
2988 -                if (info)
2989 -                    context->main_output_mute_control_target_info = pa_binding_target_info_copy(info);
2990 -                else
2991 -                    context->main_output_mute_control_target_info = NULL;
2992 -            } else {
2993 -                if (context->main_input_mute_control_target_info)
2994 -                    pa_binding_target_info_free(context->main_input_mute_control_target_info);
2995 -
2996 -                if (info)
2997 -                    context->main_input_mute_control_target_info = pa_binding_target_info_copy(info);
2998 -                else
2999 -                    context->main_input_mute_control_target_info = NULL;
3000 +            switch (direction) {
3001 +                case PA_DIRECTION_OUTPUT:
3002 +                    return &context->output_mute_info;
3003 +
3004 +                case PA_DIRECTION_INPUT:
3005 +                    return &context->input_mute_info;
3006              }
3007              break;
3008      }
3009 +
3010 +    pa_assert_not_reached();
3011 +}
3012 +
3013 +static void context_set_binding_target(struct context *context, enum control_type type, pa_direction_t direction,
3014 +                                       pa_audio_group *group) {
3015 +    struct control_info *info;
3016 +    void *control = NULL;
3017 +
3018 +    pa_assert(context);
3019 +
3020 +    info = context_get_control_info(context, type, direction);
3021 +    info->binding_target = group;
3022 +
3023 +    if (group) {
3024 +        switch (type) {
3025 +            case CONTROL_TYPE_VOLUME:
3026 +                control = group->volume_control;
3027 +                break;
3028 +
3029 +            case CONTROL_TYPE_MUTE:
3030 +                control = group->mute_control;
3031 +                break;
3032 +        }
3033 +    }
3034 +
3035 +    info->set_control(context->main_volume_context, control);
3036 +}
3037 +
3038 +static void context_set_binding_target_name(struct context *context, enum control_type type, pa_direction_t direction,
3039 +                                            const char *name) {
3040 +    struct control_info *info;
3041 +    pa_audio_group *group = NULL;
3042 +
3043 +    pa_assert(context);
3044 +
3045 +    info = context_get_control_info(context, type, direction);
3046 +
3047 +    if (pa_safe_streq(name, info->binding_target_name))
3048 +        return;
3049 +
3050 +    pa_xfree(info->binding_target_name);
3051 +    info->binding_target_name = pa_xstrdup(name);
3052 +
3053 +    if (name)
3054 +        group = pa_hashmap_get(context->userdata->volume_api->audio_groups, name);
3055 +
3056 +    context_set_binding_target(context, type, direction, group);
3057  }
3058  
3059  static pa_hook_result_t active_main_volume_context_changed_cb(void *hook_data, void *call_data, void *userdata) {
3060      struct userdata *u = userdata;
3061      pa_main_volume_context *context;
3062 -    pa_volume_api *api;
3063 -    pa_binding_target_info *info;
3064  
3065      pa_assert(u);
3066  
3067      context = u->main_volume_policy->active_main_volume_context;
3068 -    api = u->main_volume_policy->volume_api;
3069  
3070      if (u->output_volume_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
3071 -        if (context) {
3072 -            info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
3073 -                                              PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL);
3074 -            pa_volume_api_bind_main_output_volume_control(api, info);
3075 -            pa_binding_target_info_free(info);
3076 -        } else
3077 -            pa_volume_api_set_main_output_volume_control(api, NULL);
3078 +        if (context)
3079 +            pa_volume_api_set_main_output_volume_control(u->volume_api, context->main_output_volume_control);
3080 +        else
3081 +            pa_volume_api_set_main_output_volume_control(u->volume_api, NULL);
3082      }
3083  
3084      if (u->input_volume_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
3085 -        if (context) {
3086 -            info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
3087 -                                              PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL);
3088 -            pa_volume_api_bind_main_input_volume_control(api, info);
3089 -            pa_binding_target_info_free(info);
3090 -        } else
3091 -            pa_volume_api_set_main_input_volume_control(api, NULL);
3092 +        if (context)
3093 +            pa_volume_api_set_main_input_volume_control(u->volume_api, context->main_input_volume_control);
3094 +        else
3095 +            pa_volume_api_set_main_input_volume_control(u->volume_api, NULL);
3096      }
3097  
3098      if (u->output_mute_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
3099 -        if (context) {
3100 -            info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
3101 -                                              PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL);
3102 -            pa_volume_api_bind_main_output_mute_control(api, info);
3103 -            pa_binding_target_info_free(info);
3104 -        } else
3105 -            pa_volume_api_set_main_output_mute_control(api, NULL);
3106 +        if (context)
3107 +            pa_volume_api_set_main_output_mute_control(u->volume_api, context->main_output_mute_control);
3108 +        else
3109 +            pa_volume_api_set_main_output_mute_control(u->volume_api, NULL);
3110      }
3111  
3112      if (u->input_mute_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
3113 -        if (context) {
3114 -            info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
3115 -                                              PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL);
3116 -            pa_volume_api_bind_main_input_mute_control(api, info);
3117 -            pa_binding_target_info_free(info);
3118 -        } else
3119 -            pa_volume_api_set_main_input_mute_control(api, NULL);
3120 +        if (context)
3121 +            pa_volume_api_set_main_input_mute_control(u->volume_api, context->main_input_mute_control);
3122 +        else
3123 +            pa_volume_api_set_main_input_mute_control(u->volume_api, NULL);
3124 +    }
3125 +
3126 +    return PA_HOOK_OK;
3127 +}
3128 +
3129 +static void handle_context_control_change(struct context *context, enum control_type type, pa_direction_t direction) {
3130 +    struct control_info *info;
3131 +
3132 +    pa_assert(context);
3133 +
3134 +    info = context_get_control_info(context, type, direction);
3135 +
3136 +    if (*info->model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT
3137 +            && context->userdata->main_volume_policy->active_main_volume_context == context->main_volume_context)
3138 +        info->set_volume_api_control(context->userdata->volume_api, *info->control);
3139 +}
3140 +
3141 +static pa_hook_result_t main_volume_context_main_output_volume_control_changed_cb(void *hook_data, void *call_data,
3142 +                                                                                  void *userdata) {
3143 +    pa_main_volume_context *context = call_data;
3144 +
3145 +    pa_assert(context);
3146 +
3147 +    handle_context_control_change(context->userdata, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT);
3148 +
3149 +    return PA_HOOK_OK;
3150 +}
3151 +
3152 +static pa_hook_result_t main_volume_context_main_input_volume_control_changed_cb(void *hook_data, void *call_data,
3153 +                                                                                 void *userdata) {
3154 +    pa_main_volume_context *context = call_data;
3155 +
3156 +    pa_assert(context);
3157 +
3158 +    handle_context_control_change(context->userdata, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT);
3159 +
3160 +    return PA_HOOK_OK;
3161 +}
3162 +
3163 +static pa_hook_result_t main_volume_context_main_output_mute_control_changed_cb(void *hook_data, void *call_data,
3164 +                                                                                void *userdata) {
3165 +    pa_main_volume_context *context = call_data;
3166 +
3167 +    pa_assert(context);
3168 +
3169 +    handle_context_control_change(context->userdata, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT);
3170 +
3171 +    return PA_HOOK_OK;
3172 +}
3173 +
3174 +static pa_hook_result_t main_volume_context_main_input_mute_control_changed_cb(void *hook_data, void *call_data,
3175 +                                                                               void *userdata) {
3176 +    pa_main_volume_context *context = call_data;
3177 +
3178 +    pa_assert(context);
3179 +
3180 +    handle_context_control_change(context->userdata, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT);
3181 +
3182 +    return PA_HOOK_OK;
3183 +}
3184 +
3185 +static pa_hook_result_t audio_group_put_cb(void *hook_data, void *call_data, void *userdata) {
3186 +    struct userdata *u = userdata;
3187 +    pa_audio_group *group = call_data;
3188 +    struct context *context;
3189 +    void *state;
3190 +
3191 +    pa_assert(u);
3192 +    pa_assert(group);
3193 +
3194 +    PA_HASHMAP_FOREACH(context, u->contexts, state) {
3195 +        if (context->output_volume_info.binding_target_name
3196 +                && pa_streq(context->output_volume_info.binding_target_name, group->name))
3197 +            context_set_binding_target(context, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT, group);
3198 +
3199 +        if (context->input_volume_info.binding_target_name
3200 +                && pa_streq(context->input_volume_info.binding_target_name, group->name))
3201 +            context_set_binding_target(context, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT, group);
3202 +
3203 +        if (context->output_mute_info.binding_target_name
3204 +                && pa_streq(context->output_mute_info.binding_target_name, group->name))
3205 +            context_set_binding_target(context, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT, group);
3206 +
3207 +        if (context->input_mute_info.binding_target_name
3208 +                && pa_streq(context->input_mute_info.binding_target_name, group->name))
3209 +            context_set_binding_target(context, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT, group);
3210 +    }
3211 +
3212 +    return PA_HOOK_OK;
3213 +}
3214 +
3215 +static pa_hook_result_t audio_group_unlink_cb(void *hook_data, void *call_data, void *userdata) {
3216 +    struct userdata *u = userdata;
3217 +    pa_audio_group *group = call_data;
3218 +    struct context *context;
3219 +    void *state;
3220 +
3221 +    pa_assert(u);
3222 +    pa_assert(group);
3223 +
3224 +    PA_HASHMAP_FOREACH(context, u->contexts, state) {
3225 +        if (context->output_volume_info.binding_target == group)
3226 +            context_set_binding_target(context, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT, NULL);
3227 +
3228 +        if (context->input_volume_info.binding_target == group)
3229 +            context_set_binding_target(context, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT, NULL);
3230 +
3231 +        if (context->output_mute_info.binding_target == group)
3232 +            context_set_binding_target(context, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT, NULL);
3233 +
3234 +        if (context->input_mute_info.binding_target == group)
3235 +            context_set_binding_target(context, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT, NULL);
3236      }
3237  
3238      return PA_HOOK_OK;
3239  }
3240  
3241 +static void handle_audio_group_control_change(struct userdata *u, pa_audio_group *group, enum control_type type) {
3242 +    struct context *context;
3243 +    void *state;
3244 +
3245 +    pa_assert(u);
3246 +    pa_assert(group);
3247 +
3248 +    PA_HASHMAP_FOREACH(context, u->contexts, state) {
3249 +        switch (type) {
3250 +            case CONTROL_TYPE_VOLUME:
3251 +                if (context->output_volume_info.binding_target == group)
3252 +                    pa_main_volume_context_set_main_output_volume_control(context->main_volume_context, group->volume_control);
3253 +
3254 +                if (context->input_volume_info.binding_target == group)
3255 +                    pa_main_volume_context_set_main_input_volume_control(context->main_volume_context, group->volume_control);
3256 +                break;
3257 +
3258 +            case CONTROL_TYPE_MUTE:
3259 +                if (context->output_mute_info.binding_target == group)
3260 +                    pa_main_volume_context_set_main_output_mute_control(context->main_volume_context, group->mute_control);
3261 +
3262 +                if (context->input_mute_info.binding_target == group)
3263 +                    pa_main_volume_context_set_main_input_mute_control(context->main_volume_context, group->mute_control);
3264 +                break;
3265 +        }
3266 +    }
3267 +}
3268 +
3269 +static pa_hook_result_t audio_group_volume_control_changed_cb(void *hook_data, void *call_data, void *userdata) {
3270 +    struct userdata *u = userdata;
3271 +    pa_audio_group *group = call_data;
3272 +
3273 +    pa_assert(u);
3274 +    pa_assert(group);
3275 +
3276 +    handle_audio_group_control_change(u, group, CONTROL_TYPE_VOLUME);
3277 +
3278 +    return PA_HOOK_OK;
3279 +}
3280 +
3281 +static pa_hook_result_t audio_group_mute_control_changed_cb(void *hook_data, void *call_data, void *userdata) {
3282 +    struct userdata *u = userdata;
3283 +    pa_audio_group *group = call_data;
3284 +
3285 +    pa_assert(u);
3286 +    pa_assert(group);
3287 +
3288 +    handle_audio_group_control_change(u, group, CONTROL_TYPE_MUTE);
3289 +
3290 +    return PA_HOOK_OK;
3291 +}
3292 +
3293  static int parse_model(pa_config_parser_state *state) {
3294      int r;
3295  
3296 @@ -315,105 +517,81 @@ static int parse_model(pa_config_parser_state *state) {
3297      return r;
3298  }
3299  
3300 -static int parse_main_volume_contexts(pa_config_parser_state *state) {
3301 -    struct userdata *u;
3302 -    char *name;
3303 -    const char *split_state = NULL;
3304 -
3305 -    pa_assert(state);
3306 -
3307 -    u = state->userdata;
3308 -
3309 -    while ((name = pa_split_spaces(state->rvalue, &split_state)))
3310 -        pa_hashmap_put(u->context_names, name, name);
3311 -
3312 -    return 0;
3313 -}
3314 -
3315 -static struct context *get_context(struct userdata *u, const char *section) {
3316 +static int get_context(struct userdata *u, const char *section, struct context **_r) {
3317      const char *name;
3318      struct context *context;
3319  
3320      pa_assert(u);
3321  
3322      if (!section)
3323 -        return NULL;
3324 +        return -PA_ERR_INVALID;
3325  
3326      if (!pa_startswith(section, "MainVolumeContext "))
3327 -        return NULL;
3328 +        return -PA_ERR_INVALID;
3329  
3330      name = section + 18;
3331  
3332 -    context = pa_hashmap_get(u->unused_contexts, name);
3333 +    context = pa_hashmap_get(u->contexts, name);
3334      if (!context) {
3335 -        context = context_new(u, name);
3336 -        pa_hashmap_put(u->unused_contexts, context->name, context);
3337 +        int r;
3338 +
3339 +        r = context_new(u, name, &context);
3340 +        if (r < 0)
3341 +            return r;
3342 +
3343 +        pa_hashmap_put(u->contexts, (void *) context->main_volume_context->name, context);
3344      }
3345  
3346 -    return context;
3347 +    *_r = context;
3348 +    return 0;
3349  }
3350  
3351  static int parse_description(pa_config_parser_state *state) {
3352      struct userdata *u;
3353 +    int r;
3354      struct context *context;
3355  
3356      pa_assert(state);
3357  
3358      u = state->userdata;
3359  
3360 -    context = get_context(u, state->section);
3361 -    if (!context) {
3362 -        pa_log("[%s:%u] Key \"%s\" not expected in section %s.", state->filename, state->lineno, state->lvalue,
3363 +    r = get_context(u, state->section, &context);
3364 +    if (r < 0) {
3365 +        pa_log("[%s:%u] Couldn't get main volume context for section \"%s\".", state->filename, state->lineno,
3366                 pa_strnull(state->section));
3367          return -PA_ERR_INVALID;
3368      }
3369  
3370 -    context_set_description(context, state->rvalue);
3371 +    pa_main_volume_context_set_description(context->main_volume_context, state->rvalue);
3372  
3373      return 0;
3374  }
3375  
3376 -static const char *get_target_field_name(enum control_type type) {
3377 -    switch (type) {
3378 -        case CONTROL_TYPE_VOLUME:
3379 -            return "volume_control";
3380 -
3381 -        case CONTROL_TYPE_MUTE:
3382 -            return "mute_control";
3383 -    }
3384 -
3385 -    pa_assert_not_reached();
3386 -}
3387 -
3388 -static int parse_main_control(pa_config_parser_state *state, enum control_type type, pa_direction_t direction) {
3389 +static int parse_control(pa_config_parser_state *state, enum control_type type, pa_direction_t direction) {
3390      struct userdata *u;
3391 +    int r;
3392      struct context *context;
3393  
3394      pa_assert(state);
3395  
3396      u = state->userdata;
3397  
3398 -    context = get_context(u, state->section);
3399 -    if (!context) {
3400 -        pa_log("[%s:%u] Key \"%s\" not expected in section %s.", state->filename, state->lineno, state->lvalue,
3401 +    r = get_context(u, state->section, &context);
3402 +    if (r < 0) {
3403 +        pa_log("[%s:%u] Couldn't get main volume context for section \"%s\".", state->filename, state->lineno,
3404                 pa_strnull(state->section));
3405          return -PA_ERR_INVALID;
3406      }
3407  
3408      if (pa_streq(state->rvalue, "none"))
3409 -        context_set_main_control_target_info(context, type, direction, NULL);
3410 -    else if (pa_startswith(state->rvalue, "bind:")) {
3411 -        int r;
3412 -        pa_binding_target_info *info;
3413 -
3414 -        r = pa_binding_target_info_new_from_string(state->rvalue, get_target_field_name(type), &info);
3415 -        if (r < 0) {
3416 -            pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue);
3417 -            return r;
3418 +        context_set_binding_target_name(context, type, direction, NULL);
3419 +    else if (pa_startswith(state->rvalue, BIND_PREFIX)) {
3420 +        if (pa_startswith(state->rvalue, BIND_AUDIO_GROUP_PREFIX))
3421 +            context_set_binding_target_name(context, type, direction, state->rvalue + strlen(BIND_AUDIO_GROUP_PREFIX));
3422 +        else {
3423 +            pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue + strlen(BIND_PREFIX));
3424 +            return -PA_ERR_INVALID;
3425          }
3426 -
3427 -        context_set_main_control_target_info(context, type, direction, info);
3428 -        pa_binding_target_info_free(info);
3429      } else {
3430          pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
3431          return -PA_ERR_INVALID;
3432 @@ -425,69 +603,38 @@ static int parse_main_control(pa_config_parser_state *state, enum control_type t
3433  static int parse_main_output_volume_control(pa_config_parser_state *state) {
3434      pa_assert(state);
3435  
3436 -    return parse_main_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT);
3437 +    return parse_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT);
3438  }
3439  
3440  static int parse_main_input_volume_control(pa_config_parser_state *state) {
3441      pa_assert(state);
3442  
3443 -    return parse_main_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT);
3444 +    return parse_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT);
3445  }
3446  
3447  static int parse_main_output_mute_control(pa_config_parser_state *state) {
3448      pa_assert(state);
3449  
3450 -    return parse_main_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT);
3451 +    return parse_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT);
3452  }
3453  
3454  static int parse_main_input_mute_control(pa_config_parser_state *state) {
3455      pa_assert(state);
3456  
3457 -    return parse_main_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT);
3458 -}
3459 -
3460 -static void finalize_config(struct userdata *u) {
3461 -    const char *context_name;
3462 -    void *state;
3463 -    struct context *context;
3464 -
3465 -    pa_assert(u);
3466 -
3467 -    PA_HASHMAP_FOREACH(context_name, u->context_names, state) {
3468 -        int r;
3469 -
3470 -        context = pa_hashmap_remove(u->unused_contexts, context_name);
3471 -        if (!context)
3472 -            context = context_new(u, context_name);
3473 -
3474 -        r = context_put(context);
3475 -        if (r < 0) {
3476 -            pa_log_warn("Failed to create main volume context %s.", context_name);
3477 -            context_free(context);
3478 -            continue;
3479 -        }
3480 -
3481 -        pa_assert_se(pa_hashmap_put(u->contexts, context->name, context) >= 0);
3482 -    }
3483 -
3484 -    PA_HASHMAP_FOREACH(context, u->unused_contexts, state)
3485 -        pa_log_debug("Main volume context %s is not used.", context->name);
3486 -
3487 -    pa_hashmap_free(u->unused_contexts);
3488 -    u->unused_contexts = NULL;
3489 -
3490 -    pa_hashmap_free(u->context_names);
3491 -    u->context_names = NULL;
3492 +    return parse_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT);
3493  }
3494  
3495  int pa__init(pa_module *module) {
3496      struct userdata *u;
3497      FILE *f;
3498      char *fn = NULL;
3499 +    struct context *context;
3500 +    void *state;
3501  
3502      pa_assert(module);
3503  
3504      u = module->userdata = pa_xnew0(struct userdata, 1);
3505 +    u->volume_api = pa_volume_api_get(module->core);
3506      u->main_volume_policy = pa_main_volume_policy_get(module->core);
3507      u->output_volume_model = MODEL_NONE;
3508      u->input_volume_model = MODEL_NONE;
3509 @@ -498,9 +645,32 @@ int pa__init(pa_module *module) {
3510      u->active_main_volume_context_changed_slot =
3511              pa_hook_connect(&u->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_ACTIVE_MAIN_VOLUME_CONTEXT_CHANGED],
3512                              PA_HOOK_NORMAL, active_main_volume_context_changed_cb, u);
3513 -    u->context_names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
3514 -    u->unused_contexts = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
3515 -                                             (pa_free_cb_t) context_free);
3516 +    u->main_volume_context_main_output_volume_control_changed_slot =
3517 +            pa_hook_connect(&u->main_volume_policy->hooks
3518 +                                [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED],
3519 +                            PA_HOOK_NORMAL, main_volume_context_main_output_volume_control_changed_cb, u);
3520 +    u->main_volume_context_main_input_volume_control_changed_slot =
3521 +            pa_hook_connect(&u->main_volume_policy->hooks
3522 +                                [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_VOLUME_CONTROL_CHANGED],
3523 +                            PA_HOOK_NORMAL, main_volume_context_main_input_volume_control_changed_cb, u);
3524 +    u->main_volume_context_main_output_mute_control_changed_slot =
3525 +            pa_hook_connect(&u->main_volume_policy->hooks
3526 +                                [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_MUTE_CONTROL_CHANGED],
3527 +                            PA_HOOK_NORMAL, main_volume_context_main_output_mute_control_changed_cb, u);
3528 +    u->main_volume_context_main_input_mute_control_changed_slot =
3529 +            pa_hook_connect(&u->main_volume_policy->hooks
3530 +                                [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_MUTE_CONTROL_CHANGED],
3531 +                            PA_HOOK_NORMAL, main_volume_context_main_input_mute_control_changed_cb, u);
3532 +    u->audio_group_put_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_PUT], PA_HOOK_NORMAL,
3533 +                                              audio_group_put_cb, u);
3534 +    u->audio_group_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK], PA_HOOK_NORMAL,
3535 +                                                 audio_group_unlink_cb, u);
3536 +    u->audio_group_volume_control_changed_slot =
3537 +            pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED], PA_HOOK_NORMAL,
3538 +                            audio_group_volume_control_changed_cb, u);
3539 +    u->audio_group_mute_control_changed_slot =
3540 +            pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_MUTE_CONTROL_CHANGED], PA_HOOK_NORMAL,
3541 +                            audio_group_mute_control_changed_cb, u);
3542  
3543      f = pa_open_config_file(PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "main-volume-policy.conf", "main-volume-policy.conf", NULL, &fn);
3544      if (f) {
3545 @@ -509,7 +679,6 @@ int pa__init(pa_module *module) {
3546              { "input-volume-model", parse_model, &u->input_volume_model, "General" },
3547              { "output-mute-model", parse_model, &u->output_mute_model, "General" },
3548              { "input-mute-model", parse_model, &u->input_mute_model, "General" },
3549 -            { "main-volume-contexts", parse_main_volume_contexts, NULL, "General" },
3550              { "description", parse_description, NULL, NULL },
3551              { "main-output-volume-control", parse_main_output_volume_control, NULL, NULL },
3552              { "main-input-volume-control", parse_main_input_volume_control, NULL, NULL },
3553 @@ -525,7 +694,8 @@ int pa__init(pa_module *module) {
3554          f = NULL;
3555      }
3556  
3557 -    finalize_config(u);
3558 +    PA_HASHMAP_FOREACH(context, u->contexts, state)
3559 +        context_put(context);
3560  
3561      pa_log_debug("Output volume model: %s", model_to_string(u->output_volume_model));
3562      pa_log_debug("Input volume model: %s", model_to_string(u->input_volume_model));
3563 @@ -544,6 +714,30 @@ void pa__done(pa_module *module) {
3564      if (!u)
3565          return;
3566  
3567 +    if (u->audio_group_mute_control_changed_slot)
3568 +        pa_hook_slot_free(u->audio_group_mute_control_changed_slot);
3569 +
3570 +    if (u->audio_group_volume_control_changed_slot)
3571 +        pa_hook_slot_free(u->audio_group_volume_control_changed_slot);
3572 +
3573 +    if (u->audio_group_unlink_slot)
3574 +        pa_hook_slot_free(u->audio_group_unlink_slot);
3575 +
3576 +    if (u->audio_group_put_slot)
3577 +        pa_hook_slot_free(u->audio_group_put_slot);
3578 +
3579 +    if (u->main_volume_context_main_input_mute_control_changed_slot)
3580 +        pa_hook_slot_free(u->main_volume_context_main_input_mute_control_changed_slot);
3581 +
3582 +    if (u->main_volume_context_main_output_mute_control_changed_slot)
3583 +        pa_hook_slot_free(u->main_volume_context_main_output_mute_control_changed_slot);
3584 +
3585 +    if (u->main_volume_context_main_input_volume_control_changed_slot)
3586 +        pa_hook_slot_free(u->main_volume_context_main_input_volume_control_changed_slot);
3587 +
3588 +    if (u->main_volume_context_main_output_volume_control_changed_slot)
3589 +        pa_hook_slot_free(u->main_volume_context_main_output_volume_control_changed_slot);
3590 +
3591      if (u->active_main_volume_context_changed_slot)
3592          pa_hook_slot_free(u->active_main_volume_context_changed_slot);
3593  
3594 @@ -553,5 +747,8 @@ void pa__done(pa_module *module) {
3595      if (u->main_volume_policy)
3596          pa_main_volume_policy_unref(u->main_volume_policy);
3597  
3598 +    if (u->volume_api)
3599 +        pa_volume_api_unref(u->volume_api);
3600 +
3601      pa_xfree(u);
3602  }
3603 diff --git a/src/modules/volume-api/audio-group.c b/src/modules/volume-api/audio-group.c
3604 index 76bfa69..66e0f8a 100644
3605 --- a/src/modules/volume-api/audio-group.c
3606 +++ b/src/modules/volume-api/audio-group.c
3607 @@ -29,34 +29,33 @@
3608  
3609  #include <pulsecore/core-util.h>
3610  
3611 -int pa_audio_group_new(pa_volume_api *api, const char *name, const char *description, pa_audio_group **group) {
3612 -    pa_audio_group *group_local;
3613 +int pa_audio_group_new(pa_volume_api *api, const char *name, pa_audio_group **_r) {
3614 +    pa_audio_group *group = NULL;
3615      int r;
3616  
3617      pa_assert(api);
3618      pa_assert(name);
3619 -    pa_assert(description);
3620 -    pa_assert(group);
3621 +    pa_assert(_r);
3622  
3623 -    group_local = pa_xnew0(pa_audio_group, 1);
3624 -    group_local->volume_api = api;
3625 -    group_local->index = pa_volume_api_allocate_audio_group_index(api);
3626 +    group = pa_xnew0(pa_audio_group, 1);
3627 +    group->volume_api = api;
3628 +    group->index = pa_volume_api_allocate_audio_group_index(api);
3629  
3630 -    r = pa_volume_api_register_name(api, name, true, &group_local->name);
3631 +    r = pa_volume_api_register_name(api, name, true, &group->name);
3632      if (r < 0)
3633          goto fail;
3634  
3635 -    group_local->description = pa_xstrdup(description);
3636 -    group_local->proplist = pa_proplist_new();
3637 -    group_local->volume_streams = pa_hashmap_new(NULL, NULL);
3638 -    group_local->mute_streams = pa_hashmap_new(NULL, NULL);
3639 -
3640 -    *group = group_local;
3641 +    group->description = pa_xstrdup(group->name);
3642 +    group->proplist = pa_proplist_new();
3643 +    group->volume_streams = pa_hashmap_new(NULL, NULL);
3644 +    group->mute_streams = pa_hashmap_new(NULL, NULL);
3645  
3646 +    *_r = group;
3647      return 0;
3648  
3649  fail:
3650 -    pa_audio_group_free(group_local);
3651 +    if (group)
3652 +        pa_audio_group_free(group);
3653  
3654      return r;
3655  }
3656 @@ -68,7 +67,6 @@ void pa_audio_group_put(pa_audio_group *group) {
3657      pa_assert(group);
3658  
3659      pa_volume_api_add_audio_group(group->volume_api, group);
3660 -
3661      group->linked = true;
3662  
3663      pa_log_debug("Created audio group #%u.", group->index);
3664 @@ -99,9 +97,9 @@ void pa_audio_group_unlink(pa_audio_group *group) {
3665      pa_log_debug("Unlinking audio group %s.", group->name);
3666  
3667      if (group->linked)
3668 -        pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK], group);
3669 +        pa_volume_api_remove_audio_group(group->volume_api, group);
3670  
3671 -    pa_volume_api_remove_audio_group(group->volume_api, group);
3672 +    pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK], group);
3673  
3674      while ((stream = pa_hashmap_first(group->mute_streams)))
3675          pas_stream_set_audio_group_for_mute(stream, NULL);
3676 @@ -109,34 +107,15 @@ void pa_audio_group_unlink(pa_audio_group *group) {
3677      while ((stream = pa_hashmap_first(group->volume_streams)))
3678          pas_stream_set_audio_group_for_volume(stream, NULL);
3679  
3680 -    if (group->mute_control_binding) {
3681 -        pa_binding_free(group->mute_control_binding);
3682 -        group->mute_control_binding = NULL;
3683 -    }
3684 -
3685 -    if (group->volume_control_binding) {
3686 -        pa_binding_free(group->volume_control_binding);
3687 -        group->volume_control_binding = NULL;
3688 -    }
3689 -
3690 -    pa_audio_group_set_have_own_mute_control(group, false);
3691 -    pa_audio_group_set_have_own_volume_control(group, false);
3692 -
3693 -    if (group->mute_control) {
3694 -        pa_mute_control_remove_audio_group(group->mute_control, group);
3695 -        group->mute_control = NULL;
3696 -    }
3697 -
3698 -    if (group->volume_control) {
3699 -        pa_volume_control_remove_audio_group(group->volume_control, group);
3700 -        group->volume_control = NULL;
3701 -    }
3702 +    pa_audio_group_set_mute_control(group, NULL);
3703 +    pa_audio_group_set_volume_control(group, NULL);
3704  }
3705  
3706  void pa_audio_group_free(pa_audio_group *group) {
3707      pa_assert(group);
3708  
3709 -    if (!group->unlinked)
3710 +    /* unlink() expects name to be set. */
3711 +    if (!group->unlinked && group->name)
3712          pa_audio_group_unlink(group);
3713  
3714      if (group->mute_streams)
3715 @@ -156,133 +135,33 @@ void pa_audio_group_free(pa_audio_group *group) {
3716      pa_xfree(group);
3717  }
3718  
3719 -const char *pa_audio_group_get_name(pa_audio_group *group) {
3720 -    pa_assert(group);
3721 +void pa_audio_group_set_description(pa_audio_group *group, const char *description) {
3722 +    char *old_description;
3723  
3724 -    return group->name;
3725 -}
3726 -
3727 -static int volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *volume, bool set_volume,
3728 -                                        bool set_balance) {
3729 -    pa_audio_group *group;
3730 -    pas_stream *stream;
3731 -    void *state;
3732 -
3733 -    pa_assert(control);
3734 -    pa_assert(volume);
3735 -
3736 -    group = control->userdata;
3737 -
3738 -    PA_HASHMAP_FOREACH(stream, group->volume_streams, state) {
3739 -        if (stream->own_volume_control)
3740 -            pa_volume_control_set_volume(stream->own_volume_control, volume, set_volume, set_balance);
3741 -    }
3742 -
3743 -    return 0;
3744 -}
3745 -
3746 -static void volume_control_set_initial_volume_cb(pa_volume_control *control) {
3747 -    pa_audio_group *group;
3748 -    pas_stream *stream;
3749 -    void *state;
3750 -
3751 -    pa_assert(control);
3752 -
3753 -    group = control->userdata;
3754 -
3755 -    PA_HASHMAP_FOREACH(stream, group->volume_streams, state) {
3756 -        if (stream->own_volume_control)
3757 -            pa_volume_control_set_volume(stream->own_volume_control, &control->volume, true, true);
3758 -    }
3759 -}
3760 -
3761 -void pa_audio_group_set_have_own_volume_control(pa_audio_group *group, bool have) {
3762      pa_assert(group);
3763 +    pa_assert(description);
3764  
3765 -    if (have == group->have_own_volume_control)
3766 -        return;
3767 -
3768 -    if (have) {
3769 -        pa_bvolume initial_volume;
3770 -
3771 -        if (group->volume_api->core->flat_volumes)
3772 -            /* Usually the initial volume should get overridden by some module
3773 -             * that manages audio group volume levels, but if there's no such
3774 -             * module, let's try to avoid too high volume in flat volume
3775 -             * mode. */
3776 -            pa_bvolume_init_mono(&initial_volume, 0.3 * PA_VOLUME_NORM);
3777 -        else
3778 -            pa_bvolume_init_mono(&initial_volume, PA_VOLUME_NORM);
3779 -
3780 -        pa_assert(!group->own_volume_control);
3781 -        group->own_volume_control = pa_volume_control_new(group->volume_api, "audio-group-volume-control",
3782 -                                                          group->description, false, false);
3783 -        pa_volume_control_set_owner_audio_group(group->own_volume_control, group);
3784 -        group->own_volume_control->set_volume = volume_control_set_volume_cb;
3785 -        group->own_volume_control->userdata = group;
3786 -        pa_volume_control_put(group->own_volume_control, &initial_volume, volume_control_set_initial_volume_cb);
3787 -    } else {
3788 -        pa_volume_control_free(group->own_volume_control);
3789 -        group->own_volume_control = NULL;
3790 -    }
3791 -
3792 -    group->have_own_volume_control = have;
3793 -}
3794 -
3795 -static int mute_control_set_mute_cb(pa_mute_control *control, bool mute) {
3796 -    pa_audio_group *group;
3797 -    pas_stream *stream;
3798 -    void *state;
3799 -
3800 -    pa_assert(control);
3801 -
3802 -    group = control->userdata;
3803 -
3804 -    PA_HASHMAP_FOREACH(stream, group->mute_streams, state) {
3805 -        if (stream->own_mute_control)
3806 -            pa_mute_control_set_mute(stream->own_mute_control, mute);
3807 -    }
3808 -
3809 -    return 0;
3810 -}
3811 -
3812 -static void mute_control_set_initial_mute_cb(pa_mute_control *control) {
3813 -    pa_audio_group *group;
3814 -    pas_stream *stream;
3815 -    void *state;
3816 +    old_description = group->description;
3817  
3818 -    pa_assert(control);
3819 +    if (pa_streq(description, old_description))
3820 +        return;
3821  
3822 -    group = control->userdata;
3823 +    group->description = pa_xstrdup(description);
3824  
3825 -    PA_HASHMAP_FOREACH(stream, group->mute_streams, state) {
3826 -        if (stream->own_mute_control)
3827 -            pa_mute_control_set_mute(stream->own_mute_control, control->mute);
3828 +    if (!group->linked || group->unlinked) {
3829 +        pa_xfree(old_description);
3830 +        return;
3831      }
3832 -}
3833  
3834 -void pa_audio_group_set_have_own_mute_control(pa_audio_group *group, bool have) {
3835 -    pa_assert(group);
3836 +    pa_log_debug("The description of audio group %s changed from \"%s\" to \"%s\".", group->name, old_description,
3837 +                 description);
3838 +    pa_xfree(old_description);
3839  
3840 -    if (have == group->have_own_mute_control)
3841 -        return;
3842 +    pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_DESCRIPTION_CHANGED], group);
3843  
3844 -    group->have_own_mute_control = have;
3845 -
3846 -    if (have) {
3847 -        pa_assert(!group->own_mute_control);
3848 -        group->own_mute_control = pa_mute_control_new(group->volume_api, "audio-group-mute-control", group->description);
3849 -        pa_mute_control_set_owner_audio_group(group->own_mute_control, group);
3850 -        group->own_mute_control->set_mute = mute_control_set_mute_cb;
3851 -        group->own_mute_control->userdata = group;
3852 -        pa_mute_control_put(group->own_mute_control, false, true, mute_control_set_initial_mute_cb);
3853 -    } else {
3854 -        pa_mute_control_free(group->own_mute_control);
3855 -        group->own_mute_control = NULL;
3856 -    }
3857  }
3858  
3859 -static void set_volume_control_internal(pa_audio_group *group, pa_volume_control *control) {
3860 +void pa_audio_group_set_volume_control(pa_audio_group *group, pa_volume_control *control) {
3861      pa_volume_control *old_control;
3862  
3863      pa_assert(group);
3864 @@ -292,14 +171,8 @@ static void set_volume_control_internal(pa_audio_group *group, pa_volume_control
3865      if (control == old_control)
3866          return;
3867  
3868 -    if (old_control)
3869 -        pa_volume_control_remove_audio_group(old_control, group);
3870 -
3871      group->volume_control = control;
3872  
3873 -    if (control)
3874 -        pa_volume_control_add_audio_group(control, group);
3875 -
3876      if (!group->linked || group->unlinked)
3877          return;
3878  
3879 @@ -309,18 +182,7 @@ static void set_volume_control_internal(pa_audio_group *group, pa_volume_control
3880      pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED], group);
3881  }
3882  
3883 -void pa_audio_group_set_volume_control(pa_audio_group *group, pa_volume_control *control) {
3884 -    pa_assert(group);
3885 -
3886 -    if (group->volume_control_binding) {
3887 -        pa_binding_free(group->volume_control_binding);
3888 -        group->volume_control_binding = NULL;
3889 -    }
3890 -
3891 -    set_volume_control_internal(group, control);
3892 -}
3893 -
3894 -static void set_mute_control_internal(pa_audio_group *group, pa_mute_control *control) {
3895 +void pa_audio_group_set_mute_control(pa_audio_group *group, pa_mute_control *control) {
3896      pa_mute_control *old_control;
3897  
3898      pa_assert(group);
3899 @@ -330,14 +192,8 @@ static void set_mute_control_internal(pa_audio_group *group, pa_mute_control *co
3900      if (control == old_control)
3901          return;
3902  
3903 -    if (old_control)
3904 -        pa_mute_control_remove_audio_group(old_control, group);
3905 -
3906      group->mute_control = control;
3907  
3908 -    if (control)
3909 -        pa_mute_control_add_audio_group(control, group);
3910 -
3911      if (!group->linked || group->unlinked)
3912          return;
3913  
3914 @@ -347,57 +203,11 @@ static void set_mute_control_internal(pa_audio_group *group, pa_mute_control *co
3915      pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_MUTE_CONTROL_CHANGED], group);
3916  }
3917  
3918 -void pa_audio_group_set_mute_control(pa_audio_group *group, pa_mute_control *control) {
3919 -    pa_assert(group);
3920 -
3921 -    if (group->mute_control_binding) {
3922 -        pa_binding_free(group->mute_control_binding);
3923 -        group->mute_control_binding = NULL;
3924 -    }
3925 -
3926 -    set_mute_control_internal(group, control);
3927 -}
3928 -
3929 -void pa_audio_group_bind_volume_control(pa_audio_group *group, pa_binding_target_info *target_info) {
3930 -    pa_binding_owner_info owner_info = {
3931 -        .userdata = group,
3932 -        .set_value = (pa_binding_set_value_cb_t) set_volume_control_internal,
3933 -    };
3934 -
3935 -    pa_assert(group);
3936 -    pa_assert(target_info);
3937 -
3938 -    if (group->volume_control_binding)
3939 -        pa_binding_free(group->volume_control_binding);
3940 -
3941 -    group->volume_control_binding = pa_binding_new(group->volume_api, &owner_info, target_info);
3942 -}
3943 -
3944 -void pa_audio_group_bind_mute_control(pa_audio_group *group, pa_binding_target_info *target_info) {
3945 -    pa_binding_owner_info owner_info = {
3946 -        .userdata = group,
3947 -        .set_value = (pa_binding_set_value_cb_t) set_mute_control_internal,
3948 -    };
3949 -
3950 -    pa_assert(group);
3951 -    pa_assert(target_info);
3952 -
3953 -    if (group->mute_control_binding)
3954 -        pa_binding_free(group->mute_control_binding);
3955 -
3956 -    group->mute_control_binding = pa_binding_new(group->volume_api, &owner_info, target_info);
3957 -}
3958 -
3959  void pa_audio_group_add_volume_stream(pa_audio_group *group, pas_stream *stream) {
3960      pa_assert(group);
3961      pa_assert(stream);
3962  
3963      pa_assert_se(pa_hashmap_put(group->volume_streams, stream, stream) >= 0);
3964 -
3965 -    if (stream->own_volume_control && group->own_volume_control)
3966 -        pa_volume_control_set_volume(stream->own_volume_control, &group->own_volume_control->volume, true, true);
3967 -
3968 -    pa_log_debug("Stream %s added to audio group %s (volume).", stream->name, group->name);
3969  }
3970  
3971  void pa_audio_group_remove_volume_stream(pa_audio_group *group, pas_stream *stream) {
3972 @@ -405,8 +215,6 @@ void pa_audio_group_remove_volume_stream(pa_audio_group *group, pas_stream *stre
3973      pa_assert(stream);
3974  
3975      pa_assert_se(pa_hashmap_remove(group->volume_streams, stream));
3976 -
3977 -    pa_log_debug("Stream %s removed from audio group %s (volume).", stream->name, group->name);
3978  }
3979  
3980  void pa_audio_group_add_mute_stream(pa_audio_group *group, pas_stream *stream) {
3981 @@ -414,11 +222,6 @@ void pa_audio_group_add_mute_stream(pa_audio_group *group, pas_stream *stream) {
3982      pa_assert(stream);
3983  
3984      pa_assert_se(pa_hashmap_put(group->mute_streams, stream, stream) >= 0);
3985 -
3986 -    if (stream->own_mute_control && group->own_mute_control)
3987 -        pa_mute_control_set_mute(stream->own_mute_control, group->own_mute_control->mute);
3988 -
3989 -    pa_log_debug("Stream %s added to audio group %s (mute).", stream->name, group->name);
3990  }
3991  
3992  void pa_audio_group_remove_mute_stream(pa_audio_group *group, pas_stream *stream) {
3993 @@ -426,23 +229,4 @@ void pa_audio_group_remove_mute_stream(pa_audio_group *group, pas_stream *stream
3994      pa_assert(stream);
3995  
3996      pa_assert_se(pa_hashmap_remove(group->mute_streams, stream));
3997 -
3998 -    pa_log_debug("Stream %s removed from audio group %s (mute).", stream->name, group->name);
3999 -}
4000 -
4001 -pa_binding_target_type *pa_audio_group_create_binding_target_type(pa_volume_api *api) {
4002 -    pa_binding_target_type *type;
4003 -
4004 -    pa_assert(api);
4005 -
4006 -    type = pa_binding_target_type_new(PA_AUDIO_GROUP_BINDING_TARGET_TYPE, api->audio_groups,
4007 -                                      &api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_PUT],
4008 -                                      &api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK],
4009 -                                      (pa_binding_target_type_get_name_cb_t) pa_audio_group_get_name);
4010 -    pa_binding_target_type_add_field(type, PA_AUDIO_GROUP_BINDING_TARGET_FIELD_VOLUME_CONTROL,
4011 -                                     PA_BINDING_CALCULATE_FIELD_OFFSET(pa_audio_group, volume_control));
4012 -    pa_binding_target_type_add_field(type, PA_AUDIO_GROUP_BINDING_TARGET_FIELD_MUTE_CONTROL,
4013 -                                     PA_BINDING_CALCULATE_FIELD_OFFSET(pa_audio_group, mute_control));
4014 -
4015 -    return type;
4016  }
4017 diff --git a/src/modules/volume-api/audio-group.h b/src/modules/volume-api/audio-group.h
4018 index 41591ba..02db3eb 100644
4019 --- a/src/modules/volume-api/audio-group.h
4020 +++ b/src/modules/volume-api/audio-group.h
4021 @@ -22,7 +22,6 @@
4022    USA.
4023  ***/
4024  
4025 -#include <modules/volume-api/binding.h>
4026  #include <modules/volume-api/mute-control.h>
4027  #include <modules/volume-api/volume-control.h>
4028  
4029 @@ -32,10 +31,6 @@
4030  
4031  typedef struct pa_audio_group pa_audio_group;
4032  
4033 -#define PA_AUDIO_GROUP_BINDING_TARGET_TYPE "AudioGroup"
4034 -#define PA_AUDIO_GROUP_BINDING_TARGET_FIELD_VOLUME_CONTROL "volume_control"
4035 -#define PA_AUDIO_GROUP_BINDING_TARGET_FIELD_MUTE_CONTROL "mute_control"
4036 -
4037  struct pa_audio_group {
4038      pa_volume_api *volume_api;
4039      uint32_t index;
4040 @@ -44,13 +39,7 @@ struct pa_audio_group {
4041      pa_proplist *proplist;
4042      pa_volume_control *volume_control;
4043      pa_mute_control *mute_control;
4044 -    bool have_own_volume_control;
4045 -    bool have_own_mute_control;
4046 -    pa_volume_control *own_volume_control;
4047 -    pa_mute_control *own_mute_control;
4048  
4049 -    pa_binding *volume_control_binding;
4050 -    pa_binding *mute_control_binding;
4051      pa_hashmap *volume_streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
4052      pa_hashmap *mute_streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
4053  
4054 @@ -58,28 +47,22 @@ struct pa_audio_group {
4055      bool unlinked;
4056  };
4057  
4058 -int pa_audio_group_new(pa_volume_api *api, const char *name, const char *description, pa_audio_group **group);
4059 +int pa_audio_group_new(pa_volume_api *api, const char *name, pa_audio_group **_r);
4060  void pa_audio_group_put(pa_audio_group *group);
4061  void pa_audio_group_unlink(pa_audio_group *group);
4062  void pa_audio_group_free(pa_audio_group *group);
4063  
4064 -const char *pa_audio_group_get_name(pa_audio_group *group);
4065 -
4066 -/* Called by policy modules. */
4067 -void pa_audio_group_set_have_own_volume_control(pa_audio_group *group, bool have);
4068 -void pa_audio_group_set_have_own_mute_control(pa_audio_group *group, bool have);
4069 +/* Called by the audio group implementation. */
4070 +void pa_audio_group_set_description(pa_audio_group *group, const char *description);
4071  void pa_audio_group_set_volume_control(pa_audio_group *group, pa_volume_control *control);
4072  void pa_audio_group_set_mute_control(pa_audio_group *group, pa_mute_control *control);
4073 -void pa_audio_group_bind_volume_control(pa_audio_group *group, pa_binding_target_info *target_info);
4074 -void pa_audio_group_bind_mute_control(pa_audio_group *group, pa_binding_target_info *target_info);
4075  
4076 -/* Called from sstream.c only. */
4077 +/* Called by sstream.c only. If you want to assign a stream to an audio group, use
4078 + * pas_stream_set_audio_group_for_volume() and
4079 + * pas_stream_set_audio_group_for_mute(). */
4080  void pa_audio_group_add_volume_stream(pa_audio_group *group, pas_stream *stream);
4081  void pa_audio_group_remove_volume_stream(pa_audio_group *group, pas_stream *stream);
4082  void pa_audio_group_add_mute_stream(pa_audio_group *group, pas_stream *stream);
4083  void pa_audio_group_remove_mute_stream(pa_audio_group *group, pas_stream *stream);
4084  
4085 -/* Called from volume-api.c only. */
4086 -pa_binding_target_type *pa_audio_group_create_binding_target_type(pa_volume_api *api);
4087 -
4088  #endif
4089 diff --git a/src/modules/volume-api/binding.c b/src/modules/volume-api/binding.c
4090 deleted file mode 100644
4091 index 6e73119..0000000
4092 --- a/src/modules/volume-api/binding.c
4093 +++ /dev/null
4094 @@ -1,386 +0,0 @@
4095 -/***
4096 -  This file is part of PulseAudio.
4097 -
4098 -  Copyright 2014 Intel Corporation
4099 -
4100 -  PulseAudio is free software; you can redistribute it and/or modify
4101 -  it under the terms of the GNU Lesser General Public License as published
4102 -  by the Free Software Foundation; either version 2.1 of the License,
4103 -  or (at your option) any later version.
4104 -
4105 -  PulseAudio is distributed in the hope that it will be useful, but
4106 -  WITHOUT ANY WARRANTY; without even the implied warranty of
4107 -  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4108 -  General Public License for more details.
4109 -
4110 -  You should have received a copy of the GNU Lesser General Public License
4111 -  along with PulseAudio; if not, write to the Free Software
4112 -  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
4113 -  USA.
4114 -***/
4115 -
4116 -#ifdef HAVE_CONFIG_H
4117 -#include <config.h>
4118 -#endif
4119 -
4120 -#include "binding.h"
4121 -
4122 -#include <pulse/def.h>
4123 -#include <pulse/xmalloc.h>
4124 -
4125 -#include <pulsecore/core-util.h>
4126 -#include <pulsecore/macro.h>
4127 -
4128 -struct field_entry {
4129 -    char *name;
4130 -    size_t offset;
4131 -};
4132 -
4133 -static void set_target_type(pa_binding *binding, pa_binding_target_type *type);
4134 -static void set_target_object(pa_binding *binding, void *object);
4135 -
4136 -pa_binding_owner_info *pa_binding_owner_info_new(pa_binding_set_value_cb_t set_value, void *userdata) {
4137 -    pa_binding_owner_info *info;
4138 -
4139 -    pa_assert(set_value);
4140 -
4141 -    info = pa_xnew0(pa_binding_owner_info, 1);
4142 -    info->set_value = set_value;
4143 -    info->userdata = userdata;
4144 -
4145 -    return info;
4146 -}
4147 -
4148 -pa_binding_owner_info *pa_binding_owner_info_copy(const pa_binding_owner_info *info) {
4149 -    pa_assert(info);
4150 -
4151 -    return pa_binding_owner_info_new(info->set_value, info->userdata);
4152 -}
4153 -
4154 -void pa_binding_owner_info_free(pa_binding_owner_info *info) {
4155 -    pa_assert(info);
4156 -
4157 -    pa_xfree(info);
4158 -}
4159 -
4160 -pa_binding_target_info *pa_binding_target_info_new(const char *type, const char *name, const char *field) {
4161 -    pa_binding_target_info *info;
4162 -
4163 -    pa_assert(type);
4164 -    pa_assert(name);
4165 -    pa_assert(field);
4166 -
4167 -    info = pa_xnew0(pa_binding_target_info, 1);
4168 -    info->type = pa_xstrdup(type);
4169 -    info->name = pa_xstrdup(name);
4170 -    info->field = pa_xstrdup(field);
4171 -
4172 -    return info;
4173 -}
4174 -
4175 -int pa_binding_target_info_new_from_string(const char *str, const char *field, pa_binding_target_info **info) {
4176 -    const char *colon;
4177 -    char *type = NULL;
4178 -    char *name = NULL;
4179 -
4180 -    pa_assert(str);
4181 -    pa_assert(field);
4182 -    pa_assert(info);
4183 -
4184 -    if (!pa_startswith(str, "bind:"))
4185 -        goto fail;
4186 -
4187 -    colon = strchr(str + 5, ':');
4188 -    if (!colon)
4189 -        goto fail;
4190 -
4191 -    type = pa_xstrndup(str + 5, colon - (str + 5));
4192 -
4193 -    if (!*type)
4194 -        goto fail;
4195 -
4196 -    name = pa_xstrdup(colon + 1);
4197 -
4198 -    if (!*name)
4199 -        goto fail;
4200 -
4201 -    *info = pa_binding_target_info_new(type, name, field);
4202 -    pa_xfree(name);
4203 -    pa_xfree(type);
4204 -
4205 -    return 0;
4206 -
4207 -fail:
4208 -    pa_log("Invalid binding target: %s", str);
4209 -    pa_xfree(name);
4210 -    pa_xfree(type);
4211 -
4212 -    return -PA_ERR_INVALID;
4213 -}
4214 -
4215 -pa_binding_target_info *pa_binding_target_info_copy(const pa_binding_target_info *info) {
4216 -    pa_assert(info);
4217 -
4218 -    return pa_binding_target_info_new(info->type, info->name, info->field);
4219 -}
4220 -
4221 -void pa_binding_target_info_free(pa_binding_target_info *info) {
4222 -    pa_assert(info);
4223 -
4224 -    pa_xfree(info->field);
4225 -    pa_xfree(info->name);
4226 -    pa_xfree(info->type);
4227 -    pa_xfree(info);
4228 -}
4229 -
4230 -static void field_entry_free(struct field_entry *entry) {
4231 -    pa_assert(entry);
4232 -
4233 -    pa_xfree(entry->name);
4234 -    pa_xfree(entry);
4235 -}
4236 -
4237 -pa_binding_target_type *pa_binding_target_type_new(const char *name, pa_hashmap *objects, pa_hook *put_hook,
4238 -                                                   pa_hook *unlink_hook, pa_binding_target_type_get_name_cb_t get_name) {
4239 -    pa_binding_target_type *type;
4240 -
4241 -    pa_assert(name);
4242 -    pa_assert(objects);
4243 -    pa_assert(put_hook);
4244 -    pa_assert(unlink_hook);
4245 -    pa_assert(get_name);
4246 -
4247 -    type = pa_xnew0(pa_binding_target_type, 1);
4248 -    type->name = pa_xstrdup(name);
4249 -    type->objects = objects;
4250 -    type->put_hook = put_hook;
4251 -    type->unlink_hook = unlink_hook;
4252 -    type->get_name = get_name;
4253 -    type->fields = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) field_entry_free);
4254 -
4255 -    return type;
4256 -}
4257 -
4258 -void pa_binding_target_type_free(pa_binding_target_type *type) {
4259 -    pa_assert(type);
4260 -
4261 -    if (type->fields)
4262 -        pa_hashmap_free(type->fields);
4263 -
4264 -    pa_xfree(type->name);
4265 -    pa_xfree(type);
4266 -}
4267 -
4268 -void pa_binding_target_type_add_field(pa_binding_target_type *type, const char *name, size_t offset) {
4269 -    struct field_entry *entry;
4270 -
4271 -    pa_assert(type);
4272 -    pa_assert(name);
4273 -
4274 -    entry = pa_xnew0(struct field_entry, 1);
4275 -    entry->name = pa_xstrdup(name);
4276 -    entry->offset = offset;
4277 -
4278 -    pa_assert_se(pa_hashmap_put(type->fields, entry->name, entry) >= 0);
4279 -}
4280 -
4281 -int pa_binding_target_type_get_field_offset(pa_binding_target_type *type, const char *field, size_t *offset) {
4282 -    struct field_entry *entry;
4283 -
4284 -    pa_assert(type);
4285 -    pa_assert(field);
4286 -    pa_assert(offset);
4287 -
4288 -    entry = pa_hashmap_get(type->fields, field);
4289 -    if (!entry)
4290 -        return -PA_ERR_NOENTITY;
4291 -
4292 -    *offset = entry->offset;
4293 -
4294 -    return 0;
4295 -}
4296 -
4297 -static pa_hook_result_t target_type_added_cb(void *hook_data, void *call_data, void *userdata) {
4298 -    pa_binding_target_type *type = call_data;
4299 -    pa_binding *binding = userdata;
4300 -
4301 -    pa_assert(type);
4302 -    pa_assert(binding);
4303 -
4304 -    if (!pa_streq(type->name, binding->target_info->type))
4305 -        return PA_HOOK_OK;
4306 -
4307 -    set_target_type(binding, type);
4308 -
4309 -    return PA_HOOK_OK;
4310 -}
4311 -
4312 -static pa_hook_result_t target_type_removed_cb(void *hook_data, void *call_data, void *userdata) {
4313 -    pa_binding_target_type *type = call_data;
4314 -    pa_binding *binding = userdata;
4315 -
4316 -    pa_assert(type);
4317 -    pa_assert(binding);
4318 -
4319 -    if (type != binding->target_type)
4320 -        return PA_HOOK_OK;
4321 -
4322 -    set_target_type(binding, NULL);
4323 -
4324 -    return PA_HOOK_OK;
4325 -}
4326 -
4327 -static pa_hook_result_t target_put_cb(void *hook_data, void *call_data, void *userdata) {
4328 -    pa_binding *binding = userdata;
4329 -
4330 -    pa_assert(call_data);
4331 -    pa_assert(binding);
4332 -
4333 -    if (!pa_streq(binding->target_type->get_name(call_data), binding->target_info->name))
4334 -        return PA_HOOK_OK;
4335 -
4336 -    set_target_object(binding, call_data);
4337 -
4338 -    return PA_HOOK_OK;
4339 -}
4340 -
4341 -static pa_hook_result_t target_unlink_cb(void *hook_data, void *call_data, void *userdata) {
4342 -    pa_binding *binding = userdata;
4343 -
4344 -    pa_assert(call_data);
4345 -    pa_assert(binding);
4346 -
4347 -    if (call_data != binding->target_object)
4348 -        return PA_HOOK_OK;
4349 -
4350 -    set_target_object(binding, NULL);
4351 -
4352 -    return PA_HOOK_OK;
4353 -}
4354 -
4355 -static void set_target_object(pa_binding *binding, void *object) {
4356 -    pa_assert(binding);
4357 -
4358 -    binding->target_object = object;
4359 -
4360 -    if (object) {
4361 -        if (binding->target_put_slot) {
4362 -            pa_hook_slot_free(binding->target_put_slot);
4363 -            binding->target_put_slot = NULL;
4364 -        }
4365 -
4366 -        if (!binding->target_unlink_slot)
4367 -            binding->target_unlink_slot = pa_hook_connect(binding->target_type->unlink_hook, PA_HOOK_NORMAL, target_unlink_cb,
4368 -                                                          binding);
4369 -
4370 -        if (binding->target_field_offset_valid)
4371 -            binding->owner_info->set_value(binding->owner_info->userdata,
4372 -                                           *((void **) (((uint8_t *) object) + binding->target_field_offset)));
4373 -        else
4374 -            binding->owner_info->set_value(binding->owner_info->userdata, NULL);
4375 -    } else {
4376 -        if (binding->target_unlink_slot) {
4377 -            pa_hook_slot_free(binding->target_unlink_slot);
4378 -            binding->target_unlink_slot = NULL;
4379 -        }
4380 -
4381 -        if (binding->target_type) {
4382 -            if (!binding->target_put_slot)
4383 -                binding->target_put_slot = pa_hook_connect(binding->target_type->put_hook, PA_HOOK_NORMAL, target_put_cb, binding);
4384 -        } else {
4385 -            if (binding->target_put_slot) {
4386 -                pa_hook_slot_free(binding->target_put_slot);
4387 -                binding->target_put_slot = NULL;
4388 -            }
4389 -        }
4390 -
4391 -        binding->owner_info->set_value(binding->owner_info->userdata, NULL);
4392 -    }
4393 -}
4394 -
4395 -static void set_target_type(pa_binding *binding, pa_binding_target_type *type) {
4396 -    pa_assert(binding);
4397 -
4398 -    binding->target_type = type;
4399 -
4400 -    if (type) {
4401 -        int r;
4402 -
4403 -        if (binding->target_type_added_slot) {
4404 -            pa_hook_slot_free(binding->target_type_added_slot);
4405 -            binding->target_type_added_slot = NULL;
4406 -        }
4407 -
4408 -        if (!binding->target_type_removed_slot)
4409 -            binding->target_type_removed_slot =
4410 -                    pa_hook_connect(&binding->volume_api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_REMOVED],
4411 -                                    PA_HOOK_NORMAL, target_type_removed_cb, binding);
4412 -
4413 -        r = pa_binding_target_type_get_field_offset(type, binding->target_info->field, &binding->target_field_offset);
4414 -        if (r >= 0)
4415 -            binding->target_field_offset_valid = true;
4416 -        else {
4417 -            pa_log_warn("Reference to non-existing field \"%s\" in binding target type \"%s\".", binding->target_info->field,
4418 -                        type->name);
4419 -            binding->target_field_offset_valid = false;
4420 -        }
4421 -
4422 -        set_target_object(binding, pa_hashmap_get(type->objects, binding->target_info->name));
4423 -    } else {
4424 -        if (binding->target_type_removed_slot) {
4425 -            pa_hook_slot_free(binding->target_type_removed_slot);
4426 -            binding->target_type_removed_slot = NULL;
4427 -        }
4428 -
4429 -        if (!binding->target_type_added_slot)
4430 -            binding->target_type_added_slot =
4431 -                    pa_hook_connect(&binding->volume_api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_ADDED],
4432 -                                    PA_HOOK_NORMAL, target_type_added_cb, binding);
4433 -
4434 -        binding->target_field_offset_valid = false;
4435 -
4436 -        set_target_object(binding, NULL);
4437 -    }
4438 -}
4439 -
4440 -pa_binding *pa_binding_new(pa_volume_api *api, const pa_binding_owner_info *owner_info,
4441 -                           const pa_binding_target_info *target_info) {
4442 -    pa_binding *binding;
4443 -
4444 -    pa_assert(api);
4445 -    pa_assert(owner_info);
4446 -    pa_assert(target_info);
4447 -
4448 -    binding = pa_xnew0(pa_binding, 1);
4449 -    binding->volume_api = api;
4450 -    binding->owner_info = pa_binding_owner_info_copy(owner_info);
4451 -    binding->target_info = pa_binding_target_info_copy(target_info);
4452 -
4453 -    set_target_type(binding, pa_hashmap_get(api->binding_target_types, target_info->type));
4454 -
4455 -    return binding;
4456 -}
4457 -
4458 -void pa_binding_free(pa_binding *binding) {
4459 -    pa_assert(binding);
4460 -
4461 -    if (binding->target_unlink_slot)
4462 -        pa_hook_slot_free(binding->target_unlink_slot);
4463 -
4464 -    if (binding->target_put_slot)
4465 -        pa_hook_slot_free(binding->target_put_slot);
4466 -
4467 -    if (binding->target_type_removed_slot)
4468 -        pa_hook_slot_free(binding->target_type_removed_slot);
4469 -
4470 -    if (binding->target_type_added_slot)
4471 -        pa_hook_slot_free(binding->target_type_added_slot);
4472 -
4473 -    if (binding->target_info)
4474 -        pa_binding_target_info_free(binding->target_info);
4475 -
4476 -    if (binding->owner_info)
4477 -        pa_binding_owner_info_free(binding->owner_info);
4478 -
4479 -    pa_xfree(binding);
4480 -}
4481 diff --git a/src/modules/volume-api/binding.h b/src/modules/volume-api/binding.h
4482 deleted file mode 100644
4483 index ba4dea8..0000000
4484 --- a/src/modules/volume-api/binding.h
4485 +++ /dev/null
4486 @@ -1,128 +0,0 @@
4487 -#ifndef foobindinghfoo
4488 -#define foobindinghfoo
4489 -
4490 -/***
4491 -  This file is part of PulseAudio.
4492 -
4493 -  Copyright 2014 Intel Corporation
4494 -
4495 -  PulseAudio is free software; you can redistribute it and/or modify
4496 -  it under the terms of the GNU Lesser General Public License as published
4497 -  by the Free Software Foundation; either version 2.1 of the License,
4498 -  or (at your option) any later version.
4499 -
4500 -  PulseAudio is distributed in the hope that it will be useful, but
4501 -  WITHOUT ANY WARRANTY; without even the implied warranty of
4502 -  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4503 -  General Public License for more details.
4504 -
4505 -  You should have received a copy of the GNU Lesser General Public License
4506 -  along with PulseAudio; if not, write to the Free Software
4507 -  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
4508 -  USA.
4509 -***/
4510 -
4511 -#include <modules/volume-api/volume-api.h>
4512 -
4513 -typedef struct pa_binding pa_binding;
4514 -typedef struct pa_binding_owner_info pa_binding_owner_info;
4515 -typedef struct pa_binding_target_info pa_binding_target_info;
4516 -typedef struct pa_binding_target_type pa_binding_target_type;
4517 -
4518 -typedef void (*pa_binding_set_value_cb_t)(void *userdata, void *value);
4519 -
4520 -struct pa_binding_owner_info {
4521 -    /* This is the object that has the variable that the binding is created
4522 -     * for. */
4523 -    void *userdata;
4524 -
4525 -    /* Called when the owner object's value needs to be updated. The userdata
4526 -     * parameter of the callback is the same as the userdata field in this
4527 -     * struct, and the value parameter is the new value for whatever variable
4528 -     * the binding was created for. */
4529 -    pa_binding_set_value_cb_t set_value;
4530 -};
4531 -
4532 -pa_binding_owner_info *pa_binding_owner_info_new(pa_binding_set_value_cb_t set_value, void *userdata);
4533 -pa_binding_owner_info *pa_binding_owner_info_copy(const pa_binding_owner_info *info);
4534 -void pa_binding_owner_info_free(pa_binding_owner_info *info);
4535 -
4536 -struct pa_binding_target_info {
4537 -    /* The target type name as registered with
4538 -     * pa_binding_target_type_register(). */
4539 -    char *type;
4540 -
4541 -    /* The target object name as returned by the get_name callback of
4542 -     * pa_binding_target_type. */
4543 -    char *name;
4544 -
4545 -    /* The target field of the target object. */
4546 -    char *field;
4547 -};
4548 -
4549 -pa_binding_target_info *pa_binding_target_info_new(const char *type, const char *name, const char *field);
4550 -
4551 -/* The string format is "bind:TYPE:NAME". */
4552 -int pa_binding_target_info_new_from_string(const char *str, const char *field, pa_binding_target_info **info);
4553 -
4554 -pa_binding_target_info *pa_binding_target_info_copy(const pa_binding_target_info *info);
4555 -void pa_binding_target_info_free(pa_binding_target_info *info);
4556 -
4557 -typedef const char *(*pa_binding_target_type_get_name_cb_t)(void *object);
4558 -
4559 -struct pa_binding_target_type {
4560 -    /* Identifier for this target type. */
4561 -    char *name;
4562 -
4563 -    /* name -> object. Points directly to some "master" object hashmap, so the
4564 -     * hashmap is not owned by pa_binding_target_type. */
4565 -    pa_hashmap *objects;
4566 -
4567 -    /* The hook that notifies of new objects if this target type. The call data
4568 -     * of the hook must be a pointer to the new object (this should be true for
4569 -     * all PUT hooks, so don't worry too much). */
4570 -    pa_hook *put_hook;
4571 -
4572 -    /* The hook that notifies of unlinked objects of this target type. The call
4573 -     * data of the hook must be a pointer to the removed object (this should be
4574 -     * true for all UNLINK hooks, so don't worry too much). */
4575 -    pa_hook *unlink_hook;
4576 -
4577 -    /* Function for getting the name of an object of this target type. */
4578 -    pa_binding_target_type_get_name_cb_t get_name;
4579 -
4580 -    pa_hashmap *fields;
4581 -};
4582 -
4583 -pa_binding_target_type *pa_binding_target_type_new(const char *name, pa_hashmap *objects, pa_hook *put_hook,
4584 -                                                   pa_hook *unlink_hook, pa_binding_target_type_get_name_cb_t get_name);
4585 -void pa_binding_target_type_free(pa_binding_target_type *type);
4586 -
4587 -/* Useful when calling pa_binding_target_type_add_field(). */
4588 -#define PA_BINDING_CALCULATE_FIELD_OFFSET(type, field) ((size_t) &(((type *) 0)->field))
4589 -
4590 -/* Called during the type initialization (right after
4591 - * pa_binding_target_type_new()). */
4592 -void pa_binding_target_type_add_field(pa_binding_target_type *type, const char *name, size_t offset);
4593 -
4594 -int pa_binding_target_type_get_field_offset(pa_binding_target_type *type, const char *field, size_t *offset);
4595 -
4596 -struct pa_binding {
4597 -    pa_volume_api *volume_api;
4598 -    pa_binding_owner_info *owner_info;
4599 -    pa_binding_target_info *target_info;
4600 -    pa_binding_target_type *target_type;
4601 -    void *target_object;
4602 -    size_t target_field_offset;
4603 -    bool target_field_offset_valid;
4604 -    pa_hook_slot *target_type_added_slot;
4605 -    pa_hook_slot *target_type_removed_slot;
4606 -    pa_hook_slot *target_put_slot;
4607 -    pa_hook_slot *target_unlink_slot;
4608 -};
4609 -
4610 -pa_binding *pa_binding_new(pa_volume_api *api, const pa_binding_owner_info *owner_info,
4611 -                           const pa_binding_target_info *target_info);
4612 -void pa_binding_free(pa_binding *binding);
4613 -
4614 -#endif
4615 diff --git a/src/modules/volume-api/bvolume.h b/src/modules/volume-api/bvolume.h
4616 index 0317fb6..75545dd 100644
4617 --- a/src/modules/volume-api/bvolume.h
4618 +++ b/src/modules/volume-api/bvolume.h
4619 @@ -29,13 +29,16 @@ typedef pa_ext_volume_api_bvolume pa_bvolume;
4620  #define pa_balance_valid pa_ext_volume_api_balance_valid
4621  #define pa_bvolume_valid pa_ext_volume_api_bvolume_valid
4622  #define pa_bvolume_init_invalid pa_ext_volume_api_bvolume_init_invalid
4623 +#define pa_bvolume_init pa_ext_volume_api_bvolume_init
4624  #define pa_bvolume_init_mono pa_ext_volume_api_bvolume_init_mono
4625 +#define pa_bvolume_parse_balance pa_ext_volume_api_bvolume_parse_balance
4626  #define pa_bvolume_equal pa_ext_volume_api_bvolume_equal
4627  #define pa_bvolume_from_cvolume pa_ext_volume_api_bvolume_from_cvolume
4628  #define pa_bvolume_to_cvolume pa_ext_volume_api_bvolume_to_cvolume
4629  #define pa_bvolume_copy_balance pa_ext_volume_api_bvolume_copy_balance
4630  #define pa_bvolume_reset_balance pa_ext_volume_api_bvolume_reset_balance
4631  #define pa_bvolume_remap pa_ext_volume_api_bvolume_remap
4632 +#define pa_bvolume_balance_to_string pa_ext_volume_api_bvolume_balance_to_string
4633  
4634  #define PA_BVOLUME_SNPRINT_BALANCE_MAX PA_EXT_VOLUME_API_BVOLUME_SNPRINT_BALANCE_MAX
4635  #define pa_bvolume_snprint_balance pa_ext_volume_api_bvolume_snprint_balance
4636 diff --git a/src/modules/volume-api/device-creator.c b/src/modules/volume-api/device-creator.c
4637 index f35fab0..fc486f8 100644
4638 --- a/src/modules/volume-api/device-creator.c
4639 +++ b/src/modules/volume-api/device-creator.c
4640 @@ -59,6 +59,8 @@ struct device_volume_control {
4641      pa_hook_slot *volume_changed_slot;
4642  };
4643  
4644 +static void device_volume_control_free(struct device_volume_control *control);
4645 +
4646  struct device_mute_control {
4647      struct device *device;
4648      pa_mute_control *mute_control;
4649 @@ -68,6 +70,8 @@ struct device_mute_control {
4650      pa_hook_slot *mute_changed_slot;
4651  };
4652  
4653 +static void device_mute_control_free(struct device_mute_control *control);
4654 +
4655  struct device {
4656      pa_device_creator *creator;
4657      enum device_type type;
4658 @@ -85,6 +89,8 @@ struct device {
4659      struct device *monitor;
4660  };
4661  
4662 +static void device_free(struct device *device);
4663 +
4664  static const char *device_type_from_icon_name(const char *icon_name) {
4665      if (!icon_name)
4666          return NULL;
4667 @@ -168,112 +174,6 @@ static const char *get_source_description(pa_source *source) {
4668      return source->name;
4669  }
4670  
4671 -static int volume_control_set_volume_cb(pa_volume_control *c, const pa_bvolume *volume, bool set_volume, bool set_balance) {
4672 -    struct device_volume_control *control;
4673 -    struct device *device;
4674 -    pa_bvolume bvolume;
4675 -    pa_cvolume cvolume;
4676 -
4677 -    pa_assert(c);
4678 -    pa_assert(volume);
4679 -
4680 -    control = c->userdata;
4681 -    device = control->device;
4682 -
4683 -    switch (device->type) {
4684 -        case DEVICE_TYPE_PORT:
4685 -            if (device->port->direction == PA_DIRECTION_OUTPUT)
4686 -                pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
4687 -            else
4688 -                pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
4689 -            break;
4690 -
4691 -        case DEVICE_TYPE_SINK:
4692 -            pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
4693 -            break;
4694 -
4695 -        case DEVICE_TYPE_PORT_MONITOR:
4696 -        case DEVICE_TYPE_SOURCE:
4697 -            pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
4698 -            break;
4699 -    }
4700 -
4701 -    if (set_volume)
4702 -        bvolume.volume = volume->volume;
4703 -
4704 -    if (set_balance)
4705 -        pa_bvolume_copy_balance(&bvolume, volume);
4706 -
4707 -    pa_bvolume_to_cvolume(&bvolume, &cvolume);
4708 -
4709 -    switch (device->type) {
4710 -        case DEVICE_TYPE_PORT:
4711 -            if (device->port->direction == PA_DIRECTION_OUTPUT)
4712 -                pa_sink_set_volume(device->sink, &cvolume, true, true);
4713 -            else
4714 -                pa_source_set_volume(device->source, &cvolume, true, true);
4715 -            break;
4716 -
4717 -        case DEVICE_TYPE_PORT_MONITOR:
4718 -        case DEVICE_TYPE_SOURCE:
4719 -            pa_source_set_volume(device->source, &cvolume, true, true);
4720 -            break;
4721 -
4722 -        case DEVICE_TYPE_SINK:
4723 -            pa_sink_set_volume(device->sink, &cvolume, true, true);
4724 -            break;
4725 -    }
4726 -
4727 -    return 0;
4728 -}
4729 -
4730 -static struct device_volume_control *device_volume_control_new(struct device *device) {
4731 -    struct device_volume_control *control;
4732 -    const char *name = NULL;
4733 -    bool convertible_to_dB = false;
4734 -    bool channel_map_is_writable;
4735 -
4736 -    pa_assert(device);
4737 -
4738 -    control = pa_xnew0(struct device_volume_control, 1);
4739 -    control->device = device;
4740 -
4741 -    switch (device->type) {
4742 -        case DEVICE_TYPE_PORT:
4743 -            name = "port-volume-control";
4744 -
4745 -            if (device->port->direction == PA_DIRECTION_OUTPUT)
4746 -                convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
4747 -            else
4748 -                convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
4749 -
4750 -            break;
4751 -
4752 -        case DEVICE_TYPE_PORT_MONITOR:
4753 -            name = "port-monitor-volume-control";
4754 -            convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
4755 -            break;
4756 -
4757 -        case DEVICE_TYPE_SINK:
4758 -            name = "sink-volume-control";
4759 -            convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
4760 -            break;
4761 -
4762 -        case DEVICE_TYPE_SOURCE:
4763 -            name = "source-volume-control";
4764 -            convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
4765 -            break;
4766 -    }
4767 -
4768 -    channel_map_is_writable = false;
4769 -    control->volume_control = pa_volume_control_new(device->creator->volume_api, name, device->device->description,
4770 -                                                    convertible_to_dB, channel_map_is_writable);
4771 -    control->volume_control->set_volume = volume_control_set_volume_cb;
4772 -    control->volume_control->userdata = control;
4773 -
4774 -    return control;
4775 -}
4776 -
4777  static pa_hook_result_t sink_or_source_volume_changed_cb(void *hook_data, void *call_data, void *userdata) {
4778      struct device_volume_control *control = userdata;
4779      struct device *device;
4780 @@ -309,24 +209,55 @@ static pa_hook_result_t sink_or_source_volume_changed_cb(void *hook_data, void *
4781  
4782      if (sink)
4783          pa_bvolume_from_cvolume(&bvolume, &sink->reference_volume, &sink->channel_map);
4784 -    else
4785 +    else if (source)
4786          pa_bvolume_from_cvolume(&bvolume, &source->reference_volume, &source->channel_map);
4787 +    else
4788 +        pa_assert_not_reached();
4789  
4790 -    pa_volume_control_volume_changed(control->volume_control, &bvolume, true, true);
4791 +    pa_volume_control_set_volume(control->volume_control, &bvolume, true, true);
4792  
4793      return PA_HOOK_OK;
4794  }
4795  
4796 -static void volume_control_set_initial_volume_cb(pa_volume_control *c) {
4797 +static int volume_control_set_volume_cb(pa_volume_control *c, const pa_bvolume *original_volume,
4798 +                                        const pa_bvolume *remapped_volume, bool set_volume, bool set_balance) {
4799      struct device_volume_control *control;
4800      struct device *device;
4801 +    pa_bvolume bvolume;
4802      pa_cvolume cvolume;
4803  
4804      pa_assert(c);
4805 +    pa_assert(original_volume);
4806 +    pa_assert(remapped_volume);
4807  
4808      control = c->userdata;
4809      device = control->device;
4810 -    pa_bvolume_to_cvolume(&control->volume_control->volume, &cvolume);
4811 +
4812 +    switch (device->type) {
4813 +        case DEVICE_TYPE_PORT:
4814 +            if (device->port->direction == PA_DIRECTION_OUTPUT)
4815 +                pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
4816 +            else
4817 +                pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
4818 +            break;
4819 +
4820 +        case DEVICE_TYPE_SINK:
4821 +            pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
4822 +            break;
4823 +
4824 +        case DEVICE_TYPE_PORT_MONITOR:
4825 +        case DEVICE_TYPE_SOURCE:
4826 +            pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
4827 +            break;
4828 +    }
4829 +
4830 +    if (set_volume)
4831 +        bvolume.volume = remapped_volume->volume;
4832 +
4833 +    if (set_balance)
4834 +        pa_bvolume_copy_balance(&bvolume, remapped_volume);
4835 +
4836 +    pa_bvolume_to_cvolume(&bvolume, &cvolume);
4837  
4838      switch (device->type) {
4839          case DEVICE_TYPE_PORT:
4840 @@ -345,44 +276,91 @@ static void volume_control_set_initial_volume_cb(pa_volume_control *c) {
4841              pa_sink_set_volume(device->sink, &cvolume, true, true);
4842              break;
4843      }
4844 +
4845 +    return 0;
4846  }
4847  
4848 -static void device_volume_control_put(struct device_volume_control *control) {
4849 -    struct device *device;
4850 +static int device_volume_control_new(struct device *device, struct device_volume_control **_r) {
4851 +    struct device_volume_control *control = NULL;
4852 +    const char *name = NULL;
4853      pa_bvolume volume;
4854 +    bool convertible_to_dB = false;
4855 +    int r;
4856  
4857 -    pa_assert(control);
4858 +    pa_assert(device);
4859 +    pa_assert(_r);
4860  
4861 -    device = control->device;
4862 +    control = pa_xnew0(struct device_volume_control, 1);
4863 +    control->device = device;
4864  
4865      switch (device->type) {
4866          case DEVICE_TYPE_PORT:
4867 +            name = "port-volume-control";
4868 +
4869              if (device->port->direction == PA_DIRECTION_OUTPUT) {
4870                  control->volume_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED],
4871                                                                 PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
4872                  pa_bvolume_from_cvolume(&volume, &device->sink->reference_volume, &device->sink->channel_map);
4873 +                convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
4874              } else {
4875                  control->volume_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
4876                                                                 PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
4877                  pa_bvolume_from_cvolume(&volume, &device->source->reference_volume, &device->source->channel_map);
4878 +                convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
4879              }
4880 +
4881              break;
4882  
4883          case DEVICE_TYPE_PORT_MONITOR:
4884 -        case DEVICE_TYPE_SOURCE:
4885 +            name = "port-monitor-volume-control";
4886              control->volume_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
4887                                                             PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
4888              pa_bvolume_from_cvolume(&volume, &device->source->reference_volume, &device->source->channel_map);
4889 +            convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
4890              break;
4891  
4892          case DEVICE_TYPE_SINK:
4893 +            name = "sink-volume-control";
4894              control->volume_changed_slot = pa_hook_connect(&device->sink->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED],
4895                                                             PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
4896              pa_bvolume_from_cvolume(&volume, &device->sink->reference_volume, &device->sink->channel_map);
4897 +            convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
4898 +            break;
4899 +
4900 +        case DEVICE_TYPE_SOURCE:
4901 +            name = "source-volume-control";
4902 +            control->volume_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
4903 +                                                           PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
4904 +            pa_bvolume_from_cvolume(&volume, &device->source->reference_volume, &device->source->channel_map);
4905 +            convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
4906              break;
4907      }
4908  
4909 -    pa_volume_control_put(control->volume_control, &volume, volume_control_set_initial_volume_cb);
4910 +    r = pa_volume_control_new(device->creator->volume_api, name, false, &control->volume_control);
4911 +    if (r < 0)
4912 +        goto fail;
4913 +
4914 +    pa_volume_control_set_description(control->volume_control, device->device->description);
4915 +    pa_volume_control_set_channel_map(control->volume_control, &volume.channel_map);
4916 +    pa_volume_control_set_volume(control->volume_control, &volume, true, true);
4917 +    pa_volume_control_set_convertible_to_dB(control->volume_control, convertible_to_dB);
4918 +    control->volume_control->set_volume = volume_control_set_volume_cb;
4919 +    control->volume_control->userdata = control;
4920 +
4921 +    *_r = control;
4922 +    return 0;
4923 +
4924 +fail:
4925 +    if (control)
4926 +        device_volume_control_free(control);
4927 +
4928 +    return r;
4929 +}
4930 +
4931 +static void device_volume_control_put(struct device_volume_control *control) {
4932 +    pa_assert(control);
4933 +
4934 +    pa_volume_control_put(control->volume_control);
4935  }
4936  
4937  static void device_volume_control_unlink(struct device_volume_control *control) {
4938 @@ -395,11 +373,6 @@ static void device_volume_control_unlink(struct device_volume_control *control)
4939  
4940      if (control->volume_control)
4941          pa_volume_control_unlink(control->volume_control);
4942 -
4943 -    if (control->volume_changed_slot) {
4944 -        pa_hook_slot_free(control->volume_changed_slot);
4945 -        control->volume_changed_slot = NULL;
4946 -    }
4947  }
4948  
4949  static void device_volume_control_free(struct device_volume_control *control) {
4950 @@ -411,71 +384,10 @@ static void device_volume_control_free(struct device_volume_control *control) {
4951      if (control->volume_control)
4952          pa_volume_control_free(control->volume_control);
4953  
4954 -    pa_xfree(control);
4955 -}
4956 -
4957 -static int mute_control_set_mute_cb(pa_mute_control *c, bool mute) {
4958 -    struct device_mute_control *control;
4959 -    struct device *device;
4960 -
4961 -    pa_assert(c);
4962 -
4963 -    control = c->userdata;
4964 -    device = control->device;
4965 -
4966 -    switch (device->type) {
4967 -        case DEVICE_TYPE_PORT:
4968 -            if (device->port->direction == PA_DIRECTION_OUTPUT)
4969 -                pa_sink_set_mute(device->sink, mute, true);
4970 -            else
4971 -                pa_source_set_mute(device->source, mute, true);
4972 -            break;
4973 -
4974 -        case DEVICE_TYPE_PORT_MONITOR:
4975 -        case DEVICE_TYPE_SOURCE:
4976 -            pa_source_set_mute(device->source, mute, true);
4977 -            break;
4978 -
4979 -        case DEVICE_TYPE_SINK:
4980 -            pa_sink_set_mute(device->sink, mute, true);
4981 -            break;
4982 -    }
4983 -
4984 -    return 0;
4985 -}
4986 -
4987 -static struct device_mute_control *device_mute_control_new(struct device *device) {
4988 -    struct device_mute_control *control;
4989 -    const char *name = NULL;
4990 -
4991 -    pa_assert(device);
4992 -
4993 -    control = pa_xnew0(struct device_mute_control, 1);
4994 -    control->device = device;
4995 -
4996 -    switch (device->type) {
4997 -        case DEVICE_TYPE_PORT:
4998 -            name = "port-mute-control";
4999 -            break;
5000 -
5001 -        case DEVICE_TYPE_PORT_MONITOR:
5002 -            name = "port-monitor-mute-control";
5003 -            break;
5004 -
5005 -        case DEVICE_TYPE_SINK:
5006 -            name = "sink-mute-control";
5007 -            break;
5008 -
5009 -        case DEVICE_TYPE_SOURCE:
5010 -            name = "source-mute-control";
5011 -            break;
5012 -    }
5013 -
5014 -    control->mute_control = pa_mute_control_new(device->creator->volume_api, name, device->device->description);
5015 -    control->mute_control->set_mute = mute_control_set_mute_cb;
5016 -    control->mute_control->userdata = control;
5017 +    if (control->volume_changed_slot)
5018 +        pa_hook_slot_free(control->volume_changed_slot);
5019  
5020 -    return control;
5021 +    pa_xfree(control);
5022  }
5023  
5024  static pa_hook_result_t sink_or_source_mute_changed_cb(void *hook_data, void *call_data, void *userdata) {
5025 @@ -518,13 +430,13 @@ static pa_hook_result_t sink_or_source_mute_changed_cb(void *hook_data, void *ca
5026      else
5027          pa_assert_not_reached();
5028  
5029 -    pa_mute_control_mute_changed(control->mute_control, mute);
5030 +    pa_mute_control_set_mute(control->mute_control, mute);
5031  
5032      return PA_HOOK_OK;
5033  }
5034  
5035 -static void mute_control_set_initial_mute_cb(pa_mute_control *c) {
5036 -    struct device_volume_control *control;
5037 +static int mute_control_set_mute_cb(pa_mute_control *c, bool mute) {
5038 +    struct device_mute_control *control;
5039      struct device *device;
5040  
5041      pa_assert(c);
5042 @@ -535,32 +447,40 @@ static void mute_control_set_initial_mute_cb(pa_mute_control *c) {
5043      switch (device->type) {
5044          case DEVICE_TYPE_PORT:
5045              if (device->port->direction == PA_DIRECTION_OUTPUT)
5046 -                pa_sink_set_mute(device->sink, c->mute, true);
5047 +                pa_sink_set_mute(device->sink, mute, true);
5048              else
5049 -                pa_source_set_mute(device->source, c->mute, true);
5050 +                pa_source_set_mute(device->source, mute, true);
5051              break;
5052  
5053          case DEVICE_TYPE_PORT_MONITOR:
5054          case DEVICE_TYPE_SOURCE:
5055 -            pa_source_set_mute(device->source, c->mute, true);
5056 +            pa_source_set_mute(device->source, mute, true);
5057              break;
5058  
5059          case DEVICE_TYPE_SINK:
5060 -            pa_sink_set_mute(device->sink, c->mute, true);
5061 +            pa_sink_set_mute(device->sink, mute, true);
5062              break;
5063      }
5064 +
5065 +    return 0;
5066  }
5067  
5068 -static void device_mute_control_put(struct device_mute_control *control) {
5069 -    struct device *device;
5070 +static int device_mute_control_new(struct device *device, struct device_mute_control **_r) {
5071 +    struct device_mute_control *control = NULL;
5072 +    const char *name = NULL;
5073      bool mute = false;
5074 +    int r;
5075  
5076 -    pa_assert(control);
5077 +    pa_assert(device);
5078 +    pa_assert(_r);
5079  
5080 -    device = control->device;
5081 +    control = pa_xnew0(struct device_mute_control, 1);
5082 +    control->device = device;
5083  
5084      switch (device->type) {
5085          case DEVICE_TYPE_PORT:
5086 +            name = "port-mute-control";
5087 +
5088              if (device->port->direction == PA_DIRECTION_OUTPUT) {
5089                  control->mute_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED],
5090                                                               PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
5091 @@ -573,20 +493,50 @@ static void device_mute_control_put(struct device_mute_control *control) {
5092              break;
5093  
5094          case DEVICE_TYPE_PORT_MONITOR:
5095 -        case DEVICE_TYPE_SOURCE:
5096 -            control->mute_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED],
5097 +            name = "port-monitor-mute-control";
5098 +            control->mute_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED],
5099                                                           PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
5100              mute = device->source->muted;
5101              break;
5102  
5103          case DEVICE_TYPE_SINK:
5104 +            name = "sink-mute-control";
5105              control->mute_changed_slot = pa_hook_connect(&device->sink->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED],
5106                                                           PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
5107              mute = device->sink->muted;
5108              break;
5109 +
5110 +        case DEVICE_TYPE_SOURCE:
5111 +            name = "source-mute-control";
5112 +            control->mute_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED],
5113 +                                                         PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
5114 +            mute = device->source->muted;
5115 +            break;
5116      }
5117  
5118 -    pa_mute_control_put(control->mute_control, mute, true, mute_control_set_initial_mute_cb);
5119 +    r = pa_mute_control_new(device->creator->volume_api, name, false, &control->mute_control);
5120 +    if (r < 0)
5121 +        goto fail;
5122 +
5123 +    pa_mute_control_set_description(control->mute_control, device->device->description);
5124 +    pa_mute_control_set_mute(control->mute_control, mute);
5125 +    control->mute_control->set_mute = mute_control_set_mute_cb;
5126 +    control->mute_control->userdata = control;
5127 +
5128 +    *_r = control;
5129 +    return 0;
5130 +
5131 +fail:
5132 +    if (control)
5133 +        device_mute_control_free(control);
5134 +
5135 +    return r;
5136 +}
5137 +
5138 +static void device_mute_control_put(struct device_mute_control *control) {
5139 +    pa_assert(control);
5140 +
5141 +    pa_mute_control_put(control->mute_control);
5142  }
5143  
5144  static void device_mute_control_unlink(struct device_mute_control *control) {
5145 @@ -599,11 +549,6 @@ static void device_mute_control_unlink(struct device_mute_control *control) {
5146  
5147      if (control->mute_control)
5148          pa_mute_control_unlink(control->mute_control);
5149 -
5150 -    if (control->mute_changed_slot) {
5151 -        pa_hook_slot_free(control->mute_changed_slot);
5152 -        control->mute_changed_slot = NULL;
5153 -    }
5154  }
5155  
5156  static void device_mute_control_free(struct device_mute_control *control) {
5157 @@ -615,6 +560,9 @@ static void device_mute_control_free(struct device_mute_control *control) {
5158      if (control->mute_control)
5159          pa_mute_control_free(control->mute_control);
5160  
5161 +    if (control->mute_changed_slot)
5162 +        pa_hook_slot_free(control->mute_changed_slot);
5163 +
5164      pa_xfree(control);
5165  }
5166  
5167 @@ -707,22 +655,24 @@ static pa_hook_result_t sink_or_source_proplist_changed_cb(void *hook_data, void
5168      }
5169  
5170      pa_device_description_changed(device->device, description);
5171 -    pa_volume_control_description_changed(device->volume_control->volume_control, description);
5172 -    pa_mute_control_description_changed(device->mute_control->mute_control, description);
5173 +    pa_volume_control_set_description(device->volume_control->volume_control, description);
5174 +    pa_mute_control_set_description(device->mute_control->mute_control, description);
5175  
5176      return PA_HOOK_OK;
5177  }
5178  
5179 -static struct device *device_new(pa_device_creator *creator, enum device_type type, void *core_device) {
5180 +static int device_new(pa_device_creator *creator, enum device_type type, void *core_device, struct device **_r) {
5181      struct device *device = NULL;
5182      const char *name = NULL;
5183      char *description = NULL;
5184      pa_direction_t direction = PA_DIRECTION_OUTPUT;
5185      const char *device_type = NULL;
5186      bool create_volume_and_mute_controls = true;
5187 +    int r;
5188  
5189      pa_assert(creator);
5190      pa_assert(core_device);
5191 +    pa_assert(_r);
5192  
5193      device = pa_xnew0(struct device, 1);
5194      device->creator = creator;
5195 @@ -767,18 +717,20 @@ static struct device *device_new(pa_device_creator *creator, enum device_type ty
5196              break;
5197      }
5198  
5199 -    device->device = pa_device_new(creator->volume_api, name, description, direction, &device_type, device_type ? 1 : 0);
5200 +    r = pa_device_new(creator->volume_api, name, description, direction, &device_type, device_type ? 1 : 0, &device->device);
5201      pa_xfree(description);
5202 +    if (r < 0)
5203 +        goto fail;
5204  
5205      if (create_volume_and_mute_controls) {
5206 -        device->volume_control = device_volume_control_new(device);
5207 -        device->mute_control = device_mute_control_new(device);
5208 +        device_volume_control_new(device, &device->volume_control);
5209 +        device_mute_control_new(device, &device->mute_control);
5210      }
5211  
5212      switch (type) {
5213          case DEVICE_TYPE_PORT:
5214              if (device->port->direction == PA_DIRECTION_OUTPUT)
5215 -                device->monitor = device_new(creator, DEVICE_TYPE_PORT_MONITOR, device->port);
5216 +                device_new(creator, DEVICE_TYPE_PORT_MONITOR, device->port, &device->monitor);
5217              break;
5218  
5219          case DEVICE_TYPE_PORT_MONITOR:
5220 @@ -795,7 +747,14 @@ static struct device *device_new(pa_device_creator *creator, enum device_type ty
5221              break;
5222      }
5223  
5224 -    return device;
5225 +    *_r = device;
5226 +    return 0;
5227 +
5228 +fail:
5229 +    if (device)
5230 +        device_free(device);
5231 +
5232 +    return r;
5233  }
5234  
5235  static pa_hook_result_t port_active_changed_cb(void *hook_data, void *call_data, void *userdata) {
5236 @@ -825,25 +784,36 @@ static pa_hook_result_t port_active_changed_cb(void *hook_data, void *call_data,
5237              pa_assert_not_reached();
5238      }
5239  
5240 -    if (should_have_volume_and_mute_controls && !device->volume_control) {
5241 -        pa_assert(!device->mute_control);
5242 +    if (should_have_volume_and_mute_controls) {
5243 +        int r;
5244  
5245 -        device->volume_control = device_volume_control_new(device);
5246 -        device_volume_control_put(device->volume_control);
5247 -        pa_device_set_default_volume_control(device->device, device->volume_control->volume_control);
5248 +        if (!device->volume_control) {
5249 +            r = device_volume_control_new(device, &device->volume_control);
5250 +            if (r >= 0) {
5251 +                device_volume_control_put(device->volume_control);
5252 +                pa_device_set_default_volume_control(device->device, device->volume_control->volume_control);
5253 +            }
5254 +        }
5255  
5256 -        device->mute_control = device_mute_control_new(device);
5257 -        device_mute_control_put(device->mute_control);
5258 -        pa_device_set_default_mute_control(device->device, device->mute_control->mute_control);
5259 +        if (!device->mute_control) {
5260 +            r = device_mute_control_new(device, &device->mute_control);
5261 +            if (r >= 0) {
5262 +                device_mute_control_put(device->mute_control);
5263 +                pa_device_set_default_mute_control(device->device, device->mute_control->mute_control);
5264 +            }
5265 +        }
5266      }
5267  
5268 -    if (!should_have_volume_and_mute_controls && device->volume_control) {
5269 -        pa_assert(device->mute_control);
5270 +    if (!should_have_volume_and_mute_controls) {
5271 +        if (device->mute_control) {
5272 +            device_mute_control_free(device->mute_control);
5273 +            device->mute_control = NULL;
5274 +        }
5275  
5276 -        device_mute_control_free(device->mute_control);
5277 -        device->mute_control = NULL;
5278 -        device_volume_control_free(device->volume_control);
5279 -        device->volume_control = NULL;
5280 +        if (device->volume_control) {
5281 +            device_volume_control_free(device->volume_control);
5282 +            device->volume_control = NULL;
5283 +        }
5284      }
5285  
5286      return PA_HOOK_OK;
5287 @@ -928,6 +898,7 @@ static void device_free(struct device *device) {
5288  
5289  static void create_device(pa_device_creator *creator, enum device_type type, void *core_device) {
5290      struct device *device;
5291 +    int r;
5292  
5293      pa_assert(creator);
5294      pa_assert(core_device);
5295 @@ -956,9 +927,11 @@ static void create_device(pa_device_creator *creator, enum device_type type, voi
5296          }
5297      }
5298  
5299 -    device = device_new(creator, type, core_device);
5300 -    pa_hashmap_put(creator->devices, core_device, device);
5301 -    device_put(device);
5302 +    r = device_new(creator, type, core_device, &device);
5303 +    if (r >= 0) {
5304 +        pa_hashmap_put(creator->devices, core_device, device);
5305 +        device_put(device);
5306 +    }
5307  }
5308  
5309  static pa_hook_result_t card_put_cb(void *hook_data, void *call_data, void *userdata) {
5310 diff --git a/src/modules/volume-api/device.c b/src/modules/volume-api/device.c
5311 index ea496ba..c1a580c 100644
5312 --- a/src/modules/volume-api/device.c
5313 +++ b/src/modules/volume-api/device.c
5314 @@ -32,20 +32,26 @@
5315  
5316  #include <pulsecore/core-util.h>
5317  
5318 -pa_device *pa_device_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction,
5319 -                         const char * const *device_types, unsigned n_device_types) {
5320 -    pa_device *device;
5321 +int pa_device_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction,
5322 +                  const char * const *device_types, unsigned n_device_types, pa_device **_r) {
5323 +    pa_device *device = NULL;
5324 +    int r;
5325      unsigned i;
5326  
5327      pa_assert(api);
5328      pa_assert(name);
5329      pa_assert(description);
5330      pa_assert(device_types || n_device_types == 0);
5331 +    pa_assert(_r);
5332  
5333      device = pa_xnew0(pa_device, 1);
5334      device->volume_api = api;
5335      device->index = pa_volume_api_allocate_device_index(api);
5336 -    pa_assert_se(pa_volume_api_register_name(api, name, false, &device->name) >= 0);
5337 +
5338 +    r = pa_volume_api_register_name(api, name, false, &device->name);
5339 +    if (r < 0)
5340 +        goto fail;
5341 +
5342      device->description = pa_xstrdup(description);
5343      device->direction = direction;
5344      device->device_types = pa_dynarray_new(pa_xfree);
5345 @@ -57,7 +63,14 @@ pa_device *pa_device_new(pa_volume_api *api, const char *name, const char *descr
5346      device->use_default_volume_control = true;
5347      device->use_default_mute_control = true;
5348  
5349 -    return device;
5350 +    *_r = device;
5351 +    return 0;
5352 +
5353 +fail:
5354 +    if (device)
5355 +        pa_device_free(device);
5356 +
5357 +    return r;
5358  }
5359  
5360  void pa_device_put(pa_device *device, pa_volume_control *default_volume_control, pa_mute_control *default_mute_control) {
5361 @@ -84,7 +97,6 @@ void pa_device_put(pa_device *device, pa_volume_control *default_volume_control,
5362      }
5363  
5364      pa_volume_api_add_device(device->volume_api, device);
5365 -
5366      device->linked = true;
5367  
5368      device_types_str = pa_join((const char * const *) pa_dynarray_get_raw_array(device->device_types),
5369 @@ -120,35 +132,21 @@ void pa_device_unlink(pa_device *device) {
5370      pa_log_debug("Unlinking device %s.", device->name);
5371  
5372      if (device->linked)
5373 -        pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_UNLINK], device);
5374 -
5375 -    pa_volume_api_remove_device(device->volume_api, device);
5376 +        pa_volume_api_remove_device(device->volume_api, device);
5377  
5378 -    if (device->mute_control) {
5379 -        pa_mute_control_remove_device(device->mute_control, device);
5380 -        device->mute_control = NULL;
5381 -    }
5382 -
5383 -    if (device->default_mute_control) {
5384 -        pa_mute_control_remove_default_for_device(device->default_mute_control, device);
5385 -        device->default_mute_control = NULL;
5386 -    }
5387 +    pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_UNLINK], device);
5388  
5389 -    if (device->volume_control) {
5390 -        pa_volume_control_remove_device(device->volume_control, device);
5391 -        device->volume_control = NULL;
5392 -    }
5393 -
5394 -    if (device->default_volume_control) {
5395 -        pa_volume_control_remove_default_for_device(device->default_volume_control, device);
5396 -        device->default_volume_control = NULL;
5397 -    }
5398 +    pa_device_set_mute_control(device, NULL);
5399 +    pa_device_set_default_mute_control(device, NULL);
5400 +    pa_device_set_volume_control(device, NULL);
5401 +    pa_device_set_default_volume_control(device, NULL);
5402  }
5403  
5404  void pa_device_free(pa_device *device) {
5405      pa_assert(device);
5406  
5407 -    if (!device->unlinked)
5408 +    /* unlink() expects name to be set. */
5409 +    if (!device->unlinked && device->name)
5410          pa_device_unlink(device);
5411  
5412      if (device->proplist)
5413 diff --git a/src/modules/volume-api/device.h b/src/modules/volume-api/device.h
5414 index 9eac7e9..8bd5158 100644
5415 --- a/src/modules/volume-api/device.h
5416 +++ b/src/modules/volume-api/device.h
5417 @@ -51,8 +51,8 @@ struct pa_device {
5418      bool unlinked;
5419  };
5420  
5421 -pa_device *pa_device_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction,
5422 -                         const char * const *device_types, unsigned n_device_types);
5423 +int pa_device_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction,
5424 +                  const char * const *device_types, unsigned n_device_types, pa_device **_r);
5425  void pa_device_put(pa_device *device, pa_volume_control *default_volume_control, pa_mute_control *default_mute_control);
5426  void pa_device_unlink(pa_device *device);
5427  void pa_device_free(pa_device *device);
5428 diff --git a/src/modules/volume-api/inidb.c b/src/modules/volume-api/inidb.c
5429 new file mode 100644
5430 index 0000000..8116e72
5431 --- /dev/null
5432 +++ b/src/modules/volume-api/inidb.c
5433 @@ -0,0 +1,553 @@
5434 +/***
5435 +  This file is part of PulseAudio.
5436 +
5437 +  Copyright 2014 Intel Corporation
5438 +
5439 +  PulseAudio is free software; you can redistribute it and/or modify
5440 +  it under the terms of the GNU Lesser General Public License as published
5441 +  by the Free Software Foundation; either version 2.1 of the License,
5442 +  or (at your option) any later version.
5443 +
5444 +  PulseAudio is distributed in the hope that it will be useful, but
5445 +  WITHOUT ANY WARRANTY; without even the implied warranty of
5446 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5447 +  General Public License for more details.
5448 +
5449 +  You should have received a copy of the GNU Lesser General Public License
5450 +  along with PulseAudio; if not, write to the Free Software
5451 +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
5452 +  USA.
5453 +***/
5454 +
5455 +#ifdef HAVE_CONFIG_H
5456 +#include <config.h>
5457 +#endif
5458 +
5459 +#include "inidb.h"
5460 +
5461 +#include <pulse/mainloop-api.h>
5462 +#include <pulse/rtclock.h>
5463 +#include <pulse/timeval.h>
5464 +#include <pulse/xmalloc.h>
5465 +
5466 +#include <pulsecore/conf-parser.h>
5467 +#include <pulsecore/core-error.h>
5468 +#include <pulsecore/core-rtclock.h>
5469 +#include <pulsecore/core-util.h>
5470 +#include <pulsecore/hashmap.h>
5471 +#include <pulsecore/macro.h>
5472 +#include <pulsecore/namereg.h>
5473 +
5474 +#include <errno.h>
5475 +
5476 +#define SAVE_INTERVAL_USEC (10 * PA_USEC_PER_SEC)
5477 +
5478 +struct pa_inidb {
5479 +    pa_core *core;
5480 +    char *name;
5481 +    char *file_path;
5482 +    char *tmp_file_path;
5483 +    pa_hashmap *tables; /* table name -> pa_inidb_table */
5484 +    pa_time_event *time_event;
5485 +    bool failed;
5486 +    void *userdata;
5487 +};
5488 +
5489 +struct pa_inidb_table {
5490 +    pa_inidb *db;
5491 +    char *name;
5492 +    pa_hashmap *columns; /* column name -> column */
5493 +    pa_hashmap *rows; /* row id -> pa_inidb_row */
5494 +    pa_inidb_get_object_cb_t get_object;
5495 +};
5496 +
5497 +struct column {
5498 +    char *name;
5499 +    pa_inidb_parse_cb_t parse;
5500 +};
5501 +
5502 +struct pa_inidb_row {
5503 +    char *id;
5504 +    char *header;
5505 +    pa_hashmap *cells; /* column name -> cell */
5506 +};
5507 +
5508 +struct pa_inidb_cell {
5509 +    pa_inidb *db;
5510 +    struct column *column;
5511 +    char *value;
5512 +    char *assignment;
5513 +};
5514 +
5515 +static void save(pa_inidb *db);
5516 +
5517 +static pa_inidb_table *table_new(pa_inidb *db, const char *name, pa_inidb_get_object_cb_t get_object_cb);
5518 +static void table_free(pa_inidb_table *table);
5519 +static pa_inidb_row *table_add_row_internal(pa_inidb_table *table, const char *row_id);
5520 +
5521 +static struct column *column_new(const char *name, pa_inidb_parse_cb_t parse_cb);
5522 +static void column_free(struct column *column);
5523 +
5524 +static pa_inidb_row *row_new(pa_inidb_table *table, const char *id);
5525 +static void row_free(pa_inidb_row *row);
5526 +
5527 +static pa_inidb_cell *cell_new(pa_inidb *db, struct column *column);
5528 +static void cell_free(pa_inidb_cell *cell);
5529 +static void cell_set_value_internal(pa_inidb_cell *cell, const char *value);
5530 +
5531 +static int parse_assignment(pa_config_parser_state *state) {
5532 +    pa_inidb *db;
5533 +    const char *end_of_table_name;
5534 +    size_t table_name_len;
5535 +    char *table_name;
5536 +    pa_inidb_table *table;
5537 +    const char *row_id;
5538 +    pa_inidb_row *row;
5539 +    int r;
5540 +    void *object;
5541 +    struct column *column;
5542 +    pa_inidb_cell *cell;
5543 +
5544 +    pa_assert(state);
5545 +
5546 +    db = state->userdata;
5547 +
5548 +    /* FIXME: pa_config_parser should be improved so that it could parse the
5549 +     * table name and row id for us in the section header. */
5550 +    end_of_table_name = strchr(state->section, ' ');
5551 +    if (!end_of_table_name) {
5552 +        pa_log("[%s:%u] Failed to parse table name and row id in section \"%s\"", state->filename, state->lineno,
5553 +               state->section);
5554 +        return -PA_ERR_INVALID;
5555 +    }
5556 +
5557 +    table_name_len = end_of_table_name - state->section;
5558 +    table_name = pa_xstrndup(state->section, table_name_len);
5559 +    table = pa_hashmap_get(db->tables, table_name);
5560 +    if (!table)
5561 +        pa_log("[%s:%u] Unknown table name: \"%s\"", state->filename, state->lineno, table_name);
5562 +    pa_xfree(table_name);
5563 +    if (!table)
5564 +        return -PA_ERR_INVALID;
5565 +
5566 +    row_id = end_of_table_name + 1;
5567 +    if (!pa_namereg_is_valid_name(row_id)) {
5568 +        pa_log("[%s:%u] Invalid row id: \"%s\"", state->filename, state->lineno, row_id);
5569 +        return -PA_ERR_INVALID;
5570 +    }
5571 +
5572 +    /* This is not strictly necessary, but we do this to avoid saving the
5573 +     * database when there is no actual change. Without this, the get_object()
5574 +     * callback would cause redundant saving whenever creating new objects. */
5575 +    if (!(row = pa_hashmap_get(table->rows, row_id)))
5576 +        row = table_add_row_internal(table, row_id);
5577 +
5578 +    r = table->get_object(db, row_id, &object);
5579 +    if (r < 0) {
5580 +        pa_log("[%s:%u] Failed to create object %s.", state->filename, state->lineno, row_id);
5581 +        return r;
5582 +    }
5583 +
5584 +    column = pa_hashmap_get(table->columns, state->lvalue);
5585 +    if (!column) {
5586 +        pa_log("[%s:%u] Unknown column name: \"%s\"", state->filename, state->lineno, state->lvalue);
5587 +        return -PA_ERR_INVALID;
5588 +    }
5589 +
5590 +    /* This is not strictly necessary, but we do this to avoid saving the
5591 +     * database when there is no actual change. Without this, the parse()
5592 +     * callback would cause redundant saving whenever setting the cell value
5593 +     * for the first time. */
5594 +    cell = pa_hashmap_get(row->cells, column->name);
5595 +    cell_set_value_internal(cell, state->rvalue);
5596 +
5597 +    r = column->parse(db, state->rvalue, object);
5598 +    if (r < 0) {
5599 +        pa_log("[%s:%u] Failed to parse %s value \"%s\".", state->filename, state->lineno, column->name, state->rvalue);
5600 +        return r;
5601 +    }
5602 +
5603 +    return 0;
5604 +}
5605 +
5606 +pa_inidb *pa_inidb_new(pa_core *core, const char *name, void *userdata) {
5607 +    pa_inidb *db;
5608 +    int r;
5609 +
5610 +    pa_assert(core);
5611 +    pa_assert(name);
5612 +
5613 +    db = pa_xnew0(pa_inidb, 1);
5614 +    db->core = core;
5615 +    db->name = pa_xstrdup(name);
5616 +
5617 +    r = pa_append_to_config_home_dir(name, true, &db->file_path);
5618 +    if (r < 0) {
5619 +        pa_log("Failed to find the file location for database \"%s\". The database will start empty, and updates will not be "
5620 +               "saved on disk.", name);
5621 +        db->failed = true;
5622 +    }
5623 +
5624 +    if (db->file_path)
5625 +        db->tmp_file_path = pa_sprintf_malloc("%s.tmp", db->file_path);
5626 +
5627 +    db->tables = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
5628 +                                     (pa_free_cb_t) table_free);
5629 +    db->userdata = userdata;
5630 +
5631 +    return db;
5632 +}
5633 +
5634 +void pa_inidb_free(pa_inidb *db) {
5635 +    pa_assert(db);
5636 +
5637 +    if (db->time_event) {
5638 +        db->core->mainloop->time_free(db->time_event);
5639 +        save(db);
5640 +    }
5641 +
5642 +    if (db->tables)
5643 +        pa_hashmap_free(db->tables);
5644 +
5645 +    pa_xfree(db->tmp_file_path);
5646 +    pa_xfree(db->file_path);
5647 +    pa_xfree(db->name);
5648 +    pa_xfree(db);
5649 +}
5650 +
5651 +void *pa_inidb_get_userdata(pa_inidb *db) {
5652 +    pa_assert(db);
5653 +
5654 +    return db->userdata;
5655 +}
5656 +
5657 +pa_inidb_table *pa_inidb_add_table(pa_inidb *db, const char *name, pa_inidb_get_object_cb_t get_object_cb) {
5658 +    pa_inidb_table *table;
5659 +
5660 +    pa_assert(db);
5661 +    pa_assert(name);
5662 +    pa_assert(get_object_cb);
5663 +
5664 +    table = table_new(db, name, get_object_cb);
5665 +    pa_assert_se(pa_hashmap_put(db->tables, table->name, table) >= 0);
5666 +
5667 +    return table;
5668 +}
5669 +
5670 +void pa_inidb_load(pa_inidb *db) {
5671 +    unsigned n_config_items;
5672 +    pa_inidb_table *table;
5673 +    void *state;
5674 +    pa_config_item *config_items;
5675 +    unsigned i;
5676 +
5677 +    pa_assert(db);
5678 +
5679 +    if (db->failed)
5680 +        return;
5681 +
5682 +    n_config_items = 0;
5683 +    PA_HASHMAP_FOREACH(table, db->tables, state)
5684 +        n_config_items += pa_hashmap_size(table->columns);
5685 +
5686 +    config_items = pa_xnew0(pa_config_item, n_config_items + 1);
5687 +
5688 +    i = 0;
5689 +    PA_HASHMAP_FOREACH(table, db->tables, state) {
5690 +        struct column *column;
5691 +        void *state2;
5692 +
5693 +        PA_HASHMAP_FOREACH(column, table->columns, state2) {
5694 +            config_items[i].lvalue = column->name;
5695 +            config_items[i].parse = parse_assignment;
5696 +            i++;
5697 +        }
5698 +    }
5699 +
5700 +    pa_config_parse(db->file_path, NULL, config_items, NULL, db);
5701 +    pa_xfree(config_items);
5702 +}
5703 +
5704 +static void save(pa_inidb *db) {
5705 +    FILE *f;
5706 +    pa_inidb_table *table;
5707 +    void *state;
5708 +    int r;
5709 +
5710 +    pa_assert(db);
5711 +
5712 +    if (db->failed)
5713 +        return;
5714 +
5715 +    f = pa_fopen_cloexec(db->tmp_file_path, "w");
5716 +    if (!f) {
5717 +        pa_log("pa_fopen_cloexec() failed: %s", pa_cstrerror(errno));
5718 +        goto fail;
5719 +    }
5720 +
5721 +    PA_HASHMAP_FOREACH(table, db->tables, state) {
5722 +        pa_inidb_row *row;
5723 +        void *state2;
5724 +
5725 +        PA_HASHMAP_FOREACH(row, table->rows, state2) {
5726 +            size_t len;
5727 +            size_t items_written;
5728 +            pa_inidb_cell *cell;
5729 +            void *state3;
5730 +
5731 +            len = strlen(row->header);
5732 +            items_written = fwrite(row->header, len, 1, f);
5733 +
5734 +            if (items_written != 1) {
5735 +                pa_log("fwrite() failed: %s", pa_cstrerror(errno));
5736 +                goto fail;
5737 +            }
5738 +
5739 +            PA_HASHMAP_FOREACH(cell, row->cells, state3) {
5740 +                if (!cell->assignment)
5741 +                    continue;
5742 +
5743 +                len = strlen(cell->assignment);
5744 +                items_written = fwrite(cell->assignment, len, 1, f);
5745 +
5746 +                if (items_written != 1) {
5747 +                    pa_log("fwrite() failed: %s", pa_cstrerror(errno));
5748 +                    goto fail;
5749 +                }
5750 +            }
5751 +
5752 +            items_written = fwrite("\n", 1, 1, f);
5753 +
5754 +            if (items_written != 1) {
5755 +                pa_log("fwrite() failed: %s", pa_cstrerror(errno));
5756 +                goto fail;
5757 +            }
5758 +        }
5759 +    }
5760 +
5761 +    r = fclose(f);
5762 +    if (r < 0) {
5763 +        pa_log("fclose() failed: %s", pa_cstrerror(errno));
5764 +        goto fail;
5765 +    }
5766 +
5767 +    r = rename(db->tmp_file_path, db->file_path);
5768 +    if (r < 0) {
5769 +        pa_log("rename() failed: %s", pa_cstrerror(errno));
5770 +        goto fail;
5771 +    }
5772 +
5773 +    pa_log_debug("Database \"%s\" saved.", db->name);
5774 +
5775 +    return;
5776 +
5777 +fail:
5778 +    if (f)
5779 +        fclose(f);
5780 +
5781 +    db->failed = true;
5782 +    pa_log("Saving database \"%s\" failed, current and future database changes will not be written to the disk.", db->name);
5783 +}
5784 +
5785 +static pa_inidb_table *table_new(pa_inidb *db, const char *name, pa_inidb_get_object_cb_t get_object_cb) {
5786 +    pa_inidb_table *table;
5787 +
5788 +    pa_assert(db);
5789 +    pa_assert(name);
5790 +    pa_assert(get_object_cb);
5791 +
5792 +    table = pa_xnew0(pa_inidb_table, 1);
5793 +    table->db = db;
5794 +    table->name = pa_xstrdup(name);
5795 +    table->columns = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
5796 +                                         (pa_free_cb_t) column_free);
5797 +    table->rows = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
5798 +                                      (pa_free_cb_t) row_free);
5799 +    table->get_object = get_object_cb;
5800 +
5801 +    return table;
5802 +}
5803 +
5804 +static void table_free(pa_inidb_table *table) {
5805 +    pa_assert(table);
5806 +
5807 +    if (table->rows)
5808 +        pa_hashmap_free(table->rows);
5809 +
5810 +    if (table->columns)
5811 +        pa_hashmap_free(table->columns);
5812 +
5813 +    pa_xfree(table->name);
5814 +    pa_xfree(table);
5815 +}
5816 +
5817 +void pa_inidb_table_add_column(pa_inidb_table *table, const char *name, pa_inidb_parse_cb_t parse_cb) {
5818 +    struct column *column;
5819 +
5820 +    pa_assert(table);
5821 +    pa_assert(name);
5822 +    pa_assert(parse_cb);
5823 +
5824 +    column = column_new(name, parse_cb);
5825 +    pa_assert_se(pa_hashmap_put(table->columns, column->name, column) >= 0);
5826 +}
5827 +
5828 +static pa_inidb_row *table_add_row_internal(pa_inidb_table *table, const char *row_id) {
5829 +    pa_inidb_row *row;
5830 +
5831 +    pa_assert(table);
5832 +    pa_assert(row_id);
5833 +
5834 +    row = row_new(table, row_id);
5835 +    pa_assert_se(pa_hashmap_put(table->rows, row->id, row) >= 0);
5836 +
5837 +    return row;
5838 +}
5839 +
5840 +static void time_cb(pa_mainloop_api *api, pa_time_event *event, const struct timeval *tv, void *userdata) {
5841 +    pa_inidb *db = userdata;
5842 +
5843 +    pa_assert(api);
5844 +    pa_assert(db);
5845 +
5846 +    api->time_free(event);
5847 +    db->time_event = NULL;
5848 +
5849 +    save(db);
5850 +}
5851 +
5852 +static void trigger_save(pa_inidb *db) {
5853 +    struct timeval tv;
5854 +
5855 +    pa_assert(db);
5856 +
5857 +    if (db->time_event)
5858 +        return;
5859 +
5860 +    pa_timeval_rtstore(&tv, pa_rtclock_now() + SAVE_INTERVAL_USEC, true);
5861 +    db->time_event = db->core->mainloop->time_new(db->core->mainloop, &tv, time_cb, db);
5862 +}
5863 +
5864 +pa_inidb_row *pa_inidb_table_add_row(pa_inidb_table *table, const char *row_id) {
5865 +    pa_inidb_row *row;
5866 +
5867 +    pa_assert(table);
5868 +    pa_assert(row_id);
5869 +
5870 +    row = pa_hashmap_get(table->rows, row_id);
5871 +    if (row)
5872 +        return row;
5873 +
5874 +    row = table_add_row_internal(table, row_id);
5875 +    trigger_save(table->db);
5876 +
5877 +    return row;
5878 +}
5879 +
5880 +static struct column *column_new(const char *name, pa_inidb_parse_cb_t parse_cb) {
5881 +    struct column *column;
5882 +
5883 +    pa_assert(name);
5884 +    pa_assert(parse_cb);
5885 +
5886 +    column = pa_xnew(struct column, 1);
5887 +    column->name = pa_xstrdup(name);
5888 +    column->parse = parse_cb;
5889 +
5890 +    return column;
5891 +}
5892 +
5893 +static void column_free(struct column *column) {
5894 +    pa_assert(column);
5895 +
5896 +    pa_xfree(column->name);
5897 +    pa_xfree(column);
5898 +}
5899 +
5900 +static pa_inidb_row *row_new(pa_inidb_table *table, const char *id) {
5901 +    pa_inidb_row *row;
5902 +    struct column *column;
5903 +    void *state;
5904 +
5905 +    pa_assert(table);
5906 +    pa_assert(id);
5907 +
5908 +    row = pa_xnew0(pa_inidb_row, 1);
5909 +    row->id = pa_xstrdup(id);
5910 +    row->header = pa_sprintf_malloc("[%s %s]\n", table->name, id);
5911 +    row->cells = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
5912 +                                     (pa_free_cb_t) cell_free);
5913 +
5914 +    PA_HASHMAP_FOREACH(column, table->columns, state) {
5915 +        pa_inidb_cell *cell;
5916 +
5917 +        cell = cell_new(table->db, column);
5918 +        pa_hashmap_put(row->cells, cell->column->name, cell);
5919 +    }
5920 +
5921 +    return row;
5922 +}
5923 +
5924 +static void row_free(pa_inidb_row *row) {
5925 +    pa_assert(row);
5926 +
5927 +    pa_xfree(row->header);
5928 +    pa_xfree(row->id);
5929 +    pa_xfree(row);
5930 +}
5931 +
5932 +pa_inidb_cell *pa_inidb_row_get_cell(pa_inidb_row *row, const char *column_name) {
5933 +    pa_inidb_cell *cell;
5934 +
5935 +    pa_assert(row);
5936 +    pa_assert(column_name);
5937 +
5938 +    pa_assert_se(cell = pa_hashmap_get(row->cells, column_name));
5939 +
5940 +    return cell;
5941 +}
5942 +
5943 +static pa_inidb_cell *cell_new(pa_inidb *db, struct column *column) {
5944 +    pa_inidb_cell *cell;
5945 +
5946 +    pa_assert(db);
5947 +    pa_assert(column);
5948 +
5949 +    cell = pa_xnew0(pa_inidb_cell, 1);
5950 +    cell->db = db;
5951 +    cell->column = column;
5952 +
5953 +    return cell;
5954 +}
5955 +
5956 +static void cell_free(pa_inidb_cell *cell) {
5957 +    pa_assert(cell);
5958 +
5959 +    pa_xfree(cell->assignment);
5960 +    pa_xfree(cell->value);
5961 +    pa_xfree(cell);
5962 +}
5963 +
5964 +static void cell_set_value_internal(pa_inidb_cell *cell, const char *value) {
5965 +    pa_assert(cell);
5966 +    pa_assert(value);
5967 +
5968 +    pa_xfree(cell->value);
5969 +    cell->value = pa_xstrdup(value);
5970 +
5971 +    pa_xfree(cell->assignment);
5972 +    if (value)
5973 +        cell->assignment = pa_sprintf_malloc("%s = %s\n", cell->column->name, value);
5974 +    else
5975 +        cell->assignment = NULL;
5976 +}
5977 +
5978 +void pa_inidb_cell_set_value(pa_inidb_cell *cell, const char *value) {
5979 +    pa_assert(cell);
5980 +
5981 +    if (pa_safe_streq(value, cell->value))
5982 +        return;
5983 +
5984 +    cell_set_value_internal(cell, value);
5985 +    trigger_save(cell->db);
5986 +}
5987 diff --git a/src/modules/volume-api/inidb.h b/src/modules/volume-api/inidb.h
5988 new file mode 100644
5989 index 0000000..ded73ba
5990 --- /dev/null
5991 +++ b/src/modules/volume-api/inidb.h
5992 @@ -0,0 +1,54 @@
5993 +#ifndef fooinidbhfoo
5994 +#define fooinidbhfoo
5995 +
5996 +/***
5997 +  This file is part of PulseAudio.
5998 +
5999 +  Copyright 2014 Intel Corporation
6000 +
6001 +  PulseAudio is free software; you can redistribute it and/or modify
6002 +  it under the terms of the GNU Lesser General Public License as published
6003 +  by the Free Software Foundation; either version 2.1 of the License,
6004 +  or (at your option) any later version.
6005 +
6006 +  PulseAudio is distributed in the hope that it will be useful, but
6007 +  WITHOUT ANY WARRANTY; without even the implied warranty of
6008 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6009 +  General Public License for more details.
6010 +
6011 +  You should have received a copy of the GNU Lesser General Public License
6012 +  along with PulseAudio; if not, write to the Free Software
6013 +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
6014 +  USA.
6015 +***/
6016 +
6017 +#include <pulsecore/core.h>
6018 +
6019 +typedef struct pa_inidb pa_inidb;
6020 +typedef struct pa_inidb_cell pa_inidb_cell;
6021 +typedef struct pa_inidb_row pa_inidb_row;
6022 +typedef struct pa_inidb_table pa_inidb_table;
6023 +
6024 +/* If there's no object with the given name, the implementation is expected to
6025 + * create a new object (or at least try to). */
6026 +typedef int (*pa_inidb_get_object_cb_t)(pa_inidb *db, const char *name, void **_r);
6027 +
6028 +/* The implementation is expected to parse the value, and set the parsed value
6029 + * on the object. */
6030 +typedef int (*pa_inidb_parse_cb_t)(pa_inidb *db, const char *value, void *object);
6031 +
6032 +pa_inidb *pa_inidb_new(pa_core *core, const char *name, void *userdata);
6033 +void pa_inidb_free(pa_inidb *db);
6034 +
6035 +void *pa_inidb_get_userdata(pa_inidb *db);
6036 +pa_inidb_table *pa_inidb_add_table(pa_inidb *db, const char *name, pa_inidb_get_object_cb_t get_object_cb);
6037 +void pa_inidb_load(pa_inidb *db);
6038 +
6039 +void pa_inidb_table_add_column(pa_inidb_table *table, const char *name, pa_inidb_parse_cb_t parse_cb);
6040 +pa_inidb_row *pa_inidb_table_add_row(pa_inidb_table *table, const char *row_id);
6041 +
6042 +pa_inidb_cell *pa_inidb_row_get_cell(pa_inidb_row *row, const char *column_name);
6043 +
6044 +void pa_inidb_cell_set_value(pa_inidb_cell *cell, const char *value);
6045 +
6046 +#endif
6047 diff --git a/src/modules/volume-api/module-volume-api.c b/src/modules/volume-api/module-volume-api.c
6048 index 845ac09..7b112f6 100644
6049 --- a/src/modules/volume-api/module-volume-api.c
6050 +++ b/src/modules/volume-api/module-volume-api.c
6051 @@ -51,6 +51,7 @@ struct userdata {
6052      pa_hook_slot *volume_control_unlink_slot;
6053      pa_hook_slot *volume_control_description_changed_slot;
6054      pa_hook_slot *volume_control_volume_changed_slot;
6055 +    pa_hook_slot *volume_control_convertible_to_db_changed_slot;
6056      pa_hook_slot *mute_control_put_slot;
6057      pa_hook_slot *mute_control_unlink_slot;
6058      pa_hook_slot *mute_control_description_changed_slot;
6059 @@ -63,10 +64,13 @@ struct userdata {
6060      pa_hook_slot *stream_put_slot;
6061      pa_hook_slot *stream_unlink_slot;
6062      pa_hook_slot *stream_description_changed_slot;
6063 +    pa_hook_slot *stream_proplist_changed_slot;
6064      pa_hook_slot *stream_volume_control_changed_slot;
6065 +    pa_hook_slot *stream_relative_volume_control_changed_slot;
6066      pa_hook_slot *stream_mute_control_changed_slot;
6067      pa_hook_slot *audio_group_put_slot;
6068      pa_hook_slot *audio_group_unlink_slot;
6069 +    pa_hook_slot *audio_group_description_changed_slot;
6070      pa_hook_slot *audio_group_volume_control_changed_slot;
6071      pa_hook_slot *audio_group_mute_control_changed_slot;
6072      pa_hook_slot *main_output_volume_control_changed_slot;
6073 @@ -1247,6 +1251,9 @@ int pa__init(pa_module *module) {
6074      u->volume_control_volume_changed_slot =
6075              pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_VOLUME_CHANGED],
6076                              PA_HOOK_NORMAL, volume_control_event_cb, u);
6077 +    u->volume_control_convertible_to_db_changed_slot =
6078 +            pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_CONVERTIBLE_TO_DB_CHANGED], PA_HOOK_NORMAL,
6079 +                            volume_control_event_cb, u);
6080      u->mute_control_put_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_PUT],
6081                                                 PA_HOOK_NORMAL, mute_control_put_cb, u);
6082      u->mute_control_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK],
6083 @@ -1277,9 +1284,14 @@ int pa__init(pa_module *module) {
6084      u->stream_description_changed_slot =
6085              pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED], PA_HOOK_NORMAL,
6086                              stream_event_cb, u);
6087 +    u->stream_proplist_changed_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_PROPLIST_CHANGED],
6088 +                                                      PA_HOOK_NORMAL, stream_event_cb, u);
6089      u->stream_volume_control_changed_slot =
6090              pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED],
6091                              PA_HOOK_NORMAL, stream_event_cb, u);
6092 +    u->stream_relative_volume_control_changed_slot =
6093 +            pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_RELATIVE_VOLUME_CONTROL_CHANGED],
6094 +                            PA_HOOK_NORMAL, stream_event_cb, u);
6095      u->stream_mute_control_changed_slot =
6096              pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED], PA_HOOK_NORMAL,
6097                              stream_event_cb, u);
6098 @@ -1287,6 +1299,9 @@ int pa__init(pa_module *module) {
6099                                                PA_HOOK_NORMAL, audio_group_put_cb, u);
6100      u->audio_group_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK],
6101                                                   PA_HOOK_NORMAL, audio_group_unlink_cb, u);
6102 +    u->audio_group_description_changed_slot =
6103 +            pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_DESCRIPTION_CHANGED], PA_HOOK_NORMAL,
6104 +                            audio_group_event_cb, u);
6105      u->audio_group_volume_control_changed_slot =
6106              pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED],
6107                              PA_HOOK_NORMAL, audio_group_event_cb, u);
6108 @@ -1352,6 +1367,9 @@ void pa__done(pa_module *module) {
6109      if (u->audio_group_volume_control_changed_slot)
6110          pa_hook_slot_free(u->audio_group_volume_control_changed_slot);
6111  
6112 +    if (u->audio_group_description_changed_slot)
6113 +        pa_hook_slot_free(u->audio_group_description_changed_slot);
6114 +
6115      if (u->audio_group_unlink_slot)
6116          pa_hook_slot_free(u->audio_group_unlink_slot);
6117  
6118 @@ -1361,9 +1379,15 @@ void pa__done(pa_module *module) {
6119      if (u->stream_mute_control_changed_slot)
6120          pa_hook_slot_free(u->stream_mute_control_changed_slot);
6121  
6122 +    if (u->stream_relative_volume_control_changed_slot)
6123 +        pa_hook_slot_free(u->stream_relative_volume_control_changed_slot);
6124 +
6125      if (u->stream_volume_control_changed_slot)
6126          pa_hook_slot_free(u->stream_volume_control_changed_slot);
6127  
6128 +    if (u->stream_proplist_changed_slot)
6129 +        pa_hook_slot_free(u->stream_proplist_changed_slot);
6130 +
6131      if (u->stream_description_changed_slot)
6132          pa_hook_slot_free(u->stream_description_changed_slot);
6133  
6134 @@ -1400,6 +1424,9 @@ void pa__done(pa_module *module) {
6135      if (u->mute_control_put_slot)
6136          pa_hook_slot_free(u->mute_control_put_slot);
6137  
6138 +    if (u->volume_control_convertible_to_db_changed_slot)
6139 +        pa_hook_slot_free(u->volume_control_convertible_to_db_changed_slot);
6140 +
6141      if (u->volume_control_volume_changed_slot)
6142          pa_hook_slot_free(u->volume_control_volume_changed_slot);
6143  
6144 diff --git a/src/modules/volume-api/mute-control.c b/src/modules/volume-api/mute-control.c
6145 index adc008e..1b2f276 100644
6146 --- a/src/modules/volume-api/mute-control.c
6147 +++ b/src/modules/volume-api/mute-control.c
6148 @@ -31,52 +31,73 @@
6149  
6150  #include <pulsecore/core-util.h>
6151  
6152 -pa_mute_control *pa_mute_control_new(pa_volume_api *api, const char *name, const char *description) {
6153 -    pa_mute_control *control;
6154 +int pa_mute_control_new(pa_volume_api *api, const char *name, bool persistent, pa_mute_control **_r) {
6155 +    pa_mute_control *control = NULL;
6156 +    int r;
6157  
6158      pa_assert(api);
6159      pa_assert(name);
6160 -    pa_assert(description);
6161 +    pa_assert(_r);
6162  
6163      control = pa_xnew0(pa_mute_control, 1);
6164      control->volume_api = api;
6165      control->index = pa_volume_api_allocate_mute_control_index(api);
6166 -    pa_assert_se(pa_volume_api_register_name(api, name, false, &control->name) >= 0);
6167 -    control->description = pa_xstrdup(description);
6168 +
6169 +    r = pa_volume_api_register_name(api, name, false, &control->name);
6170 +    if (r < 0)
6171 +        goto fail;
6172 +
6173 +    control->description = pa_xstrdup(control->name);
6174      control->proplist = pa_proplist_new();
6175 +    control->present = !persistent;
6176 +    control->persistent = persistent;
6177 +    control->purpose = PA_MUTE_CONTROL_PURPOSE_OTHER;
6178      control->devices = pa_hashmap_new(NULL, NULL);
6179      control->default_for_devices = pa_hashmap_new(NULL, NULL);
6180 -    control->streams = pa_hashmap_new(NULL, NULL);
6181 -    control->audio_groups = pa_hashmap_new(NULL, NULL);
6182  
6183 -    return control;
6184 +    if (persistent) {
6185 +        pa_inidb_row *row;
6186 +
6187 +        row = pa_inidb_table_add_row(api->control_db.mute_controls, control->name);
6188 +        control->db_cells.description = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION);
6189 +        control->db_cells.mute = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_MUTE);
6190 +    }
6191 +
6192 +    *_r = control;
6193 +    return 0;
6194 +
6195 +fail:
6196 +    if (control)
6197 +        pa_mute_control_free(control);
6198 +
6199 +    return r;
6200  }
6201  
6202 -void pa_mute_control_put(pa_mute_control *control, bool initial_mute, bool initial_mute_is_set,
6203 -                         pa_mute_control_set_initial_mute_cb_t set_initial_mute_cb) {
6204 +void pa_mute_control_put(pa_mute_control *control) {
6205      const char *prop_key;
6206      void *state = NULL;
6207  
6208      pa_assert(control);
6209 -    pa_assert(initial_mute_is_set || control->set_mute);
6210 -    pa_assert(set_initial_mute_cb || !control->set_mute);
6211 +    pa_assert(control->set_mute || !control->present);
6212  
6213 -    if (initial_mute_is_set)
6214 -        control->mute = initial_mute;
6215 -    else
6216 -        control->mute = false;
6217 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_IMPLEMENTATION_INITIALIZED], control);
6218 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_SET_INITIAL_MUTE], control);
6219  
6220 -    if (set_initial_mute_cb)
6221 -        set_initial_mute_cb(control);
6222 +    if (control->set_mute) {
6223 +        control->set_mute_in_progress = true;
6224 +        control->set_mute(control, control->mute);
6225 +        control->set_mute_in_progress = false;
6226 +    }
6227  
6228      pa_volume_api_add_mute_control(control->volume_api, control);
6229 -
6230      control->linked = true;
6231  
6232      pa_log_debug("Created mute control #%u.", control->index);
6233      pa_log_debug("    Name: %s", control->name);
6234      pa_log_debug("    Description: %s", control->description);
6235      pa_log_debug("    Mute: %s", pa_yes_no(control->mute));
6236 +    pa_log_debug("    Present: %s", pa_yes_no(control->present));
6237 +    pa_log_debug("    Persistent: %s", pa_yes_no(control->persistent));
6238      pa_log_debug("    Properties:");
6239  
6240      while ((prop_key = pa_proplist_iterate(control->proplist, &state)))
6241 @@ -86,9 +107,7 @@ void pa_mute_control_put(pa_mute_control *control, bool initial_mute, bool initi
6242  }
6243  
6244  void pa_mute_control_unlink(pa_mute_control *control) {
6245 -    pa_audio_group *group;
6246      pa_device *device;
6247 -    pas_stream *stream;
6248  
6249      pa_assert(control);
6250  
6251 @@ -102,15 +121,9 @@ void pa_mute_control_unlink(pa_mute_control *control) {
6252      pa_log_debug("Unlinking mute control %s.", control->name);
6253  
6254      if (control->linked)
6255 -        pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK], control);
6256 +        pa_volume_api_remove_mute_control(control->volume_api, control);
6257  
6258 -    pa_volume_api_remove_mute_control(control->volume_api, control);
6259 -
6260 -    while ((group = pa_hashmap_first(control->audio_groups)))
6261 -        pa_audio_group_set_mute_control(group, NULL);
6262 -
6263 -    while ((stream = pa_hashmap_first(control->streams)))
6264 -        pas_stream_set_mute_control(stream, NULL);
6265 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK], control);
6266  
6267      while ((device = pa_hashmap_first(control->default_for_devices)))
6268          pa_device_set_default_mute_control(device, NULL);
6269 @@ -133,19 +146,10 @@ void pa_mute_control_unlink(pa_mute_control *control) {
6270  void pa_mute_control_free(pa_mute_control *control) {
6271      pa_assert(control);
6272  
6273 -    if (!control->unlinked)
6274 +    /* unlink() expects name to be set. */
6275 +    if (!control->unlinked && control->name)
6276          pa_mute_control_unlink(control);
6277  
6278 -    if (control->audio_groups) {
6279 -        pa_assert(pa_hashmap_isempty(control->audio_groups));
6280 -        pa_hashmap_free(control->audio_groups);
6281 -    }
6282 -
6283 -    if (control->streams) {
6284 -        pa_assert(pa_hashmap_isempty(control->streams));
6285 -        pa_hashmap_free(control->streams);
6286 -    }
6287 -
6288      if (control->default_for_devices) {
6289          pa_assert(pa_hashmap_isempty(control->default_for_devices));
6290          pa_hashmap_free(control->default_for_devices);
6291 @@ -167,86 +171,137 @@ void pa_mute_control_free(pa_mute_control *control) {
6292      pa_xfree(control);
6293  }
6294  
6295 -void pa_mute_control_set_owner_audio_group(pa_mute_control *control, pa_audio_group *group) {
6296 +void pa_mute_control_set_purpose(pa_mute_control *control, pa_mute_control_purpose_t purpose, void *owner) {
6297      pa_assert(control);
6298 -    pa_assert(group);
6299 +    pa_assert(!control->linked);
6300  
6301 -    control->owner_audio_group = group;
6302 +    control->purpose = purpose;
6303 +    control->owner = owner;
6304  }
6305  
6306 -static void set_mute_internal(pa_mute_control *control, bool mute) {
6307 -    bool old_mute;
6308 -
6309 +int pa_mute_control_acquire_for_audio_group(pa_mute_control *control, pa_audio_group *group,
6310 +                                            pa_mute_control_set_mute_cb_t set_mute_cb, void *userdata) {
6311      pa_assert(control);
6312 +    pa_assert(group);
6313 +    pa_assert(set_mute_cb);
6314  
6315 -    old_mute = control->mute;
6316 +    if (control->present) {
6317 +        pa_log("Can't acquire mute control %s, it's already present.", control->name);
6318 +        return -PA_ERR_BUSY;
6319 +    }
6320  
6321 -    if (mute == old_mute)
6322 -        return;
6323 +    control->owner_audio_group = group;
6324 +    control->set_mute = set_mute_cb;
6325 +    control->userdata = userdata;
6326  
6327 -    control->mute = mute;
6328 +    control->set_mute_in_progress = true;
6329 +    control->set_mute(control, control->mute);
6330 +    control->set_mute_in_progress = false;
6331 +
6332 +    control->present = true;
6333  
6334      if (!control->linked || control->unlinked)
6335 -        return;
6336 +        return 0;
6337  
6338 -    pa_log_debug("The mute of mute control %s changed from %s to %s.", control->name, pa_yes_no(old_mute),
6339 -                 pa_yes_no(control->mute));
6340 +    pa_log_debug("Mute control %s became present.", control->name);
6341  
6342 -    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_MUTE_CHANGED], control);
6343 +    return 0;
6344  }
6345  
6346 -int pa_mute_control_set_mute(pa_mute_control *control, bool mute) {
6347 -    int r;
6348 -
6349 +void pa_mute_control_release(pa_mute_control *control) {
6350      pa_assert(control);
6351  
6352 -    if (!control->set_mute) {
6353 -        pa_log_info("Tried to set the mute of mute control %s, but the mute control doesn't support the operation.",
6354 -                    control->name);
6355 -        return -PA_ERR_NOTSUPPORTED;
6356 -    }
6357 +    if (!control->present)
6358 +        return;
6359  
6360 -    if (mute == control->mute)
6361 -        return 0;
6362 +    control->present = false;
6363  
6364 -    control->set_mute_in_progress = true;
6365 -    r = control->set_mute(control, mute);
6366 -    control->set_mute_in_progress = false;
6367 +    control->userdata = NULL;
6368 +    control->set_mute = NULL;
6369 +    control->owner_audio_group = NULL;
6370  
6371 -    if (r >= 0)
6372 -        set_mute_internal(control, mute);
6373 +    if (!control->linked || control->unlinked)
6374 +        return;
6375  
6376 -    return r;
6377 +    pa_log_debug("Mute control %s became not present.", control->name);
6378  }
6379  
6380 -void pa_mute_control_description_changed(pa_mute_control *control, const char *new_description) {
6381 +void pa_mute_control_set_description(pa_mute_control *control, const char *description) {
6382      char *old_description;
6383  
6384      pa_assert(control);
6385 -    pa_assert(new_description);
6386 +    pa_assert(description);
6387  
6388      old_description = control->description;
6389  
6390 -    if (pa_streq(new_description, old_description))
6391 +    if (pa_streq(description, old_description))
6392 +        return;
6393 +
6394 +    control->description = pa_xstrdup(description);
6395 +
6396 +    if (control->persistent)
6397 +        pa_inidb_cell_set_value(control->db_cells.description, description);
6398 +
6399 +    if (!control->linked || control->unlinked) {
6400 +        pa_xfree(old_description);
6401          return;
6402 +    }
6403  
6404 -    control->description = pa_xstrdup(new_description);
6405      pa_log_debug("The description of mute control %s changed from \"%s\" to \"%s\".", control->name, old_description,
6406 -                 new_description);
6407 +                 description);
6408      pa_xfree(old_description);
6409      pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_DESCRIPTION_CHANGED], control);
6410  }
6411  
6412 -void pa_mute_control_mute_changed(pa_mute_control *control, bool new_mute) {
6413 +static void set_mute_internal(pa_mute_control *control, bool mute) {
6414 +    bool old_mute;
6415 +
6416      pa_assert(control);
6417  
6418 -    if (!control->linked)
6419 +    old_mute = control->mute;
6420 +
6421 +    if (mute == old_mute)
6422          return;
6423  
6424 -    if (control->set_mute_in_progress)
6425 +    control->mute = mute;
6426 +
6427 +    if (control->persistent)
6428 +        pa_inidb_cell_set_value(control->db_cells.mute, pa_boolean_to_string(mute));
6429 +
6430 +    if (!control->linked || control->unlinked)
6431          return;
6432  
6433 -    set_mute_internal(control, new_mute);
6434 +    pa_log_debug("The mute of mute control %s changed from %s to %s.", control->name, pa_boolean_to_string(old_mute),
6435 +                 pa_boolean_to_string(control->mute));
6436 +
6437 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_MUTE_CHANGED], control);
6438 +}
6439 +
6440 +int pa_mute_control_set_mute(pa_mute_control *control, bool mute) {
6441 +    int r;
6442 +
6443 +    pa_assert(control);
6444 +
6445 +    if (control->set_mute_in_progress)
6446 +        return 0;
6447 +
6448 +    if (mute == control->mute)
6449 +        return 0;
6450 +
6451 +    if (control->linked && control->present) {
6452 +        control->set_mute_in_progress = true;
6453 +        r = control->set_mute(control, mute);
6454 +        control->set_mute_in_progress = false;
6455 +
6456 +        if (r < 0) {
6457 +            pa_log("Setting the mute of mute control %s failed.", control->name);
6458 +            return r;
6459 +        }
6460 +    }
6461 +
6462 +    set_mute_internal(control, mute);
6463 +
6464 +    return 0;
6465  }
6466  
6467  void pa_mute_control_add_device(pa_mute_control *control, pa_device *device) {
6468 @@ -276,31 +331,3 @@ void pa_mute_control_remove_default_for_device(pa_mute_control *control, pa_devi
6469  
6470      pa_assert_se(pa_hashmap_remove(control->default_for_devices, device));
6471  }
6472 -
6473 -void pa_mute_control_add_stream(pa_mute_control *control, pas_stream *stream) {
6474 -    pa_assert(control);
6475 -    pa_assert(stream);
6476 -
6477 -    pa_assert_se(pa_hashmap_put(control->streams, stream, stream) >= 0);
6478 -}
6479 -
6480 -void pa_mute_control_remove_stream(pa_mute_control *control, pas_stream *stream) {
6481 -    pa_assert(control);
6482 -    pa_assert(stream);
6483 -
6484 -    pa_assert_se(pa_hashmap_remove(control->streams, stream));
6485 -}
6486 -
6487 -void pa_mute_control_add_audio_group(pa_mute_control *control, pa_audio_group *group) {
6488 -    pa_assert(control);
6489 -    pa_assert(group);
6490 -
6491 -    pa_assert_se(pa_hashmap_put(control->audio_groups, group, group) >= 0);
6492 -}
6493 -
6494 -void pa_mute_control_remove_audio_group(pa_mute_control *control, pa_audio_group *group) {
6495 -    pa_assert(control);
6496 -    pa_assert(group);
6497 -
6498 -    pa_assert_se(pa_hashmap_remove(control->audio_groups, group));
6499 -}
6500 diff --git a/src/modules/volume-api/mute-control.h b/src/modules/volume-api/mute-control.h
6501 index 1f70a43..40f8a9c 100644
6502 --- a/src/modules/volume-api/mute-control.h
6503 +++ b/src/modules/volume-api/mute-control.h
6504 @@ -22,10 +22,18 @@
6505    USA.
6506  ***/
6507  
6508 +#include <modules/volume-api/inidb.h>
6509  #include <modules/volume-api/volume-api.h>
6510  
6511  typedef struct pa_mute_control pa_mute_control;
6512  
6513 +typedef enum {
6514 +    PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE,
6515 +    PA_MUTE_CONTROL_PURPOSE_OTHER,
6516 +} pa_mute_control_purpose_t;
6517 +
6518 +typedef int (*pa_mute_control_set_mute_cb_t)(pa_mute_control *control, bool mute);
6519 +
6520  struct pa_mute_control {
6521      pa_volume_api *volume_api;
6522      uint32_t index;
6523 @@ -33,6 +41,14 @@ struct pa_mute_control {
6524      char *description;
6525      pa_proplist *proplist;
6526      bool mute;
6527 +    bool present;
6528 +    bool persistent;
6529 +
6530 +    pa_mute_control_purpose_t purpose;
6531 +    union {
6532 +        pas_stream *owner_stream;
6533 +        void *owner;
6534 +    };
6535  
6536      /* If this mute control is the "own mute control" of an audio group, this
6537       * is set to point to that group, otherwise this is NULL. */
6538 @@ -40,50 +56,43 @@ struct pa_mute_control {
6539  
6540      pa_hashmap *devices; /* pa_device -> pa_device (hashmap-as-a-set) */
6541      pa_hashmap *default_for_devices; /* pa_device -> pa_device (hashmap-as-a-set) */
6542 -    pa_hashmap *streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
6543 -    pa_hashmap *audio_groups; /* pa_audio_group -> pa_audio_group (hashmap-as-a-set) */
6544 +
6545 +    struct {
6546 +        pa_inidb_cell *description;
6547 +        pa_inidb_cell *mute;
6548 +    } db_cells;
6549  
6550      bool linked;
6551      bool unlinked;
6552      bool set_mute_in_progress;
6553  
6554      /* Called from pa_mute_control_set_mute(). The implementation is expected
6555 -     * to return a negative error code on failure. May be NULL, if the mute
6556 -     * control is read-only. */
6557 -    int (*set_mute)(pa_mute_control *control, bool mute);
6558 +     * to return a negative error code on failure. */
6559 +    pa_mute_control_set_mute_cb_t set_mute;
6560  
6561      void *userdata;
6562  };
6563  
6564 -pa_mute_control *pa_mute_control_new(pa_volume_api *api, const char *name, const char *description);
6565 -
6566 -typedef void (*pa_mute_control_set_initial_mute_cb_t)(pa_mute_control *control);
6567 -
6568 -/* initial_mute is the preferred initial mute of the mute control
6569 - * implementation. It may be unset, if the implementation doesn't care about
6570 - * the initial state of the mute control. Read-only mute controls, however,
6571 - * must always set initial_mute.
6572 - *
6573 - * The implementation's initial mute preference may be overridden by policy, if
6574 - * the mute control isn't read-only. When the final initial mute is known, the
6575 - * the implementation is notified via set_initial_mute_cb (the mute can be read
6576 - * from control->mute). set_initial_mute_cb may be NULL, if the mute control is
6577 - * read-only. */
6578 -void pa_mute_control_put(pa_mute_control *control, bool initial_mute, bool initial_mute_is_set,
6579 -                         pa_mute_control_set_initial_mute_cb_t set_initial_mute_cb);
6580 -
6581 +int pa_mute_control_new(pa_volume_api *api, const char *name, bool persistent, pa_mute_control **_r);
6582 +void pa_mute_control_put(pa_mute_control *control);
6583  void pa_mute_control_unlink(pa_mute_control *control);
6584  void pa_mute_control_free(pa_mute_control *control);
6585  
6586 -/* Called by audio-group.c only. */
6587 -void pa_mute_control_set_owner_audio_group(pa_mute_control *control, pa_audio_group *group);
6588 -
6589 -/* Called by clients and policy modules. */
6590 -int pa_mute_control_set_mute(pa_mute_control *control, bool mute);
6591 +/* Called by the mute control implementation, before pa_mute_control_put(). */
6592 +void pa_mute_control_set_purpose(pa_mute_control *control, pa_mute_control_purpose_t purpose, void *owner);
6593  
6594  /* Called by the mute control implementation. */
6595 -void pa_mute_control_description_changed(pa_mute_control *control, const char *new_description);
6596 -void pa_mute_control_mute_changed(pa_mute_control *control, bool new_mute);
6597 +int pa_mute_control_acquire_for_audio_group(pa_mute_control *control, pa_audio_group *group,
6598 +                                            pa_mute_control_set_mute_cb_t set_mute_cb, void *userdata);
6599 +
6600 +/* Called by the mute control implementation. This must only be called for
6601 + * persistent controls; use pa_mute_control_free() for non-persistent
6602 + * controls. */
6603 +void pa_mute_control_release(pa_mute_control *control);
6604 +
6605 +/* Called by anyone. */
6606 +void pa_mute_control_set_description(pa_mute_control *control, const char *description);
6607 +int pa_mute_control_set_mute(pa_mute_control *control, bool mute);
6608  
6609  /* Called from device.c only. */
6610  void pa_mute_control_add_device(pa_mute_control *control, pa_device *device);
6611 @@ -91,12 +100,4 @@ void pa_mute_control_remove_device(pa_mute_control *control, pa_device *device);
6612  void pa_mute_control_add_default_for_device(pa_mute_control *control, pa_device *device);
6613  void pa_mute_control_remove_default_for_device(pa_mute_control *control, pa_device *device);
6614  
6615 -/* Called from sstream.c only. */
6616 -void pa_mute_control_add_stream(pa_mute_control *control, pas_stream *stream);
6617 -void pa_mute_control_remove_stream(pa_mute_control *control, pas_stream *stream);
6618 -
6619 -/* Called from audio-group.c only. */
6620 -void pa_mute_control_add_audio_group(pa_mute_control *control, pa_audio_group *group);
6621 -void pa_mute_control_remove_audio_group(pa_mute_control *control, pa_audio_group *group);
6622 -
6623  #endif
6624 diff --git a/src/modules/volume-api/sstream.c b/src/modules/volume-api/sstream.c
6625 index e3531a8..1738d15 100644
6626 --- a/src/modules/volume-api/sstream.c
6627 +++ b/src/modules/volume-api/sstream.c
6628 @@ -26,7 +26,6 @@
6629  #include "sstream.h"
6630  
6631  #include <modules/volume-api/audio-group.h>
6632 -#include <modules/volume-api/binding.h>
6633  #include <modules/volume-api/mute-control.h>
6634  #include <modules/volume-api/volume-control.h>
6635  
6636 @@ -34,121 +33,43 @@
6637  
6638  #include <pulsecore/core-util.h>
6639  
6640 -pas_stream *pas_stream_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction) {
6641 -    pas_stream *stream;
6642 +int pas_stream_new(pa_volume_api *api, const char *name, pas_stream **_r) {
6643 +    pas_stream *stream = NULL;
6644 +    int r;
6645  
6646      pa_assert(api);
6647      pa_assert(name);
6648 -    pa_assert(description);
6649 +    pa_assert(_r);
6650  
6651      stream = pa_xnew0(pas_stream, 1);
6652      stream->volume_api = api;
6653      stream->index = pa_volume_api_allocate_stream_index(api);
6654 -    pa_assert_se(pa_volume_api_register_name(api, name, false, &stream->name) >= 0);
6655 -    stream->description = pa_xstrdup(description);
6656 -    stream->direction = direction;
6657 -    stream->proplist = pa_proplist_new();
6658 -    stream->use_default_volume_control = true;
6659 -    stream->use_default_mute_control = true;
6660 -
6661 -    return stream;
6662 -}
6663 -
6664 -static void set_volume_control_internal(pas_stream *stream, pa_volume_control *control) {
6665 -    pa_volume_control *old_control;
6666 -
6667 -    pa_assert(stream);
6668 -
6669 -    old_control = stream->volume_control;
6670 -
6671 -    if (control == old_control)
6672 -        return;
6673 -
6674 -    if (old_control) {
6675 -        /* If the old control pointed to the own volume control of an audio
6676 -         * group, then the stream's audio group for volume needs to be
6677 -         * updated. We set it to NULL here, and if it should be non-NULL, that
6678 -         * will be fixed very soon (a few lines down). */
6679 -        pas_stream_set_audio_group_for_volume(stream, NULL);
6680 -
6681 -        pa_volume_control_remove_stream(old_control, stream);
6682 -    }
6683 -
6684 -    stream->volume_control = control;
6685 -
6686 -    if (control) {
6687 -        pa_volume_control_add_stream(control, stream);
6688 -        pas_stream_set_audio_group_for_volume(stream, control->owner_audio_group);
6689 -    }
6690 -
6691 -    if (!stream->linked || stream->unlinked)
6692 -        return;
6693 -
6694 -    pa_log_debug("The volume control of stream %s changed from %s to %s.", stream->name,
6695 -                 old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
6696 -
6697 -    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED], stream);
6698 -}
6699 -
6700 -static void set_mute_control_internal(pas_stream *stream, pa_mute_control *control) {
6701 -    pa_mute_control *old_control;
6702 -
6703 -    pa_assert(stream);
6704 -
6705 -    old_control = stream->mute_control;
6706  
6707 -    if (control == old_control)
6708 -        return;
6709 +    r = pa_volume_api_register_name(api, name, false, &stream->name);
6710 +    if (r < 0)
6711 +        goto fail;
6712  
6713 -    if (old_control) {
6714 -        /* If the old control pointed to the own mute control of an audio
6715 -         * group, then the stream's audio group for mute needs to be updated.
6716 -         * We set it to NULL here, and if it should be non-NULL, that will be
6717 -         * fixed very soon (a few lines down). */
6718 -        pas_stream_set_audio_group_for_mute(stream, NULL);
6719 -
6720 -        pa_mute_control_remove_stream(old_control, stream);
6721 -    }
6722 -
6723 -    stream->mute_control = control;
6724 -
6725 -    if (control) {
6726 -        pa_mute_control_add_stream(control, stream);
6727 -        pas_stream_set_audio_group_for_mute(stream, control->owner_audio_group);
6728 -    }
6729 +    stream->description = pa_xstrdup(stream->name);
6730 +    stream->direction = PA_DIRECTION_OUTPUT;
6731 +    stream->proplist = pa_proplist_new();
6732  
6733 -    if (!stream->linked || stream->unlinked)
6734 -        return;
6735 +    *_r = stream;
6736 +    return 0;
6737  
6738 -    pa_log_debug("The mute control of stream %s changed from %s to %s.", stream->name,
6739 -                 old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
6740 +fail:
6741 +    if (stream)
6742 +        pas_stream_free(stream);
6743  
6744 -    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED], stream);
6745 +    return r;
6746  }
6747  
6748 -void pas_stream_put(pas_stream *stream, pa_proplist *initial_properties) {
6749 +void pas_stream_put(pas_stream *stream) {
6750      const char *prop_key;
6751      void *state = NULL;
6752  
6753      pa_assert(stream);
6754 -    pa_assert(!stream->create_own_volume_control || stream->delete_own_volume_control);
6755 -    pa_assert(!stream->create_own_mute_control || stream->delete_own_mute_control);
6756 -
6757 -    if (initial_properties)
6758 -        pa_proplist_update(stream->proplist, PA_UPDATE_REPLACE, initial_properties);
6759 -
6760 -    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_VOLUME_CONTROL], stream);
6761 -
6762 -    if (stream->use_default_volume_control)
6763 -        set_volume_control_internal(stream, stream->own_volume_control);
6764 -
6765 -    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_MUTE_CONTROL], stream);
6766 -
6767 -    if (stream->use_default_mute_control)
6768 -        set_mute_control_internal(stream, stream->own_mute_control);
6769  
6770      pa_volume_api_add_stream(stream->volume_api, stream);
6771 -
6772      stream->linked = true;
6773  
6774      pa_log_debug("Created stream #%u.", stream->index);
6775 @@ -157,6 +78,10 @@ void pas_stream_put(pas_stream *stream, pa_proplist *initial_properties) {
6776      pa_log_debug("    Direction: %s", pa_direction_to_string(stream->direction));
6777      pa_log_debug("    Volume control: %s", stream->volume_control ? stream->volume_control->name : "(unset)");
6778      pa_log_debug("    Mute control: %s", stream->mute_control ? stream->mute_control->name : "(unset)");
6779 +    pa_log_debug("    Audio group for volume: %s",
6780 +                 stream->audio_group_for_volume ? stream->audio_group_for_volume->name : "(unset)");
6781 +    pa_log_debug("    Audio group for mute: %s",
6782 +                 stream->audio_group_for_mute ? stream->audio_group_for_mute->name : "(unset)");
6783      pa_log_debug("    Properties:");
6784  
6785      while ((prop_key = pa_proplist_iterate(stream->proplist, &state)))
6786 @@ -178,22 +103,22 @@ void pas_stream_unlink(pas_stream *stream) {
6787      pa_log_debug("Unlinking stream %s.", stream->name);
6788  
6789      if (stream->linked)
6790 -        pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_UNLINK], stream);
6791 +        pa_volume_api_remove_stream(stream->volume_api, stream);
6792  
6793 -    pa_volume_api_remove_stream(stream->volume_api, stream);
6794 +    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_UNLINK], stream);
6795  
6796      pas_stream_set_audio_group_for_mute(stream, NULL);
6797      pas_stream_set_audio_group_for_volume(stream, NULL);
6798      pas_stream_set_mute_control(stream, NULL);
6799 +    pas_stream_set_relative_volume_control(stream, NULL);
6800      pas_stream_set_volume_control(stream, NULL);
6801 -    pas_stream_set_have_own_mute_control(stream, false);
6802 -    pas_stream_set_have_own_volume_control(stream, false);
6803  }
6804  
6805  void pas_stream_free(pas_stream *stream) {
6806      pa_assert(stream);
6807  
6808 -    if (!stream->unlinked)
6809 +    /* unlink() expects name to be set. */
6810 +    if (!stream->unlinked && stream->name)
6811          pas_stream_unlink(stream);
6812  
6813      if (stream->proplist)
6814 @@ -207,160 +132,172 @@ void pas_stream_free(pas_stream *stream) {
6815      pa_xfree(stream);
6816  }
6817  
6818 -int pas_stream_set_have_own_volume_control(pas_stream *stream, bool have) {
6819 +void pas_stream_set_direction(pas_stream *stream, pa_direction_t direction) {
6820      pa_assert(stream);
6821 +    pa_assert(!stream->linked);
6822  
6823 -    if (have == stream->have_own_volume_control)
6824 -        return 0;
6825 +    stream->direction = direction;
6826 +}
6827  
6828 -    if (have) {
6829 -        pa_assert(!stream->own_volume_control);
6830 +void pas_stream_set_description(pas_stream *stream, const char *description) {
6831 +    char *old_description;
6832 +
6833 +    pa_assert(stream);
6834 +    pa_assert(description);
6835  
6836 -        if (!stream->create_own_volume_control) {
6837 -            pa_log_debug("Stream %s doesn't support own volume control.", stream->name);
6838 -            return -PA_ERR_NOTSUPPORTED;
6839 -        }
6840 +    old_description = stream->description;
6841 +
6842 +    if (pa_streq(description, old_description))
6843 +        return;
6844  
6845 -        stream->own_volume_control = stream->create_own_volume_control(stream);
6846 -    } else {
6847 -        stream->delete_own_volume_control(stream);
6848 -        stream->own_volume_control = NULL;
6849 +    stream->description = pa_xstrdup(description);
6850 +
6851 +    if (!stream->linked || stream->unlinked) {
6852 +        pa_xfree(old_description);
6853 +        return;
6854      }
6855  
6856 -    stream->have_own_volume_control = have;
6857 +    pa_log_debug("Stream %s description changed from \"%s\" to \"%s\".", stream->name, old_description,
6858 +                 description);
6859 +    pa_xfree(old_description);
6860  
6861 -    return 0;
6862 +    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED], stream);
6863  }
6864  
6865 -int pas_stream_set_have_own_mute_control(pas_stream *stream, bool have) {
6866 +void pas_stream_set_property(pas_stream *stream, const char *key, const char *value) {
6867 +    const char *old_value;
6868 +
6869      pa_assert(stream);
6870 +    pa_assert(key);
6871  
6872 -    if (have == stream->have_own_mute_control)
6873 -        return 0;
6874 +    old_value = pa_proplist_gets(stream->proplist, key);
6875  
6876 -    if (have) {
6877 -        pa_assert(!stream->own_mute_control);
6878 +    if (pa_safe_streq(value, old_value))
6879 +        return;
6880  
6881 -        if (!stream->create_own_mute_control) {
6882 -            pa_log_debug("Stream %s doesn't support own mute control.", stream->name);
6883 -            return -PA_ERR_NOTSUPPORTED;
6884 -        }
6885 +    if (value)
6886 +        pa_proplist_sets(stream->proplist, key, value);
6887 +    else
6888 +        pa_proplist_unset(stream->proplist, key);
6889  
6890 -        stream->own_mute_control = stream->create_own_mute_control(stream);
6891 -    } else {
6892 -        stream->delete_own_mute_control(stream);
6893 -        stream->own_mute_control = NULL;
6894 -    }
6895 +    if (!stream->linked || stream->unlinked)
6896 +        return;
6897  
6898 -    stream->have_own_mute_control = have;
6899 +    pa_log_debug("Stream %s property \"%s\" changed from \"%s\" to \"%s\".", stream->name, key,
6900 +                 old_value ? old_value : "(unset)", value ? value : "(unset)");
6901  
6902 -    return 0;
6903 +    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_PROPLIST_CHANGED], stream);
6904  }
6905  
6906  void pas_stream_set_volume_control(pas_stream *stream, pa_volume_control *control) {
6907 -    pa_assert(stream);
6908 +    pa_volume_control *old_control;
6909  
6910 -    stream->use_default_volume_control = false;
6911 +    pa_assert(stream);
6912  
6913 -    if (stream->volume_control_binding) {
6914 -        pa_binding_free(stream->volume_control_binding);
6915 -        stream->volume_control_binding = NULL;
6916 -    }
6917 +    old_control = stream->volume_control;
6918  
6919 -    set_volume_control_internal(stream, control);
6920 -}
6921 +    if (control == old_control)
6922 +        return;
6923  
6924 -void pas_stream_set_mute_control(pas_stream *stream, pa_mute_control *control) {
6925 -    pa_assert(stream);
6926 +    stream->volume_control = control;
6927  
6928 -    stream->use_default_mute_control = false;
6929 +    if (!stream->linked || stream->unlinked)
6930 +        return;
6931  
6932 -    if (stream->mute_control_binding) {
6933 -        pa_binding_free(stream->mute_control_binding);
6934 -        stream->mute_control_binding = NULL;
6935 -    }
6936 +    pa_log_debug("The volume control of stream %s changed from %s to %s.", stream->name,
6937 +                 old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
6938  
6939 -    set_mute_control_internal(stream, control);
6940 +    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED], stream);
6941  }
6942  
6943 -void pas_stream_bind_volume_control(pas_stream *stream, pa_binding_target_info *target_info) {
6944 -    pa_binding_owner_info owner_info = {
6945 -        .userdata = stream,
6946 -        .set_value = (pa_binding_set_value_cb_t) set_volume_control_internal,
6947 -    };
6948 +void pas_stream_set_relative_volume_control(pas_stream *stream, pa_volume_control *control) {
6949 +    pa_volume_control *old_control;
6950  
6951      pa_assert(stream);
6952 -    pa_assert(target_info);
6953 -
6954 -    stream->use_default_volume_control = false;
6955  
6956 -    if (stream->volume_control_binding)
6957 -        pa_binding_free(stream->volume_control_binding);
6958 +    old_control = stream->relative_volume_control;
6959  
6960 -    stream->volume_control_binding = pa_binding_new(stream->volume_api, &owner_info, target_info);
6961 -}
6962 +    if (control == old_control)
6963 +        return;
6964  
6965 -void pas_stream_bind_mute_control(pas_stream *stream, pa_binding_target_info *target_info) {
6966 -    pa_binding_owner_info owner_info = {
6967 -        .userdata = stream,
6968 -        .set_value = (pa_binding_set_value_cb_t) set_mute_control_internal,
6969 -    };
6970 +    stream->relative_volume_control = control;
6971  
6972 -    pa_assert(stream);
6973 -    pa_assert(target_info);
6974 -
6975 -    stream->use_default_mute_control = false;
6976 +    if (!stream->linked || stream->unlinked)
6977 +        return;
6978  
6979 -    if (stream->mute_control_binding)
6980 -        pa_binding_free(stream->mute_control_binding);
6981 +    pa_log_debug("The relative volume control of stream %s changed from %s to %s.", stream->name,
6982 +                 old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
6983  
6984 -    stream->mute_control_binding = pa_binding_new(stream->volume_api, &owner_info, target_info);
6985 +    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_RELATIVE_VOLUME_CONTROL_CHANGED], stream);
6986  }
6987  
6988 -void pas_stream_description_changed(pas_stream *stream, const char *new_description) {
6989 -    char *old_description;
6990 +void pas_stream_set_mute_control(pas_stream *stream, pa_mute_control *control) {
6991 +    pa_mute_control *old_control;
6992  
6993      pa_assert(stream);
6994 -    pa_assert(new_description);
6995  
6996 -    old_description = stream->description;
6997 +    old_control = stream->mute_control;
6998  
6999 -    if (pa_streq(new_description, old_description))
7000 +    if (control == old_control)
7001          return;
7002  
7003 -    stream->description = pa_xstrdup(new_description);
7004 -    pa_log_debug("The description of stream %s changed from \"%s\" to \"%s\".", stream->name, old_description,
7005 -                 new_description);
7006 -    pa_xfree(old_description);
7007 -    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED], stream);
7008 +    stream->mute_control = control;
7009 +
7010 +    if (!stream->linked || stream->unlinked)
7011 +        return;
7012 +
7013 +    pa_log_debug("The mute control of stream %s changed from %s to %s.", stream->name,
7014 +                 old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
7015 +
7016 +    pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED], stream);
7017  }
7018  
7019  void pas_stream_set_audio_group_for_volume(pas_stream *stream, pa_audio_group *group) {
7020 +    pa_audio_group *old_group;
7021 +
7022      pa_assert(stream);
7023  
7024 -    if (group == stream->audio_group_for_volume)
7025 +    old_group = stream->audio_group_for_volume;
7026 +
7027 +    if (group == old_group)
7028          return;
7029  
7030 -    if (stream->audio_group_for_volume)
7031 -        pa_audio_group_remove_volume_stream(stream->audio_group_for_volume, stream);
7032 +    if (old_group)
7033 +        pa_audio_group_remove_volume_stream(old_group, stream);
7034  
7035      stream->audio_group_for_volume = group;
7036  
7037      if (group)
7038          pa_audio_group_add_volume_stream(group, stream);
7039 +
7040 +    if (!stream->linked || stream->unlinked)
7041 +        return;
7042 +
7043 +    pa_log_debug("Stream %s audio group for volume changed from %s to %s.", stream->name,
7044 +                 old_group ? old_group->name : "(unset)", group ? group->name : "(unset)");
7045  }
7046  
7047  void pas_stream_set_audio_group_for_mute(pas_stream *stream, pa_audio_group *group) {
7048 +    pa_audio_group *old_group;
7049 +
7050      pa_assert(stream);
7051  
7052 -    if (group == stream->audio_group_for_mute)
7053 +    old_group = stream->audio_group_for_mute;
7054 +
7055 +    if (group == old_group)
7056          return;
7057  
7058 -    if (stream->audio_group_for_mute)
7059 -        pa_audio_group_remove_mute_stream(stream->audio_group_for_mute, stream);
7060 +    if (old_group)
7061 +        pa_audio_group_remove_mute_stream(old_group, stream);
7062  
7063      stream->audio_group_for_mute = group;
7064  
7065      if (group)
7066          pa_audio_group_add_mute_stream(group, stream);
7067 +
7068 +    if (!stream->linked || stream->unlinked)
7069 +        return;
7070 +
7071 +    pa_log_debug("Stream %s audio group for mute changed from %s to %s.", stream->name,
7072 +                 old_group ? old_group->name : "(unset)", group ? group->name : "(unset)");
7073  }
7074 diff --git a/src/modules/volume-api/sstream.h b/src/modules/volume-api/sstream.h
7075 index a65b34c..715bf2c 100644
7076 --- a/src/modules/volume-api/sstream.h
7077 +++ b/src/modules/volume-api/sstream.h
7078 @@ -39,69 +39,33 @@ struct pas_stream {
7079      pa_direction_t direction;
7080      pa_proplist *proplist;
7081      pa_volume_control *volume_control;
7082 +    pa_volume_control *relative_volume_control;
7083      pa_mute_control *mute_control;
7084 -    bool use_default_volume_control;
7085 -    bool use_default_mute_control;
7086 -    bool have_own_volume_control;
7087 -    bool have_own_mute_control;
7088 -    pa_volume_control *own_volume_control;
7089 -    pa_mute_control *own_mute_control;
7090 -
7091 -    pa_binding *volume_control_binding;
7092 -    pa_binding *mute_control_binding;
7093      pa_audio_group *audio_group_for_volume;
7094      pa_audio_group *audio_group_for_mute;
7095  
7096      bool linked;
7097      bool unlinked;
7098  
7099 -    /* Called when the own volume control is enabled. The callback
7100 -     * implementation should return a new linked volume control object. The
7101 -     * callback may be NULL, in which case the own volume control can't be
7102 -     * enabled. */
7103 -    pa_volume_control *(*create_own_volume_control)(pas_stream *stream);
7104 -
7105 -    /* Called when the own volume control is disabled. The implementation
7106 -     * should free stream->own_volume_control. The callback may be NULL only if
7107 -     * create_own_volume_control is NULL also. */
7108 -    void (*delete_own_volume_control)(pas_stream *stream);
7109 -
7110 -    /* Called when the own mute control is enabled. The callback implementation
7111 -     * should return a new linked mute control object. The callback may be
7112 -     * NULL, in which case the own mute control can't be enabled. */
7113 -    pa_mute_control *(*create_own_mute_control)(pas_stream *stream);
7114 -
7115 -    /* Called when the own mute control is disabled. The implementation should
7116 -     * free stream->own_mute_control. The callback may be NULL only if
7117 -     * create_own_mute_control is NULL also. */
7118 -    void (*delete_own_mute_control)(pas_stream *stream);
7119 -
7120      void *userdata;
7121  };
7122  
7123 -pas_stream *pas_stream_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction);
7124 -void pas_stream_put(pas_stream *stream, pa_proplist *initial_properties);
7125 +int pas_stream_new(pa_volume_api *api, const char *name, pas_stream **_r);
7126 +void pas_stream_put(pas_stream *stream);
7127  void pas_stream_unlink(pas_stream *stream);
7128  void pas_stream_free(pas_stream *stream);
7129  
7130 -/* Called by the stream implementation and possibly by policy modules.
7131 - * Enabling own controls may fail (the stream may not support own controls),
7132 - * disabling will never fail. */
7133 -int pas_stream_set_have_own_volume_control(pas_stream *stream, bool have);
7134 -int pas_stream_set_have_own_mute_control(pas_stream *stream, bool have);
7135 +/* Called by the stream implementation, only during initialization. */
7136 +void pas_stream_set_direction(pas_stream *stream, pa_direction_t direction);
7137  
7138 -/* Called by policy modules. */
7139 +/* Called by the stream implementation. */
7140 +void pas_stream_set_description(pas_stream *stream, const char *description);
7141 +void pas_stream_set_property(pas_stream *stream, const char *key, const char *value);
7142  void pas_stream_set_volume_control(pas_stream *stream, pa_volume_control *control);
7143 +void pas_stream_set_relative_volume_control(pas_stream *stream, pa_volume_control *control);
7144  void pas_stream_set_mute_control(pas_stream *stream, pa_mute_control *control);
7145 -void pas_stream_bind_volume_control(pas_stream *stream, pa_binding_target_info *target_info);
7146 -void pas_stream_bind_mute_control(pas_stream *stream, pa_binding_target_info *target_info);
7147 -
7148 -/* Called by the stream implementation. */
7149 -void pas_stream_description_changed(pas_stream *stream, const char *new_description);
7150  
7151 -/* Called by audio-group.c only. Adding a stream to an audio group happens
7152 - * implicitly when the volume or mute control of a stream is set to point to
7153 - * the own control of an audio group. */
7154 +/* Called by anyone. */
7155  void pas_stream_set_audio_group_for_volume(pas_stream *stream, pa_audio_group *group);
7156  void pas_stream_set_audio_group_for_mute(pas_stream *stream, pa_audio_group *group);
7157  
7158 diff --git a/src/modules/volume-api/stream-creator.c b/src/modules/volume-api/stream-creator.c
7159 index f6ca7b3..0d9ea24 100644
7160 --- a/src/modules/volume-api/stream-creator.c
7161 +++ b/src/modules/volume-api/stream-creator.c
7162 @@ -35,9 +35,9 @@
7163  struct pa_stream_creator {
7164      pa_volume_api *volume_api;
7165      pa_hashmap *streams; /* pa_sink_input/pa_source_output -> struct stream */
7166 -    pa_hook_slot *sink_input_put_slot;
7167 +    pa_hook_slot *sink_input_fixate_slot;
7168      pa_hook_slot *sink_input_unlink_slot;
7169 -    pa_hook_slot *source_output_put_slot;
7170 +    pa_hook_slot *source_output_fixate_slot;
7171      pa_hook_slot *source_output_unlink_slot;
7172  };
7173  
7174 @@ -47,73 +47,61 @@ enum stream_type {
7175  };
7176  
7177  struct stream {
7178 +    pa_core *core;
7179      pa_stream_creator *creator;
7180      enum stream_type type;
7181 +    pa_sink_input_new_data *sink_input_new_data;
7182      pa_sink_input *sink_input;
7183 +    pa_source_output_new_data *source_output_new_data;
7184      pa_source_output *source_output;
7185      pa_client *client;
7186 +    pa_volume_control *volume_control;
7187 +    pa_volume_control *relative_volume_control;
7188 +    pa_mute_control *mute_control;
7189      pas_stream *stream;
7190  
7191 -    bool unlinked;
7192 -
7193      pa_hook_slot *proplist_changed_slot;
7194 -    pa_hook_slot *client_proplist_changed_slot;
7195      pa_hook_slot *volume_changed_slot;
7196 +    pa_hook_slot *reference_ratio_changed_slot;
7197      pa_hook_slot *mute_changed_slot;
7198  };
7199  
7200 -static char *get_stream_volume_and_mute_control_description_malloc(struct stream *stream) {
7201 -    const char *application_name = NULL;
7202 -    char *description;
7203 -
7204 -    pa_assert(stream);
7205 -
7206 -    if (stream->client)
7207 -        application_name = pa_proplist_gets(stream->client->proplist, PA_PROP_APPLICATION_NAME);
7208 -
7209 -    if (application_name)
7210 -        description = pa_sprintf_malloc("%s: %s", application_name, stream->stream->description);
7211 -    else
7212 -        description = pa_xstrdup(stream->stream->description);
7213 -
7214 -    return description;
7215 -}
7216 +static void stream_free(struct stream *stream);
7217  
7218 -static int volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance) {
7219 +static int volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *original_volume,
7220 +                                        const pa_bvolume *remapped_volume, bool set_volume, bool set_balance) {
7221      struct stream *stream;
7222      pa_bvolume bvolume;
7223      pa_cvolume cvolume;
7224  
7225      pa_assert(control);
7226 -    pa_assert(volume);
7227 +    pa_assert(original_volume);
7228 +    pa_assert(remapped_volume);
7229  
7230      stream = control->userdata;
7231 -
7232 -    switch (stream->type) {
7233 -        case STREAM_TYPE_SINK_INPUT:
7234 -            pa_bvolume_from_cvolume(&bvolume, &stream->sink_input->volume, &stream->sink_input->channel_map);
7235 -            break;
7236 -
7237 -        case STREAM_TYPE_SOURCE_OUTPUT:
7238 -            pa_bvolume_from_cvolume(&bvolume, &stream->source_output->volume, &stream->source_output->channel_map);
7239 -            break;
7240 -    }
7241 +    bvolume = control->volume;
7242  
7243      if (set_volume)
7244 -        bvolume.volume = volume->volume;
7245 +        bvolume.volume = remapped_volume->volume;
7246  
7247      if (set_balance)
7248 -        pa_bvolume_copy_balance(&bvolume, volume);
7249 +        pa_bvolume_copy_balance(&bvolume, remapped_volume);
7250  
7251      pa_bvolume_to_cvolume(&bvolume, &cvolume);
7252  
7253      switch (stream->type) {
7254          case STREAM_TYPE_SINK_INPUT:
7255 -            pa_sink_input_set_volume(stream->sink_input, &cvolume, true, true);
7256 +            if (stream->sink_input->state == PA_SINK_INPUT_INIT)
7257 +                pa_sink_input_new_data_set_volume(stream->sink_input_new_data, &cvolume, false);
7258 +            else
7259 +                pa_sink_input_set_volume(stream->sink_input, &cvolume, true, true);
7260              break;
7261  
7262          case STREAM_TYPE_SOURCE_OUTPUT:
7263 -            pa_source_output_set_volume(stream->source_output, &cvolume, true, true);
7264 +            if (stream->source_output->state == PA_SOURCE_OUTPUT_INIT)
7265 +                pa_source_output_new_data_set_volume(stream->source_output_new_data, &cvolume, false);
7266 +            else
7267 +                pa_source_output_set_volume(stream->source_output, &cvolume, true, true);
7268              break;
7269      }
7270  
7271 @@ -129,6 +117,9 @@ static pa_hook_result_t sink_input_or_source_output_volume_changed_cb(void *hook
7272      pa_assert(stream);
7273      pa_assert(call_data);
7274  
7275 +    if (!stream->volume_control)
7276 +        return PA_HOOK_OK;
7277 +
7278      switch (stream->type) {
7279          case STREAM_TYPE_SINK_INPUT:
7280              input = call_data;
7281 @@ -144,63 +135,95 @@ static pa_hook_result_t sink_input_or_source_output_volume_changed_cb(void *hook
7282  
7283      if (input)
7284          pa_bvolume_from_cvolume(&bvolume, &input->volume, &input->channel_map);
7285 -    else
7286 +    else if (output)
7287          pa_bvolume_from_cvolume(&bvolume, &output->volume, &output->channel_map);
7288 +    else
7289 +        pa_assert_not_reached();
7290  
7291 -    pa_volume_control_volume_changed(stream->stream->own_volume_control, &bvolume, true, true);
7292 +    pa_volume_control_set_volume(stream->volume_control, &bvolume, true, true);
7293  
7294      return PA_HOOK_OK;
7295  }
7296  
7297 -static void volume_control_set_initial_volume_cb(pa_volume_control *control) {
7298 +static int relative_volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *original_volume,
7299 +                                                 const pa_bvolume *remapped_volume, bool set_volume, bool set_balance) {
7300      struct stream *stream;
7301 +    pa_bvolume bvolume;
7302      pa_cvolume cvolume;
7303  
7304      pa_assert(control);
7305 +    pa_assert(original_volume);
7306 +    pa_assert(remapped_volume);
7307  
7308      stream = control->userdata;
7309 -    pa_bvolume_to_cvolume(&control->volume, &cvolume);
7310 -
7311 -    switch (stream->type) {
7312 -        case STREAM_TYPE_SINK_INPUT:
7313 -            pa_sink_input_set_volume(stream->sink_input, &cvolume, true, true);
7314 -            break;
7315 +    bvolume = control->volume;
7316  
7317 -        case STREAM_TYPE_SOURCE_OUTPUT:
7318 -            pa_source_output_set_volume(stream->source_output, &cvolume, true, true);
7319 -            break;
7320 -    }
7321 -}
7322 -
7323 -static int mute_control_set_mute_cb(pa_mute_control *control, bool mute) {
7324 -    struct stream *stream;
7325 +    if (set_volume)
7326 +        bvolume.volume = remapped_volume->volume;
7327  
7328 -    pa_assert(control);
7329 +    if (set_balance)
7330 +        pa_bvolume_copy_balance(&bvolume, remapped_volume);
7331  
7332 -    stream = control->userdata;
7333 +    pa_bvolume_to_cvolume(&bvolume, &cvolume);
7334  
7335      switch (stream->type) {
7336          case STREAM_TYPE_SINK_INPUT:
7337 -            pa_sink_input_set_mute(stream->sink_input, mute, true);
7338 +            if (stream->sink_input->state == PA_SINK_INPUT_INIT) {
7339 +                pa_sink_input_new_data_set_volume(stream->sink_input_new_data, &cvolume, true);
7340 +
7341 +                /* XXX: This is a bit ugly. This is needed, because when we
7342 +                 * call pa_sink_input_new_data_set_volume(), there's no
7343 +                 * automatic notification to the primary volume control object
7344 +                 * about the changed volume. This problem should go away once
7345 +                 * stream volume controls are moved into the core. */
7346 +                if (stream->volume_control) {
7347 +                    pa_bvolume absolute_volume;
7348 +
7349 +                    pa_bvolume_from_cvolume(&absolute_volume, &stream->sink_input_new_data->volume,
7350 +                                            &stream->sink_input_new_data->channel_map);
7351 +                    pa_volume_control_set_volume(stream->volume_control, &absolute_volume, true, true);
7352 +                }
7353 +            } else
7354 +                pa_sink_input_set_volume(stream->sink_input, &cvolume, true, false);
7355              break;
7356  
7357          case STREAM_TYPE_SOURCE_OUTPUT:
7358 -            pa_source_output_set_mute(stream->source_output, mute, true);
7359 +            if (stream->source_output->state == PA_SOURCE_OUTPUT_INIT) {
7360 +                pa_source_output_new_data_set_volume(stream->source_output_new_data, &cvolume, true);
7361 +
7362 +                /* XXX: This is a bit ugly. This is needed, because when we
7363 +                 * call pa_source_output_new_data_set_volume(), there's no
7364 +                 * automatic notification to the primary volume control object
7365 +                 * about the changed volume. This problem should go away once
7366 +                 * stream volume controls are moved into the core. */
7367 +                if (stream->volume_control) {
7368 +                    pa_bvolume absolute_volume;
7369 +
7370 +                    pa_bvolume_from_cvolume(&absolute_volume, &stream->source_output_new_data->volume,
7371 +                                            &stream->source_output_new_data->channel_map);
7372 +                    pa_volume_control_set_volume(stream->volume_control, &absolute_volume, true, true);
7373 +                }
7374 +            } else
7375 +                pa_source_output_set_volume(stream->source_output, &cvolume, true, false);
7376              break;
7377      }
7378  
7379      return 0;
7380  }
7381  
7382 -static pa_hook_result_t sink_input_or_source_output_mute_changed_cb(void *hook_data, void *call_data, void *userdata) {
7383 +static pa_hook_result_t sink_input_or_source_output_reference_ratio_changed_cb(void *hook_data, void *call_data,
7384 +                                                                               void *userdata) {
7385      struct stream *stream = userdata;
7386      pa_sink_input *input = NULL;
7387      pa_source_output *output = NULL;
7388 -    bool mute;
7389 +    pa_bvolume bvolume;
7390  
7391      pa_assert(stream);
7392      pa_assert(call_data);
7393  
7394 +    if (!stream->relative_volume_control)
7395 +        return PA_HOOK_OK;
7396 +
7397      switch (stream->type) {
7398          case STREAM_TYPE_SINK_INPUT:
7399              input = call_data;
7400 @@ -215,18 +238,18 @@ static pa_hook_result_t sink_input_or_source_output_mute_changed_cb(void *hook_d
7401          return PA_HOOK_OK;
7402  
7403      if (input)
7404 -        mute = input->muted;
7405 +        pa_bvolume_from_cvolume(&bvolume, &input->reference_ratio, &input->channel_map);
7406      else if (output)
7407 -        mute = output->muted;
7408 +        pa_bvolume_from_cvolume(&bvolume, &output->reference_ratio, &output->channel_map);
7409      else
7410          pa_assert_not_reached();
7411  
7412 -    pa_mute_control_mute_changed(stream->stream->own_mute_control, mute);
7413 +    pa_volume_control_set_volume(stream->relative_volume_control, &bvolume, true, true);
7414  
7415      return PA_HOOK_OK;
7416  }
7417  
7418 -static void mute_control_set_initial_mute_cb(pa_mute_control *control) {
7419 +static int mute_control_set_mute_cb(pa_mute_control *control, bool mute) {
7420      struct stream *stream;
7421  
7422      pa_assert(control);
7423 @@ -235,167 +258,66 @@ static void mute_control_set_initial_mute_cb(pa_mute_control *control) {
7424  
7425      switch (stream->type) {
7426          case STREAM_TYPE_SINK_INPUT:
7427 -            pa_sink_input_set_mute(stream->sink_input, control->mute, true);
7428 -            break;
7429 -
7430 -        case STREAM_TYPE_SOURCE_OUTPUT:
7431 -            pa_source_output_set_mute(stream->source_output, control->mute, true);
7432 -            break;
7433 -    }
7434 -}
7435 -
7436 -static const char *get_sink_input_description(pa_sink_input *input) {
7437 -    const char *description;
7438 -
7439 -    pa_assert(input);
7440 -
7441 -    description = pa_proplist_gets(input->proplist, PA_PROP_MEDIA_NAME);
7442 -    if (description)
7443 -        return description;
7444 -
7445 -    return NULL;
7446 -}
7447 -
7448 -static const char *get_source_output_description(pa_source_output *output) {
7449 -    const char *description;
7450 -
7451 -    pa_assert(output);
7452 -
7453 -    description = pa_proplist_gets(output->proplist, PA_PROP_MEDIA_NAME);
7454 -    if (description)
7455 -        return description;
7456 -
7457 -    return NULL;
7458 -}
7459 -
7460 -static pa_volume_control *stream_create_own_volume_control_cb(pas_stream *s) {
7461 -    struct stream *stream;
7462 -    const char *name = NULL;
7463 -    char *description;
7464 -    pa_volume_control *control;
7465 -    pa_bvolume volume;
7466 -
7467 -    pa_assert(s);
7468 -
7469 -    stream = s->userdata;
7470 -
7471 -    switch (stream->type) {
7472 -        case STREAM_TYPE_SINK_INPUT:
7473 -            name = "sink-input-volume-control";
7474 -            break;
7475 -
7476 -        case STREAM_TYPE_SOURCE_OUTPUT:
7477 -            name = "source-output-volume-control";
7478 -            break;
7479 -    }
7480 -
7481 -    description = get_stream_volume_and_mute_control_description_malloc(stream);
7482 -    control = pa_volume_control_new(stream->creator->volume_api, name, description, true, false);
7483 -    pa_xfree(description);
7484 -    control->set_volume = volume_control_set_volume_cb;
7485 -    control->userdata = stream;
7486 -
7487 -    pa_assert(!stream->volume_changed_slot);
7488 -
7489 -    switch (stream->type) {
7490 -        case STREAM_TYPE_SINK_INPUT:
7491 -            stream->volume_changed_slot =
7492 -                    pa_hook_connect(&stream->sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED], PA_HOOK_NORMAL,
7493 -                                    sink_input_or_source_output_volume_changed_cb, stream);
7494 -            pa_bvolume_from_cvolume(&volume, &stream->sink_input->volume, &stream->sink_input->channel_map);
7495 +            if (stream->sink_input->state == PA_SINK_INPUT_INIT)
7496 +                pa_sink_input_new_data_set_muted(stream->sink_input_new_data, mute);
7497 +            else
7498 +                pa_sink_input_set_mute(stream->sink_input, mute, true);
7499              break;
7500  
7501          case STREAM_TYPE_SOURCE_OUTPUT:
7502 -            stream->volume_changed_slot =
7503 -                    pa_hook_connect(&stream->source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED],
7504 -                                    PA_HOOK_NORMAL, sink_input_or_source_output_volume_changed_cb, stream);
7505 -            pa_bvolume_from_cvolume(&volume, &stream->source_output->volume, &stream->source_output->channel_map);
7506 +            if (stream->source_output->state == PA_SOURCE_OUTPUT_INIT)
7507 +                pa_source_output_new_data_set_muted(stream->source_output_new_data, mute);
7508 +            else
7509 +                pa_source_output_set_mute(stream->source_output, mute, true);
7510              break;
7511      }
7512  
7513 -    pa_volume_control_put(control, &volume, volume_control_set_initial_volume_cb);
7514 -
7515 -    return control;
7516 -}
7517 -
7518 -static void stream_delete_own_volume_control_cb(pas_stream *s) {
7519 -    struct stream *stream;
7520 -
7521 -    pa_assert(s);
7522 -
7523 -    stream = s->userdata;
7524 -    pa_hook_slot_free(stream->volume_changed_slot);
7525 -    stream->volume_changed_slot = NULL;
7526 -    pa_volume_control_free(s->own_volume_control);
7527 +    return 0;
7528  }
7529  
7530 -static pa_mute_control *stream_create_own_mute_control_cb(pas_stream *s) {
7531 -    struct stream *stream;
7532 -    const char *name = NULL;
7533 -    char *description;
7534 -    pa_mute_control *control;
7535 -    bool mute = false;
7536 -
7537 -    pa_assert(s);
7538 -
7539 -    stream = s->userdata;
7540 -
7541 -    switch (stream->type) {
7542 -        case STREAM_TYPE_SINK_INPUT:
7543 -            name = "sink-input-mute-control";
7544 -            break;
7545 -
7546 -        case STREAM_TYPE_SOURCE_OUTPUT:
7547 -            name = "source-output-mute-control";
7548 -            break;
7549 -    }
7550 +static pa_hook_result_t sink_input_or_source_output_mute_changed_cb(void *hook_data, void *call_data, void *userdata) {
7551 +    struct stream *stream = userdata;
7552 +    pa_sink_input *input = NULL;
7553 +    pa_source_output *output = NULL;
7554 +    bool mute;
7555  
7556 -    description = get_stream_volume_and_mute_control_description_malloc(stream);
7557 -    control = pa_mute_control_new(stream->creator->volume_api, name, description);
7558 -    pa_xfree(description);
7559 -    control->set_mute = mute_control_set_mute_cb;
7560 -    control->userdata = stream;
7561 +    pa_assert(stream);
7562 +    pa_assert(call_data);
7563  
7564 -    pa_assert(!stream->mute_changed_slot);
7565 +    if (!stream->mute_control)
7566 +        return PA_HOOK_OK;
7567  
7568      switch (stream->type) {
7569          case STREAM_TYPE_SINK_INPUT:
7570 -            stream->mute_changed_slot =
7571 -                    pa_hook_connect(&stream->sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], PA_HOOK_NORMAL,
7572 -                                    sink_input_or_source_output_mute_changed_cb, stream);
7573 -            mute = stream->sink_input->muted;
7574 +            input = call_data;
7575              break;
7576  
7577          case STREAM_TYPE_SOURCE_OUTPUT:
7578 -            stream->mute_changed_slot =
7579 -                    pa_hook_connect(&stream->source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED],
7580 -                                    PA_HOOK_NORMAL, sink_input_or_source_output_mute_changed_cb, stream);
7581 -            mute = stream->source_output->muted;
7582 +            output = call_data;
7583              break;
7584      }
7585  
7586 -    pa_mute_control_put(control, mute, true, mute_control_set_initial_mute_cb);
7587 -
7588 -    return control;
7589 -}
7590 +    if ((input && input != stream->sink_input) || (output && output != stream->source_output))
7591 +        return PA_HOOK_OK;
7592  
7593 -static void stream_delete_own_mute_control_cb(pas_stream *s) {
7594 -    struct stream *stream;
7595 +    if (input)
7596 +        mute = input->muted;
7597 +    else if (output)
7598 +        mute = output->muted;
7599 +    else
7600 +        pa_assert_not_reached();
7601  
7602 -    pa_assert(s);
7603 +    pa_mute_control_set_mute(stream->mute_control, mute);
7604  
7605 -    stream = s->userdata;
7606 -    pa_hook_slot_free(stream->mute_changed_slot);
7607 -    stream->mute_changed_slot = NULL;
7608 -    pa_mute_control_free(s->own_mute_control);
7609 +    return PA_HOOK_OK;
7610  }
7611  
7612  static pa_hook_result_t sink_input_or_source_output_proplist_changed_cb(void *hook_data, void *call_data, void *userdata) {
7613      struct stream *stream = userdata;
7614      pa_sink_input *input = NULL;
7615      pa_source_output *output = NULL;
7616 -    const char *new_stream_description = NULL;
7617 -    char *new_control_description;
7618 +    pa_proplist *proplist = NULL;
7619 +    const char *description = NULL;
7620  
7621      pa_assert(stream);
7622      pa_assert(call_data);
7623 @@ -407,9 +329,7 @@ static pa_hook_result_t sink_input_or_source_output_proplist_changed_cb(void *ho
7624              if (input != stream->sink_input)
7625                  return PA_HOOK_OK;
7626  
7627 -            new_stream_description = get_sink_input_description(input);
7628 -            if (!new_stream_description)
7629 -                new_stream_description = stream->stream->name;
7630 +            proplist = stream->sink_input->proplist;
7631              break;
7632  
7633          case STREAM_TYPE_SOURCE_OUTPUT:
7634 @@ -418,187 +338,290 @@ static pa_hook_result_t sink_input_or_source_output_proplist_changed_cb(void *ho
7635              if (output != stream->source_output)
7636                  return PA_HOOK_OK;
7637  
7638 -            new_stream_description = get_source_output_description(output);
7639 -            if (!new_stream_description)
7640 -                new_stream_description = stream->stream->name;
7641 +            proplist = stream->source_output->proplist;
7642              break;
7643      }
7644  
7645 -    pas_stream_description_changed(stream->stream, new_stream_description);
7646 -
7647 -    new_control_description = get_stream_volume_and_mute_control_description_malloc(stream);
7648 +    description = pa_proplist_gets(proplist, PA_PROP_MEDIA_NAME);
7649 +    if (!description)
7650 +        description = stream->stream->name;
7651  
7652 -    if (stream->stream->own_volume_control)
7653 -        pa_volume_control_description_changed(stream->stream->own_volume_control, new_control_description);
7654 -
7655 -    if (stream->stream->own_mute_control)
7656 -        pa_mute_control_description_changed(stream->stream->own_mute_control, new_control_description);
7657 -
7658 -    pa_xfree(new_control_description);
7659 +    pas_stream_set_description(stream->stream, description);
7660  
7661      return PA_HOOK_OK;
7662  }
7663  
7664 -static pa_hook_result_t client_proplist_changed_cb(void *hook_data, void *call_data, void *userdata) {
7665 -    struct stream *stream = userdata;
7666 -    pa_client *client = call_data;
7667 -    char *description;
7668 -
7669 -    pa_assert(stream);
7670 -    pa_assert(client);
7671 -
7672 -    if (client != stream->client)
7673 -        return PA_HOOK_OK;
7674 -
7675 -    description = get_stream_volume_and_mute_control_description_malloc(stream);
7676 -
7677 -    if (stream->stream->own_volume_control)
7678 -        pa_volume_control_description_changed(stream->stream->own_volume_control, description);
7679 -
7680 -    if (stream->stream->own_mute_control)
7681 -        pa_mute_control_description_changed(stream->stream->own_mute_control, description);
7682 -
7683 -    pa_xfree(description);
7684 -
7685 -    return PA_HOOK_OK;
7686 -}
7687 -
7688 -static struct stream *stream_new(pa_stream_creator *creator, enum stream_type type, void *core_stream) {
7689 -    struct stream *stream;
7690 -    const char *name = NULL;
7691 +static int stream_new(pa_stream_creator *creator, enum stream_type type, void *new_data, void *core_stream,
7692 +                      struct stream **_r) {
7693 +    struct stream *stream = NULL;
7694 +    pa_proplist *proplist = NULL;
7695 +    pa_channel_map *channel_map = NULL;
7696 +    bool volume_available = false;
7697 +    pa_bvolume volume;
7698 +    pa_bvolume relative_volume;
7699 +    bool mute = false;
7700 +    const char *stream_name = NULL;
7701      const char *description = NULL;
7702 +    const char *volume_control_name = NULL;
7703 +    const char *relative_volume_control_name = NULL;
7704 +    const char *mute_control_name = NULL;
7705      pa_direction_t direction = PA_DIRECTION_OUTPUT;
7706 +    int r;
7707 +    const char *prop_key;
7708 +    void *state = NULL;
7709  
7710      pa_assert(creator);
7711      pa_assert(core_stream);
7712 +    pa_assert(_r);
7713 +
7714 +    pa_bvolume_init_invalid(&volume);
7715 +    pa_bvolume_init_invalid(&relative_volume);
7716  
7717      stream = pa_xnew0(struct stream, 1);
7718 +    stream->core = creator->volume_api->core;
7719      stream->creator = creator;
7720      stream->type = type;
7721  
7722      switch (type) {
7723          case STREAM_TYPE_SINK_INPUT:
7724 +            stream->sink_input_new_data = new_data;
7725              stream->sink_input = core_stream;
7726 -            stream->client = stream->sink_input->client;
7727 -            name = "sink-input-stream";
7728  
7729 -            description = get_sink_input_description(stream->sink_input);
7730 -            if (!description)
7731 -                description = name;
7732 +            if (new_data) {
7733 +                stream->client = stream->sink_input_new_data->client;
7734 +                proplist = stream->sink_input_new_data->proplist;
7735 +                channel_map = &stream->sink_input_new_data->channel_map;
7736 +                volume_available = stream->sink_input_new_data->volume_writable;
7737 +
7738 +                if (volume_available) {
7739 +                    if (!stream->sink_input_new_data->volume_is_set) {
7740 +                        pa_cvolume cvolume;
7741 +
7742 +                        pa_cvolume_reset(&cvolume, channel_map->channels);
7743 +                        pa_sink_input_new_data_set_volume(stream->sink_input_new_data, &cvolume, true);
7744 +                    }
7745 +
7746 +                    pa_bvolume_from_cvolume(&volume, &stream->sink_input_new_data->volume, channel_map);
7747 +                    pa_bvolume_from_cvolume(&relative_volume, &stream->sink_input_new_data->reference_ratio, channel_map);
7748 +                }
7749 +
7750 +                if (!stream->sink_input_new_data->muted_is_set)
7751 +                    pa_sink_input_new_data_set_muted(stream->sink_input_new_data, false);
7752 +
7753 +                mute = stream->sink_input_new_data->muted;
7754 +            } else {
7755 +                stream->client = stream->sink_input->client;
7756 +                proplist = stream->sink_input->proplist;
7757 +                channel_map = &stream->sink_input->channel_map;
7758 +                pa_bvolume_from_cvolume(&volume, &stream->sink_input->volume, channel_map);
7759 +                pa_bvolume_from_cvolume(&relative_volume, &stream->sink_input->reference_ratio, channel_map);
7760 +                mute = stream->sink_input->muted;
7761 +            }
7762 +
7763 +            stream_name = "sink-input-stream";
7764 +            volume_control_name = "sink-input-volume-control";
7765 +            relative_volume_control_name = "sink-input-relative-volume-control";
7766 +            mute_control_name = "sink-input-mute-control";
7767  
7768              direction = PA_DIRECTION_OUTPUT;
7769 +
7770 +            stream->proplist_changed_slot =
7771 +                    pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], PA_HOOK_NORMAL,
7772 +                                    sink_input_or_source_output_proplist_changed_cb, stream);
7773 +            stream->volume_changed_slot =
7774 +                    pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED], PA_HOOK_NORMAL,
7775 +                                    sink_input_or_source_output_volume_changed_cb, stream);
7776 +            stream->reference_ratio_changed_slot =
7777 +                    pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SINK_INPUT_REFERENCE_RATIO_CHANGED], PA_HOOK_NORMAL,
7778 +                                    sink_input_or_source_output_reference_ratio_changed_cb, stream);
7779 +            stream->mute_changed_slot =
7780 +                    pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], PA_HOOK_NORMAL,
7781 +                                    sink_input_or_source_output_mute_changed_cb, stream);
7782              break;
7783  
7784          case STREAM_TYPE_SOURCE_OUTPUT:
7785 +            stream->source_output_new_data = new_data;
7786              stream->source_output = core_stream;
7787 -            stream->client = stream->source_output->client;
7788 -            name = "source-output-stream";
7789  
7790 -            description = get_source_output_description(stream->source_output);
7791 -            if (!description)
7792 -                description = name;
7793 +            if (new_data) {
7794 +                stream->client = stream->source_output_new_data->client;
7795 +                proplist = stream->source_output_new_data->proplist;
7796 +                channel_map = &stream->source_output_new_data->channel_map;
7797 +                volume_available = stream->source_output_new_data->volume_writable;
7798 +
7799 +                if (volume_available) {
7800 +                    if (!stream->source_output_new_data->volume_is_set) {
7801 +                        pa_cvolume cvolume;
7802 +
7803 +                        pa_cvolume_reset(&cvolume, channel_map->channels);
7804 +                        pa_source_output_new_data_set_volume(stream->source_output_new_data, &cvolume, true);
7805 +                    }
7806 +
7807 +                    pa_bvolume_from_cvolume(&volume, &stream->source_output_new_data->volume, channel_map);
7808 +                    pa_bvolume_from_cvolume(&relative_volume, &stream->source_output_new_data->reference_ratio, channel_map);
7809 +                }
7810 +
7811 +                if (!stream->source_output_new_data->muted_is_set)
7812 +                    pa_source_output_new_data_set_muted(stream->source_output_new_data, false);
7813 +
7814 +                mute = stream->source_output_new_data->muted;
7815 +            } else {
7816 +                stream->client = stream->source_output->client;
7817 +                proplist = stream->source_output->proplist;
7818 +                channel_map = &stream->source_output->channel_map;
7819 +                pa_bvolume_from_cvolume(&volume, &stream->source_output->volume, channel_map);
7820 +                pa_bvolume_from_cvolume(&relative_volume, &stream->source_output->reference_ratio, channel_map);
7821 +                mute = stream->source_output->muted;
7822 +            }
7823 +
7824 +            stream_name = "source-output-stream";
7825 +            volume_control_name = "source-output-volume-control";
7826 +            relative_volume_control_name = "source-output-relative-volume-control";
7827 +            mute_control_name = "source-output-mute-control";
7828  
7829              direction = PA_DIRECTION_INPUT;
7830 -            break;
7831 -    }
7832 -
7833 -    stream->stream = pas_stream_new(creator->volume_api, name, description, direction);
7834 -    stream->stream->create_own_volume_control = stream_create_own_volume_control_cb;
7835 -    stream->stream->delete_own_volume_control = stream_delete_own_volume_control_cb;
7836 -    stream->stream->create_own_mute_control = stream_create_own_mute_control_cb;
7837 -    stream->stream->delete_own_mute_control = stream_delete_own_mute_control_cb;
7838 -    stream->stream->userdata = stream;
7839 -    pas_stream_set_have_own_volume_control(stream->stream, true);
7840 -    pas_stream_set_have_own_mute_control(stream->stream, true);
7841  
7842 -    switch (type) {
7843 -        case STREAM_TYPE_SINK_INPUT:
7844              stream->proplist_changed_slot =
7845 -                    pa_hook_connect(&stream->sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], PA_HOOK_NORMAL,
7846 +                    pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], PA_HOOK_NORMAL,
7847                                      sink_input_or_source_output_proplist_changed_cb, stream);
7848 -            break;
7849  
7850 -        case STREAM_TYPE_SOURCE_OUTPUT:
7851 -            stream->proplist_changed_slot =
7852 -                    pa_hook_connect(&stream->source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED],
7853 -                                    PA_HOOK_NORMAL, sink_input_or_source_output_proplist_changed_cb, stream);
7854 +            if (volume_available) {
7855 +                stream->volume_changed_slot =
7856 +                        pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED], PA_HOOK_NORMAL,
7857 +                                        sink_input_or_source_output_volume_changed_cb, stream);
7858 +                stream->reference_ratio_changed_slot =
7859 +                        pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_REFERENCE_RATIO_CHANGED],
7860 +                                        PA_HOOK_NORMAL, sink_input_or_source_output_reference_ratio_changed_cb, stream);
7861 +            }
7862 +
7863 +            stream->mute_changed_slot =
7864 +                    pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED], PA_HOOK_NORMAL,
7865 +                                    sink_input_or_source_output_mute_changed_cb, stream);
7866              break;
7867      }
7868  
7869 -    stream->client_proplist_changed_slot =
7870 -            pa_hook_connect(&stream->creator->volume_api->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED],
7871 -                            PA_HOOK_NORMAL, client_proplist_changed_cb, stream);
7872 +    r = pas_stream_new(creator->volume_api, stream_name, &stream->stream);
7873 +    if (r < 0)
7874 +        goto fail;
7875  
7876 -    return stream;
7877 -}
7878 +    description = pa_proplist_gets(proplist, PA_PROP_MEDIA_NAME);
7879 +    if (!description)
7880 +        description = stream->stream->name;
7881  
7882 -static void stream_put(struct stream *stream) {
7883 -    pa_proplist *proplist = NULL;
7884 +    pas_stream_set_description(stream->stream, description);
7885  
7886 -    pa_assert(stream);
7887 +    while ((prop_key = pa_proplist_iterate(proplist, &state)))
7888 +        pas_stream_set_property(stream->stream, prop_key, pa_proplist_gets(proplist, prop_key));
7889  
7890 -    switch (stream->type) {
7891 -        case STREAM_TYPE_SINK_INPUT:
7892 -            proplist = stream->sink_input->proplist;
7893 -            break;
7894 +    pas_stream_set_direction(stream->stream, direction);
7895 +    stream->stream->userdata = stream;
7896  
7897 -        case STREAM_TYPE_SOURCE_OUTPUT:
7898 -            proplist = stream->source_output->proplist;
7899 -            break;
7900 +    if (volume_available) {
7901 +        r = pa_volume_control_new(stream->creator->volume_api, volume_control_name, false,
7902 +                                  &stream->volume_control);
7903 +        if (r >= 0) {
7904 +            pa_volume_control_set_description(stream->volume_control, _("Volume"));
7905 +            pa_volume_control_set_channel_map(stream->volume_control, channel_map);
7906 +            pa_volume_control_set_volume(stream->volume_control, &volume, true, true);
7907 +            pa_volume_control_set_convertible_to_dB(stream->volume_control, true);
7908 +            stream->volume_control->set_volume = volume_control_set_volume_cb;
7909 +            stream->volume_control->userdata = stream;
7910 +
7911 +            pas_stream_set_volume_control(stream->stream, stream->volume_control);
7912 +        }
7913 +
7914 +        r = pa_volume_control_new(stream->creator->volume_api, relative_volume_control_name, false,
7915 +                                  &stream->relative_volume_control);
7916 +        if (r >= 0) {
7917 +            pa_volume_control_set_description(stream->relative_volume_control, _("Relative volume"));
7918 +            pa_volume_control_set_channel_map(stream->relative_volume_control, channel_map);
7919 +            pa_volume_control_set_volume(stream->relative_volume_control, &relative_volume, true, true);
7920 +            pa_volume_control_set_convertible_to_dB(stream->relative_volume_control, true);
7921 +            pa_volume_control_set_purpose(stream->relative_volume_control, PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME,
7922 +                                          stream->stream);
7923 +            stream->relative_volume_control->set_volume = relative_volume_control_set_volume_cb;
7924 +            stream->relative_volume_control->userdata = stream;
7925 +
7926 +            pas_stream_set_relative_volume_control(stream->stream, stream->relative_volume_control);
7927 +        }
7928      }
7929  
7930 -    pas_stream_put(stream->stream, proplist);
7931 -}
7932 +    r = pa_mute_control_new(stream->creator->volume_api, mute_control_name, false, &stream->mute_control);
7933 +    if (r >= 0) {
7934 +        pa_mute_control_set_description(stream->mute_control, _("Mute"));
7935 +        pa_mute_control_set_mute(stream->mute_control, mute);
7936 +        pa_mute_control_set_purpose(stream->mute_control, PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE, stream->stream);
7937 +        stream->mute_control->set_mute = mute_control_set_mute_cb;
7938 +        stream->mute_control->userdata = stream;
7939  
7940 -static void stream_unlink(struct stream *stream) {
7941 -    pa_assert(stream);
7942 +        pas_stream_set_mute_control(stream->stream, stream->mute_control);
7943 +    }
7944  
7945 -    if (stream->unlinked)
7946 -        return;
7947 +    pas_stream_put(stream->stream);
7948  
7949 -    stream->unlinked = true;
7950 +    if (stream->volume_control)
7951 +        pa_volume_control_put(stream->volume_control);
7952  
7953 -    if (stream->stream)
7954 -        pas_stream_unlink(stream->stream);
7955 +    if (stream->relative_volume_control)
7956 +        pa_volume_control_put(stream->relative_volume_control);
7957 +
7958 +    if (stream->mute_control)
7959 +        pa_mute_control_put(stream->mute_control);
7960 +
7961 +    *_r = stream;
7962 +    return 0;
7963 +
7964 +fail:
7965 +    if (stream)
7966 +        stream_free(stream);
7967 +
7968 +    return r;
7969  }
7970  
7971  static void stream_free(struct stream *stream) {
7972      pa_assert(stream);
7973  
7974 -    if (!stream->unlinked)
7975 -        stream_unlink(stream);
7976 +    if (stream->mute_changed_slot)
7977 +        pa_hook_slot_free(stream->mute_changed_slot);
7978 +
7979 +    if (stream->reference_ratio_changed_slot)
7980 +        pa_hook_slot_free(stream->reference_ratio_changed_slot);
7981  
7982 -    if (stream->client_proplist_changed_slot)
7983 -        pa_hook_slot_free(stream->client_proplist_changed_slot);
7984 +    if (stream->volume_changed_slot)
7985 +        pa_hook_slot_free(stream->volume_changed_slot);
7986  
7987      if (stream->proplist_changed_slot)
7988          pa_hook_slot_free(stream->proplist_changed_slot);
7989  
7990 +    if (stream->mute_control)
7991 +        pa_mute_control_free(stream->mute_control);
7992 +
7993 +    if (stream->relative_volume_control)
7994 +        pa_volume_control_free(stream->relative_volume_control);
7995 +
7996 +    if (stream->volume_control)
7997 +        pa_volume_control_free(stream->volume_control);
7998 +
7999      if (stream->stream)
8000          pas_stream_free(stream->stream);
8001  
8002      pa_xfree(stream);
8003  }
8004  
8005 -static void create_stream(pa_stream_creator *creator, enum stream_type type, void *core_stream) {
8006 +static pa_hook_result_t sink_input_fixate_cb(void *hook_data, void *call_data, void *userdata) {
8007 +    pa_stream_creator *creator = userdata;
8008 +    pa_sink_input_new_data *data = call_data;
8009 +    int r;
8010      struct stream *stream;
8011  
8012      pa_assert(creator);
8013 -    pa_assert(core_stream);
8014 +    pa_assert(data);
8015  
8016 -    stream = stream_new(creator, type, core_stream);
8017 -    pa_hashmap_put(creator->streams, core_stream, stream);
8018 -    stream_put(stream);
8019 -}
8020 -
8021 -static pa_hook_result_t sink_input_put_cb(void *hook_data, void *call_data, void *userdata) {
8022 -    pa_stream_creator *creator = userdata;
8023 -    pa_sink_input *input = call_data;
8024 -
8025 -    pa_assert(creator);
8026 -    pa_assert(input);
8027 +    r = stream_new(creator, STREAM_TYPE_SINK_INPUT, data, data->sink_input, &stream);
8028 +    if (r < 0)
8029 +        return PA_HOOK_OK;
8030  
8031 -    create_stream(creator, STREAM_TYPE_SINK_INPUT, input);
8032 +    pa_hashmap_put(creator->streams, stream->sink_input, stream);
8033  
8034      return PA_HOOK_OK;
8035  }
8036 @@ -615,14 +638,20 @@ static pa_hook_result_t sink_input_unlink_cb(void *hook_data, void *call_data, v
8037      return PA_HOOK_OK;
8038  }
8039  
8040 -static pa_hook_result_t source_output_put_cb(void *hook_data, void *call_data, void *userdata) {
8041 +static pa_hook_result_t source_output_fixate_cb(void *hook_data, void *call_data, void *userdata) {
8042      pa_stream_creator *creator = userdata;
8043 -    pa_source_output *output = call_data;
8044 +    pa_source_output_new_data *data = call_data;
8045 +    int r;
8046 +    struct stream *stream;
8047  
8048      pa_assert(creator);
8049 -    pa_assert(output);
8050 +    pa_assert(data);
8051 +
8052 +    r = stream_new(creator, STREAM_TYPE_SOURCE_OUTPUT, data, data->source_output, &stream);
8053 +    if (r < 0)
8054 +        return PA_HOOK_OK;
8055  
8056 -    create_stream(creator, STREAM_TYPE_SOURCE_OUTPUT, output);
8057 +    pa_hashmap_put(creator->streams, stream->source_output, stream);
8058  
8059      return PA_HOOK_OK;
8060  }
8061 @@ -644,26 +673,34 @@ pa_stream_creator *pa_stream_creator_new(pa_volume_api *api) {
8062      uint32_t idx;
8063      pa_sink_input *input;
8064      pa_source_output *output;
8065 +    int r;
8066 +    struct stream *stream;
8067  
8068      pa_assert(api);
8069  
8070      creator = pa_xnew0(pa_stream_creator, 1);
8071      creator->volume_api = api;
8072      creator->streams = pa_hashmap_new_full(NULL, NULL, NULL, (pa_free_cb_t) stream_free);
8073 -    creator->sink_input_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_NORMAL,
8074 -                                                   sink_input_put_cb, creator);
8075 +    creator->sink_input_fixate_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_NORMAL,
8076 +                                                      sink_input_fixate_cb, creator);
8077      creator->sink_input_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_NORMAL,
8078                                                        sink_input_unlink_cb, creator);
8079 -    creator->source_output_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_NORMAL,
8080 -                                                      source_output_put_cb, creator);
8081 +    creator->source_output_fixate_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL,
8082 +                                                         source_output_fixate_cb, creator);
8083      creator->source_output_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], PA_HOOK_NORMAL,
8084                                                           source_output_unlink_cb, creator);
8085  
8086 -    PA_IDXSET_FOREACH(input, api->core->sink_inputs, idx)
8087 -        create_stream(creator, STREAM_TYPE_SINK_INPUT, input);
8088 +    PA_IDXSET_FOREACH(input, api->core->sink_inputs, idx) {
8089 +        r = stream_new(creator, STREAM_TYPE_SINK_INPUT, NULL, input, &stream);
8090 +        if (r >= 0)
8091 +            pa_hashmap_put(creator->streams, stream->sink_input, stream);
8092 +    }
8093  
8094 -    PA_IDXSET_FOREACH(output, api->core->source_outputs, idx)
8095 -        create_stream(creator, STREAM_TYPE_SOURCE_OUTPUT, output);
8096 +    PA_IDXSET_FOREACH(output, api->core->source_outputs, idx) {
8097 +        r = stream_new(creator, STREAM_TYPE_SOURCE_OUTPUT, NULL, output, &stream);
8098 +        if (r >= 0)
8099 +            pa_hashmap_put(creator->streams, stream->source_output, stream);
8100 +    }
8101  
8102      return creator;
8103  }
8104 @@ -677,14 +714,14 @@ void pa_stream_creator_free(pa_stream_creator *creator) {
8105      if (creator->source_output_unlink_slot)
8106          pa_hook_slot_free(creator->source_output_unlink_slot);
8107  
8108 -    if (creator->source_output_put_slot)
8109 -        pa_hook_slot_free(creator->source_output_put_slot);
8110 +    if (creator->source_output_fixate_slot)
8111 +        pa_hook_slot_free(creator->source_output_fixate_slot);
8112  
8113      if (creator->sink_input_unlink_slot)
8114          pa_hook_slot_free(creator->sink_input_unlink_slot);
8115  
8116 -    if (creator->sink_input_put_slot)
8117 -        pa_hook_slot_free(creator->sink_input_put_slot);
8118 +    if (creator->sink_input_fixate_slot)
8119 +        pa_hook_slot_free(creator->sink_input_fixate_slot);
8120  
8121      if (creator->streams)
8122          pa_hashmap_free(creator->streams);
8123 diff --git a/src/modules/volume-api/volume-api.c b/src/modules/volume-api/volume-api.c
8124 index 9abea7e..4a8a2e6 100644
8125 --- a/src/modules/volume-api/volume-api.c
8126 +++ b/src/modules/volume-api/volume-api.c
8127 @@ -26,16 +26,20 @@
8128  #include "volume-api.h"
8129  
8130  #include <modules/volume-api/audio-group.h>
8131 -#include <modules/volume-api/binding.h>
8132  #include <modules/volume-api/device.h>
8133  #include <modules/volume-api/device-creator.h>
8134 +#include <modules/volume-api/inidb.h>
8135  #include <modules/volume-api/sstream.h>
8136  #include <modules/volume-api/stream-creator.h>
8137  #include <modules/volume-api/volume-control.h>
8138  
8139  #include <pulsecore/core-util.h>
8140 +#include <pulsecore/namereg.h>
8141  #include <pulsecore/shared.h>
8142  
8143 +#define CONTROL_DB_TABLE_NAME_VOLUME_CONTROL "VolumeControl"
8144 +#define CONTROL_DB_TABLE_NAME_MUTE_CONTROL "MuteControl"
8145 +
8146  static pa_volume_api *volume_api_new(pa_core *core);
8147  static void volume_api_free(pa_volume_api *api);
8148  
8149 @@ -76,44 +80,209 @@ void pa_volume_api_unref(pa_volume_api *api) {
8150      }
8151  }
8152  
8153 -void pa_volume_api_add_binding_target_type(pa_volume_api *api, pa_binding_target_type *type) {
8154 -    pa_assert(api);
8155 -    pa_assert(type);
8156 +static int control_db_get_volume_control_cb(pa_inidb *db, const char *name, void **_r) {
8157 +    pa_volume_api *api;
8158 +    pa_volume_control *control;
8159 +
8160 +    pa_assert(db);
8161 +    pa_assert(name);
8162 +    pa_assert(_r);
8163 +
8164 +    api = pa_inidb_get_userdata(db);
8165  
8166 -    pa_assert_se(pa_hashmap_put(api->binding_target_types, type->name, type) >= 0);
8167 +    control = pa_hashmap_get(api->volume_controls_from_db, name);
8168 +    if (!control) {
8169 +        int r;
8170  
8171 -    pa_log_debug("Added binding target type %s.", type->name);
8172 +        r = pa_volume_control_new(api, name, true, &control);
8173 +        if (r < 0)
8174 +            return r;
8175  
8176 -    pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_ADDED], type);
8177 +        pa_hashmap_put(api->volume_controls_from_db, (void *) control->name, control);
8178 +    }
8179 +
8180 +    *_r = control;
8181 +    return 0;
8182  }
8183  
8184 -void pa_volume_api_remove_binding_target_type(pa_volume_api *api, pa_binding_target_type *type) {
8185 -    pa_assert(api);
8186 -    pa_assert(type);
8187 +static int control_db_parse_volume_control_description_cb(pa_inidb *db, const char *value, void *object) {
8188 +    pa_volume_control *control = object;
8189 +
8190 +    pa_assert(db);
8191 +    pa_assert(value);
8192 +    pa_assert(control);
8193  
8194 -    pa_log_debug("Removing binding target type %s.", type->name);
8195 +    pa_volume_control_set_description(control, value);
8196  
8197 -    pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_REMOVED], type);
8198 +    return 0;
8199 +}
8200 +
8201 +static int control_db_parse_volume_control_volume_cb(pa_inidb *db, const char *value, void *object) {
8202 +    pa_volume_control *control = object;
8203 +    int r;
8204 +    pa_bvolume bvolume;
8205 +
8206 +    pa_assert(db);
8207 +    pa_assert(value);
8208 +    pa_assert(control);
8209 +
8210 +    r = pa_atou(value, &bvolume.volume);
8211 +    if (r < 0)
8212 +        return -PA_ERR_INVALID;
8213 +
8214 +    if (!PA_VOLUME_IS_VALID(bvolume.volume))
8215 +        return -PA_ERR_INVALID;
8216  
8217 -    pa_assert_se(pa_hashmap_remove(api->binding_target_types, type->name));
8218 +    pa_volume_control_set_volume(control, &bvolume, true, false);
8219 +
8220 +    return 0;
8221  }
8222  
8223 -static void create_builtin_binding_target_types(pa_volume_api *api) {
8224 -    pa_binding_target_type *type;
8225 +static int control_db_parse_volume_control_balance_cb(pa_inidb *db, const char *value, void *object) {
8226 +    pa_volume_control *control = object;
8227 +    int r;
8228 +    pa_bvolume bvolume;
8229  
8230 -    pa_assert(api);
8231 +    pa_assert(db);
8232 +    pa_assert(value);
8233 +    pa_assert(control);
8234 +
8235 +    r = pa_bvolume_parse_balance(value, &bvolume);
8236 +    if (r < 0)
8237 +        return -PA_ERR_INVALID;
8238 +
8239 +    pa_volume_control_set_channel_map(control, &bvolume.channel_map);
8240 +    pa_volume_control_set_volume(control, &bvolume, false, true);
8241 +
8242 +    return 0;
8243 +}
8244 +
8245 +static int control_db_parse_volume_control_convertible_to_dB_cb(pa_inidb *db, const char *value, void *object) {
8246 +    pa_volume_control *control = object;
8247 +    int r;
8248 +
8249 +    pa_assert(db);
8250 +    pa_assert(value);
8251 +    pa_assert(control);
8252 +
8253 +    r = pa_parse_boolean(value);
8254 +    if (r < 0)
8255 +        return -PA_ERR_INVALID;
8256 +
8257 +    pa_volume_control_set_convertible_to_dB(control, r);
8258 +
8259 +    return 0;
8260 +}
8261 +
8262 +static int control_db_get_mute_control_cb(pa_inidb *db, const char *name, void **_r) {
8263 +    pa_volume_api *api;
8264 +    pa_mute_control *control;
8265 +
8266 +    pa_assert(db);
8267 +    pa_assert(name);
8268 +    pa_assert(_r);
8269 +
8270 +    api = pa_inidb_get_userdata(db);
8271 +
8272 +    control = pa_hashmap_get(api->mute_controls_from_db, name);
8273 +    if (!control) {
8274 +        int r;
8275 +
8276 +        r = pa_mute_control_new(api, name, true, &control);
8277 +        if (r < 0)
8278 +            return r;
8279 +
8280 +        pa_hashmap_put(api->mute_controls_from_db, (void *) control->name, control);
8281 +    }
8282 +
8283 +    *_r = control;
8284 +    return 0;
8285 +}
8286 +
8287 +static int control_db_parse_mute_control_description_cb(pa_inidb *db, const char *value, void *object) {
8288 +    pa_mute_control *control = object;
8289  
8290 -    type = pa_audio_group_create_binding_target_type(api);
8291 -    pa_volume_api_add_binding_target_type(api, type);
8292 +    pa_assert(db);
8293 +    pa_assert(value);
8294 +    pa_assert(control);
8295 +
8296 +    pa_mute_control_set_description(control, value);
8297 +
8298 +    return 0;
8299  }
8300  
8301 -static void delete_builtin_binding_target_types(pa_volume_api *api) {
8302 -    pa_binding_target_type *type;
8303 +static int control_db_parse_mute_control_mute_cb(pa_inidb *db, const char *value, void *object) {
8304 +    pa_mute_control *control = object;
8305 +    int mute;
8306 +
8307 +    pa_assert(db);
8308 +    pa_assert(value);
8309 +    pa_assert(control);
8310 +
8311 +    mute = pa_parse_boolean(value);
8312 +    if (mute < 0)
8313 +        return -PA_ERR_INVALID;
8314 +
8315 +    pa_mute_control_set_mute(control, mute);
8316 +
8317 +    return 0;
8318 +}
8319 +
8320 +static void create_control_db(pa_volume_api *api) {
8321 +    pa_volume_control *volume_control;
8322 +    pa_mute_control *mute_control;
8323 +
8324 +    pa_assert(api);
8325 +    pa_assert(!api->control_db.db);
8326 +
8327 +    api->control_db.db = pa_inidb_new(api->core, "controls", api);
8328 +
8329 +    api->control_db.volume_controls = pa_inidb_add_table(api->control_db.db, CONTROL_DB_TABLE_NAME_VOLUME_CONTROL,
8330 +                                                         control_db_get_volume_control_cb);
8331 +    pa_inidb_table_add_column(api->control_db.volume_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION,
8332 +                              control_db_parse_volume_control_description_cb);
8333 +    pa_inidb_table_add_column(api->control_db.volume_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_VOLUME,
8334 +                              control_db_parse_volume_control_volume_cb);
8335 +    pa_inidb_table_add_column(api->control_db.volume_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_BALANCE,
8336 +                              control_db_parse_volume_control_balance_cb);
8337 +    pa_inidb_table_add_column(api->control_db.volume_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_CONVERTIBLE_TO_DB,
8338 +                              control_db_parse_volume_control_convertible_to_dB_cb);
8339 +
8340 +    api->control_db.mute_controls = pa_inidb_add_table(api->control_db.db, CONTROL_DB_TABLE_NAME_MUTE_CONTROL,
8341 +                                                       control_db_get_mute_control_cb);
8342 +    pa_inidb_table_add_column(api->control_db.mute_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION,
8343 +                              control_db_parse_mute_control_description_cb);
8344 +    pa_inidb_table_add_column(api->control_db.mute_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_MUTE,
8345 +                              control_db_parse_mute_control_mute_cb);
8346 +
8347 +    api->volume_controls_from_db = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
8348 +    api->mute_controls_from_db = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
8349 +
8350 +    pa_inidb_load(api->control_db.db);
8351 +
8352 +    while ((volume_control = pa_hashmap_steal_first(api->volume_controls_from_db)))
8353 +        pa_volume_control_put(volume_control);
8354  
8355 +    pa_hashmap_free(api->volume_controls_from_db);
8356 +    api->volume_controls_from_db = NULL;
8357 +
8358 +    while ((mute_control = pa_hashmap_steal_first(api->mute_controls_from_db)))
8359 +        pa_mute_control_put(mute_control);
8360 +
8361 +    pa_hashmap_free(api->mute_controls_from_db);
8362 +    api->mute_controls_from_db = NULL;
8363 +}
8364 +
8365 +static void delete_control_db(pa_volume_api *api) {
8366      pa_assert(api);
8367  
8368 -    type = pa_hashmap_get(api->binding_target_types, PA_AUDIO_GROUP_BINDING_TARGET_TYPE);
8369 -    pa_volume_api_remove_binding_target_type(api, type);
8370 +    if (!api->control_db.db)
8371 +        return;
8372 +
8373 +    pa_inidb_free(api->control_db.db);
8374 +    api->control_db.mute_controls = NULL;
8375 +    api->control_db.volume_controls = NULL;
8376 +    api->control_db.db = NULL;
8377  }
8378  
8379  static void create_objects_defer_event_cb(pa_mainloop_api *mainloop_api, pa_defer_event *event, void *userdata) {
8380 @@ -138,7 +307,6 @@ static pa_volume_api *volume_api_new(pa_core *core) {
8381      api = pa_xnew0(pa_volume_api, 1);
8382      api->core = core;
8383      api->refcnt = 1;
8384 -    api->binding_target_types = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
8385      api->names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
8386      api->volume_controls = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
8387      api->mute_controls = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
8388 @@ -149,7 +317,7 @@ static pa_volume_api *volume_api_new(pa_core *core) {
8389      for (i = 0; i < PA_VOLUME_API_HOOK_MAX; i++)
8390          pa_hook_init(&api->hooks[i], api);
8391  
8392 -    create_builtin_binding_target_types(api);
8393 +    create_control_db(api);
8394  
8395      /* We delay the object creation to ensure that policy modules have a chance
8396       * to affect the initialization of the objects. If we created the objects
8397 @@ -170,6 +338,9 @@ static void volume_api_free(pa_volume_api *api) {
8398  
8399      pa_log_debug("Freeing the pa_volume_api object.");
8400  
8401 +    pa_assert(!api->mute_controls_from_db);
8402 +    pa_assert(!api->volume_controls_from_db);
8403 +
8404      if (api->stream_creator)
8405          pa_stream_creator_free(api->stream_creator);
8406  
8407 @@ -179,8 +350,7 @@ static void volume_api_free(pa_volume_api *api) {
8408      if (api->create_objects_defer_event)
8409          api->core->mainloop->defer_free(api->create_objects_defer_event);
8410  
8411 -    if (api->binding_target_types)
8412 -        delete_builtin_binding_target_types(api);
8413 +    delete_control_db(api);
8414  
8415      for (i = 0; i < PA_VOLUME_API_HOOK_MAX; i++)
8416          pa_hook_done(&api->hooks[i]);
8417 @@ -201,12 +371,24 @@ static void volume_api_free(pa_volume_api *api) {
8418      }
8419  
8420      if (api->mute_controls) {
8421 -        pa_assert(pa_hashmap_isempty(api->mute_controls));
8422 +        pa_mute_control *control;
8423 +
8424 +        while ((control = pa_hashmap_first(api->mute_controls))) {
8425 +            pa_assert(!control->present);
8426 +            pa_mute_control_free(control);
8427 +        }
8428 +
8429          pa_hashmap_free(api->mute_controls);
8430      }
8431  
8432      if (api->volume_controls) {
8433 -        pa_assert(pa_hashmap_isempty(api->volume_controls));
8434 +        pa_volume_control *control;
8435 +
8436 +        while ((control = pa_hashmap_first(api->volume_controls))) {
8437 +            pa_assert(!control->present);
8438 +            pa_volume_control_free(control);
8439 +        }
8440 +
8441          pa_hashmap_free(api->volume_controls);
8442      }
8443  
8444 @@ -215,11 +397,6 @@ static void volume_api_free(pa_volume_api *api) {
8445          pa_hashmap_free(api->names);
8446      }
8447  
8448 -    if (api->binding_target_types) {
8449 -        pa_assert(pa_hashmap_isempty(api->binding_target_types));
8450 -        pa_hashmap_free(api->binding_target_types);
8451 -    }
8452 -
8453      pa_xfree(api);
8454  }
8455  
8456 @@ -231,19 +408,24 @@ int pa_volume_api_register_name(pa_volume_api *api, const char *requested_name,
8457      pa_assert(requested_name);
8458      pa_assert(registered_name);
8459  
8460 +    if (!pa_namereg_is_valid_name(requested_name)) {
8461 +        pa_log("Invalid name: \"%s\"", requested_name);
8462 +        return -PA_ERR_INVALID;
8463 +    }
8464 +
8465      n = pa_xstrdup(requested_name);
8466  
8467      if (pa_hashmap_put(api->names, n, n) < 0) {
8468          unsigned i = 1;
8469  
8470 -        pa_xfree(n);
8471 -
8472          if (fail_if_already_registered) {
8473 +            pa_xfree(n);
8474              pa_log("Name %s already registered.", requested_name);
8475              return -PA_ERR_EXIST;
8476          }
8477  
8478          do {
8479 +            pa_xfree(n);
8480              i++;
8481              n = pa_sprintf_malloc("%s.%u", requested_name, i);
8482          } while (pa_hashmap_put(api->names, n, n) < 0);
8483 @@ -271,38 +453,6 @@ uint32_t pa_volume_api_allocate_volume_control_index(pa_volume_api *api) {
8484      return idx;
8485  }
8486  
8487 -static void set_main_output_volume_control_internal(pa_volume_api *api, pa_volume_control *control) {
8488 -    pa_volume_control *old_control;
8489 -
8490 -    pa_assert(api);
8491 -
8492 -    old_control = api->main_output_volume_control;
8493 -
8494 -    if (control == old_control)
8495 -        return;
8496 -
8497 -    api->main_output_volume_control = control;
8498 -    pa_log_debug("Main output volume control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8499 -                 control ? control->name : "(unset)");
8500 -    pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED], api);
8501 -}
8502 -
8503 -static void set_main_input_volume_control_internal(pa_volume_api *api, pa_volume_control *control) {
8504 -    pa_volume_control *old_control;
8505 -
8506 -    pa_assert(api);
8507 -
8508 -    old_control = api->main_input_volume_control;
8509 -
8510 -    if (control == old_control)
8511 -        return;
8512 -
8513 -    api->main_input_volume_control = control;
8514 -    pa_log_debug("Main input volume control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8515 -                 control ? control->name : "(unset)");
8516 -    pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_INPUT_VOLUME_CONTROL_CHANGED], api);
8517 -}
8518 -
8519  void pa_volume_api_add_volume_control(pa_volume_api *api, pa_volume_control *control) {
8520      pa_assert(api);
8521      pa_assert(control);
8522 @@ -318,10 +468,10 @@ int pa_volume_api_remove_volume_control(pa_volume_api *api, pa_volume_control *c
8523          return -1;
8524  
8525      if (control == api->main_output_volume_control)
8526 -        set_main_output_volume_control_internal(api, NULL);
8527 +        pa_volume_api_set_main_output_volume_control(api, NULL);
8528  
8529      if (control == api->main_input_volume_control)
8530 -        set_main_input_volume_control_internal(api, NULL);
8531 +        pa_volume_api_set_main_input_volume_control(api, NULL);
8532  
8533      return 0;
8534  }
8535 @@ -350,38 +500,6 @@ uint32_t pa_volume_api_allocate_mute_control_index(pa_volume_api *api) {
8536      return idx;
8537  }
8538  
8539 -static void set_main_output_mute_control_internal(pa_volume_api *api, pa_mute_control *control) {
8540 -    pa_mute_control *old_control;
8541 -
8542 -    pa_assert(api);
8543 -
8544 -    old_control = api->main_output_mute_control;
8545 -
8546 -    if (control == old_control)
8547 -        return;
8548 -
8549 -    api->main_output_mute_control = control;
8550 -    pa_log_debug("Main output mute control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8551 -                 control ? control->name : "(unset)");
8552 -    pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_OUTPUT_MUTE_CONTROL_CHANGED], api);
8553 -}
8554 -
8555 -static void set_main_input_mute_control_internal(pa_volume_api *api, pa_mute_control *control) {
8556 -    pa_mute_control *old_control;
8557 -
8558 -    pa_assert(api);
8559 -
8560 -    old_control = api->main_input_mute_control;
8561 -
8562 -    if (control == old_control)
8563 -        return;
8564 -
8565 -    api->main_input_mute_control = control;
8566 -    pa_log_debug("Main input mute control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8567 -                 control ? control->name : "(unset)");
8568 -    pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_INPUT_MUTE_CONTROL_CHANGED], api);
8569 -}
8570 -
8571  void pa_volume_api_add_mute_control(pa_volume_api *api, pa_mute_control *control) {
8572      pa_assert(api);
8573      pa_assert(control);
8574 @@ -397,10 +515,10 @@ int pa_volume_api_remove_mute_control(pa_volume_api *api, pa_mute_control *contr
8575          return -1;
8576  
8577      if (control == api->main_output_mute_control)
8578 -        set_main_output_mute_control_internal(api, NULL);
8579 +        pa_volume_api_set_main_output_mute_control(api, NULL);
8580  
8581      if (control == api->main_input_mute_control)
8582 -        set_main_input_mute_control_internal(api, NULL);
8583 +        pa_volume_api_set_main_input_mute_control(api, NULL);
8584  
8585      return 0;
8586  }
8587 @@ -543,105 +661,73 @@ pa_audio_group *pa_volume_api_get_audio_group_by_index(pa_volume_api *api, uint3
8588  }
8589  
8590  void pa_volume_api_set_main_output_volume_control(pa_volume_api *api, pa_volume_control *control) {
8591 +    pa_volume_control *old_control;
8592 +
8593      pa_assert(api);
8594  
8595 -    if (api->main_output_volume_control_binding) {
8596 -        pa_binding_free(api->main_output_volume_control_binding);
8597 -        api->main_output_volume_control_binding = NULL;
8598 -    }
8599 +    old_control = api->main_output_volume_control;
8600  
8601 -    set_main_output_volume_control_internal(api, control);
8602 -}
8603 +    if (control == old_control)
8604 +        return;
8605  
8606 -void pa_volume_api_set_main_input_volume_control(pa_volume_api *api, pa_volume_control *control) {
8607 -    pa_assert(api);
8608 +    api->main_output_volume_control = control;
8609  
8610 -    if (api->main_input_volume_control_binding) {
8611 -        pa_binding_free(api->main_input_volume_control_binding);
8612 -        api->main_input_volume_control_binding = NULL;
8613 -    }
8614 +    pa_log_debug("Main output volume control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8615 +                 control ? control->name : "(unset)");
8616  
8617 -    set_main_input_volume_control_internal(api, control);
8618 +    pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED], api);
8619  }
8620  
8621 -void pa_volume_api_set_main_output_mute_control(pa_volume_api *api, pa_mute_control *control) {
8622 +void pa_volume_api_set_main_input_volume_control(pa_volume_api *api, pa_volume_control *control) {
8623 +    pa_volume_control *old_control;
8624 +
8625      pa_assert(api);
8626  
8627 -    if (api->main_output_mute_control_binding) {
8628 -        pa_binding_free(api->main_output_mute_control_binding);
8629 -        api->main_output_mute_control_binding = NULL;
8630 -    }
8631 +    old_control = api->main_input_volume_control;
8632  
8633 -    set_main_output_mute_control_internal(api, control);
8634 -}
8635 +    if (control == old_control)
8636 +        return;
8637  
8638 -void pa_volume_api_set_main_input_mute_control(pa_volume_api *api, pa_mute_control *control) {
8639 -    pa_assert(api);
8640 +    api->main_input_volume_control = control;
8641  
8642 -    if (api->main_input_mute_control_binding) {
8643 -        pa_binding_free(api->main_input_mute_control_binding);
8644 -        api->main_input_mute_control_binding = NULL;
8645 -    }
8646 +    pa_log_debug("Main input volume control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8647 +                 control ? control->name : "(unset)");
8648  
8649 -    set_main_input_mute_control_internal(api, control);
8650 +    pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_INPUT_VOLUME_CONTROL_CHANGED], api);
8651  }
8652  
8653 -void pa_volume_api_bind_main_output_volume_control(pa_volume_api *api, pa_binding_target_info *target_info) {
8654 -    pa_binding_owner_info owner_info = {
8655 -        .userdata = api,
8656 -        .set_value = (pa_binding_set_value_cb_t) set_main_output_volume_control_internal,
8657 -    };
8658 +void pa_volume_api_set_main_output_mute_control(pa_volume_api *api, pa_mute_control *control) {
8659 +    pa_mute_control *old_control;
8660  
8661      pa_assert(api);
8662 -    pa_assert(target_info);
8663 -
8664 -    if (api->main_output_volume_control_binding)
8665 -        pa_binding_free(api->main_output_volume_control_binding);
8666  
8667 -    api->main_output_volume_control_binding = pa_binding_new(api, &owner_info, target_info);
8668 -}
8669 +    old_control = api->main_output_mute_control;
8670  
8671 -void pa_volume_api_bind_main_input_volume_control(pa_volume_api *api, pa_binding_target_info *target_info) {
8672 -    pa_binding_owner_info owner_info = {
8673 -        .userdata = api,
8674 -        .set_value = (pa_binding_set_value_cb_t) set_main_input_volume_control_internal,
8675 -    };
8676 +    if (control == old_control)
8677 +        return;
8678  
8679 -    pa_assert(api);
8680 -    pa_assert(target_info);
8681 +    api->main_output_mute_control = control;
8682  
8683 -    if (api->main_input_volume_control_binding)
8684 -        pa_binding_free(api->main_input_volume_control_binding);
8685 +    pa_log_debug("Main output mute control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8686 +                 control ? control->name : "(unset)");
8687  
8688 -    api->main_input_volume_control_binding = pa_binding_new(api, &owner_info, target_info);
8689 +    pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_OUTPUT_MUTE_CONTROL_CHANGED], api);
8690  }
8691  
8692 -void pa_volume_api_bind_main_output_mute_control(pa_volume_api *api, pa_binding_target_info *target_info) {
8693 -    pa_binding_owner_info owner_info = {
8694 -        .userdata = api,
8695 -        .set_value = (pa_binding_set_value_cb_t) set_main_output_mute_control_internal,
8696 -    };
8697 +void pa_volume_api_set_main_input_mute_control(pa_volume_api *api, pa_mute_control *control) {
8698 +    pa_mute_control *old_control;
8699  
8700      pa_assert(api);
8701 -    pa_assert(target_info);
8702 -
8703 -    if (api->main_output_mute_control_binding)
8704 -        pa_binding_free(api->main_output_mute_control_binding);
8705  
8706 -    api->main_output_mute_control_binding = pa_binding_new(api, &owner_info, target_info);
8707 -}
8708 +    old_control = api->main_input_mute_control;
8709  
8710 -void pa_volume_api_bind_main_input_mute_control(pa_volume_api *api, pa_binding_target_info *target_info) {
8711 -    pa_binding_owner_info owner_info = {
8712 -        .userdata = api,
8713 -        .set_value = (pa_binding_set_value_cb_t) set_main_input_mute_control_internal,
8714 -    };
8715 +    if (control == old_control)
8716 +        return;
8717  
8718 -    pa_assert(api);
8719 -    pa_assert(target_info);
8720 +    api->main_input_mute_control = control;
8721  
8722 -    if (api->main_input_mute_control_binding)
8723 -        pa_binding_free(api->main_input_mute_control_binding);
8724 +    pa_log_debug("Main input mute control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8725 +                 control ? control->name : "(unset)");
8726  
8727 -    api->main_input_mute_control_binding = pa_binding_new(api, &owner_info, target_info);
8728 +    pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_INPUT_MUTE_CONTROL_CHANGED], api);
8729  }
8730 diff --git a/src/modules/volume-api/volume-api.h b/src/modules/volume-api/volume-api.h
8731 index 73a1410..f99182a 100644
8732 --- a/src/modules/volume-api/volume-api.h
8733 +++ b/src/modules/volume-api/volume-api.h
8734 @@ -24,27 +24,56 @@
8735  
8736  #include <pulsecore/core.h>
8737  
8738 +#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION "description"
8739 +#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_VOLUME "volume"
8740 +#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_BALANCE "balance"
8741 +#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_CONVERTIBLE_TO_DB "convertible-to-dB"
8742 +#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_MUTE "mute"
8743 +
8744  typedef struct pa_volume_api pa_volume_api;
8745  
8746  /* Avoid circular dependencies... */
8747  typedef struct pa_audio_group pa_audio_group;
8748 -typedef struct pa_binding pa_binding;
8749 -typedef struct pa_binding_target_info pa_binding_target_info;
8750 -typedef struct pa_binding_target_type pa_binding_target_type;
8751  typedef struct pa_device pa_device;
8752  typedef struct pa_device_creator pa_device_creator;
8753 +typedef struct pa_inidb pa_inidb;
8754 +typedef struct pa_inidb_table pa_inidb_table;
8755  typedef struct pa_mute_control pa_mute_control;
8756  typedef struct pas_stream pas_stream;
8757  typedef struct pa_stream_creator pa_stream_creator;
8758  typedef struct pa_volume_control pa_volume_control;
8759  
8760  enum {
8761 -    PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_ADDED,
8762 -    PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_REMOVED,
8763 +    /* This is fired after the volume control implementation has done its part
8764 +     * of the volume control initialization, but before policy modules have
8765 +     * done their part of the initialization. Hook users are expected to not
8766 +     * modify the volume control state in this hook. */
8767 +    PA_VOLUME_API_HOOK_VOLUME_CONTROL_IMPLEMENTATION_INITIALIZED,
8768 +
8769 +    /* Policy modules can use this hook to initialize the volume control
8770 +     * volume. This is fired before PUT. If a policy module sets the volume, it
8771 +     * should return PA_HOOK_STOP to prevent lower-priority policy modules from
8772 +     * modifying the volume. */
8773 +    PA_VOLUME_API_HOOK_VOLUME_CONTROL_SET_INITIAL_VOLUME,
8774 +
8775      PA_VOLUME_API_HOOK_VOLUME_CONTROL_PUT,
8776      PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK,
8777      PA_VOLUME_API_HOOK_VOLUME_CONTROL_DESCRIPTION_CHANGED,
8778      PA_VOLUME_API_HOOK_VOLUME_CONTROL_VOLUME_CHANGED,
8779 +    PA_VOLUME_API_HOOK_VOLUME_CONTROL_CONVERTIBLE_TO_DB_CHANGED,
8780 +
8781 +    /* This is fired after the mute control implementation has done its part of
8782 +     * the mute control initialization, but before policy modules have done
8783 +     * their part of the initialization. Hook users are expected to not modify
8784 +     * the mute control state in this hook. */
8785 +    PA_VOLUME_API_HOOK_MUTE_CONTROL_IMPLEMENTATION_INITIALIZED,
8786 +
8787 +    /* Policy modules can use this hook to initialize the mute control mute.
8788 +     * This is fired before PUT. If a policy module sets the mute, it should
8789 +     * return PA_HOOK_STOP to prevent lower-priority policy modules from
8790 +     * modifying the mute. */
8791 +    PA_VOLUME_API_HOOK_MUTE_CONTROL_SET_INITIAL_MUTE,
8792 +
8793      PA_VOLUME_API_HOOK_MUTE_CONTROL_PUT,
8794      PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK,
8795      PA_VOLUME_API_HOOK_MUTE_CONTROL_DESCRIPTION_CHANGED,
8796 @@ -54,26 +83,16 @@ enum {
8797      PA_VOLUME_API_HOOK_DEVICE_DESCRIPTION_CHANGED,
8798      PA_VOLUME_API_HOOK_DEVICE_VOLUME_CONTROL_CHANGED,
8799      PA_VOLUME_API_HOOK_DEVICE_MUTE_CONTROL_CHANGED,
8800 -
8801 -    /* Policy modules can use this to set the initial volume control for a
8802 -     * stream. The hook callback should use pas_stream_set_volume_control() to
8803 -     * set the volume control. The hook callback should not do anything if
8804 -     * stream->volume_control is already non-NULL. */
8805 -    PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_VOLUME_CONTROL,
8806 -
8807 -    /* Policy modules can use this to set the initial mute control for a
8808 -     * stream. The hook callback should use pas_stream_set_mute_control() to
8809 -     * set the mute control. The hook callback should not do anything if
8810 -     * stream->mute_control is already non-NULL. */
8811 -    PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_MUTE_CONTROL,
8812 -
8813      PA_VOLUME_API_HOOK_STREAM_PUT,
8814      PA_VOLUME_API_HOOK_STREAM_UNLINK,
8815      PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED,
8816 +    PA_VOLUME_API_HOOK_STREAM_PROPLIST_CHANGED,
8817      PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED,
8818 +    PA_VOLUME_API_HOOK_STREAM_RELATIVE_VOLUME_CONTROL_CHANGED,
8819      PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED,
8820      PA_VOLUME_API_HOOK_AUDIO_GROUP_PUT,
8821      PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK,
8822 +    PA_VOLUME_API_HOOK_AUDIO_GROUP_DESCRIPTION_CHANGED,
8823      PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED,
8824      PA_VOLUME_API_HOOK_AUDIO_GROUP_MUTE_CONTROL_CHANGED,
8825      PA_VOLUME_API_HOOK_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED,
8826 @@ -86,7 +105,6 @@ enum {
8827  struct pa_volume_api {
8828      pa_core *core;
8829      unsigned refcnt;
8830 -    pa_hashmap *binding_target_types; /* name -> pa_binding_target_type */
8831      pa_hashmap *names; /* object name -> object name (hashmap-as-a-set) */
8832      pa_hashmap *volume_controls; /* name -> pa_volume_control */
8833      pa_hashmap *mute_controls; /* name -> pa_mute_control */
8834 @@ -103,27 +121,28 @@ struct pa_volume_api {
8835      uint32_t next_device_index;
8836      uint32_t next_stream_index;
8837      uint32_t next_audio_group_index;
8838 -    pa_binding *main_output_volume_control_binding;
8839 -    pa_binding *main_input_volume_control_binding;
8840 -    pa_binding *main_output_mute_control_binding;
8841 -    pa_binding *main_input_mute_control_binding;
8842      pa_hook hooks[PA_VOLUME_API_HOOK_MAX];
8843 +
8844 +    struct {
8845 +        pa_inidb *db;
8846 +        pa_inidb_table *volume_controls;
8847 +        pa_inidb_table *mute_controls;
8848 +    } control_db;
8849 +
8850      pa_defer_event *create_objects_defer_event;
8851      pa_device_creator *device_creator;
8852      pa_stream_creator *stream_creator;
8853 +
8854 +    pa_hashmap *volume_controls_from_db; /* control name -> pa_volume_control, only used during initialization. */
8855 +    pa_hashmap *mute_controls_from_db; /* control name -> pa_mute_control, only used during initialization. */
8856  };
8857  
8858  pa_volume_api *pa_volume_api_get(pa_core *core);
8859  pa_volume_api *pa_volume_api_ref(pa_volume_api *api);
8860  void pa_volume_api_unref(pa_volume_api *api);
8861  
8862 -void pa_volume_api_add_binding_target_type(pa_volume_api *api, pa_binding_target_type *type);
8863 -void pa_volume_api_remove_binding_target_type(pa_volume_api *api, pa_binding_target_type *type);
8864 -
8865 -/* If fail_if_already_registered is false, this function never fails. */
8866  int pa_volume_api_register_name(pa_volume_api *api, const char *requested_name, bool fail_if_already_registered,
8867                                  const char **registered_name);
8868 -
8869  void pa_volume_api_unregister_name(pa_volume_api *api, const char *name);
8870  
8871  uint32_t pa_volume_api_allocate_volume_control_index(pa_volume_api *api);
8872 @@ -155,9 +174,5 @@ void pa_volume_api_set_main_output_volume_control(pa_volume_api *api, pa_volume_
8873  void pa_volume_api_set_main_input_volume_control(pa_volume_api *api, pa_volume_control *control);
8874  void pa_volume_api_set_main_output_mute_control(pa_volume_api *api, pa_mute_control *control);
8875  void pa_volume_api_set_main_input_mute_control(pa_volume_api *api, pa_mute_control *control);
8876 -void pa_volume_api_bind_main_output_volume_control(pa_volume_api *api, pa_binding_target_info *target_info);
8877 -void pa_volume_api_bind_main_input_volume_control(pa_volume_api *api, pa_binding_target_info *target_info);
8878 -void pa_volume_api_bind_main_output_mute_control(pa_volume_api *api, pa_binding_target_info *target_info);
8879 -void pa_volume_api_bind_main_input_mute_control(pa_volume_api *api, pa_binding_target_info *target_info);
8880  
8881  #endif
8882 diff --git a/src/modules/volume-api/volume-control.c b/src/modules/volume-api/volume-control.c
8883 index c7f5dbb..bf4db71 100644
8884 --- a/src/modules/volume-api/volume-control.c
8885 +++ b/src/modules/volume-api/volume-control.c
8886 @@ -27,88 +27,96 @@
8887  
8888  #include <modules/volume-api/audio-group.h>
8889  #include <modules/volume-api/device.h>
8890 +#include <modules/volume-api/inidb.h>
8891  #include <modules/volume-api/sstream.h>
8892  
8893  #include <pulsecore/core-util.h>
8894  
8895 -pa_volume_control *pa_volume_control_new(pa_volume_api *api, const char *name, const char *description, bool convertible_to_dB,
8896 -                                         bool channel_map_is_writable) {
8897 -    pa_volume_control *control;
8898 +int pa_volume_control_new(pa_volume_api *api, const char *name, bool persistent, pa_volume_control **_r) {
8899 +    pa_volume_control *control = NULL;
8900 +    int r;
8901  
8902      pa_assert(api);
8903      pa_assert(name);
8904 -    pa_assert(description);
8905 +    pa_assert(_r);
8906  
8907      control = pa_xnew0(pa_volume_control, 1);
8908      control->volume_api = api;
8909      control->index = pa_volume_api_allocate_volume_control_index(api);
8910 -    pa_assert_se(pa_volume_api_register_name(api, name, false, &control->name) >= 0);
8911 -    control->description = pa_xstrdup(description);
8912 +
8913 +    r = pa_volume_api_register_name(api, name, persistent, &control->name);
8914 +    if (r < 0)
8915 +        goto fail;
8916 +
8917 +    control->description = pa_xstrdup(control->name);
8918      control->proplist = pa_proplist_new();
8919 -    pa_bvolume_init_invalid(&control->volume);
8920 -    control->convertible_to_dB = convertible_to_dB;
8921 -    control->channel_map_is_writable = channel_map_is_writable;
8922 +    pa_bvolume_init_mono(&control->volume, PA_VOLUME_NORM);
8923 +    control->present = !persistent;
8924 +    control->persistent = persistent;
8925 +    control->purpose = PA_VOLUME_CONTROL_PURPOSE_OTHER;
8926      control->devices = pa_hashmap_new(NULL, NULL);
8927      control->default_for_devices = pa_hashmap_new(NULL, NULL);
8928 -    control->streams = pa_hashmap_new(NULL, NULL);
8929 -    control->audio_groups = pa_hashmap_new(NULL, NULL);
8930  
8931 -    return control;
8932 +    if (persistent) {
8933 +        pa_inidb_row *row;
8934 +
8935 +        row = pa_inidb_table_add_row(api->control_db.volume_controls, control->name);
8936 +        control->db_cells.description = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION);
8937 +        control->db_cells.volume = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_VOLUME);
8938 +        control->db_cells.balance = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_BALANCE);
8939 +        control->db_cells.convertible_to_dB = pa_inidb_row_get_cell(row,
8940 +                                                                    PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_CONVERTIBLE_TO_DB);
8941 +    }
8942 +
8943 +    *_r = control;
8944 +    return 0;
8945 +
8946 +fail:
8947 +    if (control)
8948 +        pa_volume_control_free(control);
8949 +
8950 +    return r;
8951  }
8952  
8953 -void pa_volume_control_put(pa_volume_control *control, const pa_bvolume *initial_volume,
8954 -                           pa_volume_control_set_initial_volume_cb_t set_initial_volume_cb) {
8955 +void pa_volume_control_put(pa_volume_control *control) {
8956      const char *prop_key;
8957      void *state = NULL;
8958      char volume_str[PA_VOLUME_SNPRINT_VERBOSE_MAX];
8959      char balance_str[PA_BVOLUME_SNPRINT_BALANCE_MAX];
8960  
8961      pa_assert(control);
8962 -    pa_assert((initial_volume && pa_bvolume_valid(initial_volume, true, true)) || control->set_volume);
8963 -    pa_assert((initial_volume && pa_channel_map_valid(&initial_volume->channel_map)) || control->channel_map_is_writable);
8964 -    pa_assert(set_initial_volume_cb || !control->set_volume);
8965 -
8966 -    if (initial_volume && pa_bvolume_valid(initial_volume, true, false))
8967 -        control->volume.volume = initial_volume->volume;
8968 -    else
8969 -        control->volume.volume = PA_VOLUME_NORM / 3;
8970 -
8971 -    if (initial_volume && pa_bvolume_valid(initial_volume, false, true))
8972 -        pa_bvolume_copy_balance(&control->volume, initial_volume);
8973 -    else if (initial_volume && pa_channel_map_valid(&initial_volume->channel_map))
8974 -        pa_bvolume_reset_balance(&control->volume, &initial_volume->channel_map);
8975 -    else {
8976 -        pa_channel_map_init_mono(&control->volume.channel_map);
8977 -        pa_bvolume_reset_balance(&control->volume, &control->volume.channel_map);
8978 -    }
8979 +    pa_assert(control->set_volume || !control->present);
8980  
8981 -    if (set_initial_volume_cb)
8982 -        set_initial_volume_cb(control);
8983 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_IMPLEMENTATION_INITIALIZED], control);
8984 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_SET_INITIAL_VOLUME], control);
8985  
8986 -    pa_volume_api_add_volume_control(control->volume_api, control);
8987 +    if (control->set_volume) {
8988 +        control->set_volume_in_progress = true;
8989 +        control->set_volume(control, &control->volume, &control->volume, true, true);
8990 +        control->set_volume_in_progress = false;
8991 +    }
8992  
8993 +    pa_volume_api_add_volume_control(control->volume_api, control);
8994      control->linked = true;
8995  
8996      pa_log_debug("Created volume control #%u.", control->index);
8997      pa_log_debug("    Name: %s", control->name);
8998      pa_log_debug("    Description: %s", control->description);
8999 +    pa_log_debug("    Volume: %s", pa_volume_snprint_verbose(volume_str, sizeof(volume_str), control->volume.volume,
9000 +                 control->convertible_to_dB));
9001 +    pa_log_debug("    Balance: %s", pa_bvolume_snprint_balance(balance_str, sizeof(balance_str), &control->volume));
9002 +    pa_log_debug("    Present: %s", pa_yes_no(control->present));
9003 +    pa_log_debug("    Persistent: %s", pa_yes_no(control->persistent));
9004      pa_log_debug("    Properties:");
9005  
9006      while ((prop_key = pa_proplist_iterate(control->proplist, &state)))
9007          pa_log_debug("        %s = %s", prop_key, pa_strnull(pa_proplist_gets(control->proplist, prop_key)));
9008  
9009 -    pa_log_debug("    Volume: %s", pa_volume_snprint_verbose(volume_str, sizeof(volume_str), control->volume.volume,
9010 -                 control->convertible_to_dB));
9011 -    pa_log_debug("    Balance: %s", pa_bvolume_snprint_balance(balance_str, sizeof(balance_str), &control->volume));
9012 -    pa_log_debug("    Channel map is writable: %s", pa_yes_no(control->channel_map_is_writable));
9013 -
9014      pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_PUT], control);
9015  }
9016  
9017  void pa_volume_control_unlink(pa_volume_control *control) {
9018 -    pa_audio_group *group;
9019      pa_device *device;
9020 -    pas_stream *stream;
9021  
9022      pa_assert(control);
9023  
9024 @@ -122,15 +130,9 @@ void pa_volume_control_unlink(pa_volume_control *control) {
9025      pa_log_debug("Unlinking volume control %s.", control->name);
9026  
9027      if (control->linked)
9028 -        pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK], control);
9029 -
9030 -    pa_volume_api_remove_volume_control(control->volume_api, control);
9031 -
9032 -    while ((group = pa_hashmap_first(control->audio_groups)))
9033 -        pa_audio_group_set_volume_control(group, NULL);
9034 +        pa_volume_api_remove_volume_control(control->volume_api, control);
9035  
9036 -    while ((stream = pa_hashmap_first(control->streams)))
9037 -        pas_stream_set_volume_control(stream, NULL);
9038 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK], control);
9039  
9040      while ((device = pa_hashmap_first(control->default_for_devices)))
9041          pa_device_set_default_volume_control(device, NULL);
9042 @@ -153,19 +155,10 @@ void pa_volume_control_unlink(pa_volume_control *control) {
9043  void pa_volume_control_free(pa_volume_control *control) {
9044      pa_assert(control);
9045  
9046 -    if (!control->unlinked)
9047 +    /* unlink() expects name to be set. */
9048 +    if (!control->unlinked && control->name)
9049          pa_volume_control_unlink(control);
9050  
9051 -    if (control->audio_groups) {
9052 -        pa_assert(pa_hashmap_isempty(control->audio_groups));
9053 -        pa_hashmap_free(control->audio_groups);
9054 -    }
9055 -
9056 -    if (control->streams) {
9057 -        pa_assert(pa_hashmap_isempty(control->streams));
9058 -        pa_hashmap_free(control->streams);
9059 -    }
9060 -
9061      if (control->default_for_devices) {
9062          pa_assert(pa_hashmap_isempty(control->default_for_devices));
9063          pa_hashmap_free(control->default_for_devices);
9064 @@ -187,17 +180,91 @@ void pa_volume_control_free(pa_volume_control *control) {
9065      pa_xfree(control);
9066  }
9067  
9068 -void pa_volume_control_set_owner_audio_group(pa_volume_control *control, pa_audio_group *group) {
9069 +void pa_volume_control_set_purpose(pa_volume_control *control, pa_volume_control_purpose_t purpose, void *owner) {
9070 +    pa_assert(control);
9071 +    pa_assert(!control->linked);
9072 +
9073 +    control->purpose = purpose;
9074 +    control->owner = owner;
9075 +}
9076 +
9077 +int pa_volume_control_acquire_for_audio_group(pa_volume_control *control, pa_audio_group *group,
9078 +                                              pa_volume_control_set_volume_cb_t set_volume_cb, void *userdata) {
9079      pa_assert(control);
9080      pa_assert(group);
9081 +    pa_assert(set_volume_cb);
9082 +
9083 +    if (control->present) {
9084 +        pa_log("Can't acquire volume control %s, it's already present.", control->name);
9085 +        return -PA_ERR_BUSY;
9086 +    }
9087 +
9088 +    control->set_volume = set_volume_cb;
9089 +    control->userdata = userdata;
9090 +
9091 +    control->set_volume_in_progress = true;
9092 +    control->set_volume(control, &control->volume, &control->volume, true, true);
9093 +    control->set_volume_in_progress = false;
9094 +
9095 +    control->present = true;
9096 +
9097 +    if (!control->linked || control->unlinked)
9098 +        return 0;
9099 +
9100 +    pa_log_debug("Volume control %s became present.", control->name);
9101 +
9102 +    return 0;
9103 +}
9104 +
9105 +void pa_volume_control_release(pa_volume_control *control) {
9106 +    pa_assert(control);
9107 +
9108 +    if (!control->present)
9109 +        return;
9110 +
9111 +    control->present = false;
9112 +
9113 +    control->userdata = NULL;
9114 +    control->set_volume = NULL;
9115 +
9116 +    if (!control->linked || control->unlinked)
9117 +        return;
9118 +
9119 +    pa_log_debug("Volume control %s became not present.", control->name);
9120 +}
9121 +
9122 +void pa_volume_control_set_description(pa_volume_control *control, const char *description) {
9123 +    char *old_description;
9124 +
9125 +    pa_assert(control);
9126 +    pa_assert(description);
9127  
9128 -    control->owner_audio_group = group;
9129 +    old_description = control->description;
9130 +
9131 +    if (pa_streq(description, old_description))
9132 +        return;
9133 +
9134 +    control->description = pa_xstrdup(description);
9135 +
9136 +    if (control->persistent)
9137 +        pa_inidb_cell_set_value(control->db_cells.description, description);
9138 +
9139 +    if (!control->linked || control->unlinked) {
9140 +        pa_xfree(old_description);
9141 +        return;
9142 +    }
9143 +
9144 +    pa_log_debug("The description of volume control %s changed from \"%s\" to \"%s\".", control->name, old_description,
9145 +                 description);
9146 +    pa_xfree(old_description);
9147 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_DESCRIPTION_CHANGED], control);
9148  }
9149  
9150  static void set_volume_internal(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance) {
9151      pa_bvolume old_volume;
9152      bool volume_changed;
9153      bool balance_changed;
9154 +    char *str;
9155  
9156      pa_assert(control);
9157      pa_assert(volume);
9158 @@ -209,12 +276,26 @@ static void set_volume_internal(pa_volume_control *control, const pa_bvolume *vo
9159      if (!volume_changed && !balance_changed)
9160          return;
9161  
9162 -    if (volume_changed)
9163 +    if (volume_changed) {
9164          control->volume.volume = volume->volume;
9165  
9166 -    if (balance_changed)
9167 +        if (control->persistent) {
9168 +            str = pa_sprintf_malloc("%u", control->volume.volume);
9169 +            pa_inidb_cell_set_value(control->db_cells.volume, str);
9170 +            pa_xfree(str);
9171 +        }
9172 +    }
9173 +
9174 +    if (balance_changed) {
9175          pa_bvolume_copy_balance(&control->volume, volume);
9176  
9177 +        if (control->persistent) {
9178 +            pa_assert_se(pa_bvolume_balance_to_string(&control->volume, &str) >= 0);
9179 +            pa_inidb_cell_set_value(control->db_cells.balance, str);
9180 +            pa_xfree(str);
9181 +        }
9182 +    }
9183 +
9184      if (!control->linked || control->unlinked)
9185          return;
9186  
9187 @@ -248,62 +329,69 @@ int pa_volume_control_set_volume(pa_volume_control *control, const pa_bvolume *v
9188      pa_assert(control);
9189      pa_assert(volume);
9190  
9191 -    volume_local = *volume;
9192 +    if (control->set_volume_in_progress)
9193 +        return 0;
9194  
9195 -    if (!control->set_volume) {
9196 -        pa_log_info("Tried to set the volume of volume control %s, but the volume control doesn't support the operation.",
9197 -                    control->name);
9198 -        return -PA_ERR_NOTSUPPORTED;
9199 -    }
9200 +    volume_local = *volume;
9201  
9202 -    if (set_balance
9203 -            && !control->channel_map_is_writable
9204 -            && !pa_channel_map_equal(&volume_local.channel_map, &control->volume.channel_map))
9205 +    if (set_balance && !pa_channel_map_equal(&volume_local.channel_map, &control->volume.channel_map))
9206          pa_bvolume_remap(&volume_local, &control->volume.channel_map);
9207  
9208      if (pa_bvolume_equal(&volume_local, &control->volume, set_volume, set_balance))
9209          return 0;
9210  
9211 -    control->set_volume_in_progress = true;
9212 -    r = control->set_volume(control, &volume_local, set_volume, set_balance);
9213 -    control->set_volume_in_progress = false;
9214 +    if (control->linked && control->present) {
9215 +        control->set_volume_in_progress = true;
9216 +        r = control->set_volume(control, volume, &volume_local, set_volume, set_balance);
9217 +        control->set_volume_in_progress = false;
9218  
9219 -    if (r >= 0)
9220 -        set_volume_internal(control, &volume_local, set_volume, set_balance);
9221 +        if (r < 0) {
9222 +            pa_log("Setting the volume of volume control %s failed.", control->name);
9223 +            return r;
9224 +        }
9225 +    }
9226  
9227 -    return r;
9228 +    set_volume_internal(control, &volume_local, set_volume, set_balance);
9229 +
9230 +    return 0;
9231  }
9232  
9233 -void pa_volume_control_description_changed(pa_volume_control *control, const char *new_description) {
9234 -    char *old_description;
9235 +void pa_volume_control_set_channel_map(pa_volume_control *control, const pa_channel_map *map) {
9236 +    pa_bvolume bvolume;
9237  
9238      pa_assert(control);
9239 -    pa_assert(new_description);
9240 +    pa_assert(map);
9241  
9242 -    old_description = control->description;
9243 -
9244 -    if (pa_streq(new_description, old_description))
9245 +    if (pa_channel_map_equal(map, &control->volume.channel_map))
9246          return;
9247  
9248 -    control->description = pa_xstrdup(new_description);
9249 -    pa_log_debug("The description of volume control %s changed from \"%s\" to \"%s\".", control->name, old_description,
9250 -                 new_description);
9251 -    pa_xfree(old_description);
9252 -    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_DESCRIPTION_CHANGED], control);
9253 +    pa_bvolume_copy_balance(&bvolume, &control->volume);
9254 +    pa_bvolume_remap(&bvolume, map);
9255 +
9256 +    set_volume_internal(control, &bvolume, false, true);
9257  }
9258  
9259 -void pa_volume_control_volume_changed(pa_volume_control *control, const pa_bvolume *new_volume, bool volume_changed,
9260 -                                      bool balance_changed) {
9261 +void pa_volume_control_set_convertible_to_dB(pa_volume_control *control, bool convertible) {
9262 +    bool old_convertible;
9263 +
9264      pa_assert(control);
9265 -    pa_assert(new_volume);
9266  
9267 -    if (!control->linked)
9268 +    old_convertible = control->convertible_to_dB;
9269 +
9270 +    if (convertible == old_convertible)
9271          return;
9272  
9273 -    if (control->set_volume_in_progress)
9274 +    control->convertible_to_dB = convertible;
9275 +
9276 +    if (control->persistent)
9277 +        pa_inidb_cell_set_value(control->db_cells.convertible_to_dB, pa_boolean_to_string(convertible));
9278 +
9279 +    if (!control->linked || control->unlinked)
9280          return;
9281  
9282 -    set_volume_internal(control, new_volume, volume_changed, balance_changed);
9283 +    pa_log_debug("The volume of volume control %s became %sconvertible to dB.", control->name, convertible ? "" : "not ");
9284 +
9285 +    pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_CONVERTIBLE_TO_DB_CHANGED], control);
9286  }
9287  
9288  void pa_volume_control_add_device(pa_volume_control *control, pa_device *device) {
9289 @@ -333,31 +421,3 @@ void pa_volume_control_remove_default_for_device(pa_volume_control *control, pa_
9290  
9291      pa_assert_se(pa_hashmap_remove(control->default_for_devices, device));
9292  }
9293 -
9294 -void pa_volume_control_add_stream(pa_volume_control *control, pas_stream *stream) {
9295 -    pa_assert(control);
9296 -    pa_assert(stream);
9297 -
9298 -    pa_assert_se(pa_hashmap_put(control->streams, stream, stream) >= 0);
9299 -}
9300 -
9301 -void pa_volume_control_remove_stream(pa_volume_control *control, pas_stream *stream) {
9302 -    pa_assert(control);
9303 -    pa_assert(stream);
9304 -
9305 -    pa_assert_se(pa_hashmap_remove(control->streams, stream));
9306 -}
9307 -
9308 -void pa_volume_control_add_audio_group(pa_volume_control *control, pa_audio_group *group) {
9309 -    pa_assert(control);
9310 -    pa_assert(group);
9311 -
9312 -    pa_assert_se(pa_hashmap_put(control->audio_groups, group, group) >= 0);
9313 -}
9314 -
9315 -void pa_volume_control_remove_audio_group(pa_volume_control *control, pa_audio_group *group) {
9316 -    pa_assert(control);
9317 -    pa_assert(group);
9318 -
9319 -    pa_assert_se(pa_hashmap_remove(control->audio_groups, group));
9320 -}
9321 diff --git a/src/modules/volume-api/volume-control.h b/src/modules/volume-api/volume-control.h
9322 index aaba758..a47ab20 100644
9323 --- a/src/modules/volume-api/volume-control.h
9324 +++ b/src/modules/volume-api/volume-control.h
9325 @@ -23,10 +23,23 @@
9326  ***/
9327  
9328  #include <modules/volume-api/bvolume.h>
9329 +#include <modules/volume-api/inidb.h>
9330  #include <modules/volume-api/volume-api.h>
9331  
9332  typedef struct pa_volume_control pa_volume_control;
9333  
9334 +typedef enum {
9335 +    PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME,
9336 +    PA_VOLUME_CONTROL_PURPOSE_OTHER,
9337 +} pa_volume_control_purpose_t;
9338 +
9339 +/* Usually remapped_volume is the volume to use, because it has a matching
9340 + * channel map with the control, but in case the volume needs to be propagated
9341 + * to another control, original_volume can be used to avoid loss of precision
9342 + * that can result from remapping. */
9343 +typedef int (*pa_volume_control_set_volume_cb_t)(pa_volume_control *control, const pa_bvolume *original_volume,
9344 +                                                 const pa_bvolume *remapped_volume, bool set_volume, bool set_balance);
9345 +
9346  struct pa_volume_control {
9347      pa_volume_api *volume_api;
9348      uint32_t index;
9349 @@ -35,65 +48,61 @@ struct pa_volume_control {
9350      pa_proplist *proplist;
9351      pa_bvolume volume;
9352      bool convertible_to_dB;
9353 -    bool channel_map_is_writable;
9354 +    bool present;
9355 +    bool persistent;
9356  
9357 -    /* If this volume control is the "own volume control" of an audio group,
9358 -     * this is set to point to that group, otherwise this is NULL. */
9359 -    pa_audio_group *owner_audio_group;
9360 +    pa_volume_control_purpose_t purpose;
9361 +    union {
9362 +        pas_stream *owner_stream;
9363 +        void *owner;
9364 +    };
9365  
9366      pa_hashmap *devices; /* pa_device -> pa_device (hashmap-as-a-set) */
9367      pa_hashmap *default_for_devices; /* pa_device -> pa_device (hashmap-as-a-set) */
9368 -    pa_hashmap *streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
9369 -    pa_hashmap *audio_groups; /* pa_audio_group -> pa_audio_group (hashmap-as-a-set) */
9370 +
9371 +    struct {
9372 +        pa_inidb_cell *description;
9373 +        pa_inidb_cell *volume;
9374 +        pa_inidb_cell *balance;
9375 +        pa_inidb_cell *convertible_to_dB;
9376 +    } db_cells;
9377  
9378      bool linked;
9379      bool unlinked;
9380      bool set_volume_in_progress;
9381  
9382      /* Called from pa_volume_control_set_volume(). The implementation is
9383 -     * expected to return a negative error code on failure. May be NULL, if the
9384 -     * volume control is read-only. */
9385 -    int (*set_volume)(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance);
9386 +     * expected to return a negative error code on failure. */
9387 +    pa_volume_control_set_volume_cb_t set_volume;
9388  
9389      void *userdata;
9390  };
9391  
9392 -pa_volume_control *pa_volume_control_new(pa_volume_api *api, const char *name, const char *description, bool convertible_to_dB,
9393 -                                         bool channel_map_is_writable);
9394 -
9395 -typedef void (*pa_volume_control_set_initial_volume_cb_t)(pa_volume_control *control);
9396 -
9397 -/* initial_volume is the preferred initial volume of the volume control
9398 - * implementation. It may be NULL or partially invalid, if the implementation
9399 - * doesn't care about the initial state of the volume control, as long as these
9400 - * two rules are followed:
9401 - *
9402 - *   1) Read-only volume controls must always specify fully valid initial
9403 - *      volume.
9404 - *   2) Volume controls with read-only channel map must always specify a valid
9405 - *      channel map in initial_volume.
9406 - *
9407 - * The implementation's initial volume preference may be overridden by policy,
9408 - * if the volume control isn't read-only. When the final initial volume is
9409 - * known, the implementation is notified via set_initial_volume_cb (the volume
9410 - * can be read from control->volume). set_initial_volume_cb may be NULL, if the
9411 - * volume control is read-only. */
9412 -void pa_volume_control_put(pa_volume_control *control, const pa_bvolume *initial_volume,
9413 -                           pa_volume_control_set_initial_volume_cb_t set_initial_volume_cb);
9414 -
9415 +int pa_volume_control_new(pa_volume_api *api, const char *name, bool persistent, pa_volume_control **_r);
9416 +void pa_volume_control_put(pa_volume_control *control);
9417  void pa_volume_control_unlink(pa_volume_control *control);
9418  void pa_volume_control_free(pa_volume_control *control);
9419  
9420 -/* Called by audio-group.c only. */
9421 -void pa_volume_control_set_owner_audio_group(pa_volume_control *control, pa_audio_group *group);
9422 +/* Called by the volume control implementation, before
9423 + * pa_volume_control_put(). */
9424 +void pa_volume_control_set_purpose(pa_volume_control *control, pa_volume_control_purpose_t purpose, void *owner);
9425  
9426 -/* Called by clients and policy modules. */
9427 +/* Called by the volume control implementation. */
9428 +int pa_volume_control_acquire_for_audio_group(pa_volume_control *control, pa_audio_group *group,
9429 +                                              pa_volume_control_set_volume_cb_t set_volume_cb, void *userdata);
9430 +
9431 +/* Called by the volume control implementation. This must only be called for
9432 + * persistent controls; use pa_volume_control_free() for non-persistent
9433 + * controls. */
9434 +void pa_volume_control_release(pa_volume_control *control);
9435 +
9436 +/* Called by anyone. */
9437 +void pa_volume_control_set_description(pa_volume_control *control, const char *description);
9438  int pa_volume_control_set_volume(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance);
9439  
9440  /* Called by the volume control implementation. */
9441 -void pa_volume_control_description_changed(pa_volume_control *control, const char *new_description);
9442 -void pa_volume_control_volume_changed(pa_volume_control *control, const pa_bvolume *new_volume, bool volume_changed,
9443 -                                      bool balance_changed);
9444 +void pa_volume_control_set_channel_map(pa_volume_control *control, const pa_channel_map *map);
9445 +void pa_volume_control_set_convertible_to_dB(pa_volume_control *control, bool convertible);
9446  
9447  /* Called from device.c only. */
9448  void pa_volume_control_add_device(pa_volume_control *control, pa_device *device);
9449 @@ -101,12 +110,4 @@ void pa_volume_control_remove_device(pa_volume_control *control, pa_device *devi
9450  void pa_volume_control_add_default_for_device(pa_volume_control *control, pa_device *device);
9451  void pa_volume_control_remove_default_for_device(pa_volume_control *control, pa_device *device);
9452  
9453 -/* Called from sstream.c only. */
9454 -void pa_volume_control_add_stream(pa_volume_control *control, pas_stream *stream);
9455 -void pa_volume_control_remove_stream(pa_volume_control *control, pas_stream *stream);
9456 -
9457 -/* Called from audio-group.c only. */
9458 -void pa_volume_control_add_audio_group(pa_volume_control *control, pa_audio_group *group);
9459 -void pa_volume_control_remove_audio_group(pa_volume_control *control, pa_audio_group *group);
9460 -
9461  #endif
9462 diff --git a/src/pulse/ext-volume-api.c b/src/pulse/ext-volume-api.c
9463 index 032e108..b81909a 100644
9464 --- a/src/pulse/ext-volume-api.c
9465 +++ b/src/pulse/ext-volume-api.c
9466 @@ -36,6 +36,7 @@
9467  #include <pulsecore/i18n.h>
9468  #include <pulsecore/macro.h>
9469  #include <pulsecore/pstream-util.h>
9470 +#include <pulsecore/strbuf.h>
9471  
9472  #include <math.h>
9473  
9474 @@ -94,6 +95,21 @@ void pa_ext_volume_api_bvolume_init_invalid(pa_ext_volume_api_bvolume *volume) {
9475      pa_channel_map_init(&volume->channel_map);
9476  }
9477  
9478 +void pa_ext_volume_api_bvolume_init(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume, pa_channel_map *map) {
9479 +    unsigned i;
9480 +
9481 +    pa_assert(bvolume);
9482 +    pa_assert(PA_VOLUME_IS_VALID(volume));
9483 +    pa_assert(map);
9484 +    pa_assert(pa_channel_map_valid(map));
9485 +
9486 +    bvolume->volume = volume;
9487 +    bvolume->channel_map = *map;
9488 +
9489 +    for (i = 0; i < map->channels; i++)
9490 +        bvolume->balance[i] = 1.0;
9491 +}
9492 +
9493  void pa_ext_volume_api_bvolume_init_mono(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume) {
9494      pa_assert(bvolume);
9495      pa_assert(PA_VOLUME_IS_VALID(volume));
9496 @@ -103,6 +119,67 @@ void pa_ext_volume_api_bvolume_init_mono(pa_ext_volume_api_bvolume *bvolume, pa_
9497      pa_channel_map_init_mono(&bvolume->channel_map);
9498  }
9499  
9500 +int pa_ext_volume_api_bvolume_parse_balance(const char *str, pa_ext_volume_api_bvolume *_r) {
9501 +    pa_ext_volume_api_bvolume bvolume;
9502 +
9503 +    pa_assert(str);
9504 +    pa_assert(_r);
9505 +
9506 +    bvolume.channel_map.channels = 0;
9507 +
9508 +    for (;;) {
9509 +        const char *colon;
9510 +        size_t channel_name_len;
9511 +        char *channel_name;
9512 +        pa_channel_position_t position;
9513 +        const char *space;
9514 +        size_t balance_str_len;
9515 +        char *balance_str;
9516 +        int r;
9517 +        double balance;
9518 +
9519 +        colon = strchr(str, ':');
9520 +        if (!colon)
9521 +            return -PA_ERR_INVALID;
9522 +
9523 +        channel_name_len = colon - str;
9524 +        channel_name = pa_xstrndup(str, channel_name_len);
9525 +
9526 +        position = pa_channel_position_from_string(channel_name);
9527 +        pa_xfree(channel_name);
9528 +        if (position == PA_CHANNEL_POSITION_INVALID)
9529 +            return -PA_ERR_INVALID;
9530 +
9531 +        bvolume.channel_map.map[bvolume.channel_map.channels] = position;
9532 +        str = colon + 1;
9533 +
9534 +        space = strchr(str, ' ');
9535 +        if (space)
9536 +            balance_str_len = space - str;
9537 +        else
9538 +            balance_str_len = strlen(str);
9539 +
9540 +        balance_str = pa_xstrndup(str, balance_str_len);
9541 +
9542 +        r = pa_atod(balance_str, &balance);
9543 +        if (r < 0)
9544 +            return -PA_ERR_INVALID;
9545 +
9546 +        if (!pa_ext_volume_api_balance_valid(balance))
9547 +            return -PA_ERR_INVALID;
9548 +
9549 +        bvolume.balance[bvolume.channel_map.channels++] = balance;
9550 +
9551 +        if (space)
9552 +            str = space + 1;
9553 +        else
9554 +            break;
9555 +    }
9556 +
9557 +    pa_ext_volume_api_bvolume_copy_balance(_r, &bvolume);
9558 +    return 0;
9559 +}
9560 +
9561  int pa_ext_volume_api_bvolume_equal(const pa_ext_volume_api_bvolume *a, const pa_ext_volume_api_bvolume *b,
9562                                      int check_volume, int check_balance) {
9563      unsigned i;
9564 @@ -266,6 +343,29 @@ void pa_ext_volume_api_bvolume_set_rear_front_balance(pa_ext_volume_api_bvolume
9565      volume->volume = old_volume;
9566  }
9567  
9568 +int pa_ext_volume_api_bvolume_balance_to_string(const pa_ext_volume_api_bvolume *volume, char **_r) {
9569 +    pa_strbuf *buf;
9570 +    unsigned i;
9571 +
9572 +    pa_assert(volume);
9573 +    pa_assert(_r);
9574 +
9575 +    if (!pa_ext_volume_api_bvolume_valid(volume, false, true))
9576 +        return -PA_ERR_INVALID;
9577 +
9578 +    buf = pa_strbuf_new();
9579 +
9580 +    for (i = 0; i < volume->channel_map.channels; i++) {
9581 +        if (i != 0)
9582 +            pa_strbuf_putc(buf, ' ');
9583 +
9584 +        pa_strbuf_printf(buf, "%s:%.2f", pa_channel_position_to_string(volume->channel_map.map[i]), volume->balance[i]);
9585 +    }
9586 +
9587 +    *_r = pa_strbuf_tostring_free(buf);
9588 +    return 0;
9589 +}
9590 +
9591  char *pa_ext_volume_api_bvolume_snprint_balance(char *buf, size_t buf_len,
9592                                                  const pa_ext_volume_api_bvolume *volume) {
9593      char *e;
9594 diff --git a/src/pulse/ext-volume-api.h b/src/pulse/ext-volume-api.h
9595 index 720ff39..6402f4b 100644
9596 --- a/src/pulse/ext-volume-api.h
9597 +++ b/src/pulse/ext-volume-api.h
9598 @@ -50,7 +50,9 @@ int pa_ext_volume_api_balance_valid(double balance) PA_GCC_CONST;
9599  int pa_ext_volume_api_bvolume_valid(const pa_ext_volume_api_bvolume *volume, int check_volume, int check_balance)
9600          PA_GCC_PURE;
9601  void pa_ext_volume_api_bvolume_init_invalid(pa_ext_volume_api_bvolume *volume);
9602 +void pa_ext_volume_api_bvolume_init(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume, pa_channel_map *map);
9603  void pa_ext_volume_api_bvolume_init_mono(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume);
9604 +int pa_ext_volume_api_bvolume_parse_balance(const char *str, pa_ext_volume_api_bvolume *bvolume);
9605  int pa_ext_volume_api_bvolume_equal(const pa_ext_volume_api_bvolume *a, const pa_ext_volume_api_bvolume *b,
9606                                      int check_volume, int check_balance) PA_GCC_PURE;
9607  void pa_ext_volume_api_bvolume_from_cvolume(pa_ext_volume_api_bvolume *bvolume, const pa_cvolume *cvolume,
9608 @@ -64,6 +66,7 @@ double pa_ext_volume_api_bvolume_get_left_right_balance(const pa_ext_volume_api_
9609  void pa_ext_volume_api_bvolume_set_left_right_balance(pa_ext_volume_api_bvolume *volume, double balance);
9610  double pa_ext_volume_api_bvolume_get_rear_front_balance(const pa_ext_volume_api_bvolume *volume) PA_GCC_PURE;
9611  void pa_ext_volume_api_bvolume_set_rear_front_balance(pa_ext_volume_api_bvolume *volume, double balance);
9612 +int pa_ext_volume_api_bvolume_balance_to_string(const pa_ext_volume_api_bvolume *volume, char **_r);
9613  
9614  #define PA_EXT_VOLUME_API_BVOLUME_SNPRINT_BALANCE_MAX 500
9615  char *pa_ext_volume_api_bvolume_snprint_balance(char *buf, size_t buf_size,
9616 diff --git a/src/tizen-ivi/audio-groups.conf b/src/tizen-ivi/audio-groups.conf
9617 index 4839307..182df0d 100644
9618 --- a/src/tizen-ivi/audio-groups.conf
9619 +++ b/src/tizen-ivi/audio-groups.conf
9620 @@ -1,33 +1,32 @@
9621  [General]
9622 -audio-groups = x-tizen-ivi-call-downlink-audio-group x-tizen-ivi-navigator-output-audio-group x-tizen-ivi-default-output-audio-group
9623 -streams = call-downlink navigator-output default-output
9624 +stream-rules = call-downlink navigator-output default-output
9625  
9626  [AudioGroup x-tizen-ivi-call-downlink-audio-group]
9627  description = Call downlink
9628 -volume-control = create
9629 -mute-control = create
9630 +volume-control = create:call-downlink-volume-control
9631 +mute-control = create:call-downlink-mute-control
9632  
9633  [AudioGroup x-tizen-ivi-navigator-output-audio-group]
9634  description = Navigator
9635 -volume-control = create
9636 -mute-control = create
9637 +volume-control = bind:AudioGroup:x-tizen-ivi-default-output-audio-group
9638 +mute-control = bind:AudioGroup:x-tizen-ivi-default-output-audio-group
9639  
9640  [AudioGroup x-tizen-ivi-default-output-audio-group]
9641  description = Default
9642 -volume-control = create
9643 -mute-control = create
9644 +volume-control = create:default-output-volume-control
9645 +mute-control = create:default-output-mute-control
9646  
9647 -[Stream call-downlink]
9648 +[StreamRule call-downlink]
9649  match = (direction output AND property media.role=phone)
9650  audio-group-for-volume = x-tizen-ivi-call-downlink-audio-group
9651  audio-group-for-mute = x-tizen-ivi-call-downlink-audio-group
9652  
9653 -[Stream navigator-output]
9654 +[StreamRule navigator-output]
9655  match = (direction output AND property media.role=navigator)
9656  audio-group-for-volume = x-tizen-ivi-navigator-output-audio-group
9657  audio-group-for-mute = x-tizen-ivi-navigator-output-audio-group
9658  
9659 -[Stream default-output]
9660 -match = (direction output)
9661 +[StreamRule default-output]
9662 +match = (direction output AND NEG property media.role=filter)
9663  audio-group-for-volume = x-tizen-ivi-default-output-audio-group
9664  audio-group-for-mute = x-tizen-ivi-default-output-audio-group
9665 diff --git a/src/tizen-ivi/main-volume-policy.conf b/src/tizen-ivi/main-volume-policy.conf
9666 index 0a83968..f2b3513 100644
9667 --- a/src/tizen-ivi/main-volume-policy.conf
9668 +++ b/src/tizen-ivi/main-volume-policy.conf
9669 @@ -3,7 +3,6 @@ output-volume-model = by-active-main-volume-context
9670  input-volume-model = none
9671  output-mute-model = by-active-main-volume-context
9672  input-mute-model = none
9673 -main-volume-contexts = x-tizen-ivi-call default
9674  
9675  [MainVolumeContext x-tizen-ivi-call]
9676  description = Call main volume context