Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work
[profile/ivi/pulseaudio-panda.git] / src / modules / dbus / iface-stream.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2009 Tanu Kaskinen
5   Copyright 2009 Vincent Filali-Ansary <filali.v@azurdigitalnetworks.net>
6
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.
11
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.
16
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
20   USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <pulsecore/core-util.h>
28 #include <pulsecore/dbus-util.h>
29 #include <pulsecore/protocol-dbus.h>
30
31 #include "iface-stream.h"
32
33 #define PLAYBACK_OBJECT_NAME "playback_stream"
34 #define RECORD_OBJECT_NAME "record_stream"
35
36 enum stream_type {
37     STREAM_TYPE_PLAYBACK,
38     STREAM_TYPE_RECORD
39 };
40
41 struct pa_dbusiface_stream {
42     union {
43         pa_sink_input *sink_input;
44         pa_source_output *source_output;
45     };
46     enum stream_type type;
47     char *path;
48     pa_cvolume volume;
49     pa_bool_t is_muted;
50     pa_proplist *proplist;
51
52     pa_dbus_protocol *dbus_protocol;
53     pa_subscription *subscription;
54 };
55
56 static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
57 /*static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
58 static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
59 static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata);
60 static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
61 static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
62 static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
63 static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);*/
64 static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
65 static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
66 static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
67 static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
68 /*static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
69 static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
70 static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata);*/
71 static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
72
73 static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
74
75 /*static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata);
76 static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);*/
77
78 enum property_handler_index {
79     PROPERTY_HANDLER_INDEX,
80 /*    PROPERTY_HANDLER_DRIVER,
81     PROPERTY_HANDLER_OWNER_MODULE,
82     PROPERTY_HANDLER_CLIENT,
83     PROPERTY_HANDLER_DEVICE,
84     PROPERTY_HANDLER_SAMPLE_FORMAT,
85     PROPERTY_HANDLER_SAMPLE_RATE,
86     PROPERTY_HANDLER_CHANNELS,*/
87     PROPERTY_HANDLER_VOLUME,
88     PROPERTY_HANDLER_IS_MUTED,
89 /*    PROPERTY_HANDLER_BUFFER_LATENCY,
90     PROPERTY_HANDLER_DEVICE_LATENCY,
91     PROPERTY_HANDLER_RESAMPLE_METHOD,*/
92     PROPERTY_HANDLER_PROPERTY_LIST,
93     PROPERTY_HANDLER_MAX
94 };
95
96 static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
97     [PROPERTY_HANDLER_INDEX]           = { .property_name = "Index",          .type = "u",      .get_cb = handle_get_index,           .set_cb = NULL },
98 /*    [PROPERTY_HANDLER_DRIVER]          = { .property_name = "Driver",         .type = "s",      .get_cb = handle_get_driver,          .set_cb = NULL },
99     [PROPERTY_HANDLER_OWNER_MODULE]    = { .property_name = "OwnerModule",    .type = "o",      .get_cb = handle_get_owner_module,    .set_cb = NULL },
100     [PROPERTY_HANDLER_CLIENT]          = { .property_name = "Client",         .type = "o",      .get_cb = handle_get_client,          .set_cb = NULL },
101     [PROPERTY_HANDLER_DEVICE]          = { .property_name = "Device",         .type = "o",      .get_cb = handle_get_device,          .set_cb = NULL },
102     [PROPERTY_HANDLER_SAMPLE_FORMAT]   = { .property_name = "SampleFormat",   .type = "u",      .get_cb = handle_get_sample_format,   .set_cb = NULL },
103     [PROPERTY_HANDLER_SAMPLE_RATE]     = { .property_name = "SampleRate",     .type = "u",      .get_cb = handle_get_sample_rate,     .set_cb = NULL },
104     [PROPERTY_HANDLER_CHANNELS]        = { .property_name = "Channels",       .type = "au",     .get_cb = handle_get_channels,        .set_cb = NULL },*/
105     [PROPERTY_HANDLER_VOLUME]          = { .property_name = "Volume",         .type = "au",     .get_cb = handle_get_volume,          .set_cb = handle_set_volume },
106     [PROPERTY_HANDLER_IS_MUTED]        = { .property_name = "IsMuted",        .type = "b",      .get_cb = handle_get_is_muted,        .set_cb = handle_set_is_muted },
107 /*    [PROPERTY_HANDLER_BUFFER_LATENCY]  = { .property_name = "BufferLatency",  .type = "t",      .get_cb = handle_get_buffer_latency,  .set_cb = NULL },
108     [PROPERTY_HANDLER_DEVICE_LATENCY]  = { .property_name = "DeviceLatency",  .type = "t",      .get_cb = handle_get_device_latency,  .set_cb = NULL },
109     [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s",      .get_cb = handle_get_resample_method, .set_cb = NULL },*/
110     [PROPERTY_HANDLER_PROPERTY_LIST]   = { .property_name = "PropertyList",   .type = "a{say}", .get_cb = handle_get_property_list,   .set_cb = NULL }
111 };
112
113 /*enum method_handler_index {
114     METHOD_HANDLER_MOVE,
115     METHOD_HANDLER_KILL,
116     METHOD_HANDLER_MAX
117 };
118
119 static pa_dbus_arg_info move_args[] = { { "device", "o", "in" } };
120
121 static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
122     [METHOD_HANDLER_MOVE] = {
123         .method_name = "Move",
124         .arguments = move_args,
125         .n_arguments = sizeof(move_args) / sizeof(pa_dbus_arg_info),
126         .receive_cb = handle_move },
127     [METHOD_HANDLER_KILL] = {
128         .method_name = "Kill",
129         .arguments = NULL,
130         .n_arguments = 0,
131         .receive_cb = handle_kill }
132 };*/
133
134 enum signal_index {
135 /*    SIGNAL_DEVICE_UPDATED,
136     SIGNAL_SAMPLE_RATE_UPDATED,*/
137     SIGNAL_VOLUME_UPDATED,
138     SIGNAL_MUTE_UPDATED,
139     SIGNAL_PROPERTY_LIST_UPDATED,
140 /*    SIGNAL_STREAM_EVENT,*/
141     SIGNAL_MAX
142 };
143
144 /*static pa_dbus_arg_info device_updated_args[]        = { { "device",        "o",      NULL } };
145 static pa_dbus_arg_info sample_rate_updated_args[]   = { { "sample_rate",   "u",      NULL } };*/
146 static pa_dbus_arg_info volume_updated_args[]        = { { "volume",        "au",     NULL } };
147 static pa_dbus_arg_info mute_updated_args[]          = { { "muted",         "b",      NULL } };
148 static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
149 /*static pa_dbus_arg_info stream_event_args[]          = { { "name",          "s",      NULL }, { "property_list", "a{say}", NULL } };*/
150
151 static pa_dbus_signal_info signals[SIGNAL_MAX] = {
152 /*    [SIGNAL_DEVICE_UPDATED]        = { .name = "DeviceUpdated",       .arguments = device_updated_args,        .n_arguments = 1 },
153     [SIGNAL_SAMPLE_RATE_UPDATED]   = { .name = "SampleRateUpdated",   .arguments = sample_rate_updated_args,   .n_arguments = 1 },*/
154     [SIGNAL_VOLUME_UPDATED]        = { .name = "VolumeUpdated",       .arguments = volume_updated_args,        .n_arguments = 1 },
155     [SIGNAL_MUTE_UPDATED]          = { .name = "MuteUpdated",         .arguments = mute_updated_args,          .n_arguments = 1 },
156     [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }/*,
157     [SIGNAL_STREAM_EVENT]          = { .name = "StreamEvent",         .arguments = stream_event_args,          .n_arguments = sizeof(stream_event_args) / sizeof(pa_dbus_arg_info) }*/
158 };
159
160 static pa_dbus_interface_info stream_interface_info = {
161     .name = PA_DBUSIFACE_STREAM_INTERFACE,
162     .method_handlers = /*method_handlers*/ NULL,
163     .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0,
164     .property_handlers = property_handlers,
165     .n_property_handlers = PROPERTY_HANDLER_MAX,
166     .get_all_properties_cb = handle_get_all,
167     .signals = signals,
168     .n_signals = SIGNAL_MAX
169 };
170
171 static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
172     pa_dbusiface_stream *s = userdata;
173     dbus_uint32_t idx;
174
175     pa_assert(conn);
176     pa_assert(msg);
177     pa_assert(s);
178
179     idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index;
180
181     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
182 }
183
184 static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
185     pa_dbusiface_stream *s = userdata;
186     dbus_uint32_t volume[PA_CHANNELS_MAX];
187     unsigned i = 0;
188
189     pa_assert(conn);
190     pa_assert(msg);
191     pa_assert(s);
192
193     if (s->type == STREAM_TYPE_RECORD) {
194         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume.");
195         return;
196     }
197
198     for (i = 0; i < s->volume.channels; ++i)
199         volume[i] = s->volume.values[i];
200
201     pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, s->volume.channels);
202 }
203
204 static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
205     pa_dbusiface_stream *s = userdata;
206     unsigned stream_channels = 0;
207     dbus_uint32_t *volume = NULL;
208     unsigned n_volume_entries = 0;
209     pa_cvolume new_vol;
210     unsigned i = 0;
211
212     pa_assert(conn);
213     pa_assert(msg);
214     pa_assert(s);
215
216     if (s->type == STREAM_TYPE_RECORD) {
217         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume.");
218         return;
219     }
220
221     pa_cvolume_init(&new_vol);
222
223     stream_channels = s->sink_input->channel_map.channels;
224
225     new_vol.channels = stream_channels;
226
227     if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &volume, &n_volume_entries) < 0)
228         return;
229
230     if (n_volume_entries != stream_channels) {
231         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", stream_channels, n_volume_entries);
232         return;
233     }
234
235     for (i = 0; i < n_volume_entries; ++i) {
236         if (volume[i] > PA_VOLUME_MAX) {
237             pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too large volume value: %u", volume[i]);
238             return;
239         }
240         new_vol.values[i] = volume[i];
241     }
242
243     pa_sink_input_set_volume(s->sink_input, &new_vol, TRUE, TRUE);
244
245     pa_dbus_send_empty_reply(conn, msg);
246 }
247
248 static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) {
249     pa_dbusiface_stream *s = userdata;
250
251     pa_assert(conn);
252     pa_assert(msg);
253     pa_assert(s);
254
255     if (s->type == STREAM_TYPE_RECORD) {
256         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute.");
257         return;
258     }
259
260     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->is_muted);
261 }
262
263 static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) {
264     pa_dbusiface_stream *s = userdata;
265     dbus_bool_t is_muted = FALSE;
266
267     pa_assert(conn);
268     pa_assert(msg);
269     pa_assert(s);
270
271     if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &is_muted) < 0)
272         return;
273
274     if (s->type == STREAM_TYPE_RECORD) {
275         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute.");
276         return;
277     }
278
279     pa_sink_input_set_mute(s->sink_input, is_muted, TRUE);
280
281     pa_dbus_send_empty_reply(conn, msg);
282 };
283
284 static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
285     pa_dbusiface_stream *s = userdata;
286
287     pa_assert(conn);
288     pa_assert(msg);
289     pa_assert(s);
290
291     pa_dbus_send_proplist_variant_reply(conn, msg, s->proplist);
292 }
293
294 static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
295     pa_dbusiface_stream *s = userdata;
296     DBusMessage *reply = NULL;
297     DBusMessageIter msg_iter;
298     DBusMessageIter dict_iter;
299     dbus_uint32_t idx;
300     dbus_uint32_t volume[PA_CHANNELS_MAX];
301     unsigned i = 0;
302
303     pa_assert(conn);
304     pa_assert(msg);
305     pa_assert(s);
306
307     idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index;
308     if (s->type == STREAM_TYPE_PLAYBACK) {
309         for (i = 0; i < s->volume.channels; ++i)
310             volume[i] = s->volume.values[i];
311     }
312
313     pa_assert_se((reply = dbus_message_new_method_return(msg)));
314
315     dbus_message_iter_init_append(reply, &msg_iter);
316     pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
317
318     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
319
320     if (s->type == STREAM_TYPE_PLAYBACK) {
321         pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels);
322         pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &s->is_muted);
323     }
324
325     pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist);
326
327     pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
328     pa_assert_se(dbus_connection_send(conn, reply, NULL));
329     dbus_message_unref(reply);
330 }
331
332 static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
333     pa_dbusiface_stream *s = userdata;
334
335     pa_assert(c);
336     pa_assert(s);
337
338     if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
339         DBusMessage *signal = NULL;
340         pa_proplist *new_proplist = NULL;
341         unsigned i = 0;
342
343         pa_assert(((s->type == STREAM_TYPE_PLAYBACK)
344                     && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT))
345                   || ((s->type == STREAM_TYPE_RECORD)
346                        && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT)));
347
348         if (s->type == STREAM_TYPE_PLAYBACK) {
349             pa_cvolume new_volume;
350             pa_bool_t new_muted = FALSE;
351
352             pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE);
353
354             if (!pa_cvolume_equal(&s->volume, &new_volume)) {
355                 dbus_uint32_t volume[PA_CHANNELS_MAX];
356                 dbus_uint32_t *volume_ptr = volume;
357
358                 s->volume = new_volume;
359
360                 for (i = 0; i < s->volume.channels; ++i)
361                     volume[i] = s->volume.values[i];
362
363                 pa_assert_se(signal = dbus_message_new_signal(s->path,
364                                                               PA_DBUSIFACE_STREAM_INTERFACE,
365                                                               signals[SIGNAL_VOLUME_UPDATED].name));
366                 pa_assert_se(dbus_message_append_args(signal,
367                                                       DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels,
368                                                       DBUS_TYPE_INVALID));
369
370                 pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
371                 dbus_message_unref(signal);
372                 signal = NULL;
373             }
374
375             new_muted = pa_sink_input_get_mute(s->sink_input);
376
377             if (s->is_muted != new_muted) {
378                 s->is_muted = new_muted;
379
380                 pa_assert_se(signal = dbus_message_new_signal(s->path,
381                                                               PA_DBUSIFACE_STREAM_INTERFACE,
382                                                               signals[SIGNAL_MUTE_UPDATED].name));
383                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->is_muted, DBUS_TYPE_INVALID));
384
385                 pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
386                 dbus_message_unref(signal);
387                 signal = NULL;
388             }
389         }
390
391         new_proplist = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->proplist : s->source_output->proplist;
392
393         if (!pa_proplist_equal(s->proplist, new_proplist)) {
394             DBusMessageIter msg_iter;
395
396             pa_proplist_update(s->proplist, PA_UPDATE_SET, new_proplist);
397
398             pa_assert_se(signal = dbus_message_new_signal(s->path,
399                                                           PA_DBUSIFACE_STREAM_INTERFACE,
400                                                           signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
401             dbus_message_iter_init_append(signal, &msg_iter);
402             pa_dbus_append_proplist(&msg_iter, s->proplist);
403
404             pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
405             dbus_message_unref(signal);
406             signal = NULL;
407         }
408     }
409 }
410
411 pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) {
412     pa_dbusiface_stream *s;
413
414     pa_assert(core);
415     pa_assert(sink_input);
416
417     s = pa_xnew(pa_dbusiface_stream, 1);
418     s->sink_input = pa_sink_input_ref(sink_input);
419     s->type = STREAM_TYPE_PLAYBACK;
420     s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index);
421     pa_sink_input_get_volume(sink_input, &s->volume, TRUE);
422     s->is_muted = pa_sink_input_get_mute(sink_input);
423     s->proplist = pa_proplist_copy(sink_input->proplist);
424     s->dbus_protocol = pa_dbus_protocol_get(sink_input->core);
425     s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s);
426
427     pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0);
428
429     return s;
430 }
431
432 pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_source_output *source_output) {
433     pa_dbusiface_stream *s;
434
435     pa_assert(core);
436     pa_assert(source_output);
437
438     s = pa_xnew(pa_dbusiface_stream, 1);
439     s->source_output = pa_source_output_ref(source_output);
440     s->type = STREAM_TYPE_RECORD;
441     s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, RECORD_OBJECT_NAME, source_output->index);
442     pa_cvolume_init(&s->volume);
443     s->is_muted = FALSE;
444     s->proplist = pa_proplist_copy(source_output->proplist);
445     s->dbus_protocol = pa_dbus_protocol_get(source_output->core);
446     s->subscription = pa_subscription_new(source_output->core, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscription_cb, s);
447
448     pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0);
449
450     return s;
451 }
452
453 void pa_dbusiface_stream_free(pa_dbusiface_stream *s) {
454     pa_assert(s);
455
456     pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, stream_interface_info.name) >= 0);
457
458     if (s->type == STREAM_TYPE_PLAYBACK)
459         pa_sink_input_unref(s->sink_input);
460     else
461         pa_source_output_unref(s->source_output);
462
463     pa_proplist_free(s->proplist);
464     pa_dbus_protocol_unref(s->dbus_protocol);
465     pa_subscription_free(s->subscription);
466
467     pa_xfree(s->path);
468     pa_xfree(s);
469 }
470
471 const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s) {
472     pa_assert(s);
473
474     return s->path;
475 }