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
;
60 pa_bool_t read_only_volume
;
62 pa_dbus_protocol
*dbus_protocol
;
63 pa_subscription
*subscription
;
64 pa_hook_slot
*send_event_slot
;
67 static void handle_get_index(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
68 static void handle_get_driver(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
69 static void handle_get_owner_module(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
70 static void handle_get_client(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
71 static void handle_get_device(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
72 static void handle_get_sample_format(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
73 static void handle_get_sample_rate(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
74 static void handle_get_channels(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
75 static void handle_get_volume(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
76 static void handle_set_volume(DBusConnection
*conn
, DBusMessage
*msg
, DBusMessageIter
*iter
, void *userdata
);
77 static void handle_get_mute(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
78 static void handle_set_mute(DBusConnection
*conn
, DBusMessage
*msg
, DBusMessageIter
*iter
, void *userdata
);
79 static void handle_get_buffer_latency(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
80 static void handle_get_device_latency(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
81 static void handle_get_resample_method(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
82 static void handle_get_property_list(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
84 static void handle_get_all(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
86 static void handle_move(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
87 static void handle_kill(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
89 enum property_handler_index
{
90 PROPERTY_HANDLER_INDEX
,
91 PROPERTY_HANDLER_DRIVER
,
92 PROPERTY_HANDLER_OWNER_MODULE
,
93 PROPERTY_HANDLER_CLIENT
,
94 PROPERTY_HANDLER_DEVICE
,
95 PROPERTY_HANDLER_SAMPLE_FORMAT
,
96 PROPERTY_HANDLER_SAMPLE_RATE
,
97 PROPERTY_HANDLER_CHANNELS
,
98 PROPERTY_HANDLER_VOLUME
,
99 PROPERTY_HANDLER_MUTE
,
100 PROPERTY_HANDLER_BUFFER_LATENCY
,
101 PROPERTY_HANDLER_DEVICE_LATENCY
,
102 PROPERTY_HANDLER_RESAMPLE_METHOD
,
103 PROPERTY_HANDLER_PROPERTY_LIST
,
107 static pa_dbus_property_handler property_handlers
[PROPERTY_HANDLER_MAX
] = {
108 [PROPERTY_HANDLER_INDEX
] = { .property_name
= "Index", .type
= "u", .get_cb
= handle_get_index
, .set_cb
= NULL
},
109 [PROPERTY_HANDLER_DRIVER
] = { .property_name
= "Driver", .type
= "s", .get_cb
= handle_get_driver
, .set_cb
= NULL
},
110 [PROPERTY_HANDLER_OWNER_MODULE
] = { .property_name
= "OwnerModule", .type
= "o", .get_cb
= handle_get_owner_module
, .set_cb
= NULL
},
111 [PROPERTY_HANDLER_CLIENT
] = { .property_name
= "Client", .type
= "o", .get_cb
= handle_get_client
, .set_cb
= NULL
},
112 [PROPERTY_HANDLER_DEVICE
] = { .property_name
= "Device", .type
= "o", .get_cb
= handle_get_device
, .set_cb
= NULL
},
113 [PROPERTY_HANDLER_SAMPLE_FORMAT
] = { .property_name
= "SampleFormat", .type
= "u", .get_cb
= handle_get_sample_format
, .set_cb
= NULL
},
114 [PROPERTY_HANDLER_SAMPLE_RATE
] = { .property_name
= "SampleRate", .type
= "u", .get_cb
= handle_get_sample_rate
, .set_cb
= NULL
},
115 [PROPERTY_HANDLER_CHANNELS
] = { .property_name
= "Channels", .type
= "au", .get_cb
= handle_get_channels
, .set_cb
= NULL
},
116 [PROPERTY_HANDLER_VOLUME
] = { .property_name
= "Volume", .type
= "au", .get_cb
= handle_get_volume
, .set_cb
= handle_set_volume
},
117 [PROPERTY_HANDLER_MUTE
] = { .property_name
= "Mute", .type
= "b", .get_cb
= handle_get_mute
, .set_cb
= handle_set_mute
},
118 [PROPERTY_HANDLER_BUFFER_LATENCY
] = { .property_name
= "BufferLatency", .type
= "t", .get_cb
= handle_get_buffer_latency
, .set_cb
= NULL
},
119 [PROPERTY_HANDLER_DEVICE_LATENCY
] = { .property_name
= "DeviceLatency", .type
= "t", .get_cb
= handle_get_device_latency
, .set_cb
= NULL
},
120 [PROPERTY_HANDLER_RESAMPLE_METHOD
] = { .property_name
= "ResampleMethod", .type
= "s", .get_cb
= handle_get_resample_method
, .set_cb
= NULL
},
121 [PROPERTY_HANDLER_PROPERTY_LIST
] = { .property_name
= "PropertyList", .type
= "a{say}", .get_cb
= handle_get_property_list
, .set_cb
= NULL
}
124 enum method_handler_index
{
130 static pa_dbus_arg_info move_args
[] = { { "device", "o", "in" } };
132 static pa_dbus_method_handler method_handlers
[METHOD_HANDLER_MAX
] = {
133 [METHOD_HANDLER_MOVE
] = {
134 .method_name
= "Move",
135 .arguments
= move_args
,
136 .n_arguments
= sizeof(move_args
) / sizeof(pa_dbus_arg_info
),
137 .receive_cb
= handle_move
},
138 [METHOD_HANDLER_KILL
] = {
139 .method_name
= "Kill",
142 .receive_cb
= handle_kill
}
146 SIGNAL_DEVICE_UPDATED
,
147 SIGNAL_SAMPLE_RATE_UPDATED
,
148 SIGNAL_VOLUME_UPDATED
,
150 SIGNAL_PROPERTY_LIST_UPDATED
,
155 static pa_dbus_arg_info device_updated_args
[] = { { "device", "o", NULL
} };
156 static pa_dbus_arg_info sample_rate_updated_args
[] = { { "sample_rate", "u", NULL
} };
157 static pa_dbus_arg_info volume_updated_args
[] = { { "volume", "au", NULL
} };
158 static pa_dbus_arg_info mute_updated_args
[] = { { "muted", "b", NULL
} };
159 static pa_dbus_arg_info property_list_updated_args
[] = { { "property_list", "a{say}", NULL
} };
160 static pa_dbus_arg_info stream_event_args
[] = { { "name", "s", NULL
}, { "property_list", "a{say}", NULL
} };
162 static pa_dbus_signal_info signals
[SIGNAL_MAX
] = {
163 [SIGNAL_DEVICE_UPDATED
] = { .name
= "DeviceUpdated", .arguments
= device_updated_args
, .n_arguments
= 1 },
164 [SIGNAL_SAMPLE_RATE_UPDATED
] = { .name
= "SampleRateUpdated", .arguments
= sample_rate_updated_args
, .n_arguments
= 1 },
165 [SIGNAL_VOLUME_UPDATED
] = { .name
= "VolumeUpdated", .arguments
= volume_updated_args
, .n_arguments
= 1 },
166 [SIGNAL_MUTE_UPDATED
] = { .name
= "MuteUpdated", .arguments
= mute_updated_args
, .n_arguments
= 1 },
167 [SIGNAL_PROPERTY_LIST_UPDATED
] = { .name
= "PropertyListUpdated", .arguments
= property_list_updated_args
, .n_arguments
= 1 },
168 [SIGNAL_STREAM_EVENT
] = { .name
= "StreamEvent", .arguments
= stream_event_args
, .n_arguments
= sizeof(stream_event_args
) / sizeof(pa_dbus_arg_info
) }
171 static pa_dbus_interface_info stream_interface_info
= {
172 .name
= PA_DBUSIFACE_STREAM_INTERFACE
,
173 .method_handlers
= method_handlers
,
174 .n_method_handlers
= METHOD_HANDLER_MAX
,
175 .property_handlers
= property_handlers
,
176 .n_property_handlers
= PROPERTY_HANDLER_MAX
,
177 .get_all_properties_cb
= handle_get_all
,
179 .n_signals
= SIGNAL_MAX
182 static void handle_get_index(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
183 pa_dbusiface_stream
*s
= userdata
;
190 idx
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? s
->sink_input
->index
: s
->source_output
->index
;
192 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, &idx
);
195 /* The returned string has to be freed with pa_xfree() by the caller. */
196 static char *stream_to_string(pa_dbusiface_stream
*s
) {
197 if (s
->type
== STREAM_TYPE_PLAYBACK
)
198 return pa_sprintf_malloc("Playback stream %u", (unsigned) s
->sink_input
->index
);
200 return pa_sprintf_malloc("Record stream %u", (unsigned) s
->source_output
->index
);
203 static void handle_get_driver(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
204 pa_dbusiface_stream
*s
= userdata
;
205 const char *driver
= NULL
;
211 driver
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? s
->sink_input
->driver
: s
->source_output
->driver
;
214 char *str
= stream_to_string(s
);
216 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "%s doesn't have a driver.", str
);
222 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_STRING
, &driver
);
225 static void handle_get_owner_module(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
226 pa_dbusiface_stream
*s
= userdata
;
227 pa_module
*owner_module
= NULL
;
228 const char *object_path
= NULL
;
234 owner_module
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? s
->sink_input
->module
: s
->source_output
->module
;
237 char *str
= stream_to_string(s
);
239 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "%s doesn't have an owner module.", str
);
245 object_path
= pa_dbusiface_core_get_module_path(s
->core
, owner_module
);
247 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_OBJECT_PATH
, &object_path
);
250 static void handle_get_client(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
251 pa_dbusiface_stream
*s
= userdata
;
252 pa_client
*client
= NULL
;
253 const char *object_path
= NULL
;
259 client
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? s
->sink_input
->client
: s
->source_output
->client
;
262 char *str
= stream_to_string(s
);
264 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "%s isn't associated to any client.", str
);
270 object_path
= pa_dbusiface_core_get_client_path(s
->core
, client
);
272 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_OBJECT_PATH
, &object_path
);
275 static void handle_get_device(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
276 pa_dbusiface_stream
*s
= userdata
;
277 const char *device
= NULL
;
283 if (s
->type
== STREAM_TYPE_PLAYBACK
)
284 device
= pa_dbusiface_core_get_sink_path(s
->core
, s
->sink
);
286 device
= pa_dbusiface_core_get_source_path(s
->core
, s
->source
);
288 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_OBJECT_PATH
, &device
);
291 static void handle_get_sample_format(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
292 pa_dbusiface_stream
*s
= userdata
;
293 dbus_uint32_t sample_format
= 0;
299 sample_format
= (s
->type
== STREAM_TYPE_PLAYBACK
)
300 ? s
->sink_input
->sample_spec
.format
301 : s
->source_output
->sample_spec
.format
;
303 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, &sample_format
);
306 static void handle_get_sample_rate(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
307 pa_dbusiface_stream
*s
= userdata
;
313 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, &s
->sample_rate
);
316 static void handle_get_channels(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
317 pa_dbusiface_stream
*s
= userdata
;
318 pa_channel_map
*channel_map
= NULL
;
319 dbus_uint32_t channels
[PA_CHANNELS_MAX
];
326 channel_map
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? &s
->sink_input
->channel_map
: &s
->source_output
->channel_map
;
328 for (i
= 0; i
< channel_map
->channels
; ++i
)
329 channels
[i
] = channel_map
->map
[i
];
331 pa_dbus_send_basic_array_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, channels
, channel_map
->channels
);
334 static void handle_get_volume(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
335 pa_dbusiface_stream
*s
= userdata
;
336 dbus_uint32_t volume
[PA_CHANNELS_MAX
];
343 if (!s
->has_volume
) {
344 char *str
= stream_to_string(s
);
346 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "%s doesn't have volume.", str
);
352 for (i
= 0; i
< s
->volume
.channels
; ++i
)
353 volume
[i
] = s
->volume
.values
[i
];
355 pa_dbus_send_basic_array_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, volume
, s
->volume
.channels
);
358 static void handle_set_volume(DBusConnection
*conn
, DBusMessage
*msg
, DBusMessageIter
*iter
, void *userdata
) {
359 pa_dbusiface_stream
*s
= userdata
;
360 DBusMessageIter array_iter
;
361 int stream_channels
= 0;
362 dbus_uint32_t
*volume
= NULL
;
363 int n_volume_entries
= 0;
372 if (!s
->has_volume
|| s
->read_only_volume
) {
373 char *str
= stream_to_string(s
);
376 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "%s doesn't have volume.", str
);
377 else if (s
->read_only_volume
)
378 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_ACCESS_DENIED
, "%s has read-only volume.", str
);
384 stream_channels
= s
->sink_input
->channel_map
.channels
;
386 dbus_message_iter_recurse(iter
, &array_iter
);
387 dbus_message_iter_get_fixed_array(&array_iter
, &volume
, &n_volume_entries
);
389 if (n_volume_entries
!= stream_channels
&& n_volume_entries
!= 1) {
390 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
,
391 "Expected %u volume entries, got %u.", stream_channels
, n_volume_entries
);
395 pa_cvolume_init(&new_vol
);
396 new_vol
.channels
= n_volume_entries
;
398 for (i
= 0; i
< n_volume_entries
; ++i
) {
399 if (!PA_VOLUME_IS_VALID(volume
[i
])) {
400 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
, "Invalid volume: %u", volume
[i
]);
403 new_vol
.values
[i
] = volume
[i
];
406 pa_sink_input_set_volume(s
->sink_input
, &new_vol
, TRUE
, TRUE
);
408 pa_dbus_send_empty_reply(conn
, msg
);
411 static void handle_get_mute(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
412 pa_dbusiface_stream
*s
= userdata
;
418 if (s
->type
== STREAM_TYPE_RECORD
) {
419 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "Record streams don't have mute.");
423 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_BOOLEAN
, &s
->mute
);
426 static void handle_set_mute(DBusConnection
*conn
, DBusMessage
*msg
, DBusMessageIter
*iter
, void *userdata
) {
427 pa_dbusiface_stream
*s
= userdata
;
428 dbus_bool_t mute
= FALSE
;
435 dbus_message_iter_get_basic(iter
, &mute
);
437 if (s
->type
== STREAM_TYPE_RECORD
) {
438 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "Record streams don't have mute.");
442 pa_sink_input_set_mute(s
->sink_input
, mute
, TRUE
);
444 pa_dbus_send_empty_reply(conn
, msg
);
447 static void handle_get_buffer_latency(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
448 pa_dbusiface_stream
*s
= userdata
;
449 dbus_uint64_t buffer_latency
= 0;
455 if (s
->type
== STREAM_TYPE_PLAYBACK
)
456 buffer_latency
= pa_sink_input_get_latency(s
->sink_input
, NULL
);
458 buffer_latency
= pa_source_output_get_latency(s
->source_output
, NULL
);
460 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT64
, &buffer_latency
);
463 static void handle_get_device_latency(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
464 pa_dbusiface_stream
*s
= userdata
;
465 dbus_uint64_t device_latency
= 0;
471 if (s
->type
== STREAM_TYPE_PLAYBACK
)
472 pa_sink_input_get_latency(s
->sink_input
, &device_latency
);
474 pa_source_output_get_latency(s
->source_output
, &device_latency
);
476 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT64
, &device_latency
);
479 static void handle_get_resample_method(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
480 pa_dbusiface_stream
*s
= userdata
;
481 const char *resample_method
= NULL
;
487 if (s
->type
== STREAM_TYPE_PLAYBACK
)
488 resample_method
= pa_resample_method_to_string(s
->sink_input
->actual_resample_method
);
490 resample_method
= pa_resample_method_to_string(s
->source_output
->actual_resample_method
);
492 if (!resample_method
)
493 resample_method
= "";
495 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_STRING
, &resample_method
);
498 static void handle_get_property_list(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
499 pa_dbusiface_stream
*s
= userdata
;
505 pa_dbus_send_proplist_variant_reply(conn
, msg
, s
->proplist
);
508 static void handle_get_all(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
509 pa_dbusiface_stream
*s
= userdata
;
510 DBusMessage
*reply
= NULL
;
511 DBusMessageIter msg_iter
;
512 DBusMessageIter dict_iter
;
513 dbus_uint32_t idx
= 0;
514 const char *driver
= NULL
;
515 pa_module
*owner_module
= NULL
;
516 const char *owner_module_path
= NULL
;
517 pa_client
*client
= NULL
;
518 const char *client_path
= NULL
;
519 const char *device
= NULL
;
520 dbus_uint32_t sample_format
= 0;
521 pa_channel_map
*channel_map
= NULL
;
522 dbus_uint32_t channels
[PA_CHANNELS_MAX
];
523 dbus_uint32_t volume
[PA_CHANNELS_MAX
];
524 dbus_uint64_t buffer_latency
= 0;
525 dbus_uint64_t device_latency
= 0;
526 const char *resample_method
= NULL
;
534 for (i
= 0; i
< s
->volume
.channels
; ++i
)
535 volume
[i
] = s
->volume
.values
[i
];
538 if (s
->type
== STREAM_TYPE_PLAYBACK
) {
539 idx
= s
->sink_input
->index
;
540 driver
= s
->sink_input
->driver
;
541 owner_module
= s
->sink_input
->module
;
542 client
= s
->sink_input
->client
;
543 device
= pa_dbusiface_core_get_sink_path(s
->core
, s
->sink
);
544 sample_format
= s
->sink_input
->sample_spec
.format
;
545 channel_map
= &s
->sink_input
->channel_map
;
546 buffer_latency
= pa_sink_input_get_latency(s
->sink_input
, &device_latency
);
547 resample_method
= pa_resample_method_to_string(s
->sink_input
->actual_resample_method
);
549 idx
= s
->source_output
->index
;
550 driver
= s
->source_output
->driver
;
551 owner_module
= s
->source_output
->module
;
552 client
= s
->source_output
->client
;
553 device
= pa_dbusiface_core_get_source_path(s
->core
, s
->source
);
554 sample_format
= s
->source_output
->sample_spec
.format
;
555 channel_map
= &s
->source_output
->channel_map
;
556 buffer_latency
= pa_source_output_get_latency(s
->source_output
, &device_latency
);
557 resample_method
= pa_resample_method_to_string(s
->source_output
->actual_resample_method
);
560 owner_module_path
= pa_dbusiface_core_get_module_path(s
->core
, owner_module
);
562 client_path
= pa_dbusiface_core_get_client_path(s
->core
, client
);
563 for (i
= 0; i
< channel_map
->channels
; ++i
)
564 channels
[i
] = channel_map
->map
[i
];
565 if (!resample_method
)
566 resample_method
= "";
568 pa_assert_se((reply
= dbus_message_new_method_return(msg
)));
570 dbus_message_iter_init_append(reply
, &msg_iter
);
571 pa_assert_se(dbus_message_iter_open_container(&msg_iter
, DBUS_TYPE_ARRAY
, "{sv}", &dict_iter
));
573 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_INDEX
].property_name
, DBUS_TYPE_UINT32
, &idx
);
576 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_DRIVER
].property_name
, DBUS_TYPE_STRING
, &driver
);
579 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_OWNER_MODULE
].property_name
, DBUS_TYPE_OBJECT_PATH
, &owner_module_path
);
582 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_CLIENT
].property_name
, DBUS_TYPE_OBJECT_PATH
, &client_path
);
584 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_DEVICE
].property_name
, DBUS_TYPE_OBJECT_PATH
, &device
);
585 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_SAMPLE_FORMAT
].property_name
, DBUS_TYPE_UINT32
, &sample_format
);
586 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_SAMPLE_RATE
].property_name
, DBUS_TYPE_UINT32
, &s
->sample_rate
);
587 pa_dbus_append_basic_array_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_CHANNELS
].property_name
, DBUS_TYPE_UINT32
, channels
, channel_map
->channels
);
590 pa_dbus_append_basic_array_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_VOLUME
].property_name
, DBUS_TYPE_UINT32
, volume
, s
->volume
.channels
);
591 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_MUTE
].property_name
, DBUS_TYPE_BOOLEAN
, &s
->mute
);
594 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_BUFFER_LATENCY
].property_name
, DBUS_TYPE_UINT64
, &buffer_latency
);
595 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_DEVICE_LATENCY
].property_name
, DBUS_TYPE_UINT64
, &device_latency
);
596 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_RESAMPLE_METHOD
].property_name
, DBUS_TYPE_STRING
, &resample_method
);
597 pa_dbus_append_proplist_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_PROPERTY_LIST
].property_name
, s
->proplist
);
599 pa_assert_se(dbus_message_iter_close_container(&msg_iter
, &dict_iter
));
600 pa_assert_se(dbus_connection_send(conn
, reply
, NULL
));
601 dbus_message_unref(reply
);
604 static void handle_move(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
605 pa_dbusiface_stream
*s
= userdata
;
606 const char *device
= NULL
;
612 pa_assert_se(dbus_message_get_args(msg
, NULL
, DBUS_TYPE_STRING
, &device
, DBUS_TYPE_INVALID
));
614 if (s
->type
== STREAM_TYPE_PLAYBACK
) {
615 pa_sink
*sink
= pa_dbusiface_core_get_sink(s
->core
, device
);
618 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NOT_FOUND
, "%s: No such sink.", device
);
622 if (pa_sink_input_move_to(s
->sink_input
, sink
, TRUE
) < 0) {
623 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_FAILED
,
624 "Moving playback stream %u to sink %s failed.", s
->sink_input
->index
, sink
->name
);
628 pa_source
*source
= pa_dbusiface_core_get_source(s
->core
, device
);
631 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NOT_FOUND
, "%s: No such source.", device
);
635 if (pa_source_output_move_to(s
->source_output
, source
, TRUE
) < 0) {
636 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_FAILED
,
637 "Moving record stream %u to source %s failed.", s
->source_output
->index
, source
->name
);
642 pa_dbus_send_empty_reply(conn
, msg
);
645 static void handle_kill(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
646 pa_dbusiface_stream
*s
= userdata
;
652 if (s
->type
== STREAM_TYPE_PLAYBACK
)
653 pa_sink_input_kill(s
->sink_input
);
655 pa_source_output_kill(s
->source_output
);
657 pa_dbus_send_empty_reply(conn
, msg
);
660 static void subscription_cb(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
661 pa_dbusiface_stream
*s
= userdata
;
662 DBusMessage
*signal_msg
= NULL
;
663 const char *new_device_path
= NULL
;
664 uint32_t new_sample_rate
= 0;
665 pa_proplist
*new_proplist
= NULL
;
671 if ((s
->type
== STREAM_TYPE_PLAYBACK
&& idx
!= s
->sink_input
->index
)
672 || (s
->type
== STREAM_TYPE_RECORD
&& idx
!= s
->source_output
->index
))
675 if ((t
& PA_SUBSCRIPTION_EVENT_TYPE_MASK
) != PA_SUBSCRIPTION_EVENT_CHANGE
)
678 pa_assert(((s
->type
== STREAM_TYPE_PLAYBACK
)
679 && ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK_INPUT
))
680 || ((s
->type
== STREAM_TYPE_RECORD
)
681 && ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
)));
683 if (s
->type
== STREAM_TYPE_PLAYBACK
) {
684 pa_sink
*new_sink
= s
->sink_input
->sink
;
686 if (s
->sink
!= new_sink
) {
687 pa_sink_unref(s
->sink
);
688 s
->sink
= pa_sink_ref(new_sink
);
690 new_device_path
= pa_dbusiface_core_get_sink_path(s
->core
, new_sink
);
692 pa_assert_se(signal_msg
= dbus_message_new_signal(s
->path
,
693 PA_DBUSIFACE_STREAM_INTERFACE
,
694 signals
[SIGNAL_DEVICE_UPDATED
].name
));
695 pa_assert_se(dbus_message_append_args(signal_msg
, DBUS_TYPE_OBJECT_PATH
, &new_device_path
, DBUS_TYPE_INVALID
));
697 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal_msg
);
698 dbus_message_unref(signal_msg
);
702 pa_source
*new_source
= s
->source_output
->source
;
704 if (s
->source
!= new_source
) {
705 pa_source_unref(s
->source
);
706 s
->source
= pa_source_ref(new_source
);
708 new_device_path
= pa_dbusiface_core_get_source_path(s
->core
, new_source
);
710 pa_assert_se(signal_msg
= dbus_message_new_signal(s
->path
,
711 PA_DBUSIFACE_STREAM_INTERFACE
,
712 signals
[SIGNAL_DEVICE_UPDATED
].name
));
713 pa_assert_se(dbus_message_append_args(signal_msg
, DBUS_TYPE_OBJECT_PATH
, &new_device_path
, DBUS_TYPE_INVALID
));
715 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal_msg
);
716 dbus_message_unref(signal_msg
);
721 new_sample_rate
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? s
->sink_input
->sample_spec
.rate
: s
->source_output
->sample_spec
.rate
;
723 if (s
->sample_rate
!= new_sample_rate
) {
724 s
->sample_rate
= new_sample_rate
;
726 pa_assert_se(signal_msg
= dbus_message_new_signal(s
->path
,
727 PA_DBUSIFACE_STREAM_INTERFACE
,
728 signals
[SIGNAL_SAMPLE_RATE_UPDATED
].name
));
729 pa_assert_se(dbus_message_append_args(signal_msg
, DBUS_TYPE_UINT32
, &s
->sample_rate
, DBUS_TYPE_INVALID
));
731 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal_msg
);
732 dbus_message_unref(signal_msg
);
736 if (s
->type
== STREAM_TYPE_PLAYBACK
) {
737 pa_bool_t new_mute
= FALSE
;
740 pa_cvolume new_volume
;
742 pa_sink_input_get_volume(s
->sink_input
, &new_volume
, TRUE
);
744 if (!pa_cvolume_equal(&s
->volume
, &new_volume
)) {
745 dbus_uint32_t volume
[PA_CHANNELS_MAX
];
746 dbus_uint32_t
*volume_ptr
= volume
;
748 s
->volume
= new_volume
;
750 for (i
= 0; i
< s
->volume
.channels
; ++i
)
751 volume
[i
] = s
->volume
.values
[i
];
753 pa_assert_se(signal_msg
= dbus_message_new_signal(s
->path
,
754 PA_DBUSIFACE_STREAM_INTERFACE
,
755 signals
[SIGNAL_VOLUME_UPDATED
].name
));
756 pa_assert_se(dbus_message_append_args(signal_msg
,
757 DBUS_TYPE_ARRAY
, DBUS_TYPE_UINT32
, &volume_ptr
, s
->volume
.channels
,
760 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal_msg
);
761 dbus_message_unref(signal_msg
);
766 new_mute
= pa_sink_input_get_mute(s
->sink_input
);
768 if (s
->mute
!= new_mute
) {
771 pa_assert_se(signal_msg
= dbus_message_new_signal(s
->path
,
772 PA_DBUSIFACE_STREAM_INTERFACE
,
773 signals
[SIGNAL_MUTE_UPDATED
].name
));
774 pa_assert_se(dbus_message_append_args(signal_msg
, DBUS_TYPE_BOOLEAN
, &s
->mute
, DBUS_TYPE_INVALID
));
776 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal_msg
);
777 dbus_message_unref(signal_msg
);
782 new_proplist
= (s
->type
== STREAM_TYPE_PLAYBACK
) ? s
->sink_input
->proplist
: s
->source_output
->proplist
;
784 if (!pa_proplist_equal(s
->proplist
, new_proplist
)) {
785 DBusMessageIter msg_iter
;
787 pa_proplist_update(s
->proplist
, PA_UPDATE_SET
, new_proplist
);
789 pa_assert_se(signal_msg
= dbus_message_new_signal(s
->path
,
790 PA_DBUSIFACE_STREAM_INTERFACE
,
791 signals
[SIGNAL_PROPERTY_LIST_UPDATED
].name
));
792 dbus_message_iter_init_append(signal_msg
, &msg_iter
);
793 pa_dbus_append_proplist(&msg_iter
, s
->proplist
);
795 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal_msg
);
796 dbus_message_unref(signal_msg
);
801 static pa_hook_result_t
send_event_cb(void *hook_data
, void *call_data
, void *slot_data
) {
802 pa_dbusiface_stream
*s
= slot_data
;
803 DBusMessage
*signal_msg
= NULL
;
804 DBusMessageIter msg_iter
;
805 const char *name
= NULL
;
806 pa_proplist
*property_list
= NULL
;
808 pa_assert(call_data
);
811 if (s
->type
== STREAM_TYPE_PLAYBACK
) {
812 pa_sink_input_send_event_hook_data
*data
= call_data
;
814 if (data
->sink_input
!= s
->sink_input
)
818 property_list
= data
->data
;
820 pa_source_output_send_event_hook_data
*data
= call_data
;
822 if (data
->source_output
!= s
->source_output
)
826 property_list
= data
->data
;
829 pa_assert_se(signal_msg
= dbus_message_new_signal(s
->path
,
830 PA_DBUSIFACE_STREAM_INTERFACE
,
831 signals
[SIGNAL_STREAM_EVENT
].name
));
832 dbus_message_iter_init_append(signal_msg
, &msg_iter
);
833 pa_assert_se(dbus_message_iter_append_basic(&msg_iter
, DBUS_TYPE_STRING
, &name
));
834 pa_dbus_append_proplist(&msg_iter
, property_list
);
836 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal_msg
);
837 dbus_message_unref(signal_msg
);
842 pa_dbusiface_stream
*pa_dbusiface_stream_new_playback(pa_dbusiface_core
*core
, pa_sink_input
*sink_input
) {
843 pa_dbusiface_stream
*s
;
846 pa_assert(sink_input
);
848 s
= pa_xnew(pa_dbusiface_stream
, 1);
850 s
->sink_input
= pa_sink_input_ref(sink_input
);
851 s
->type
= STREAM_TYPE_PLAYBACK
;
852 s
->path
= pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH
, PLAYBACK_OBJECT_NAME
, sink_input
->index
);
853 s
->sink
= pa_sink_ref(sink_input
->sink
);
854 s
->sample_rate
= sink_input
->sample_spec
.rate
;
855 s
->has_volume
= pa_sink_input_is_volume_readable(sink_input
);
856 s
->read_only_volume
= s
->has_volume
? !pa_sink_input_is_volume_writable(sink_input
) : FALSE
;
859 pa_sink_input_get_volume(sink_input
, &s
->volume
, TRUE
);
861 pa_cvolume_init(&s
->volume
);
863 s
->mute
= pa_sink_input_get_mute(sink_input
);
864 s
->proplist
= pa_proplist_copy(sink_input
->proplist
);
865 s
->dbus_protocol
= pa_dbus_protocol_get(sink_input
->core
);
866 s
->subscription
= pa_subscription_new(sink_input
->core
, PA_SUBSCRIPTION_MASK_SINK_INPUT
, subscription_cb
, s
);
867 s
->send_event_slot
= pa_hook_connect(&sink_input
->core
->hooks
[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT
],
872 pa_assert_se(pa_dbus_protocol_add_interface(s
->dbus_protocol
, s
->path
, &stream_interface_info
, s
) >= 0);
877 pa_dbusiface_stream
*pa_dbusiface_stream_new_record(pa_dbusiface_core
*core
, pa_source_output
*source_output
) {
878 pa_dbusiface_stream
*s
;
881 pa_assert(source_output
);
883 s
= pa_xnew(pa_dbusiface_stream
, 1);
885 s
->source_output
= pa_source_output_ref(source_output
);
886 s
->type
= STREAM_TYPE_RECORD
;
887 s
->path
= pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH
, RECORD_OBJECT_NAME
, source_output
->index
);
888 s
->source
= pa_source_ref(source_output
->source
);
889 s
->sample_rate
= source_output
->sample_spec
.rate
;
890 pa_cvolume_init(&s
->volume
);
892 s
->proplist
= pa_proplist_copy(source_output
->proplist
);
893 s
->has_volume
= FALSE
;
894 s
->read_only_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
) {