• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, see <http://www.gnu.org/licenses/>.
19 ***/
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <pulsecore/core-util.h>
26 #include <pulsecore/dbus-util.h>
27 #include <pulsecore/protocol-dbus.h>
28 
29 #include "iface-stream.h"
30 
31 #define PLAYBACK_OBJECT_NAME "playback_stream"
32 #define RECORD_OBJECT_NAME "record_stream"
33 
34 enum stream_type {
35     STREAM_TYPE_PLAYBACK,
36     STREAM_TYPE_RECORD
37 };
38 
39 struct pa_dbusiface_stream {
40     pa_dbusiface_core *core;
41 
42     union {
43         pa_sink_input *sink_input;
44         pa_source_output *source_output;
45     };
46     enum stream_type type;
47     char *path;
48     union {
49         pa_sink *sink;
50         pa_source *source;
51     };
52     uint32_t sample_rate;
53     pa_cvolume volume;
54     dbus_bool_t mute;
55     pa_proplist *proplist;
56 
57     bool has_volume;
58 
59     pa_dbus_protocol *dbus_protocol;
60     pa_hook_slot *send_event_slot;
61     pa_hook_slot *move_finish_slot;
62     pa_hook_slot *volume_changed_slot;
63     pa_hook_slot *mute_changed_slot;
64     pa_hook_slot *proplist_changed_slot;
65     pa_hook_slot *state_changed_slot;
66 };
67 
68 static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
69 static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
70 static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
71 static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata);
72 static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
73 static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
74 static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
75 static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
76 static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
77 static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
78 static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
79 static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
80 static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
81 static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
82 static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata);
83 static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
84 
85 static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
86 
87 static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata);
88 static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);
89 
90 enum property_handler_index {
91     PROPERTY_HANDLER_INDEX,
92     PROPERTY_HANDLER_DRIVER,
93     PROPERTY_HANDLER_OWNER_MODULE,
94     PROPERTY_HANDLER_CLIENT,
95     PROPERTY_HANDLER_DEVICE,
96     PROPERTY_HANDLER_SAMPLE_FORMAT,
97     PROPERTY_HANDLER_SAMPLE_RATE,
98     PROPERTY_HANDLER_CHANNELS,
99     PROPERTY_HANDLER_VOLUME,
100     PROPERTY_HANDLER_MUTE,
101     PROPERTY_HANDLER_BUFFER_LATENCY,
102     PROPERTY_HANDLER_DEVICE_LATENCY,
103     PROPERTY_HANDLER_RESAMPLE_METHOD,
104     PROPERTY_HANDLER_PROPERTY_LIST,
105     PROPERTY_HANDLER_MAX
106 };
107 
108 static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
109     [PROPERTY_HANDLER_INDEX]           = { .property_name = "Index",          .type = "u",      .get_cb = handle_get_index,           .set_cb = NULL },
110     [PROPERTY_HANDLER_DRIVER]          = { .property_name = "Driver",         .type = "s",      .get_cb = handle_get_driver,          .set_cb = NULL },
111     [PROPERTY_HANDLER_OWNER_MODULE]    = { .property_name = "OwnerModule",    .type = "o",      .get_cb = handle_get_owner_module,    .set_cb = NULL },
112     [PROPERTY_HANDLER_CLIENT]          = { .property_name = "Client",         .type = "o",      .get_cb = handle_get_client,          .set_cb = NULL },
113     [PROPERTY_HANDLER_DEVICE]          = { .property_name = "Device",         .type = "o",      .get_cb = handle_get_device,          .set_cb = NULL },
114     [PROPERTY_HANDLER_SAMPLE_FORMAT]   = { .property_name = "SampleFormat",   .type = "u",      .get_cb = handle_get_sample_format,   .set_cb = NULL },
115     [PROPERTY_HANDLER_SAMPLE_RATE]     = { .property_name = "SampleRate",     .type = "u",      .get_cb = handle_get_sample_rate,     .set_cb = NULL },
116     [PROPERTY_HANDLER_CHANNELS]        = { .property_name = "Channels",       .type = "au",     .get_cb = handle_get_channels,        .set_cb = NULL },
117     [PROPERTY_HANDLER_VOLUME]          = { .property_name = "Volume",         .type = "au",     .get_cb = handle_get_volume,          .set_cb = handle_set_volume },
118     [PROPERTY_HANDLER_MUTE]            = { .property_name = "Mute",           .type = "b",      .get_cb = handle_get_mute,            .set_cb = handle_set_mute },
119     [PROPERTY_HANDLER_BUFFER_LATENCY]  = { .property_name = "BufferLatency",  .type = "t",      .get_cb = handle_get_buffer_latency,  .set_cb = NULL },
120     [PROPERTY_HANDLER_DEVICE_LATENCY]  = { .property_name = "DeviceLatency",  .type = "t",      .get_cb = handle_get_device_latency,  .set_cb = NULL },
121     [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s",      .get_cb = handle_get_resample_method, .set_cb = NULL },
122     [PROPERTY_HANDLER_PROPERTY_LIST]   = { .property_name = "PropertyList",   .type = "a{say}", .get_cb = handle_get_property_list,   .set_cb = NULL }
123 };
124 
125 enum method_handler_index {
126     METHOD_HANDLER_MOVE,
127     METHOD_HANDLER_KILL,
128     METHOD_HANDLER_MAX
129 };
130 
131 static pa_dbus_arg_info move_args[] = { { "device", "o", "in" } };
132 
133 static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
134     [METHOD_HANDLER_MOVE] = {
135         .method_name = "Move",
136         .arguments = move_args,
137         .n_arguments = sizeof(move_args) / sizeof(pa_dbus_arg_info),
138         .receive_cb = handle_move },
139     [METHOD_HANDLER_KILL] = {
140         .method_name = "Kill",
141         .arguments = NULL,
142         .n_arguments = 0,
143         .receive_cb = handle_kill }
144 };
145 
146 enum signal_index {
147     SIGNAL_DEVICE_UPDATED,
148     SIGNAL_SAMPLE_RATE_UPDATED,
149     SIGNAL_VOLUME_UPDATED,
150     SIGNAL_MUTE_UPDATED,
151     SIGNAL_PROPERTY_LIST_UPDATED,
152     SIGNAL_STREAM_EVENT,
153     SIGNAL_MAX
154 };
155 
156 static pa_dbus_arg_info device_updated_args[]        = { { "device",        "o",      NULL } };
157 static pa_dbus_arg_info sample_rate_updated_args[]   = { { "sample_rate",   "u",      NULL } };
158 static pa_dbus_arg_info volume_updated_args[]        = { { "volume",        "au",     NULL } };
159 static pa_dbus_arg_info mute_updated_args[]          = { { "muted",         "b",      NULL } };
160 static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
161 static pa_dbus_arg_info stream_event_args[]          = { { "name",          "s",      NULL }, { "property_list", "a{say}", NULL } };
162 
163 static pa_dbus_signal_info signals[SIGNAL_MAX] = {
164     [SIGNAL_DEVICE_UPDATED]        = { .name = "DeviceUpdated",       .arguments = device_updated_args,        .n_arguments = 1 },
165     [SIGNAL_SAMPLE_RATE_UPDATED]   = { .name = "SampleRateUpdated",   .arguments = sample_rate_updated_args,   .n_arguments = 1 },
166     [SIGNAL_VOLUME_UPDATED]        = { .name = "VolumeUpdated",       .arguments = volume_updated_args,        .n_arguments = 1 },
167     [SIGNAL_MUTE_UPDATED]          = { .name = "MuteUpdated",         .arguments = mute_updated_args,          .n_arguments = 1 },
168     [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 },
169     [SIGNAL_STREAM_EVENT]          = { .name = "StreamEvent",         .arguments = stream_event_args,          .n_arguments = sizeof(stream_event_args) / sizeof(pa_dbus_arg_info) }
170 };
171 
172 static pa_dbus_interface_info stream_interface_info = {
173     .name = PA_DBUSIFACE_STREAM_INTERFACE,
174     .method_handlers = method_handlers,
175     .n_method_handlers = METHOD_HANDLER_MAX,
176     .property_handlers = property_handlers,
177     .n_property_handlers = PROPERTY_HANDLER_MAX,
178     .get_all_properties_cb = handle_get_all,
179     .signals = signals,
180     .n_signals = SIGNAL_MAX
181 };
182 
handle_get_index(DBusConnection * conn,DBusMessage * msg,void * userdata)183 static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
184     pa_dbusiface_stream *s = userdata;
185     dbus_uint32_t idx;
186 
187     pa_assert(conn);
188     pa_assert(msg);
189     pa_assert(s);
190 
191     idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index;
192 
193     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
194 }
195 
196 /* The returned string has to be freed with pa_xfree() by the caller. */
stream_to_string(pa_dbusiface_stream * s)197 static char *stream_to_string(pa_dbusiface_stream *s) {
198     if (s->type == STREAM_TYPE_PLAYBACK)
199         return pa_sprintf_malloc("Playback stream %u", (unsigned) s->sink_input->index);
200     else
201         return pa_sprintf_malloc("Record stream %u", (unsigned) s->source_output->index);
202 }
203 
handle_get_driver(DBusConnection * conn,DBusMessage * msg,void * userdata)204 static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) {
205     pa_dbusiface_stream *s = userdata;
206     const char *driver = NULL;
207 
208     pa_assert(conn);
209     pa_assert(msg);
210     pa_assert(s);
211 
212     driver = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->driver : s->source_output->driver;
213 
214     if (!driver) {
215         char *str = stream_to_string(s);
216 
217         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have a driver.", str);
218         pa_xfree(str);
219 
220         return;
221     }
222 
223     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &driver);
224 }
225 
handle_get_owner_module(DBusConnection * conn,DBusMessage * msg,void * userdata)226 static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
227     pa_dbusiface_stream *s = userdata;
228     pa_module *owner_module = NULL;
229     const char *object_path = NULL;
230 
231     pa_assert(conn);
232     pa_assert(msg);
233     pa_assert(s);
234 
235     owner_module = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->module : s->source_output->module;
236 
237     if (!owner_module) {
238         char *str = stream_to_string(s);
239 
240         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have an owner module.", str);
241         pa_xfree(str);
242 
243         return;
244     }
245 
246     object_path = pa_dbusiface_core_get_module_path(s->core, owner_module);
247 
248     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
249 }
250 
handle_get_client(DBusConnection * conn,DBusMessage * msg,void * userdata)251 static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata) {
252     pa_dbusiface_stream *s = userdata;
253     pa_client *client = NULL;
254     const char *object_path = NULL;
255 
256     pa_assert(conn);
257     pa_assert(msg);
258     pa_assert(s);
259 
260     client = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->client : s->source_output->client;
261 
262     if (!client) {
263         char *str = stream_to_string(s);
264 
265         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s isn't associated to any client.", str);
266         pa_xfree(str);
267 
268         return;
269     }
270 
271     object_path = pa_dbusiface_core_get_client_path(s->core, client);
272 
273     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
274 }
275 
handle_get_device(DBusConnection * conn,DBusMessage * msg,void * userdata)276 static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
277     pa_dbusiface_stream *s = userdata;
278     const char *device = NULL;
279 
280     pa_assert(conn);
281     pa_assert(msg);
282     pa_assert(s);
283 
284     if (s->type == STREAM_TYPE_PLAYBACK)
285         device = pa_dbusiface_core_get_sink_path(s->core, s->sink);
286     else
287         device = pa_dbusiface_core_get_source_path(s->core, s->source);
288 
289     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &device);
290 }
291 
handle_get_sample_format(DBusConnection * conn,DBusMessage * msg,void * userdata)292 static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
293     pa_dbusiface_stream *s = userdata;
294     dbus_uint32_t sample_format = 0;
295 
296     pa_assert(conn);
297     pa_assert(msg);
298     pa_assert(s);
299 
300     sample_format = (s->type == STREAM_TYPE_PLAYBACK)
301                     ? s->sink_input->sample_spec.format
302                     : s->source_output->sample_spec.format;
303 
304     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format);
305 }
306 
handle_get_sample_rate(DBusConnection * conn,DBusMessage * msg,void * userdata)307 static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
308     pa_dbusiface_stream *s = userdata;
309 
310     pa_assert(conn);
311     pa_assert(msg);
312     pa_assert(s);
313 
314     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &s->sample_rate);
315 }
316 
handle_get_channels(DBusConnection * conn,DBusMessage * msg,void * userdata)317 static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
318     pa_dbusiface_stream *s = userdata;
319     pa_channel_map *channel_map = NULL;
320     dbus_uint32_t channels[PA_CHANNELS_MAX];
321     unsigned i = 0;
322 
323     pa_assert(conn);
324     pa_assert(msg);
325     pa_assert(s);
326 
327     channel_map = (s->type == STREAM_TYPE_PLAYBACK) ? &s->sink_input->channel_map : &s->source_output->channel_map;
328 
329     for (i = 0; i < channel_map->channels; ++i)
330         channels[i] = channel_map->map[i];
331 
332     pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, channel_map->channels);
333 }
334 
handle_get_volume(DBusConnection * conn,DBusMessage * msg,void * userdata)335 static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
336     pa_dbusiface_stream *s = userdata;
337     dbus_uint32_t volume[PA_CHANNELS_MAX];
338     unsigned i = 0;
339 
340     pa_assert(conn);
341     pa_assert(msg);
342     pa_assert(s);
343 
344     if (!s->has_volume) {
345         char *str = stream_to_string(s);
346 
347         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have volume.", str);
348         pa_xfree(str);
349 
350         return;
351     }
352 
353     for (i = 0; i < s->volume.channels; ++i)
354         volume[i] = s->volume.values[i];
355 
356     pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, s->volume.channels);
357 }
358 
handle_set_volume(DBusConnection * conn,DBusMessage * msg,DBusMessageIter * iter,void * userdata)359 static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
360     pa_dbusiface_stream *s = userdata;
361     bool volume_writable = true;
362     DBusMessageIter array_iter;
363     int stream_channels = 0;
364     dbus_uint32_t *volume = NULL;
365     int n_volume_entries = 0;
366     pa_cvolume new_vol;
367     int i = 0;
368 
369     pa_assert(conn);
370     pa_assert(msg);
371     pa_assert(iter);
372     pa_assert(s);
373 
374     volume_writable = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->volume_writable : false;
375 
376     if (!s->has_volume || !volume_writable) {
377         char *str = stream_to_string(s);
378 
379         if (!s->has_volume)
380             pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have volume.", str);
381         else if (!volume_writable)
382             pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "%s has read-only volume.", str);
383         pa_xfree(str);
384 
385         return;
386     }
387 
388     stream_channels = s->sink_input->channel_map.channels;
389 
390     dbus_message_iter_recurse(iter, &array_iter);
391     dbus_message_iter_get_fixed_array(&array_iter, &volume, &n_volume_entries);
392 
393     if (n_volume_entries != stream_channels && n_volume_entries != 1) {
394         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
395                            "Expected %u volume entries, got %u.", stream_channels, n_volume_entries);
396         return;
397     }
398 
399     pa_cvolume_init(&new_vol);
400     new_vol.channels = n_volume_entries;
401 
402     for (i = 0; i < n_volume_entries; ++i) {
403         if (!PA_VOLUME_IS_VALID(volume[i])) {
404             pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume: %u", volume[i]);
405             return;
406         }
407         new_vol.values[i] = volume[i];
408     }
409 
410     pa_sink_input_set_volume(s->sink_input, &new_vol, true, true);
411 
412     pa_dbus_send_empty_reply(conn, msg);
413 }
414 
handle_get_mute(DBusConnection * conn,DBusMessage * msg,void * userdata)415 static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
416     pa_dbusiface_stream *s = userdata;
417 
418     pa_assert(conn);
419     pa_assert(msg);
420     pa_assert(s);
421 
422     if (s->type == STREAM_TYPE_RECORD) {
423         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute.");
424         return;
425     }
426 
427     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->mute);
428 }
429 
handle_set_mute(DBusConnection * conn,DBusMessage * msg,DBusMessageIter * iter,void * userdata)430 static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
431     pa_dbusiface_stream *s = userdata;
432     dbus_bool_t mute = FALSE;
433 
434     pa_assert(conn);
435     pa_assert(msg);
436     pa_assert(iter);
437     pa_assert(s);
438 
439     dbus_message_iter_get_basic(iter, &mute);
440 
441     if (s->type == STREAM_TYPE_RECORD) {
442         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute.");
443         return;
444     }
445 
446     pa_sink_input_set_mute(s->sink_input, mute, true);
447 
448     pa_dbus_send_empty_reply(conn, msg);
449 }
450 
handle_get_buffer_latency(DBusConnection * conn,DBusMessage * msg,void * userdata)451 static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
452     pa_dbusiface_stream *s = userdata;
453     dbus_uint64_t buffer_latency = 0;
454 
455     pa_assert(conn);
456     pa_assert(msg);
457     pa_assert(s);
458 
459     if (s->type == STREAM_TYPE_PLAYBACK)
460         buffer_latency = pa_sink_input_get_latency(s->sink_input, NULL);
461     else
462         buffer_latency = pa_source_output_get_latency(s->source_output, NULL);
463 
464     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &buffer_latency);
465 }
466 
handle_get_device_latency(DBusConnection * conn,DBusMessage * msg,void * userdata)467 static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
468     pa_dbusiface_stream *s = userdata;
469     dbus_uint64_t device_latency = 0;
470 
471     pa_assert(conn);
472     pa_assert(msg);
473     pa_assert(s);
474 
475     if (s->type == STREAM_TYPE_PLAYBACK)
476         pa_sink_input_get_latency(s->sink_input, &device_latency);
477     else
478         pa_source_output_get_latency(s->source_output, &device_latency);
479 
480     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &device_latency);
481 }
482 
handle_get_resample_method(DBusConnection * conn,DBusMessage * msg,void * userdata)483 static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata) {
484     pa_dbusiface_stream *s = userdata;
485     const char *resample_method = NULL;
486 
487     pa_assert(conn);
488     pa_assert(msg);
489     pa_assert(s);
490 
491     if (s->type == STREAM_TYPE_PLAYBACK)
492         resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method);
493     else
494         resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method);
495 
496     if (!resample_method)
497         resample_method = "";
498 
499     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &resample_method);
500 }
501 
handle_get_property_list(DBusConnection * conn,DBusMessage * msg,void * userdata)502 static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
503     pa_dbusiface_stream *s = userdata;
504 
505     pa_assert(conn);
506     pa_assert(msg);
507     pa_assert(s);
508 
509     pa_dbus_send_proplist_variant_reply(conn, msg, s->proplist);
510 }
511 
handle_get_all(DBusConnection * conn,DBusMessage * msg,void * userdata)512 static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
513     pa_dbusiface_stream *s = userdata;
514     DBusMessage *reply = NULL;
515     DBusMessageIter msg_iter;
516     DBusMessageIter dict_iter;
517     dbus_uint32_t idx = 0;
518     const char *driver = NULL;
519     pa_module *owner_module = NULL;
520     const char *owner_module_path = NULL;
521     pa_client *client = NULL;
522     const char *client_path = NULL;
523     const char *device = NULL;
524     dbus_uint32_t sample_format = 0;
525     pa_channel_map *channel_map = NULL;
526     dbus_uint32_t channels[PA_CHANNELS_MAX];
527     dbus_uint32_t volume[PA_CHANNELS_MAX];
528     dbus_uint64_t buffer_latency = 0;
529     dbus_uint64_t device_latency = 0;
530     const char *resample_method = NULL;
531     unsigned i = 0;
532 
533     pa_assert(conn);
534     pa_assert(msg);
535     pa_assert(s);
536 
537     if (s->has_volume) {
538         for (i = 0; i < s->volume.channels; ++i)
539             volume[i] = s->volume.values[i];
540     }
541 
542     if (s->type == STREAM_TYPE_PLAYBACK) {
543         idx = s->sink_input->index;
544         driver = s->sink_input->driver;
545         owner_module = s->sink_input->module;
546         client = s->sink_input->client;
547         device = pa_dbusiface_core_get_sink_path(s->core, s->sink);
548         sample_format = s->sink_input->sample_spec.format;
549         channel_map = &s->sink_input->channel_map;
550         buffer_latency = pa_sink_input_get_latency(s->sink_input, &device_latency);
551         resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method);
552     } else {
553         idx = s->source_output->index;
554         driver = s->source_output->driver;
555         owner_module = s->source_output->module;
556         client = s->source_output->client;
557         device = pa_dbusiface_core_get_source_path(s->core, s->source);
558         sample_format = s->source_output->sample_spec.format;
559         channel_map = &s->source_output->channel_map;
560         buffer_latency = pa_source_output_get_latency(s->source_output, &device_latency);
561         resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method);
562     }
563     if (owner_module)
564         owner_module_path = pa_dbusiface_core_get_module_path(s->core, owner_module);
565     if (client)
566         client_path = pa_dbusiface_core_get_client_path(s->core, client);
567     for (i = 0; i < channel_map->channels; ++i)
568         channels[i] = channel_map->map[i];
569     if (!resample_method)
570         resample_method = "";
571 
572     pa_assert_se((reply = dbus_message_new_method_return(msg)));
573 
574     dbus_message_iter_init_append(reply, &msg_iter);
575     pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
576 
577     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
578 
579     if (driver)
580         pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &driver);
581 
582     if (owner_module)
583         pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module_path);
584 
585     if (client)
586         pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CLIENT].property_name, DBUS_TYPE_OBJECT_PATH, &client_path);
587 
588     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEVICE].property_name, DBUS_TYPE_OBJECT_PATH, &device);
589     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format);
590     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &s->sample_rate);
591     pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, channel_map->channels);
592 
593     if (s->has_volume) {
594         pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels);
595         pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &s->mute);
596     }
597 
598     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BUFFER_LATENCY].property_name, DBUS_TYPE_UINT64, &buffer_latency);
599     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEVICE_LATENCY].property_name, DBUS_TYPE_UINT64, &device_latency);
600     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RESAMPLE_METHOD].property_name, DBUS_TYPE_STRING, &resample_method);
601     pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist);
602 
603     pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
604     pa_assert_se(dbus_connection_send(conn, reply, NULL));
605     dbus_message_unref(reply);
606 }
607 
handle_move(DBusConnection * conn,DBusMessage * msg,void * userdata)608 static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata) {
609     pa_dbusiface_stream *s = userdata;
610     const char *device = NULL;
611 
612     pa_assert(conn);
613     pa_assert(msg);
614     pa_assert(s);
615 
616     pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, DBUS_TYPE_INVALID));
617 
618     if (s->type == STREAM_TYPE_PLAYBACK) {
619         pa_sink *sink = pa_dbusiface_core_get_sink(s->core, device);
620 
621         if (!sink) {
622             pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", device);
623             return;
624         }
625 
626         if (pa_sink_input_move_to(s->sink_input, sink, true) < 0) {
627             pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
628                                "Moving playback stream %u to sink %s failed.", s->sink_input->index, sink->name);
629             return;
630         }
631     } else {
632         pa_source *source = pa_dbusiface_core_get_source(s->core, device);
633 
634         if (!source) {
635             pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", device);
636             return;
637         }
638 
639         if (pa_source_output_move_to(s->source_output, source, true) < 0) {
640             pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
641                                "Moving record stream %u to source %s failed.", s->source_output->index, source->name);
642             return;
643         }
644     }
645 
646     pa_dbus_send_empty_reply(conn, msg);
647 }
648 
handle_kill(DBusConnection * conn,DBusMessage * msg,void * userdata)649 static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata) {
650     pa_dbusiface_stream *s = userdata;
651 
652     pa_assert(conn);
653     pa_assert(msg);
654     pa_assert(s);
655 
656     if (s->type == STREAM_TYPE_PLAYBACK)
657         pa_sink_input_kill(s->sink_input);
658     else
659         pa_source_output_kill(s->source_output);
660 
661     pa_dbus_send_empty_reply(conn, msg);
662 }
663 
check_and_signal_rate(pa_dbusiface_stream * s)664 static void check_and_signal_rate(pa_dbusiface_stream *s) {
665     DBusMessage *signal_msg = NULL;
666     uint32_t new_sample_rate = 0;
667 
668     pa_assert(s);
669 
670     new_sample_rate = (s->type == STREAM_TYPE_PLAYBACK)
671                       ? s->sink_input->sample_spec.rate
672                       : s->source_output->sample_spec.rate;
673 
674     if (s->sample_rate != new_sample_rate) {
675         s->sample_rate = new_sample_rate;
676 
677         pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
678                                                           PA_DBUSIFACE_STREAM_INTERFACE,
679                                                           signals[SIGNAL_SAMPLE_RATE_UPDATED].name));
680         pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_UINT32, &s->sample_rate, DBUS_TYPE_INVALID));
681 
682         pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
683         dbus_message_unref(signal_msg);
684     }
685 }
686 
move_finish_cb(void * hook_data,void * call_data,void * slot_data)687 static pa_hook_result_t move_finish_cb(void *hook_data, void *call_data, void *slot_data) {
688     pa_dbusiface_stream *s = slot_data;
689     const char *new_device_path = NULL;
690     DBusMessage *signal_msg = NULL;
691 
692     if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
693         (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
694         return PA_HOOK_OK;
695 
696     if (s->type == STREAM_TYPE_PLAYBACK) {
697         pa_sink *new_sink = s->sink_input->sink;
698 
699         if (s->sink != new_sink) {
700             pa_sink_unref(s->sink);
701             s->sink = pa_sink_ref(new_sink);
702 
703             new_device_path = pa_dbusiface_core_get_sink_path(s->core, new_sink);
704 
705             pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
706                                                               PA_DBUSIFACE_STREAM_INTERFACE,
707                                                               signals[SIGNAL_DEVICE_UPDATED].name));
708             pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID));
709 
710             pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
711             dbus_message_unref(signal_msg);
712         }
713     } else {
714         pa_source *new_source = s->source_output->source;
715 
716         if (s->source != new_source) {
717             pa_source_unref(s->source);
718             s->source = pa_source_ref(new_source);
719 
720             new_device_path = pa_dbusiface_core_get_source_path(s->core, new_source);
721 
722             pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
723                                                               PA_DBUSIFACE_STREAM_INTERFACE,
724                                                               signals[SIGNAL_DEVICE_UPDATED].name));
725             pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID));
726 
727             pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
728             dbus_message_unref(signal_msg);
729         }
730     }
731 
732     check_and_signal_rate(s);
733 
734     return PA_HOOK_OK;
735 }
736 
volume_changed_cb(void * hook_data,void * call_data,void * slot_data)737 static pa_hook_result_t volume_changed_cb(void *hook_data, void *call_data, void *slot_data) {
738     pa_dbusiface_stream *s = slot_data;
739     DBusMessage *signal_msg = NULL;
740     unsigned i = 0;
741 
742     if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
743         (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
744         return PA_HOOK_OK;
745 
746     if (s->type == STREAM_TYPE_PLAYBACK && s->has_volume) {
747         pa_cvolume new_volume;
748 
749         pa_sink_input_get_volume(s->sink_input, &new_volume, true);
750 
751         if (!pa_cvolume_equal(&s->volume, &new_volume)) {
752             dbus_uint32_t volume[PA_CHANNELS_MAX];
753             dbus_uint32_t *volume_ptr = volume;
754 
755             s->volume = new_volume;
756 
757             for (i = 0; i < s->volume.channels; ++i)
758                 volume[i] = s->volume.values[i];
759 
760             pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
761                                                               PA_DBUSIFACE_STREAM_INTERFACE,
762                                                               signals[SIGNAL_VOLUME_UPDATED].name));
763             pa_assert_se(dbus_message_append_args(signal_msg,
764                                                   DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels,
765                                                   DBUS_TYPE_INVALID));
766 
767             pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
768             dbus_message_unref(signal_msg);
769         }
770     }
771 
772     return PA_HOOK_OK;
773 }
774 
mute_changed_cb(void * hook_data,void * call_data,void * slot_data)775 static pa_hook_result_t mute_changed_cb(void *hook_data, void *call_data, void *slot_data) {
776     pa_dbusiface_stream *s = slot_data;
777     DBusMessage *signal_msg = NULL;
778 
779     if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
780         (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
781         return PA_HOOK_OK;
782 
783     if (s->type == STREAM_TYPE_PLAYBACK) {
784         bool new_mute = false;
785 
786         new_mute = s->sink_input->muted;
787 
788         if (s->mute != new_mute) {
789             s->mute = new_mute;
790 
791             pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
792                                                               PA_DBUSIFACE_STREAM_INTERFACE,
793                                                               signals[SIGNAL_MUTE_UPDATED].name));
794             pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_BOOLEAN, &s->mute, DBUS_TYPE_INVALID));
795 
796             pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
797             dbus_message_unref(signal_msg);
798             signal_msg = NULL;
799         }
800     }
801 
802     return PA_HOOK_OK;
803 }
804 
proplist_changed_cb(void * hook_data,void * call_data,void * slot_data)805 static pa_hook_result_t proplist_changed_cb(void *hook_data, void *call_data, void *slot_data) {
806     pa_dbusiface_stream *s = slot_data;
807     DBusMessage *signal_msg = NULL;
808     pa_proplist *new_proplist = NULL;
809 
810     if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
811         (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
812         return PA_HOOK_OK;
813 
814     new_proplist = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->proplist : s->source_output->proplist;
815 
816     if (!pa_proplist_equal(s->proplist, new_proplist)) {
817         DBusMessageIter msg_iter;
818 
819         pa_proplist_update(s->proplist, PA_UPDATE_SET, new_proplist);
820 
821         pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
822                                                           PA_DBUSIFACE_STREAM_INTERFACE,
823                                                           signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
824         dbus_message_iter_init_append(signal_msg, &msg_iter);
825         pa_dbus_append_proplist(&msg_iter, s->proplist);
826 
827         pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
828         dbus_message_unref(signal_msg);
829     }
830 
831     return PA_HOOK_OK;
832 }
833 
state_changed_cb(void * hook_data,void * call_data,void * slot_data)834 static pa_hook_result_t state_changed_cb(void *hook_data, void *call_data, void *slot_data) {
835     pa_dbusiface_stream *s = slot_data;
836 
837     pa_assert(s);
838 
839     if ((s->type == STREAM_TYPE_PLAYBACK && s->sink_input != call_data) ||
840         (s->type == STREAM_TYPE_RECORD && s->source_output != call_data))
841         return PA_HOOK_OK;
842 
843     check_and_signal_rate(s);
844 
845     return PA_HOOK_OK;
846 }
847 
send_event_cb(void * hook_data,void * call_data,void * slot_data)848 static pa_hook_result_t send_event_cb(void *hook_data, void *call_data, void *slot_data) {
849     pa_dbusiface_stream *s = slot_data;
850     DBusMessage *signal_msg = NULL;
851     DBusMessageIter msg_iter;
852     const char *name = NULL;
853     pa_proplist *property_list = NULL;
854 
855     pa_assert(call_data);
856     pa_assert(s);
857 
858     if (s->type == STREAM_TYPE_PLAYBACK) {
859         pa_sink_input_send_event_hook_data *data = call_data;
860 
861         if (data->sink_input != s->sink_input)
862             return PA_HOOK_OK;
863 
864         name = data->event;
865         property_list = data->data;
866     } else {
867         pa_source_output_send_event_hook_data *data = call_data;
868 
869         if (data->source_output != s->source_output)
870             return PA_HOOK_OK;
871 
872         name = data->event;
873         property_list = data->data;
874     }
875 
876     pa_assert_se(signal_msg = dbus_message_new_signal(s->path,
877                                                       PA_DBUSIFACE_STREAM_INTERFACE,
878                                                       signals[SIGNAL_STREAM_EVENT].name));
879     dbus_message_iter_init_append(signal_msg, &msg_iter);
880     pa_assert_se(dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name));
881     pa_dbus_append_proplist(&msg_iter, property_list);
882 
883     pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg);
884     dbus_message_unref(signal_msg);
885 
886     return PA_HOOK_OK;
887 }
888 
pa_dbusiface_stream_new_playback(pa_dbusiface_core * core,pa_sink_input * sink_input)889 pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) {
890     pa_dbusiface_stream *s;
891 
892     pa_assert(core);
893     pa_assert(sink_input);
894 
895     s = pa_xnew(pa_dbusiface_stream, 1);
896     s->core = core;
897     s->sink_input = pa_sink_input_ref(sink_input);
898     s->type = STREAM_TYPE_PLAYBACK;
899     s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index);
900     s->sink = pa_sink_ref(sink_input->sink);
901     s->sample_rate = sink_input->sample_spec.rate;
902     s->has_volume = pa_sink_input_is_volume_readable(sink_input);
903 
904     if (s->has_volume)
905         pa_sink_input_get_volume(sink_input, &s->volume, true);
906     else
907         pa_cvolume_init(&s->volume);
908 
909     s->mute = sink_input->muted;
910     s->proplist = pa_proplist_copy(sink_input->proplist);
911     s->dbus_protocol = pa_dbus_protocol_get(sink_input->core);
912     s->send_event_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT],
913                                          PA_HOOK_NORMAL,
914                                          send_event_cb,
915                                          s);
916     s->move_finish_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH],
917                                           PA_HOOK_NORMAL, move_finish_cb, s);
918     s->volume_changed_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED],
919                                              PA_HOOK_NORMAL, volume_changed_cb, s);
920     s->mute_changed_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED],
921                                            PA_HOOK_NORMAL, mute_changed_cb, s);
922     s->proplist_changed_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED],
923                                                PA_HOOK_NORMAL, proplist_changed_cb, s);
924     s->state_changed_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED],
925                                             PA_HOOK_NORMAL, state_changed_cb, s);
926 
927     pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0);
928 
929     return s;
930 }
931 
pa_dbusiface_stream_new_record(pa_dbusiface_core * core,pa_source_output * source_output)932 pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_source_output *source_output) {
933     pa_dbusiface_stream *s;
934 
935     pa_assert(core);
936     pa_assert(source_output);
937 
938     s = pa_xnew(pa_dbusiface_stream, 1);
939     s->core = core;
940     s->source_output = pa_source_output_ref(source_output);
941     s->type = STREAM_TYPE_RECORD;
942     s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, RECORD_OBJECT_NAME, source_output->index);
943     s->source = pa_source_ref(source_output->source);
944     s->sample_rate = source_output->sample_spec.rate;
945     pa_cvolume_init(&s->volume);
946     s->mute = false;
947     s->proplist = pa_proplist_copy(source_output->proplist);
948     s->has_volume = false;
949     s->dbus_protocol = pa_dbus_protocol_get(source_output->core);
950     s->send_event_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT],
951                                          PA_HOOK_NORMAL,
952                                          send_event_cb,
953                                          s);
954     s->move_finish_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH],
955                                           PA_HOOK_NORMAL, move_finish_cb, s);
956     s->volume_changed_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED],
957                                              PA_HOOK_NORMAL, volume_changed_cb, s);
958     s->mute_changed_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED],
959                                            PA_HOOK_NORMAL, mute_changed_cb, s);
960     s->proplist_changed_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED],
961                                                PA_HOOK_NORMAL, proplist_changed_cb, s);
962     s->state_changed_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED],
963                                             PA_HOOK_NORMAL, state_changed_cb, s);
964 
965     pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0);
966 
967     return s;
968 }
969 
pa_dbusiface_stream_free(pa_dbusiface_stream * s)970 void pa_dbusiface_stream_free(pa_dbusiface_stream *s) {
971     pa_assert(s);
972 
973     pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, stream_interface_info.name) >= 0);
974 
975     if (s->type == STREAM_TYPE_PLAYBACK) {
976         pa_sink_input_unref(s->sink_input);
977         pa_sink_unref(s->sink);
978     } else {
979         pa_source_output_unref(s->source_output);
980         pa_source_unref(s->source);
981     }
982 
983     pa_proplist_free(s->proplist);
984     pa_dbus_protocol_unref(s->dbus_protocol);
985     pa_hook_slot_free(s->send_event_slot);
986     pa_hook_slot_free(s->move_finish_slot);
987     pa_hook_slot_free(s->volume_changed_slot);
988     pa_hook_slot_free(s->mute_changed_slot);
989     pa_hook_slot_free(s->proplist_changed_slot);
990     pa_hook_slot_free(s->state_changed_slot);
991 
992     pa_xfree(s->path);
993     pa_xfree(s);
994 }
995 
pa_dbusiface_stream_get_path(pa_dbusiface_stream * s)996 const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s) {
997     pa_assert(s);
998 
999     return s->path;
1000 }
1001