2 This file is part of PulseAudio.
4 Copyright 2009 Tanu Kaskinen
5 Copyright 2009 Vincent Filali-Ansary <filali.v@azurdigitalnetworks.net>
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 #include <pulsecore/core-util.h>
28 #include <pulsecore/dbus-util.h>
29 #include <pulsecore/protocol-dbus.h>
31 #include "iface-stream.h"
33 #define PLAYBACK_OBJECT_NAME "playback_stream"
34 #define RECORD_OBJECT_NAME "record_stream"
41 struct pa_dbusiface_stream
{
42 pa_dbusiface_core
*core
;
45 pa_sink_input
*sink_input
;
46 pa_source_output
*source_output
;
48 enum stream_type type
;
57 pa_proplist
*proplist
;
61 pa_dbus_protocol
*dbus_protocol
;
62 pa_subscription
*subscription
;
63 pa_hook_slot
*send_event_slot
;
66 static void handle_get_index(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
67 static void handle_get_driver(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
68 static void handle_get_owner_module(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
69 static void handle_get_client(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
70 static void handle_get_device(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
71 static void handle_get_sample_format(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
72 static void handle_get_sample_rate(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
73 static void handle_get_channels(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
74 static void handle_get_volume(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
75 static void handle_set_volume(DBusConnection
*conn
, DBusMessage
*msg
, DBusMessageIter
*iter
, void *userdata
);
76 static void handle_get_mute(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
77 static void handle_set_mute(DBusConnection
*conn
, DBusMessage
*msg
, DBusMessageIter
*iter
, void *userdata
);
78 static void handle_get_buffer_latency(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
79 static void handle_get_device_latency(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
80 static void handle_get_resample_method(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
81 static void handle_get_property_list(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
83 static void handle_get_all(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
85 static void handle_move(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
86 static void handle_kill(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
88 enum property_handler_index
{
89 PROPERTY_HANDLER_INDEX
,
90 PROPERTY_HANDLER_DRIVER
,
91 PROPERTY_HANDLER_OWNER_MODULE
,
92 PROPERTY_HANDLER_CLIENT
,
93 PROPERTY_HANDLER_DEVICE
,
94 PROPERTY_HANDLER_SAMPLE_FORMAT
,
95 PROPERTY_HANDLER_SAMPLE_RATE
,
96 PROPERTY_HANDLER_CHANNELS
,
97 PROPERTY_HANDLER_VOLUME
,
98 PROPERTY_HANDLER_MUTE
,
99 PROPERTY_HANDLER_BUFFER_LATENCY
,
100 PROPERTY_HANDLER_DEVICE_LATENCY
,
101 PROPERTY_HANDLER_RESAMPLE_METHOD
,
102 PROPERTY_HANDLER_PROPERTY_LIST
,
106 static pa_dbus_property_handler property_handlers
[PROPERTY_HANDLER_MAX
] = {
107 [PROPERTY_HANDLER_INDEX
] = { .property_name
= "Index", .type
= "u", .get_cb
= handle_get_index
, .set_cb
= NULL
},
108 [PROPERTY_HANDLER_DRIVER
] = { .property_name
= "Driver", .type
= "s", .get_cb
= handle_get_driver
, .set_cb
= NULL
},
109 [PROPERTY_HANDLER_OWNER_MODULE
] = { .property_name
= "OwnerModule", .type
= "o", .get_cb
= handle_get_owner_module
, .set_cb
= NULL
},
110 [PROPERTY_HANDLER_CLIENT
] = { .property_name
= "Client", .type
= "o", .get_cb
= handle_get_client
, .set_cb
= NULL
},
111 [PROPERTY_HANDLER_DEVICE
] = { .property_name
= "Device", .type
= "o", .get_cb
= handle_get_device
, .set_cb
= NULL
},
112 [PROPERTY_HANDLER_SAMPLE_FORMAT
] = { .property_name
= "SampleFormat", .type
= "u", .get_cb
= handle_get_sample_format
, .set_cb
= NULL
},
113 [PROPERTY_HANDLER_SAMPLE_RATE
] = { .property_name
= "SampleRate", .type
= "u", .get_cb
= handle_get_sample_rate
, .set_cb
= NULL
},
114 [PROPERTY_HANDLER_CHANNELS
] = { .property_name
= "Channels", .type
= "au", .get_cb
= handle_get_channels
, .set_cb
= NULL
},
115 [PROPERTY_HANDLER_VOLUME
] = { .property_name
= "Volume", .type
= "au", .get_cb
= handle_get_volume
, .set_cb
= handle_set_volume
},
116 [PROPERTY_HANDLER_MUTE
] = { .property_name
= "Mute", .type
= "b", .get_cb
= handle_get_mute
, .set_cb
= handle_set_mute
},
117 [PROPERTY_HANDLER_BUFFER_LATENCY
] = { .property_name
= "BufferLatency", .type
= "t", .get_cb
= handle_get_buffer_latency
, .set_cb
= NULL
},
118 [PROPERTY_HANDLER_DEVICE_LATENCY
] = { .property_name
= "DeviceLatency", .type
= "t", .get_cb
= handle_get_device_latency
, .set_cb
= NULL
},
119 [PROPERTY_HANDLER_RESAMPLE_METHOD
] = { .property_name
= "ResampleMethod", .type
= "s", .get_cb
= handle_get_resample_method
, .set_cb
= NULL
},
120 [PROPERTY_HANDLER_PROPERTY_LIST
] = { .property_name
= "PropertyList", .type
= "a{say}", .get_cb
= handle_get_property_list
, .set_cb
= NULL
}
123 enum method_handler_index
{
129 static pa_dbus_arg_info move_args
[] = { { "device", "o", "in" } };
131 static pa_dbus_method_handler method_handlers
[METHOD_HANDLER_MAX
] = {
132 [METHOD_HANDLER_MOVE
] = {
133 .method_name
= "Move",
134 .arguments
= move_args
,
135 .n_arguments
= sizeof(move_args
) / sizeof(pa_dbus_arg_info
),
136 .receive_cb
= handle_move
},
137 [METHOD_HANDLER_KILL
] = {
138 .method_name
= "Kill",
141 .receive_cb
= handle_kill
}
145 SIGNAL_DEVICE_UPDATED
,
146 SIGNAL_SAMPLE_RATE_UPDATED
,
147 SIGNAL_VOLUME_UPDATED
,
149 SIGNAL_PROPERTY_LIST_UPDATED
,
154 static pa_dbus_arg_info device_updated_args
[] = { { "device", "o", NULL
} };
155 static pa_dbus_arg_info sample_rate_updated_args
[] = { { "sample_rate", "u", NULL
} };
156 static pa_dbus_arg_info volume_updated_args
[] = { { "volume", "au", NULL
} };
157 static pa_dbus_arg_info mute_updated_args
[] = { { "muted", "b", NULL
} };
158 static pa_dbus_arg_info property_list_updated_args
[] = { { "property_list", "a{say}", NULL
} };
159 static pa_dbus_arg_info stream_event_args
[] = { { "name", "s", NULL
}, { "property_list", "a{say}", NULL
} };
161 static pa_dbus_signal_info signals
[SIGNAL_MAX
] = {
162 [SIGNAL_DEVICE_UPDATED
] = { .name
= "DeviceUpdated", .arguments
= device_updated_args
, .n_arguments
= 1 },
163 [SIGNAL_SAMPLE_RATE_UPDATED
] = { .name
= "SampleRateUpdated", .arguments
= sample_rate_updated_args
, .n_arguments
= 1 },
164 [SIGNAL_VOLUME_UPDATED
] = { .name
= "VolumeUpdated", .arguments
= volume_updated_args
, .n_arguments
= 1 },
165 [SIGNAL_MUTE_UPDATED
] = { .name
= "MuteUpdated", .arguments
= mute_updated_args
, .n_arguments
= 1 },
166 [SIGNAL_PROPERTY_LIST_UPDATED
] = { .name
= "PropertyListUpdated", .arguments
= property_list_updated_args
, .n_arguments
= 1 },
167 [SIGNAL_STREAM_EVENT
] = { .name
= "StreamEvent", .arguments
= stream_event_args
, .n_arguments
= sizeof(stream_event_args
) / sizeof(pa_dbus_arg_info
) }
170 static pa_dbus_interface_info stream_interface_info
= {
171 .name
= PA_DBUSIFACE_STREAM_INTERFACE
,
172 .method_handlers
= method_handlers
,
173 .n_method_handlers
= METHOD_HANDLER_MAX
,
174 .property_handlers
= property_handlers
,
175 .n_property_handlers
= PROPERTY_HANDLER_MAX
,
176 .get_all_properties_cb
= handle_get_all
,
178 .n_signals
= SIGNAL_MAX
181 static void handle_get_index(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
182 pa_dbusiface_stream
*s
= userdata
;
189 idx
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? s
->sink_input
->index
: s
->source_output
->index
;
191 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, &idx
);
194 /* The returned string has to be freed with pa_xfree() by the caller. */
195 static char *stream_to_string(pa_dbusiface_stream
*s
) {
196 if (s
->type
== STREAM_TYPE_PLAYBACK
)
197 return pa_sprintf_malloc("Playback stream %u", (unsigned) s
->sink_input
->index
);
199 return pa_sprintf_malloc("Record stream %u", (unsigned) s
->source_output
->index
);
202 static void handle_get_driver(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
203 pa_dbusiface_stream
*s
= userdata
;
204 const char *driver
= NULL
;
210 driver
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? s
->sink_input
->driver
: s
->source_output
->driver
;
213 char *str
= stream_to_string(s
);
215 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "%s doesn't have a driver.", str
);
221 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_STRING
, &driver
);
224 static void handle_get_owner_module(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
225 pa_dbusiface_stream
*s
= userdata
;
226 pa_module
*owner_module
= NULL
;
227 const char *object_path
= NULL
;
233 owner_module
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? s
->sink_input
->module
: s
->source_output
->module
;
236 char *str
= stream_to_string(s
);
238 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "%s doesn't have an owner module.", str
);
244 object_path
= pa_dbusiface_core_get_module_path(s
->core
, owner_module
);
246 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_OBJECT_PATH
, &object_path
);
249 static void handle_get_client(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
250 pa_dbusiface_stream
*s
= userdata
;
251 pa_client
*client
= NULL
;
252 const char *object_path
= NULL
;
258 client
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? s
->sink_input
->client
: s
->source_output
->client
;
261 char *str
= stream_to_string(s
);
263 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "%s isn't associated to any client.", str
);
269 object_path
= pa_dbusiface_core_get_client_path(s
->core
, client
);
271 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_OBJECT_PATH
, &object_path
);
274 static void handle_get_device(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
275 pa_dbusiface_stream
*s
= userdata
;
276 const char *device
= NULL
;
282 if (s
->type
== STREAM_TYPE_PLAYBACK
)
283 device
= pa_dbusiface_core_get_sink_path(s
->core
, s
->sink
);
285 device
= pa_dbusiface_core_get_source_path(s
->core
, s
->source
);
287 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_OBJECT_PATH
, &device
);
290 static void handle_get_sample_format(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
291 pa_dbusiface_stream
*s
= userdata
;
292 dbus_uint32_t sample_format
= 0;
298 sample_format
= (s
->type
== STREAM_TYPE_PLAYBACK
)
299 ? s
->sink_input
->sample_spec
.format
300 : s
->source_output
->sample_spec
.format
;
302 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, &sample_format
);
305 static void handle_get_sample_rate(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
306 pa_dbusiface_stream
*s
= userdata
;
312 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, &s
->sample_rate
);
315 static void handle_get_channels(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
316 pa_dbusiface_stream
*s
= userdata
;
317 pa_channel_map
*channel_map
= NULL
;
318 dbus_uint32_t channels
[PA_CHANNELS_MAX
];
325 channel_map
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? &s
->sink_input
->channel_map
: &s
->source_output
->channel_map
;
327 for (i
= 0; i
< channel_map
->channels
; ++i
)
328 channels
[i
] = channel_map
->map
[i
];
330 pa_dbus_send_basic_array_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, channels
, channel_map
->channels
);
333 static void handle_get_volume(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
334 pa_dbusiface_stream
*s
= userdata
;
335 dbus_uint32_t volume
[PA_CHANNELS_MAX
];
342 if (!s
->has_volume
) {
343 char *str
= stream_to_string(s
);
345 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "%s doesn't have volume.", str
);
351 for (i
= 0; i
< s
->volume
.channels
; ++i
)
352 volume
[i
] = s
->volume
.values
[i
];
354 pa_dbus_send_basic_array_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, volume
, s
->volume
.channels
);
357 static void handle_set_volume(DBusConnection
*conn
, DBusMessage
*msg
, DBusMessageIter
*iter
, void *userdata
) {
358 pa_dbusiface_stream
*s
= userdata
;
359 bool volume_writable
= true;
360 DBusMessageIter array_iter
;
361 int stream_channels
= 0;
362 dbus_uint32_t
*volume
= NULL
;
363 int n_volume_entries
= 0;
372 volume_writable
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? s
->sink_input
->volume_writable
: false;
374 if (!s
->has_volume
|| !volume_writable
) {
375 char *str
= stream_to_string(s
);
378 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "%s doesn't have volume.", str
);
379 else if (!volume_writable
)
380 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_ACCESS_DENIED
, "%s has read-only volume.", str
);
386 stream_channels
= s
->sink_input
->channel_map
.channels
;
388 dbus_message_iter_recurse(iter
, &array_iter
);
389 dbus_message_iter_get_fixed_array(&array_iter
, &volume
, &n_volume_entries
);
391 if (n_volume_entries
!= stream_channels
&& n_volume_entries
!= 1) {
392 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
,
393 "Expected %u volume entries, got %u.", stream_channels
, n_volume_entries
);
397 pa_cvolume_init(&new_vol
);
398 new_vol
.channels
= n_volume_entries
;
400 for (i
= 0; i
< n_volume_entries
; ++i
) {
401 if (!PA_VOLUME_IS_VALID(volume
[i
])) {
402 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
, "Invalid volume: %u", volume
[i
]);
405 new_vol
.values
[i
] = volume
[i
];
408 pa_sink_input_set_volume(s
->sink_input
, &new_vol
, true, true);
410 pa_dbus_send_empty_reply(conn
, msg
);
413 static void handle_get_mute(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
414 pa_dbusiface_stream
*s
= userdata
;
420 if (s
->type
== STREAM_TYPE_RECORD
) {
421 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "Record streams don't have mute.");
425 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_BOOLEAN
, &s
->mute
);
428 static void handle_set_mute(DBusConnection
*conn
, DBusMessage
*msg
, DBusMessageIter
*iter
, void *userdata
) {
429 pa_dbusiface_stream
*s
= userdata
;
430 dbus_bool_t mute
= FALSE
;
437 dbus_message_iter_get_basic(iter
, &mute
);
439 if (s
->type
== STREAM_TYPE_RECORD
) {
440 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "Record streams don't have mute.");
444 pa_sink_input_set_mute(s
->sink_input
, mute
, true);
446 pa_dbus_send_empty_reply(conn
, msg
);
449 static void handle_get_buffer_latency(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
450 pa_dbusiface_stream
*s
= userdata
;
451 dbus_uint64_t buffer_latency
= 0;
457 if (s
->type
== STREAM_TYPE_PLAYBACK
)
458 buffer_latency
= pa_sink_input_get_latency(s
->sink_input
, NULL
);
460 buffer_latency
= pa_source_output_get_latency(s
->source_output
, NULL
);
462 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT64
, &buffer_latency
);
465 static void handle_get_device_latency(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
466 pa_dbusiface_stream
*s
= userdata
;
467 dbus_uint64_t device_latency
= 0;
473 if (s
->type
== STREAM_TYPE_PLAYBACK
)
474 pa_sink_input_get_latency(s
->sink_input
, &device_latency
);
476 pa_source_output_get_latency(s
->source_output
, &device_latency
);
478 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT64
, &device_latency
);
481 static void handle_get_resample_method(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
482 pa_dbusiface_stream
*s
= userdata
;
483 const char *resample_method
= NULL
;
489 if (s
->type
== STREAM_TYPE_PLAYBACK
)
490 resample_method
= pa_resample_method_to_string(s
->sink_input
->actual_resample_method
);
492 resample_method
= pa_resample_method_to_string(s
->source_output
->actual_resample_method
);
494 if (!resample_method
)
495 resample_method
= "";
497 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_STRING
, &resample_method
);
500 static void handle_get_property_list(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
501 pa_dbusiface_stream
*s
= userdata
;
507 pa_dbus_send_proplist_variant_reply(conn
, msg
, s
->proplist
);
510 static void handle_get_all(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
511 pa_dbusiface_stream
*s
= userdata
;
512 DBusMessage
*reply
= NULL
;
513 DBusMessageIter msg_iter
;
514 DBusMessageIter dict_iter
;
515 dbus_uint32_t idx
= 0;
516 const char *driver
= NULL
;
517 pa_module
*owner_module
= NULL
;
518 const char *owner_module_path
= NULL
;
519 pa_client
*client
= NULL
;
520 const char *client_path
= NULL
;
521 const char *device
= NULL
;
522 dbus_uint32_t sample_format
= 0;
523 pa_channel_map
*channel_map
= NULL
;
524 dbus_uint32_t channels
[PA_CHANNELS_MAX
];
525 dbus_uint32_t volume
[PA_CHANNELS_MAX
];
526 dbus_uint64_t buffer_latency
= 0;
527 dbus_uint64_t device_latency
= 0;
528 const char *resample_method
= NULL
;
536 for (i
= 0; i
< s
->volume
.channels
; ++i
)
537 volume
[i
] = s
->volume
.values
[i
];
540 if (s
->type
== STREAM_TYPE_PLAYBACK
) {
541 idx
= s
->sink_input
->index
;
542 driver
= s
->sink_input
->driver
;
543 owner_module
= s
->sink_input
->module
;
544 client
= s
->sink_input
->client
;
545 device
= pa_dbusiface_core_get_sink_path(s
->core
, s
->sink
);
546 sample_format
= s
->sink_input
->sample_spec
.format
;
547 channel_map
= &s
->sink_input
->channel_map
;
548 buffer_latency
= pa_sink_input_get_latency(s
->sink_input
, &device_latency
);
549 resample_method
= pa_resample_method_to_string(s
->sink_input
->actual_resample_method
);
551 idx
= s
->source_output
->index
;
552 driver
= s
->source_output
->driver
;
553 owner_module
= s
->source_output
->module
;
554 client
= s
->source_output
->client
;
555 device
= pa_dbusiface_core_get_source_path(s
->core
, s
->source
);
556 sample_format
= s
->source_output
->sample_spec
.format
;
557 channel_map
= &s
->source_output
->channel_map
;
558 buffer_latency
= pa_source_output_get_latency(s
->source_output
, &device_latency
);
559 resample_method
= pa_resample_method_to_string(s
->source_output
->actual_resample_method
);
562 owner_module_path
= pa_dbusiface_core_get_module_path(s
->core
, owner_module
);
564 client_path
= pa_dbusiface_core_get_client_path(s
->core
, client
);
565 for (i
= 0; i
< channel_map
->channels
; ++i
)
566 channels
[i
] = channel_map
->map
[i
];
567 if (!resample_method
)
568 resample_method
= "";
570 pa_assert_se((reply
= dbus_message_new_method_return(msg
)));
572 dbus_message_iter_init_append(reply
, &msg_iter
);
573 pa_assert_se(dbus_message_iter_open_container(&msg_iter
, DBUS_TYPE_ARRAY
, "{sv}", &dict_iter
));
575 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_INDEX
].property_name
, DBUS_TYPE_UINT32
, &idx
);
578 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_DRIVER
].property_name
, DBUS_TYPE_STRING
, &driver
);
581 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 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_CLIENT
].property_name
, DBUS_TYPE_OBJECT_PATH
, &client_path
);
586 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_DEVICE
].property_name
, DBUS_TYPE_OBJECT_PATH
, &device
);
587 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_SAMPLE_FORMAT
].property_name
, DBUS_TYPE_UINT32
, &sample_format
);
588 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_SAMPLE_RATE
].property_name
, DBUS_TYPE_UINT32
, &s
->sample_rate
);
589 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 pa_dbus_append_basic_array_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_VOLUME
].property_name
, DBUS_TYPE_UINT32
, volume
, s
->volume
.channels
);
593 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_MUTE
].property_name
, DBUS_TYPE_BOOLEAN
, &s
->mute
);
596 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_BUFFER_LATENCY
].property_name
, DBUS_TYPE_UINT64
, &buffer_latency
);
597 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_DEVICE_LATENCY
].property_name
, DBUS_TYPE_UINT64
, &device_latency
);
598 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_RESAMPLE_METHOD
].property_name
, DBUS_TYPE_STRING
, &resample_method
);
599 pa_dbus_append_proplist_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_PROPERTY_LIST
].property_name
, s
->proplist
);
601 pa_assert_se(dbus_message_iter_close_container(&msg_iter
, &dict_iter
));
602 pa_assert_se(dbus_connection_send(conn
, reply
, NULL
));
603 dbus_message_unref(reply
);
606 static void handle_move(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
607 pa_dbusiface_stream
*s
= userdata
;
608 const char *device
= NULL
;
614 pa_assert_se(dbus_message_get_args(msg
, NULL
, DBUS_TYPE_OBJECT_PATH
, &device
, DBUS_TYPE_INVALID
));
616 if (s
->type
== STREAM_TYPE_PLAYBACK
) {
617 pa_sink
*sink
= pa_dbusiface_core_get_sink(s
->core
, device
);
620 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NOT_FOUND
, "%s: No such sink.", device
);
624 if (pa_sink_input_move_to(s
->sink_input
, sink
, true) < 0) {
625 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_FAILED
,
626 "Moving playback stream %u to sink %s failed.", s
->sink_input
->index
, sink
->name
);
630 pa_source
*source
= pa_dbusiface_core_get_source(s
->core
, device
);
633 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NOT_FOUND
, "%s: No such source.", device
);
637 if (pa_source_output_move_to(s
->source_output
, source
, true) < 0) {
638 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_FAILED
,
639 "Moving record stream %u to source %s failed.", s
->source_output
->index
, source
->name
);
644 pa_dbus_send_empty_reply(conn
, msg
);
647 static void handle_kill(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
648 pa_dbusiface_stream
*s
= userdata
;
654 if (s
->type
== STREAM_TYPE_PLAYBACK
)
655 pa_sink_input_kill(s
->sink_input
);
657 pa_source_output_kill(s
->source_output
);
659 pa_dbus_send_empty_reply(conn
, msg
);
662 static void subscription_cb(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
663 pa_dbusiface_stream
*s
= userdata
;
664 DBusMessage
*signal_msg
= NULL
;
665 const char *new_device_path
= NULL
;
666 uint32_t new_sample_rate
= 0;
667 pa_proplist
*new_proplist
= NULL
;
673 if ((s
->type
== STREAM_TYPE_PLAYBACK
&& idx
!= s
->sink_input
->index
)
674 || (s
->type
== STREAM_TYPE_RECORD
&& idx
!= s
->source_output
->index
))
677 if ((t
& PA_SUBSCRIPTION_EVENT_TYPE_MASK
) != PA_SUBSCRIPTION_EVENT_CHANGE
)
680 pa_assert(((s
->type
== STREAM_TYPE_PLAYBACK
)
681 && ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK_INPUT
))
682 || ((s
->type
== STREAM_TYPE_RECORD
)
683 && ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
)));
685 if (s
->type
== STREAM_TYPE_PLAYBACK
) {
686 pa_sink
*new_sink
= s
->sink_input
->sink
;
688 if (s
->sink
!= new_sink
) {
689 pa_sink_unref(s
->sink
);
690 s
->sink
= pa_sink_ref(new_sink
);
692 new_device_path
= pa_dbusiface_core_get_sink_path(s
->core
, new_sink
);
694 pa_assert_se(signal_msg
= dbus_message_new_signal(s
->path
,
695 PA_DBUSIFACE_STREAM_INTERFACE
,
696 signals
[SIGNAL_DEVICE_UPDATED
].name
));
697 pa_assert_se(dbus_message_append_args(signal_msg
, DBUS_TYPE_OBJECT_PATH
, &new_device_path
, DBUS_TYPE_INVALID
));
699 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal_msg
);
700 dbus_message_unref(signal_msg
);
704 pa_source
*new_source
= s
->source_output
->source
;
706 if (s
->source
!= new_source
) {
707 pa_source_unref(s
->source
);
708 s
->source
= pa_source_ref(new_source
);
710 new_device_path
= pa_dbusiface_core_get_source_path(s
->core
, new_source
);
712 pa_assert_se(signal_msg
= dbus_message_new_signal(s
->path
,
713 PA_DBUSIFACE_STREAM_INTERFACE
,
714 signals
[SIGNAL_DEVICE_UPDATED
].name
));
715 pa_assert_se(dbus_message_append_args(signal_msg
, DBUS_TYPE_OBJECT_PATH
, &new_device_path
, DBUS_TYPE_INVALID
));
717 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal_msg
);
718 dbus_message_unref(signal_msg
);
723 new_sample_rate
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? s
->sink_input
->sample_spec
.rate
: s
->source_output
->sample_spec
.rate
;
725 if (s
->sample_rate
!= new_sample_rate
) {
726 s
->sample_rate
= new_sample_rate
;
728 pa_assert_se(signal_msg
= dbus_message_new_signal(s
->path
,
729 PA_DBUSIFACE_STREAM_INTERFACE
,
730 signals
[SIGNAL_SAMPLE_RATE_UPDATED
].name
));
731 pa_assert_se(dbus_message_append_args(signal_msg
, DBUS_TYPE_UINT32
, &s
->sample_rate
, DBUS_TYPE_INVALID
));
733 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal_msg
);
734 dbus_message_unref(signal_msg
);
738 if (s
->type
== STREAM_TYPE_PLAYBACK
) {
739 bool new_mute
= false;
742 pa_cvolume new_volume
;
744 pa_sink_input_get_volume(s
->sink_input
, &new_volume
, true);
746 if (!pa_cvolume_equal(&s
->volume
, &new_volume
)) {
747 dbus_uint32_t volume
[PA_CHANNELS_MAX
];
748 dbus_uint32_t
*volume_ptr
= volume
;
750 s
->volume
= new_volume
;
752 for (i
= 0; i
< s
->volume
.channels
; ++i
)
753 volume
[i
] = s
->volume
.values
[i
];
755 pa_assert_se(signal_msg
= dbus_message_new_signal(s
->path
,
756 PA_DBUSIFACE_STREAM_INTERFACE
,
757 signals
[SIGNAL_VOLUME_UPDATED
].name
));
758 pa_assert_se(dbus_message_append_args(signal_msg
,
759 DBUS_TYPE_ARRAY
, DBUS_TYPE_UINT32
, &volume_ptr
, s
->volume
.channels
,
762 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal_msg
);
763 dbus_message_unref(signal_msg
);
768 new_mute
= s
->sink_input
->muted
;
770 if (s
->mute
!= new_mute
) {
773 pa_assert_se(signal_msg
= dbus_message_new_signal(s
->path
,
774 PA_DBUSIFACE_STREAM_INTERFACE
,
775 signals
[SIGNAL_MUTE_UPDATED
].name
));
776 pa_assert_se(dbus_message_append_args(signal_msg
, DBUS_TYPE_BOOLEAN
, &s
->mute
, DBUS_TYPE_INVALID
));
778 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal_msg
);
779 dbus_message_unref(signal_msg
);
784 new_proplist
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? s
->sink_input
->proplist
: s
->source_output
->proplist
;
786 if (!pa_proplist_equal(s
->proplist
, new_proplist
)) {
787 DBusMessageIter msg_iter
;
789 pa_proplist_update(s
->proplist
, PA_UPDATE_SET
, new_proplist
);
791 pa_assert_se(signal_msg
= dbus_message_new_signal(s
->path
,
792 PA_DBUSIFACE_STREAM_INTERFACE
,
793 signals
[SIGNAL_PROPERTY_LIST_UPDATED
].name
));
794 dbus_message_iter_init_append(signal_msg
, &msg_iter
);
795 pa_dbus_append_proplist(&msg_iter
, s
->proplist
);
797 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal_msg
);
798 dbus_message_unref(signal_msg
);
803 static pa_hook_result_t
send_event_cb(void *hook_data
, void *call_data
, void *slot_data
) {
804 pa_dbusiface_stream
*s
= slot_data
;
805 DBusMessage
*signal_msg
= NULL
;
806 DBusMessageIter msg_iter
;
807 const char *name
= NULL
;
808 pa_proplist
*property_list
= NULL
;
810 pa_assert(call_data
);
813 if (s
->type
== STREAM_TYPE_PLAYBACK
) {
814 pa_sink_input_send_event_hook_data
*data
= call_data
;
816 if (data
->sink_input
!= s
->sink_input
)
820 property_list
= data
->data
;
822 pa_source_output_send_event_hook_data
*data
= call_data
;
824 if (data
->source_output
!= s
->source_output
)
828 property_list
= data
->data
;
831 pa_assert_se(signal_msg
= dbus_message_new_signal(s
->path
,
832 PA_DBUSIFACE_STREAM_INTERFACE
,
833 signals
[SIGNAL_STREAM_EVENT
].name
));
834 dbus_message_iter_init_append(signal_msg
, &msg_iter
);
835 pa_assert_se(dbus_message_iter_append_basic(&msg_iter
, DBUS_TYPE_STRING
, &name
));
836 pa_dbus_append_proplist(&msg_iter
, property_list
);
838 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal_msg
);
839 dbus_message_unref(signal_msg
);
844 pa_dbusiface_stream
*pa_dbusiface_stream_new_playback(pa_dbusiface_core
*core
, pa_sink_input
*sink_input
) {
845 pa_dbusiface_stream
*s
;
848 pa_assert(sink_input
);
850 s
= pa_xnew(pa_dbusiface_stream
, 1);
852 s
->sink_input
= pa_sink_input_ref(sink_input
);
853 s
->type
= STREAM_TYPE_PLAYBACK
;
854 s
->path
= pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH
, PLAYBACK_OBJECT_NAME
, sink_input
->index
);
855 s
->sink
= pa_sink_ref(sink_input
->sink
);
856 s
->sample_rate
= sink_input
->sample_spec
.rate
;
857 s
->has_volume
= pa_sink_input_is_volume_readable(sink_input
);
860 pa_sink_input_get_volume(sink_input
, &s
->volume
, true);
862 pa_cvolume_init(&s
->volume
);
864 s
->mute
= sink_input
->muted
;
865 s
->proplist
= pa_proplist_copy(sink_input
->proplist
);
866 s
->dbus_protocol
= pa_dbus_protocol_get(sink_input
->core
);
867 s
->subscription
= pa_subscription_new(sink_input
->core
, PA_SUBSCRIPTION_MASK_SINK_INPUT
, subscription_cb
, s
);
868 s
->send_event_slot
= pa_hook_connect(&sink_input
->core
->hooks
[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT
],
873 pa_assert_se(pa_dbus_protocol_add_interface(s
->dbus_protocol
, s
->path
, &stream_interface_info
, s
) >= 0);
878 pa_dbusiface_stream
*pa_dbusiface_stream_new_record(pa_dbusiface_core
*core
, pa_source_output
*source_output
) {
879 pa_dbusiface_stream
*s
;
882 pa_assert(source_output
);
884 s
= pa_xnew(pa_dbusiface_stream
, 1);
886 s
->source_output
= pa_source_output_ref(source_output
);
887 s
->type
= STREAM_TYPE_RECORD
;
888 s
->path
= pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH
, RECORD_OBJECT_NAME
, source_output
->index
);
889 s
->source
= pa_source_ref(source_output
->source
);
890 s
->sample_rate
= source_output
->sample_spec
.rate
;
891 pa_cvolume_init(&s
->volume
);
893 s
->proplist
= pa_proplist_copy(source_output
->proplist
);
894 s
->has_volume
= false;
895 s
->dbus_protocol
= pa_dbus_protocol_get(source_output
->core
);
896 s
->subscription
= pa_subscription_new(source_output
->core
, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT
, subscription_cb
, s
);
897 s
->send_event_slot
= pa_hook_connect(&source_output
->core
->hooks
[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT
],
902 pa_assert_se(pa_dbus_protocol_add_interface(s
->dbus_protocol
, s
->path
, &stream_interface_info
, s
) >= 0);
907 void pa_dbusiface_stream_free(pa_dbusiface_stream
*s
) {
910 pa_assert_se(pa_dbus_protocol_remove_interface(s
->dbus_protocol
, s
->path
, stream_interface_info
.name
) >= 0);
912 if (s
->type
== STREAM_TYPE_PLAYBACK
) {
913 pa_sink_input_unref(s
->sink_input
);
914 pa_sink_unref(s
->sink
);
916 pa_source_output_unref(s
->source_output
);
917 pa_source_unref(s
->source
);
920 pa_proplist_free(s
->proplist
);
921 pa_dbus_protocol_unref(s
->dbus_protocol
);
922 pa_subscription_free(s
->subscription
);
923 pa_hook_slot_free(s
->send_event_slot
);
929 const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream
*s
) {