2 This file is part of PulseAudio.
4 Copyright 2006-2008 Lennart Poettering
5 Copyright 2011 Colin Guthrie
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #include <sys/types.h>
35 #include <pulse/xmalloc.h>
36 #include <pulse/volume.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/rtclock.h>
40 #include <pulse/format.h>
41 #include <pulse/internal.h>
43 #include <pulsecore/core-error.h>
44 #include <pulsecore/module.h>
45 #include <pulsecore/core-util.h>
46 #include <pulsecore/modargs.h>
47 #include <pulsecore/log.h>
48 #include <pulsecore/core-subscribe.h>
49 #include <pulsecore/sink-input.h>
50 #include <pulsecore/source-output.h>
51 #include <pulsecore/namereg.h>
52 #include <pulsecore/protocol-native.h>
53 #include <pulsecore/pstream.h>
54 #include <pulsecore/pstream-util.h>
55 #include <pulsecore/database.h>
56 #include <pulsecore/tagstruct.h>
58 #include "module-device-restore-symdef.h"
60 PA_MODULE_AUTHOR("Lennart Poettering");
61 PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
62 PA_MODULE_VERSION(PACKAGE_VERSION
);
63 PA_MODULE_LOAD_ONCE(TRUE
);
65 "restore_port=<Save/restore port?> "
66 "restore_volume=<Save/restore volumes?> "
67 "restore_muted=<Save/restore muted states?>");
69 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
71 static const char* const valid_modargs
[] = {
81 pa_subscription
*subscription
;
84 *sink_fixate_hook_slot
,
85 *source_new_hook_slot
,
86 *source_fixate_hook_slot
,
87 *connection_unlink_hook_slot
;
88 pa_time_event
*save_time_event
;
89 pa_database
*database
;
91 pa_native_protocol
*protocol
;
92 pa_idxset
*subscribed
;
94 pa_bool_t restore_volume
:1;
95 pa_bool_t restore_muted
:1;
96 pa_bool_t restore_port
:1;
99 /* Protocol extention commands */
102 SUBCOMMAND_SUBSCRIBE
,
104 SUBCOMMAND_READ_SINK_FORMATS_ALL
,
105 SUBCOMMAND_READ_SINK_FORMATS
,
106 SUBCOMMAND_SAVE_SINK_FORMATS
110 #define ENTRY_VERSION 3
114 pa_bool_t muted_valid
, volume_valid
, port_valid
;
116 pa_channel_map channel_map
;
122 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
123 struct userdata
*u
= userdata
;
129 pa_assert(e
== u
->save_time_event
);
130 u
->core
->mainloop
->time_free(u
->save_time_event
);
131 u
->save_time_event
= NULL
;
133 pa_database_sync(u
->database
);
134 pa_log_info("Synced.");
137 static struct entry
* entry_new(void) {
138 struct entry
*r
= pa_xnew0(struct entry
, 1);
139 r
->version
= ENTRY_VERSION
;
140 r
->formats
= pa_idxset_new(NULL
, NULL
);
144 static void entry_free(struct entry
* e
) {
147 pa_idxset_free(e
->formats
, (pa_free2_cb_t
) pa_format_info_free2
, NULL
);
152 static struct entry
* entry_read(struct userdata
*u
, const char *name
) {
154 struct entry
*e
= NULL
;
155 pa_tagstruct
*t
= NULL
;
157 uint8_t i
, n_formats
;
162 key
.data
= (char*) name
;
163 key
.size
= strlen(name
);
167 if (!pa_database_get(u
->database
, &key
, &data
))
170 t
= pa_tagstruct_new(data
.data
, data
.size
);
173 if (pa_tagstruct_getu8(t
, &e
->version
) < 0 ||
174 e
->version
> ENTRY_VERSION
||
175 pa_tagstruct_get_boolean(t
, &e
->volume_valid
) < 0 ||
176 pa_tagstruct_get_channel_map(t
, &e
->channel_map
) < 0 ||
177 pa_tagstruct_get_cvolume(t
, &e
->volume
) < 0 ||
178 pa_tagstruct_get_boolean(t
, &e
->muted_valid
) < 0 ||
179 pa_tagstruct_get_boolean(t
, &e
->muted
) < 0 ||
180 pa_tagstruct_get_boolean(t
, &e
->port_valid
) < 0 ||
181 pa_tagstruct_gets(t
, &port
) < 0 ||
182 pa_tagstruct_getu8(t
, &n_formats
) < 0) {
187 e
->port
= pa_xstrdup(port
);
189 for (i
= 0; i
< n_formats
; ++i
) {
190 pa_format_info
*f
= pa_format_info_new();
191 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
192 pa_format_info_free(f
);
195 pa_idxset_put(e
->formats
, f
, NULL
);
198 if (!pa_tagstruct_eof(t
))
201 if (e
->volume_valid
&& !pa_channel_map_valid(&e
->channel_map
)) {
202 pa_log_warn("Invalid channel map stored in database for device %s", name
);
206 if (e
->volume_valid
&& (!pa_cvolume_valid(&e
->volume
) || !pa_cvolume_compatible_with_channel_map(&e
->volume
, &e
->channel_map
))) {
207 pa_log_warn("Volume and channel map don't match in database entry for device %s", name
);
211 pa_tagstruct_free(t
);
212 pa_datum_free(&data
);
218 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name
);
223 pa_tagstruct_free(t
);
224 pa_datum_free(&data
);
228 static pa_bool_t
entry_write(struct userdata
*u
, const char *name
, const struct entry
*e
) {
239 t
= pa_tagstruct_new(NULL
, 0);
240 pa_tagstruct_putu8(t
, e
->version
);
241 pa_tagstruct_put_boolean(t
, e
->volume_valid
);
242 pa_tagstruct_put_channel_map(t
, &e
->channel_map
);
243 pa_tagstruct_put_cvolume(t
, &e
->volume
);
244 pa_tagstruct_put_boolean(t
, e
->muted_valid
);
245 pa_tagstruct_put_boolean(t
, e
->muted
);
246 pa_tagstruct_put_boolean(t
, e
->port_valid
);
247 pa_tagstruct_puts(t
, e
->port
);
248 pa_tagstruct_putu8(t
, pa_idxset_size(e
->formats
));
250 PA_IDXSET_FOREACH(f
, e
->formats
, i
) {
251 pa_tagstruct_put_format_info(t
, f
);
254 key
.data
= (char *) name
;
255 key
.size
= strlen(name
);
257 data
.data
= (void*)pa_tagstruct_data(t
, &data
.size
);
259 r
= (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0);
261 pa_tagstruct_free(t
);
266 static struct entry
* entry_copy(const struct entry
*e
) {
273 r
->version
= e
->version
;
274 r
->muted_valid
= e
->muted_valid
;
275 r
->volume_valid
= e
->volume_valid
;
276 r
->port_valid
= e
->port_valid
;
278 r
->channel_map
= e
->channel_map
;
279 r
->volume
= e
->volume
;
280 r
->port
= pa_xstrdup(e
->port
);
282 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
283 pa_idxset_put(r
->formats
, pa_format_info_copy(f
), NULL
);
288 static void trigger_save(struct userdata
*u
) {
289 if (u
->save_time_event
)
292 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
295 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
298 if (a
->port_valid
!= b
->port_valid
||
299 (a
->port_valid
&& !pa_streq(a
->port
, b
->port
)))
302 if (a
->muted_valid
!= b
->muted_valid
||
303 (a
->muted_valid
&& (a
->muted
!= b
->muted
)))
307 if (a
->volume_valid
!= b
->volume_valid
||
308 (a
->volume_valid
&& !pa_cvolume_equal(pa_cvolume_remap(&t
, &b
->channel_map
, &a
->channel_map
), &a
->volume
)))
314 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
315 struct userdata
*u
= userdata
;
316 struct entry
*entry
, *old
;
322 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
323 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
324 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
325 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
))
328 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
331 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
334 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
336 if ((old
= entry_read(u
, name
)))
337 entry
= entry_copy(old
);
341 if (sink
->save_volume
) {
342 entry
->channel_map
= sink
->channel_map
;
343 entry
->volume
= *pa_sink_get_volume(sink
, FALSE
);
344 entry
->volume_valid
= TRUE
;
347 if (sink
->save_muted
) {
348 entry
->muted
= pa_sink_get_mute(sink
, FALSE
);
349 entry
->muted_valid
= TRUE
;
352 if (sink
->save_port
) {
353 pa_xfree(entry
->port
);
354 entry
->port
= pa_xstrdup(sink
->active_port
? sink
->active_port
->name
: "");
355 entry
->port_valid
= TRUE
;
361 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
363 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
366 name
= pa_sprintf_malloc("source:%s", source
->name
);
368 if ((old
= entry_read(u
, name
)))
369 entry
= entry_copy(old
);
373 if (source
->save_volume
) {
374 entry
->channel_map
= source
->channel_map
;
375 entry
->volume
= *pa_source_get_volume(source
, FALSE
);
376 entry
->volume_valid
= TRUE
;
379 if (source
->save_muted
) {
380 entry
->muted
= pa_source_get_mute(source
, FALSE
);
381 entry
->muted_valid
= TRUE
;
384 if (source
->save_port
) {
385 pa_xfree(entry
->port
);
386 entry
->port
= pa_xstrdup(source
->active_port
? source
->active_port
->name
: "");
387 entry
->port_valid
= TRUE
;
395 if (entries_equal(old
, entry
)) {
405 pa_log_info("Storing volume/mute/port for device %s.", name
);
407 if (entry_write(u
, name
, entry
))
414 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
421 pa_assert(u
->restore_port
);
423 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
425 if ((e
= entry_read(u
, name
))) {
428 if (!new_data
->active_port
) {
429 pa_log_info("Restoring port for sink %s.", name
);
430 pa_sink_new_data_set_port(new_data
, e
->port
);
431 new_data
->save_port
= TRUE
;
433 pa_log_debug("Not restoring port for sink %s, because already set.", name
);
444 static pa_hook_result_t
sink_fixate_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
451 pa_assert(u
->restore_volume
|| u
->restore_muted
);
453 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
455 if ((e
= entry_read(u
, name
))) {
457 if (u
->restore_volume
&& e
->volume_valid
) {
459 if (!new_data
->volume_is_set
) {
462 pa_log_info("Restoring volume for sink %s.", new_data
->name
);
465 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
466 pa_sink_new_data_set_volume(new_data
, &v
);
468 new_data
->save_volume
= TRUE
;
470 pa_log_debug("Not restoring volume for sink %s, because already set.", new_data
->name
);
473 if (u
->restore_muted
&& e
->muted_valid
) {
475 if (!new_data
->muted_is_set
) {
476 pa_log_info("Restoring mute state for sink %s.", new_data
->name
);
477 pa_sink_new_data_set_muted(new_data
, e
->muted
);
478 new_data
->save_muted
= TRUE
;
480 pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data
->name
);
491 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
498 pa_assert(u
->restore_port
);
500 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
502 if ((e
= entry_read(u
, name
))) {
505 if (!new_data
->active_port
) {
506 pa_log_info("Restoring port for source %s.", name
);
507 pa_source_new_data_set_port(new_data
, e
->port
);
508 new_data
->save_port
= TRUE
;
510 pa_log_debug("Not restoring port for source %s, because already set.", name
);
521 static pa_hook_result_t
source_fixate_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
528 pa_assert(u
->restore_volume
|| u
->restore_muted
);
530 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
532 if ((e
= entry_read(u
, name
))) {
534 if (u
->restore_volume
&& e
->volume_valid
) {
536 if (!new_data
->volume_is_set
) {
539 pa_log_info("Restoring volume for source %s.", new_data
->name
);
542 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
543 pa_source_new_data_set_volume(new_data
, &v
);
545 new_data
->save_volume
= TRUE
;
547 pa_log_debug("Not restoring volume for source %s, because already set.", new_data
->name
);
550 if (u
->restore_muted
&& e
->muted_valid
) {
552 if (!new_data
->muted_is_set
) {
553 pa_log_info("Restoring mute state for source %s.", new_data
->name
);
554 pa_source_new_data_set_muted(new_data
, e
->muted
);
555 new_data
->save_muted
= TRUE
;
557 pa_log_debug("Not restoring mute state for source %s, because already set.", new_data
->name
);
568 #define EXT_VERSION 1
570 static void read_sink_format_reply(struct userdata
*u
, pa_tagstruct
*reply
, pa_sink
*sink
) {
578 pa_tagstruct_putu32(reply
, sink
->index
);
580 /* Read or create an entry */
581 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
582 if (!(e
= entry_read(u
, name
))) {
583 /* Fake a reply with PCM encoding supported */
584 pa_format_info
*f
= pa_format_info_new();
586 pa_tagstruct_putu8(reply
, 1);
587 f
->encoding
= PA_ENCODING_PCM
;
588 pa_tagstruct_put_format_info(reply
, f
);
590 pa_format_info_free(f
);
595 /* Write all the formats from the entry to the reply */
596 pa_tagstruct_putu8(reply
, pa_idxset_size(e
->formats
));
597 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
598 pa_tagstruct_put_format_info(reply
, f
);
604 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
607 pa_tagstruct
*reply
= NULL
;
616 if (pa_tagstruct_getu32(t
, &command
) < 0)
619 reply
= pa_tagstruct_new(NULL
, 0);
620 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
621 pa_tagstruct_putu32(reply
, tag
);
624 case SUBCOMMAND_TEST
: {
625 if (!pa_tagstruct_eof(t
))
628 pa_tagstruct_putu32(reply
, EXT_VERSION
);
632 case SUBCOMMAND_SUBSCRIBE
: {
636 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
637 !pa_tagstruct_eof(t
))
641 pa_idxset_put(u
->subscribed
, c
, NULL
);
643 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
648 case SUBCOMMAND_READ_SINK_FORMATS_ALL
: {
652 if (!pa_tagstruct_eof(t
))
655 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
656 read_sink_format_reply(u
, reply
, sink
);
661 case SUBCOMMAND_READ_SINK_FORMATS
: {
667 /* Get the sink index and the number of formats from the tagstruct */
668 if (pa_tagstruct_getu32(t
, &sink_index
) < 0)
671 if (!pa_tagstruct_eof(t
))
674 /* Now find our sink */
675 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
)))
678 read_sink_format_reply(u
, reply
, sink
);
683 case SUBCOMMAND_SAVE_SINK_FORMATS
: {
689 uint8_t i
, n_formats
;
691 /* Get the sink index and the number of formats from the tagstruct */
692 if (pa_tagstruct_getu32(t
, &sink_index
) < 0 ||
693 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
698 /* Now find our sink */
699 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
)))
702 /* Read or create an entry */
703 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
704 if (!(e
= entry_read(u
, name
)))
707 /* Read all the formats from our tagstruct */
708 for (i
= 0; i
< n_formats
; ++i
) {
709 pa_format_info
*f
= pa_format_info_new();
710 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
711 pa_format_info_free(f
);
715 pa_idxset_put(e
->formats
, f
, NULL
);
718 if (!pa_tagstruct_eof(t
)) {
724 if (entry_write(u
, name
, e
))
727 pa_log_warn("Could not save format info for sink %s", sink
->name
);
739 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
745 pa_tagstruct_free(reply
);
750 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
755 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
759 int pa__init(pa_module
*m
) {
760 pa_modargs
*ma
= NULL
;
766 pa_bool_t restore_volume
= TRUE
, restore_muted
= TRUE
, restore_port
= TRUE
;
770 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
771 pa_log("Failed to parse module arguments");
775 if (pa_modargs_get_value_boolean(ma
, "restore_volume", &restore_volume
) < 0 ||
776 pa_modargs_get_value_boolean(ma
, "restore_muted", &restore_muted
) < 0 ||
777 pa_modargs_get_value_boolean(ma
, "restore_port", &restore_port
) < 0) {
778 pa_log("restore_port=, restore_volume= and restore_muted= expect boolean arguments");
782 if (!restore_muted
&& !restore_volume
&& !restore_port
)
783 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!");
785 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
788 u
->restore_volume
= restore_volume
;
789 u
->restore_muted
= restore_muted
;
790 u
->restore_port
= restore_port
;
792 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
794 u
->protocol
= pa_native_protocol_get(m
->core
);
795 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
797 u
->connection_unlink_hook_slot
= pa_hook_connect(&pa_native_protocol_hooks(u
->protocol
)[PA_NATIVE_HOOK_CONNECTION_UNLINK
], PA_HOOK_NORMAL
, (pa_hook_cb_t
) connection_unlink_hook_cb
, u
);
799 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
802 u
->sink_new_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_NEW
], PA_HOOK_EARLY
, (pa_hook_cb_t
) sink_new_hook_callback
, u
);
803 u
->source_new_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_NEW
], PA_HOOK_EARLY
, (pa_hook_cb_t
) source_new_hook_callback
, u
);
806 if (restore_muted
|| restore_volume
) {
807 u
->sink_fixate_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_FIXATE
], PA_HOOK_EARLY
, (pa_hook_cb_t
) sink_fixate_hook_callback
, u
);
808 u
->source_fixate_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_FIXATE
], PA_HOOK_EARLY
, (pa_hook_cb_t
) source_fixate_hook_callback
, u
);
811 if (!(fname
= pa_state_path("device-volumes", TRUE
)))
814 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
815 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
820 pa_log_info("Successfully opened database file '%s'.", fname
);
823 for (sink
= pa_idxset_first(m
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(m
->core
->sinks
, &idx
))
824 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
826 for (source
= pa_idxset_first(m
->core
->sources
, &idx
); source
; source
= pa_idxset_next(m
->core
->sources
, &idx
))
827 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
841 void pa__done(pa_module
*m
) {
846 if (!(u
= m
->userdata
))
850 pa_subscription_free(u
->subscription
);
852 if (u
->sink_fixate_hook_slot
)
853 pa_hook_slot_free(u
->sink_fixate_hook_slot
);
854 if (u
->source_fixate_hook_slot
)
855 pa_hook_slot_free(u
->source_fixate_hook_slot
);
856 if (u
->sink_new_hook_slot
)
857 pa_hook_slot_free(u
->sink_new_hook_slot
);
858 if (u
->source_new_hook_slot
)
859 pa_hook_slot_free(u
->source_new_hook_slot
);
861 if (u
->connection_unlink_hook_slot
)
862 pa_hook_slot_free(u
->connection_unlink_hook_slot
);
864 if (u
->save_time_event
)
865 u
->core
->mainloop
->time_free(u
->save_time_event
);
868 pa_database_close(u
->database
);
871 pa_native_protocol_remove_ext(u
->protocol
, m
);
872 pa_native_protocol_unref(u
->protocol
);
876 pa_idxset_free(u
->subscribed
, NULL
, NULL
);