From 2bb3eef414f80189cf6af6cd66c519630e4c0a43 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 16 Aug 2009 19:42:56 +0300 Subject: dbusiface-stream: Implement about a half of the Stream D-Bus interface. --- src/modules/dbus/iface-stream.c | 383 ++++++++++++++++++++++++++++++++++++++++ src/modules/dbus/iface-stream.h | 3 + 2 files changed, 386 insertions(+) diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index b5a17894..4333c16f 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -2,6 +2,7 @@ This file is part of PulseAudio. Copyright 2009 Tanu Kaskinen + Copyright 2009 Vincent Filali-Ansary PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -24,6 +25,7 @@ #endif #include +#include #include #include "iface-stream.h" @@ -43,8 +45,369 @@ struct pa_dbusiface_stream { }; enum stream_type type; char *path; + pa_cvolume volume; + pa_bool_t is_muted; + pa_proplist *proplist; + + pa_dbus_protocol *dbus_protocol; + pa_subscription *subscription; +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +/*static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +/*static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +/*static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ + +enum property_handler_index { + PROPERTY_HANDLER_INDEX, +/* PROPERTY_HANDLER_DRIVER, + PROPERTY_HANDLER_OWNER_MODULE, + PROPERTY_HANDLER_CLIENT, + PROPERTY_HANDLER_DEVICE, + PROPERTY_HANDLER_SAMPLE_FORMAT, + PROPERTY_HANDLER_SAMPLE_RATE, + PROPERTY_HANDLER_CHANNELS,*/ + PROPERTY_HANDLER_VOLUME, + PROPERTY_HANDLER_IS_MUTED, +/* PROPERTY_HANDLER_BUFFER_LATENCY, + PROPERTY_HANDLER_DEVICE_LATENCY, + PROPERTY_HANDLER_RESAMPLE_METHOD,*/ + PROPERTY_HANDLER_PROPERTY_LIST, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, +/* [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL }, + [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL }, + [PROPERTY_HANDLER_CLIENT] = { .property_name = "Client", .type = "o", .get_cb = handle_get_client, .set_cb = NULL }, + [PROPERTY_HANDLER_DEVICE] = { .property_name = "Device", .type = "o", .get_cb = handle_get_device, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_FORMAT] = { .property_name = "SampleFormat", .type = "u", .get_cb = handle_get_sample_format, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL }, + [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL },*/ + [PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "au", .get_cb = handle_get_volume, .set_cb = handle_set_volume }, + [PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_get_is_muted, .set_cb = handle_set_is_muted }, +/* [PROPERTY_HANDLER_BUFFER_LATENCY] = { .property_name = "BufferLatency", .type = "t", .get_cb = handle_get_buffer_latency, .set_cb = NULL }, + [PROPERTY_HANDLER_DEVICE_LATENCY] = { .property_name = "DeviceLatency", .type = "t", .get_cb = handle_get_device_latency, .set_cb = NULL }, + [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s", .get_cb = handle_get_resample_method, .set_cb = NULL },*/ + [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } +}; + +/*enum method_handler_index { + METHOD_HANDLER_MOVE, + METHOD_HANDLER_KILL, + METHOD_HANDLER_MAX +}; + +static pa_dbus_arg_info move_args[] = { { "device", "o", "in" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_MOVE] = { + .method_name = "Move", + .arguments = move_args, + .n_arguments = sizeof(move_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_move }, + [METHOD_HANDLER_KILL] = { + .method_name = "Kill", + .arguments = NULL, + .n_arguments = 0, + .receive_cb = handle_kill } +};*/ + +enum signal_index { +/* SIGNAL_DEVICE_UPDATED, + SIGNAL_SAMPLE_RATE_UPDATED,*/ + SIGNAL_VOLUME_UPDATED, + SIGNAL_MUTE_UPDATED, + SIGNAL_PROPERTY_LIST_UPDATED, +/* SIGNAL_STREAM_EVENT,*/ + SIGNAL_MAX +}; + +/*static pa_dbus_arg_info device_updated_args[] = { { "device", "o", NULL } }; +static pa_dbus_arg_info sample_rate_updated_args[] = { { "sample_rate", "u", NULL } };*/ +static pa_dbus_arg_info volume_updated_args[] = { { "volume", "au", NULL } }; +static pa_dbus_arg_info mute_updated_args[] = { { "muted", "b", NULL } }; +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; +/*static pa_dbus_arg_info stream_event_args[] = { { "name", "s", NULL }, { "property_list", "a{say}", NULL } };*/ + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { +/* [SIGNAL_DEVICE_UPDATED] = { .name = "DeviceUpdated", .arguments = device_updated_args, .n_arguments = 1 }, + [SIGNAL_SAMPLE_RATE_UPDATED] = { .name = "SampleRateUpdated", .arguments = sample_rate_updated_args, .n_arguments = 1 },*/ + [SIGNAL_VOLUME_UPDATED] = { .name = "VolumeUpdated", .arguments = volume_updated_args, .n_arguments = 1 }, + [SIGNAL_MUTE_UPDATED] = { .name = "MuteUpdated", .arguments = mute_updated_args, .n_arguments = 1 }, + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }/*, + [SIGNAL_STREAM_EVENT] = { .name = "StreamEvent", .arguments = stream_event_args, .n_arguments = sizeof(stream_event_args) / sizeof(pa_dbus_arg_info) }*/ }; +static pa_dbus_interface_info stream_interface_info = { + .name = PA_DBUSIFACE_STREAM_INTERFACE, + .method_handlers = /*method_handlers*/ NULL, + .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_uint32_t idx; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); +} + +static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_uint32_t volume[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_RECORD) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume."); + return; + } + + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, s->volume.channels); +} + +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + unsigned stream_channels = 0; + dbus_uint32_t *volume = NULL; + unsigned n_volume_entries = 0; + pa_cvolume new_vol; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_RECORD) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume."); + return; + } + + pa_cvolume_init(&new_vol); + + stream_channels = s->sink_input->channel_map.channels; + + new_vol.channels = stream_channels; + + if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &volume, &n_volume_entries) < 0) + return; + + if (n_volume_entries != stream_channels) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", stream_channels, n_volume_entries); + return; + } + + for (i = 0; i < n_volume_entries; ++i) { + if (volume[i] > PA_VOLUME_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too large volume value: %u", volume[i]); + return; + } + new_vol.values[i] = volume[i]; + } + + pa_sink_input_set_volume(s->sink_input, &new_vol, TRUE, TRUE); + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_RECORD) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute."); + return; + } + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->is_muted); +} + +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_bool_t is_muted = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &is_muted) < 0) + return; + + if (s->type == STREAM_TYPE_RECORD) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute."); + return; + } + + pa_sink_input_set_mute(s->sink_input, is_muted, TRUE); + + pa_dbus_send_empty_reply(conn, msg); +}; + +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + pa_dbus_send_proplist_variant_reply(conn, msg, s->proplist); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t idx; + dbus_uint32_t volume[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index; + if (s->type == STREAM_TYPE_PLAYBACK) { + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; + } + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + + if (s->type == STREAM_TYPE_PLAYBACK) { + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &s->is_muted); + } + + pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_stream *s = userdata; + + pa_assert(c); + pa_assert(s); + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { + DBusMessage *signal = NULL; + pa_proplist *new_proplist = NULL; + unsigned i = 0; + + pa_assert(((s->type == STREAM_TYPE_PLAYBACK) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT)) + || ((s->type == STREAM_TYPE_RECORD) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT))); + + if (s->type == STREAM_TYPE_PLAYBACK) { + pa_cvolume new_volume; + pa_bool_t new_muted = FALSE; + + pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE); + + if (!pa_cvolume_equal(&s->volume, &new_volume)) { + dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_uint32_t *volume_ptr = volume; + + s->volume = new_volume; + + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_VOLUME_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels, + DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + new_muted = pa_sink_input_get_mute(s->sink_input); + + if (s->is_muted != new_muted) { + s->is_muted = new_muted; + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_MUTE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->is_muted, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } + + new_proplist = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->proplist : s->source_output->proplist; + + if (!pa_proplist_equal(s->proplist, new_proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(s->proplist, PA_UPDATE_SET, new_proplist); + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, s->proplist); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } +} + pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) { pa_dbusiface_stream *s; @@ -55,6 +418,13 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p s->sink_input = pa_sink_input_ref(sink_input); s->type = STREAM_TYPE_PLAYBACK; s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index); + pa_sink_input_get_volume(sink_input, &s->volume, TRUE); + s->is_muted = pa_sink_input_get_mute(sink_input); + s->proplist = pa_proplist_copy(sink_input->proplist); + s->dbus_protocol = pa_dbus_protocol_get(sink_input->core); + s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s); + + pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0); return s; } @@ -69,6 +439,13 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_ s->source_output = pa_source_output_ref(source_output); s->type = STREAM_TYPE_RECORD; s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, RECORD_OBJECT_NAME, source_output->index); + pa_cvolume_init(&s->volume); + s->is_muted = FALSE; + s->proplist = pa_proplist_copy(source_output->proplist); + s->dbus_protocol = pa_dbus_protocol_get(source_output->core); + s->subscription = pa_subscription_new(source_output->core, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscription_cb, s); + + pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0); return s; } @@ -76,11 +453,17 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_ void pa_dbusiface_stream_free(pa_dbusiface_stream *s) { pa_assert(s); + pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, stream_interface_info.name) >= 0); + if (s->type == STREAM_TYPE_PLAYBACK) pa_sink_input_unref(s->sink_input); else pa_source_output_unref(s->source_output); + pa_proplist_free(s->proplist); + pa_dbus_protocol_unref(s->dbus_protocol); + pa_subscription_free(s->subscription); + pa_xfree(s->path); pa_xfree(s); } diff --git a/src/modules/dbus/iface-stream.h b/src/modules/dbus/iface-stream.h index b1b1854b..036b4e7e 100644 --- a/src/modules/dbus/iface-stream.h +++ b/src/modules/dbus/iface-stream.h @@ -28,11 +28,14 @@ * documentation. */ +#include #include #include #include "iface-core.h" +#define PA_DBUSIFACE_STREAM_INTERFACE PA_DBUS_CORE_INTERFACE ".Stream" + typedef struct pa_dbusiface_stream pa_dbusiface_stream; pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input); -- cgit