2 This file is part of PulseAudio.
4 Copyright 2009 Tanu Kaskinen
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <pulsecore/core-util.h>
27 #include <pulsecore/dbus-util.h>
28 #include <pulsecore/namereg.h>
29 #include <pulsecore/protocol-dbus.h>
31 #include "iface-sample.h"
33 #define OBJECT_NAME "sample"
35 struct pa_dbusiface_sample
{
36 pa_dbusiface_core
*core
;
38 pa_scache_entry
*sample
;
40 pa_proplist
*proplist
;
42 pa_dbus_protocol
*dbus_protocol
;
43 pa_subscription
*subscription
;
46 static void handle_get_index(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
47 static void handle_get_name(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
48 static void handle_get_sample_format(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
49 static void handle_get_sample_rate(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
50 static void handle_get_channels(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
51 static void handle_get_default_volume(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
52 static void handle_get_duration(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
53 static void handle_get_bytes(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
54 static void handle_get_property_list(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
56 static void handle_get_all(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
58 static void handle_play(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
59 static void handle_play_to_sink(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
60 static void handle_remove(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
);
62 enum property_handler_index
{
63 PROPERTY_HANDLER_INDEX
,
64 PROPERTY_HANDLER_NAME
,
65 PROPERTY_HANDLER_SAMPLE_FORMAT
,
66 PROPERTY_HANDLER_SAMPLE_RATE
,
67 PROPERTY_HANDLER_CHANNELS
,
68 PROPERTY_HANDLER_DEFAULT_VOLUME
,
69 PROPERTY_HANDLER_DURATION
,
70 PROPERTY_HANDLER_BYTES
,
71 PROPERTY_HANDLER_PROPERTY_LIST
,
75 static pa_dbus_property_handler property_handlers
[PROPERTY_HANDLER_MAX
] = {
76 [PROPERTY_HANDLER_INDEX
] = { .property_name
= "Index", .type
= "u", .get_cb
= handle_get_index
, .set_cb
= NULL
},
77 [PROPERTY_HANDLER_NAME
] = { .property_name
= "Name", .type
= "s", .get_cb
= handle_get_name
, .set_cb
= NULL
},
78 [PROPERTY_HANDLER_SAMPLE_FORMAT
] = { .property_name
= "SampleFormat", .type
= "u", .get_cb
= handle_get_sample_format
, .set_cb
= NULL
},
79 [PROPERTY_HANDLER_SAMPLE_RATE
] = { .property_name
= "SampleRate", .type
= "u", .get_cb
= handle_get_sample_rate
, .set_cb
= NULL
},
80 [PROPERTY_HANDLER_CHANNELS
] = { .property_name
= "Channels", .type
= "au", .get_cb
= handle_get_channels
, .set_cb
= NULL
},
81 [PROPERTY_HANDLER_DEFAULT_VOLUME
] = { .property_name
= "DefaultVolume", .type
= "au", .get_cb
= handle_get_default_volume
, .set_cb
= NULL
},
82 [PROPERTY_HANDLER_DURATION
] = { .property_name
= "Duration", .type
= "t", .get_cb
= handle_get_duration
, .set_cb
= NULL
},
83 [PROPERTY_HANDLER_BYTES
] = { .property_name
= "Bytes", .type
= "u", .get_cb
= handle_get_bytes
, .set_cb
= NULL
},
84 [PROPERTY_HANDLER_PROPERTY_LIST
] = { .property_name
= "PropertyList", .type
= "a{say}", .get_cb
= handle_get_property_list
, .set_cb
= NULL
}
87 enum method_handler_index
{
89 METHOD_HANDLER_PLAY_TO_SINK
,
90 METHOD_HANDLER_REMOVE
,
94 static pa_dbus_arg_info play_args
[] = { { "volume", "u", "in" }, { "property_list", "a{say}", "in" } };
95 static pa_dbus_arg_info play_to_sink_args
[] = { { "sink", "o", "in" },
96 { "volume", "u", "in" },
97 { "property_list", "a{say}", "in" } };
99 static pa_dbus_method_handler method_handlers
[METHOD_HANDLER_MAX
] = {
100 [METHOD_HANDLER_PLAY
] = {
101 .method_name
= "Play",
102 .arguments
= play_args
,
103 .n_arguments
= sizeof(play_args
) / sizeof(pa_dbus_arg_info
),
104 .receive_cb
= handle_play
},
105 [METHOD_HANDLER_PLAY_TO_SINK
] = {
106 .method_name
= "PlayToSink",
107 .arguments
= play_to_sink_args
,
108 .n_arguments
= sizeof(play_to_sink_args
) / sizeof(pa_dbus_arg_info
),
109 .receive_cb
= handle_play_to_sink
},
110 [METHOD_HANDLER_REMOVE
] = {
111 .method_name
= "Remove",
114 .receive_cb
= handle_remove
}
118 SIGNAL_PROPERTY_LIST_UPDATED
,
122 static pa_dbus_arg_info property_list_updated_args
[] = { { "property_list", "a{say}", NULL
} };
124 static pa_dbus_signal_info signals
[SIGNAL_MAX
] = {
125 [SIGNAL_PROPERTY_LIST_UPDATED
] = { .name
= "PropertyListUpdated", .arguments
= property_list_updated_args
, .n_arguments
= 1 }
128 static pa_dbus_interface_info sample_interface_info
= {
129 .name
= PA_DBUSIFACE_SAMPLE_INTERFACE
,
130 .method_handlers
= method_handlers
,
131 .n_method_handlers
= METHOD_HANDLER_MAX
,
132 .property_handlers
= property_handlers
,
133 .n_property_handlers
= PROPERTY_HANDLER_MAX
,
134 .get_all_properties_cb
= handle_get_all
,
136 .n_signals
= SIGNAL_MAX
139 static void handle_get_index(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
140 pa_dbusiface_sample
*s
= userdata
;
141 dbus_uint32_t idx
= 0;
147 idx
= s
->sample
->index
;
149 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, &idx
);
152 static void handle_get_name(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
153 pa_dbusiface_sample
*s
= userdata
;
159 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_STRING
, &s
->sample
->name
);
162 static void handle_get_sample_format(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
163 pa_dbusiface_sample
*s
= userdata
;
164 dbus_uint32_t sample_format
= 0;
170 if (!s
->sample
->memchunk
.memblock
) {
171 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
,
172 "Sample %s isn't loaded into memory yet, so its sample format is unknown.", s
->sample
->name
);
176 sample_format
= s
->sample
->sample_spec
.format
;
178 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, &sample_format
);
181 static void handle_get_sample_rate(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
182 pa_dbusiface_sample
*s
= userdata
;
183 dbus_uint32_t sample_rate
= 0;
189 if (!s
->sample
->memchunk
.memblock
) {
190 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
,
191 "Sample %s isn't loaded into memory yet, so its sample rate is unknown.", s
->sample
->name
);
195 sample_rate
= s
->sample
->sample_spec
.rate
;
197 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, &sample_rate
);
200 static void handle_get_channels(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
201 pa_dbusiface_sample
*s
= userdata
;
202 dbus_uint32_t channels
[PA_CHANNELS_MAX
];
209 if (!s
->sample
->memchunk
.memblock
) {
210 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
,
211 "Sample %s isn't loaded into memory yet, so its channel map is unknown.", s
->sample
->name
);
215 for (i
= 0; i
< s
->sample
->channel_map
.channels
; ++i
)
216 channels
[i
] = s
->sample
->channel_map
.map
[i
];
218 pa_dbus_send_basic_array_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, channels
, s
->sample
->channel_map
.channels
);
221 static void handle_get_default_volume(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
222 pa_dbusiface_sample
*s
= userdata
;
223 dbus_uint32_t default_volume
[PA_CHANNELS_MAX
];
230 if (!s
->sample
->volume_is_set
) {
231 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
,
232 "Sample %s doesn't have default volume stored.", s
->sample
->name
);
236 for (i
= 0; i
< s
->sample
->volume
.channels
; ++i
)
237 default_volume
[i
] = s
->sample
->volume
.values
[i
];
239 pa_dbus_send_basic_array_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, default_volume
, s
->sample
->volume
.channels
);
242 static void handle_get_duration(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
243 pa_dbusiface_sample
*s
= userdata
;
244 dbus_uint64_t duration
= 0;
250 if (!s
->sample
->memchunk
.memblock
) {
251 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
,
252 "Sample %s isn't loaded into memory yet, so its duration is unknown.", s
->sample
->name
);
256 duration
= pa_bytes_to_usec(s
->sample
->memchunk
.length
, &s
->sample
->sample_spec
);
258 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT64
, &duration
);
261 static void handle_get_bytes(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
262 pa_dbusiface_sample
*s
= userdata
;
263 dbus_uint32_t bytes
= 0;
269 if (!s
->sample
->memchunk
.memblock
) {
270 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
,
271 "Sample %s isn't loaded into memory yet, so its size is unknown.", s
->sample
->name
);
275 bytes
= s
->sample
->memchunk
.length
;
277 pa_dbus_send_basic_variant_reply(conn
, msg
, DBUS_TYPE_UINT32
, &bytes
);
280 static void handle_get_property_list(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
281 pa_dbusiface_sample
*s
= userdata
;
287 pa_dbus_send_proplist_variant_reply(conn
, msg
, s
->proplist
);
290 static void handle_get_all(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
291 pa_dbusiface_sample
*s
= userdata
;
292 DBusMessage
*reply
= NULL
;
293 DBusMessageIter msg_iter
;
294 DBusMessageIter dict_iter
;
295 dbus_uint32_t idx
= 0;
296 dbus_uint32_t sample_format
= 0;
297 dbus_uint32_t sample_rate
= 0;
298 dbus_uint32_t channels
[PA_CHANNELS_MAX
];
299 dbus_uint32_t default_volume
[PA_CHANNELS_MAX
];
300 dbus_uint64_t duration
= 0;
301 dbus_uint32_t bytes
= 0;
308 idx
= s
->sample
->index
;
309 if (s
->sample
->memchunk
.memblock
) {
310 sample_format
= s
->sample
->sample_spec
.format
;
311 sample_rate
= s
->sample
->sample_spec
.rate
;
312 for (i
= 0; i
< s
->sample
->channel_map
.channels
; ++i
)
313 channels
[i
] = s
->sample
->channel_map
.map
[i
];
314 duration
= pa_bytes_to_usec(s
->sample
->memchunk
.length
, &s
->sample
->sample_spec
);
315 bytes
= s
->sample
->memchunk
.length
;
317 if (s
->sample
->volume_is_set
) {
318 for (i
= 0; i
< s
->sample
->volume
.channels
; ++i
)
319 default_volume
[i
] = s
->sample
->volume
.values
[i
];
322 pa_assert_se((reply
= dbus_message_new_method_return(msg
)));
324 dbus_message_iter_init_append(reply
, &msg_iter
);
325 pa_assert_se(dbus_message_iter_open_container(&msg_iter
, DBUS_TYPE_ARRAY
, "{sv}", &dict_iter
));
327 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_INDEX
].property_name
, DBUS_TYPE_UINT32
, &idx
);
328 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_NAME
].property_name
, DBUS_TYPE_STRING
, &s
->sample
->name
);
330 if (s
->sample
->memchunk
.memblock
) {
331 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_SAMPLE_FORMAT
].property_name
, DBUS_TYPE_UINT32
, &sample_format
);
332 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_SAMPLE_RATE
].property_name
, DBUS_TYPE_UINT32
, &sample_rate
);
333 pa_dbus_append_basic_array_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_CHANNELS
].property_name
, DBUS_TYPE_UINT32
, channels
, s
->sample
->channel_map
.channels
);
336 if (s
->sample
->volume_is_set
)
337 pa_dbus_append_basic_array_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_DEFAULT_VOLUME
].property_name
, DBUS_TYPE_UINT32
, default_volume
, s
->sample
->volume
.channels
);
339 if (s
->sample
->memchunk
.memblock
) {
340 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_DURATION
].property_name
, DBUS_TYPE_UINT64
, &duration
);
341 pa_dbus_append_basic_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_BYTES
].property_name
, DBUS_TYPE_UINT32
, &bytes
);
344 pa_dbus_append_proplist_variant_dict_entry(&dict_iter
, property_handlers
[PROPERTY_HANDLER_PROPERTY_LIST
].property_name
, s
->proplist
);
346 pa_assert_se(dbus_message_iter_close_container(&msg_iter
, &dict_iter
));
347 pa_assert_se(dbus_connection_send(conn
, reply
, NULL
));
348 dbus_message_unref(reply
);
351 static void handle_play(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
352 pa_dbusiface_sample
*s
= userdata
;
353 DBusMessageIter msg_iter
;
354 dbus_uint32_t volume
= 0;
355 pa_proplist
*property_list
= NULL
;
356 pa_sink
*sink
= NULL
;
362 pa_assert_se(dbus_message_iter_init(msg
, &msg_iter
));
363 dbus_message_iter_get_basic(&msg_iter
, &volume
);
365 pa_assert_se(dbus_message_iter_next(&msg_iter
));
366 if (!(property_list
= pa_dbus_get_proplist_arg(conn
, msg
, &msg_iter
)))
369 if (volume
> PA_VOLUME_MAX
) {
370 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
, "Invalid volume.");
374 if (!(sink
= pa_namereg_get_default_sink(s
->sample
->core
))) {
375 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_FAILED
,
376 "Can't play sample %s, because there are no sinks available.", s
->sample
->name
);
380 if (pa_scache_play_item(s
->sample
->core
, s
->sample
->name
, sink
, volume
, property_list
, NULL
) < 0) {
381 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_FAILED
, "Playing sample %s failed.", s
->sample
->name
);
385 pa_dbus_send_empty_reply(conn
, msg
);
389 pa_proplist_free(property_list
);
392 static void handle_play_to_sink(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
393 pa_dbusiface_sample
*s
= userdata
;
394 DBusMessageIter msg_iter
;
395 const char *sink_path
= NULL
;
396 dbus_uint32_t volume
= 0;
397 pa_proplist
*property_list
= NULL
;
398 pa_sink
*sink
= NULL
;
404 pa_assert_se(dbus_message_iter_init(msg
, &msg_iter
));
405 dbus_message_iter_get_basic(&msg_iter
, &sink_path
);
407 pa_assert_se(dbus_message_iter_next(&msg_iter
));
408 dbus_message_iter_get_basic(&msg_iter
, &volume
);
410 pa_assert_se(dbus_message_iter_next(&msg_iter
));
411 if (!(property_list
= pa_dbus_get_proplist_arg(conn
, msg
, &msg_iter
)))
414 if (!(sink
= pa_dbusiface_core_get_sink(s
->core
, sink_path
))) {
415 pa_dbus_send_error(conn
, msg
, PA_DBUS_ERROR_NOT_FOUND
, "%s: No such sink.", sink_path
);
419 if (volume
> PA_VOLUME_MAX
) {
420 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_INVALID_ARGS
, "Invalid volume.");
424 if (pa_scache_play_item(s
->sample
->core
, s
->sample
->name
, sink
, volume
, property_list
, NULL
) < 0) {
425 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_FAILED
, "Playing sample %s failed.", s
->sample
->name
);
429 pa_dbus_send_empty_reply(conn
, msg
);
433 pa_proplist_free(property_list
);
436 static void handle_remove(DBusConnection
*conn
, DBusMessage
*msg
, void *userdata
) {
437 pa_dbusiface_sample
*s
= userdata
;
443 if (pa_scache_remove_item(s
->sample
->core
, s
->sample
->name
) < 0) {
444 pa_dbus_send_error(conn
, msg
, DBUS_ERROR_FAILED
, "Removing sample %s failed.", s
->sample
->name
);
448 pa_dbus_send_empty_reply(conn
, msg
);
451 static void subscription_cb(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
452 pa_dbusiface_sample
*s
= userdata
;
453 DBusMessage
*signal
= NULL
;
458 /* We can't use idx != s->sample->index, because the s->sample pointer may
459 * be stale at this point. */
460 if (pa_idxset_get_by_index(c
->scache
, idx
) != s
->sample
)
463 if ((t
& PA_SUBSCRIPTION_EVENT_TYPE_MASK
) != PA_SUBSCRIPTION_EVENT_CHANGE
)
466 if (!pa_proplist_equal(s
->proplist
, s
->sample
->proplist
)) {
467 DBusMessageIter msg_iter
;
469 pa_proplist_update(s
->proplist
, PA_UPDATE_SET
, s
->sample
->proplist
);
471 pa_assert_se(signal
= dbus_message_new_signal(s
->path
,
472 PA_DBUSIFACE_SAMPLE_INTERFACE
,
473 signals
[SIGNAL_PROPERTY_LIST_UPDATED
].name
));
474 dbus_message_iter_init_append(signal
, &msg_iter
);
475 pa_dbus_append_proplist(&msg_iter
, s
->proplist
);
477 pa_dbus_protocol_send_signal(s
->dbus_protocol
, signal
);
478 dbus_message_unref(signal
);
483 pa_dbusiface_sample
*pa_dbusiface_sample_new(pa_dbusiface_core
*core
, pa_scache_entry
*sample
) {
484 pa_dbusiface_sample
*s
= NULL
;
489 s
= pa_xnew0(pa_dbusiface_sample
, 1);
492 s
->path
= pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH
, OBJECT_NAME
, sample
->index
);
493 s
->proplist
= pa_proplist_copy(sample
->proplist
);
494 s
->dbus_protocol
= pa_dbus_protocol_get(sample
->core
);
495 s
->subscription
= pa_subscription_new(sample
->core
, PA_SUBSCRIPTION_MASK_SAMPLE_CACHE
, subscription_cb
, s
);
497 pa_assert_se(pa_dbus_protocol_add_interface(s
->dbus_protocol
, s
->path
, &sample_interface_info
, s
) >= 0);
502 void pa_dbusiface_sample_free(pa_dbusiface_sample
*s
) {
505 pa_assert_se(pa_dbus_protocol_remove_interface(s
->dbus_protocol
, s
->path
, sample_interface_info
.name
) >= 0);
507 pa_proplist_free(s
->proplist
);
508 pa_dbus_protocol_unref(s
->dbus_protocol
);
509 pa_subscription_free(s
->subscription
);
515 const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample
*s
) {