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