1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2009 Tanu Kaskinen 5 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. 10 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. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <pulsecore/core-util.h> 25#include <pulsecore/dbus-util.h> 26#include <pulsecore/namereg.h> 27#include <pulsecore/protocol-dbus.h> 28 29#include "iface-sample.h" 30 31#define OBJECT_NAME "sample" 32 33struct pa_dbusiface_sample { 34 pa_dbusiface_core *core; 35 36 pa_scache_entry *sample; 37 char *path; 38 pa_proplist *proplist; 39 40 pa_hook_slot *sample_cache_changed_slot; 41 42 pa_dbus_protocol *dbus_protocol; 43}; 44 45static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); 46static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); 47static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); 48static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); 49static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); 50static void handle_get_default_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); 51static void handle_get_duration(DBusConnection *conn, DBusMessage *msg, void *userdata); 52static void handle_get_bytes(DBusConnection *conn, DBusMessage *msg, void *userdata); 53static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); 54 55static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); 56 57static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata); 58static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *userdata); 59static void handle_remove(DBusConnection *conn, DBusMessage *msg, void *userdata); 60 61enum property_handler_index { 62 PROPERTY_HANDLER_INDEX, 63 PROPERTY_HANDLER_NAME, 64 PROPERTY_HANDLER_SAMPLE_FORMAT, 65 PROPERTY_HANDLER_SAMPLE_RATE, 66 PROPERTY_HANDLER_CHANNELS, 67 PROPERTY_HANDLER_DEFAULT_VOLUME, 68 PROPERTY_HANDLER_DURATION, 69 PROPERTY_HANDLER_BYTES, 70 PROPERTY_HANDLER_PROPERTY_LIST, 71 PROPERTY_HANDLER_MAX 72}; 73 74static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { 75 [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, 76 [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, 77 [PROPERTY_HANDLER_SAMPLE_FORMAT] = { .property_name = "SampleFormat", .type = "u", .get_cb = handle_get_sample_format, .set_cb = NULL }, 78 [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL }, 79 [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL }, 80 [PROPERTY_HANDLER_DEFAULT_VOLUME] = { .property_name = "DefaultVolume", .type = "au", .get_cb = handle_get_default_volume, .set_cb = NULL }, 81 [PROPERTY_HANDLER_DURATION] = { .property_name = "Duration", .type = "t", .get_cb = handle_get_duration, .set_cb = NULL }, 82 [PROPERTY_HANDLER_BYTES] = { .property_name = "Bytes", .type = "u", .get_cb = handle_get_bytes, .set_cb = NULL }, 83 [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } 84}; 85 86enum method_handler_index { 87 METHOD_HANDLER_PLAY, 88 METHOD_HANDLER_PLAY_TO_SINK, 89 METHOD_HANDLER_REMOVE, 90 METHOD_HANDLER_MAX 91}; 92 93static pa_dbus_arg_info play_args[] = { { "volume", "u", "in" }, { "property_list", "a{say}", "in" } }; 94static pa_dbus_arg_info play_to_sink_args[] = { { "sink", "o", "in" }, 95 { "volume", "u", "in" }, 96 { "property_list", "a{say}", "in" } }; 97 98static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { 99 [METHOD_HANDLER_PLAY] = { 100 .method_name = "Play", 101 .arguments = play_args, 102 .n_arguments = sizeof(play_args) / sizeof(pa_dbus_arg_info), 103 .receive_cb = handle_play }, 104 [METHOD_HANDLER_PLAY_TO_SINK] = { 105 .method_name = "PlayToSink", 106 .arguments = play_to_sink_args, 107 .n_arguments = sizeof(play_to_sink_args) / sizeof(pa_dbus_arg_info), 108 .receive_cb = handle_play_to_sink }, 109 [METHOD_HANDLER_REMOVE] = { 110 .method_name = "Remove", 111 .arguments = NULL, 112 .n_arguments = 0, 113 .receive_cb = handle_remove } 114}; 115 116enum signal_index { 117 SIGNAL_PROPERTY_LIST_UPDATED, 118 SIGNAL_MAX 119}; 120 121static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; 122 123static pa_dbus_signal_info signals[SIGNAL_MAX] = { 124 [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } 125}; 126 127static pa_dbus_interface_info sample_interface_info = { 128 .name = PA_DBUSIFACE_SAMPLE_INTERFACE, 129 .method_handlers = method_handlers, 130 .n_method_handlers = METHOD_HANDLER_MAX, 131 .property_handlers = property_handlers, 132 .n_property_handlers = PROPERTY_HANDLER_MAX, 133 .get_all_properties_cb = handle_get_all, 134 .signals = signals, 135 .n_signals = SIGNAL_MAX 136}; 137 138static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { 139 pa_dbusiface_sample *s = userdata; 140 dbus_uint32_t idx = 0; 141 142 pa_assert(conn); 143 pa_assert(msg); 144 pa_assert(s); 145 146 idx = s->sample->index; 147 148 pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); 149} 150 151static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { 152 pa_dbusiface_sample *s = userdata; 153 154 pa_assert(conn); 155 pa_assert(msg); 156 pa_assert(s); 157 158 pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &s->sample->name); 159} 160 161static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) { 162 pa_dbusiface_sample *s = userdata; 163 dbus_uint32_t sample_format = 0; 164 165 pa_assert(conn); 166 pa_assert(msg); 167 pa_assert(s); 168 169 if (!s->sample->memchunk.memblock) { 170 pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, 171 "Sample %s isn't loaded into memory yet, so its sample format is unknown.", s->sample->name); 172 return; 173 } 174 175 sample_format = s->sample->sample_spec.format; 176 177 pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format); 178} 179 180static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) { 181 pa_dbusiface_sample *s = userdata; 182 dbus_uint32_t sample_rate = 0; 183 184 pa_assert(conn); 185 pa_assert(msg); 186 pa_assert(s); 187 188 if (!s->sample->memchunk.memblock) { 189 pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, 190 "Sample %s isn't loaded into memory yet, so its sample rate is unknown.", s->sample->name); 191 return; 192 } 193 194 sample_rate = s->sample->sample_spec.rate; 195 196 pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_rate); 197} 198 199static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { 200 pa_dbusiface_sample *s = userdata; 201 dbus_uint32_t channels[PA_CHANNELS_MAX]; 202 unsigned i = 0; 203 204 pa_assert(conn); 205 pa_assert(msg); 206 pa_assert(s); 207 208 if (!s->sample->memchunk.memblock) { 209 pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, 210 "Sample %s isn't loaded into memory yet, so its channel map is unknown.", s->sample->name); 211 return; 212 } 213 214 for (i = 0; i < s->sample->channel_map.channels; ++i) 215 channels[i] = s->sample->channel_map.map[i]; 216 217 pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, s->sample->channel_map.channels); 218} 219 220static void handle_get_default_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { 221 pa_dbusiface_sample *s = userdata; 222 dbus_uint32_t default_volume[PA_CHANNELS_MAX]; 223 unsigned i = 0; 224 225 pa_assert(conn); 226 pa_assert(msg); 227 pa_assert(s); 228 229 if (!s->sample->volume_is_set) { 230 pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, 231 "Sample %s doesn't have default volume stored.", s->sample->name); 232 return; 233 } 234 235 for (i = 0; i < s->sample->volume.channels; ++i) 236 default_volume[i] = s->sample->volume.values[i]; 237 238 pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, default_volume, s->sample->volume.channels); 239} 240 241static void handle_get_duration(DBusConnection *conn, DBusMessage *msg, void *userdata) { 242 pa_dbusiface_sample *s = userdata; 243 dbus_uint64_t duration = 0; 244 245 pa_assert(conn); 246 pa_assert(msg); 247 pa_assert(s); 248 249 if (!s->sample->memchunk.memblock) { 250 pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, 251 "Sample %s isn't loaded into memory yet, so its duration is unknown.", s->sample->name); 252 return; 253 } 254 255 duration = pa_bytes_to_usec(s->sample->memchunk.length, &s->sample->sample_spec); 256 257 pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &duration); 258} 259 260static void handle_get_bytes(DBusConnection *conn, DBusMessage *msg, void *userdata) { 261 pa_dbusiface_sample *s = userdata; 262 dbus_uint32_t bytes = 0; 263 264 pa_assert(conn); 265 pa_assert(msg); 266 pa_assert(s); 267 268 if (!s->sample->memchunk.memblock) { 269 pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, 270 "Sample %s isn't loaded into memory yet, so its size is unknown.", s->sample->name); 271 return; 272 } 273 274 bytes = s->sample->memchunk.length; 275 276 pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &bytes); 277} 278 279static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { 280 pa_dbusiface_sample *s = userdata; 281 282 pa_assert(conn); 283 pa_assert(msg); 284 pa_assert(s); 285 286 pa_dbus_send_proplist_variant_reply(conn, msg, s->proplist); 287} 288 289static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { 290 pa_dbusiface_sample *s = userdata; 291 DBusMessage *reply = NULL; 292 DBusMessageIter msg_iter; 293 DBusMessageIter dict_iter; 294 dbus_uint32_t idx = 0; 295 dbus_uint32_t sample_format = 0; 296 dbus_uint32_t sample_rate = 0; 297 dbus_uint32_t channels[PA_CHANNELS_MAX]; 298 dbus_uint32_t default_volume[PA_CHANNELS_MAX]; 299 dbus_uint64_t duration = 0; 300 dbus_uint32_t bytes = 0; 301 unsigned i = 0; 302 303 pa_assert(conn); 304 pa_assert(msg); 305 pa_assert(s); 306 307 idx = s->sample->index; 308 if (s->sample->memchunk.memblock) { 309 sample_format = s->sample->sample_spec.format; 310 sample_rate = s->sample->sample_spec.rate; 311 for (i = 0; i < s->sample->channel_map.channels; ++i) 312 channels[i] = s->sample->channel_map.map[i]; 313 duration = pa_bytes_to_usec(s->sample->memchunk.length, &s->sample->sample_spec); 314 bytes = s->sample->memchunk.length; 315 } 316 if (s->sample->volume_is_set) { 317 for (i = 0; i < s->sample->volume.channels; ++i) 318 default_volume[i] = s->sample->volume.values[i]; 319 } 320 321 pa_assert_se((reply = dbus_message_new_method_return(msg))); 322 323 dbus_message_iter_init_append(reply, &msg_iter); 324 pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); 325 326 pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); 327 pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &s->sample->name); 328 329 if (s->sample->memchunk.memblock) { 330 pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format); 331 pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &sample_rate); 332 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); 333 } 334 335 if (s->sample->volume_is_set) 336 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); 337 338 if (s->sample->memchunk.memblock) { 339 pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DURATION].property_name, DBUS_TYPE_UINT64, &duration); 340 pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BYTES].property_name, DBUS_TYPE_UINT32, &bytes); 341 } 342 343 pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist); 344 345 pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); 346 pa_assert_se(dbus_connection_send(conn, reply, NULL)); 347 dbus_message_unref(reply); 348} 349 350static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata) { 351 pa_dbusiface_sample *s = userdata; 352 DBusMessageIter msg_iter; 353 dbus_uint32_t volume = 0; 354 pa_proplist *property_list = NULL; 355 356 pa_assert(conn); 357 pa_assert(msg); 358 pa_assert(s); 359 360 pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); 361 dbus_message_iter_get_basic(&msg_iter, &volume); 362 363 pa_assert_se(dbus_message_iter_next(&msg_iter)); 364 if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) 365 return; 366 367 if (!PA_VOLUME_IS_VALID(volume)) { 368 pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume."); 369 goto finish; 370 } 371 372 if (!s->sample->core->default_sink) { 373 pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, 374 "Can't play sample %s, because there are no sinks available.", s->sample->name); 375 goto finish; 376 } 377 378 if (pa_scache_play_item(s->sample->core, 379 s->sample->name, 380 s->sample->core->default_sink, 381 volume, 382 property_list, 383 NULL) < 0) { 384 pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Playing sample %s failed.", s->sample->name); 385 goto finish; 386 } 387 388 pa_dbus_send_empty_reply(conn, msg); 389 390finish: 391 if (property_list) 392 pa_proplist_free(property_list); 393} 394 395static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) { 396 pa_dbusiface_sample *s = userdata; 397 DBusMessageIter msg_iter; 398 const char *sink_path = NULL; 399 dbus_uint32_t volume = 0; 400 pa_proplist *property_list = NULL; 401 pa_sink *sink = NULL; 402 403 pa_assert(conn); 404 pa_assert(msg); 405 pa_assert(s); 406 407 pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); 408 dbus_message_iter_get_basic(&msg_iter, &sink_path); 409 410 pa_assert_se(dbus_message_iter_next(&msg_iter)); 411 dbus_message_iter_get_basic(&msg_iter, &volume); 412 413 pa_assert_se(dbus_message_iter_next(&msg_iter)); 414 if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) 415 return; 416 417 if (!(sink = pa_dbusiface_core_get_sink(s->core, sink_path))) { 418 pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_path); 419 goto finish; 420 } 421 422 if (!PA_VOLUME_IS_VALID(volume)) { 423 pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume."); 424 goto finish; 425 } 426 427 if (pa_scache_play_item(s->sample->core, s->sample->name, sink, volume, property_list, NULL) < 0) { 428 pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Playing sample %s failed.", s->sample->name); 429 goto finish; 430 } 431 432 pa_dbus_send_empty_reply(conn, msg); 433 434finish: 435 if (property_list) 436 pa_proplist_free(property_list); 437} 438 439static void handle_remove(DBusConnection *conn, DBusMessage *msg, void *userdata) { 440 pa_dbusiface_sample *s = userdata; 441 442 pa_assert(conn); 443 pa_assert(msg); 444 pa_assert(s); 445 446 if (pa_scache_remove_item(s->sample->core, s->sample->name) < 0) { 447 pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Removing sample %s failed.", s->sample->name); 448 return; 449 } 450 451 pa_dbus_send_empty_reply(conn, msg); 452} 453 454static pa_hook_result_t sample_cache_changed_cb(void *hook_data, void *call_data, void *slot_data) { 455 pa_dbusiface_sample *sample_iface = slot_data; 456 pa_scache_entry *sample = call_data; 457 DBusMessage *signal_msg; 458 459 pa_assert(sample); 460 pa_assert(sample_iface); 461 462 if (sample_iface->sample != sample) 463 return PA_HOOK_OK; 464 465 if (!pa_proplist_equal(sample_iface->proplist, sample_iface->sample->proplist)) { 466 DBusMessageIter msg_iter; 467 468 pa_proplist_update(sample_iface->proplist, PA_UPDATE_SET, sample_iface->sample->proplist); 469 470 pa_assert_se(signal_msg = dbus_message_new_signal(sample_iface->path, 471 PA_DBUSIFACE_SAMPLE_INTERFACE, 472 signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); 473 dbus_message_iter_init_append(signal_msg, &msg_iter); 474 pa_dbus_append_proplist(&msg_iter, sample_iface->proplist); 475 476 pa_dbus_protocol_send_signal(sample_iface->dbus_protocol, signal_msg); 477 dbus_message_unref(signal_msg); 478 } 479 480 return PA_HOOK_OK; 481} 482 483pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample) { 484 pa_dbusiface_sample *s = NULL; 485 486 pa_assert(core); 487 pa_assert(sample); 488 489 s = pa_xnew0(pa_dbusiface_sample, 1); 490 s->core = core; 491 s->sample = sample; 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->sample_cache_changed_slot = pa_hook_connect(&sample->core->hooks[PA_CORE_HOOK_SAMPLE_CACHE_CHANGED], 496 PA_HOOK_NORMAL, sample_cache_changed_cb, s); 497 498 pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &sample_interface_info, s) >= 0); 499 500 return s; 501} 502 503void pa_dbusiface_sample_free(pa_dbusiface_sample *s) { 504 pa_assert(s); 505 506 pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, sample_interface_info.name) >= 0); 507 508 pa_hook_slot_free(s->sample_cache_changed_slot); 509 pa_proplist_free(s->proplist); 510 pa_dbus_protocol_unref(s->dbus_protocol); 511 512 pa_xfree(s->path); 513 pa_xfree(s); 514} 515 516const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *s) { 517 pa_assert(s); 518 519 return s->path; 520} 521