diff options
Diffstat (limited to 'src/modules/dbus/iface-stream.c')
| -rw-r--r-- | src/modules/dbus/iface-stream.c | 894 | 
1 files changed, 894 insertions, 0 deletions
| diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c new file mode 100644 index 00000000..04a45e6c --- /dev/null +++ b/src/modules/dbus/iface-stream.c @@ -0,0 +1,894 @@ +/*** +  This file is part of PulseAudio. + +  Copyright 2009 Tanu Kaskinen +  Copyright 2009 Vincent Filali-Ansary <filali.v@azurdigitalnetworks.net> + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as published +  by the Free Software Foundation; either version 2.1 of the License, +  or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pulsecore/core-util.h> +#include <pulsecore/dbus-util.h> +#include <pulsecore/protocol-dbus.h> + +#include "iface-stream.h" + +#define PLAYBACK_OBJECT_NAME "playback_stream" +#define RECORD_OBJECT_NAME "record_stream" + +enum stream_type { +    STREAM_TYPE_PLAYBACK, +    STREAM_TYPE_RECORD +}; + +struct pa_dbusiface_stream { +    pa_dbusiface_core *core; + +    union { +        pa_sink_input *sink_input; +        pa_source_output *source_output; +    }; +    enum stream_type type; +    char *path; +    union { +        pa_sink *sink; +        pa_source *source; +    }; +    uint32_t sample_rate; +    pa_cvolume volume; +    dbus_bool_t mute; +    pa_proplist *proplist; + +    pa_dbus_protocol *dbus_protocol; +    pa_subscription *subscription; +    pa_hook_slot *send_event_slot; +}; + +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, DBusMessageIter *iter, void *userdata); +static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, 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_MUTE, +    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_MUTE]            = { .property_name = "Mute",           .type = "b",      .get_cb = handle_get_mute,            .set_cb = handle_set_mute }, +    [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, +    .n_method_handlers = METHOD_HANDLER_MAX, +    .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_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) { +    pa_dbusiface_stream *s = userdata; +    const char *driver = NULL; + +    pa_assert(conn); +    pa_assert(msg); +    pa_assert(s); + +    driver = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->driver : s->source_output->driver; + +    if (!driver) { +        if (s->type == STREAM_TYPE_PLAYBACK) +            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, +                               "Playback stream %u doesn't have a driver.", s->sink_input->index); +        else +            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, +                               "Record stream %u doesn't have a driver.", s->source_output->index); +        return; +    } + +    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &driver); +} + +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) { +    pa_dbusiface_stream *s = userdata; +    pa_module *owner_module = NULL; +    const char *object_path = NULL; + +    pa_assert(conn); +    pa_assert(msg); +    pa_assert(s); + +    owner_module = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->module : s->source_output->module; + +    if (!owner_module) { +        if (s->type == STREAM_TYPE_PLAYBACK) +            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, +                               "Playback stream %u doesn't have an owner module.", s->sink_input->index); +        else +            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, +                               "Record stream %u doesn't have an owner module.", s->source_output->index); +        return; +    } + +    object_path = pa_dbusiface_core_get_module_path(s->core, owner_module); + +    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata) { +    pa_dbusiface_stream *s = userdata; +    pa_client *client = NULL; +    const char *object_path = NULL; + +    pa_assert(conn); +    pa_assert(msg); +    pa_assert(s); + +    client = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->client : s->source_output->client; + +    if (!client) { +        if (s->type == STREAM_TYPE_PLAYBACK) +            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, +                               "Playback stream %u isn't associated to any client.", s->sink_input->index); +        else +            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, +                               "Record stream %u isn't associated to any client.", s->source_output->index); +        return; +    } + +    object_path = pa_dbusiface_core_get_client_path(s->core, client); + +    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata) { +    pa_dbusiface_stream *s = userdata; +    const char *device = NULL; + +    pa_assert(conn); +    pa_assert(msg); +    pa_assert(s); + +    if (s->type == STREAM_TYPE_PLAYBACK) +        device = pa_dbusiface_core_get_sink_path(s->core, s->sink); +    else +        device = pa_dbusiface_core_get_source_path(s->core, s->source); + +    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &device); +} + +static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) { +    pa_dbusiface_stream *s = userdata; +    dbus_uint32_t sample_format = 0; + +    pa_assert(conn); +    pa_assert(msg); +    pa_assert(s); + +    sample_format = (s->type == STREAM_TYPE_PLAYBACK) +                    ? s->sink_input->sample_spec.format +                    : s->source_output->sample_spec.format; + +    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format); +} + +static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) { +    pa_dbusiface_stream *s = userdata; + +    pa_assert(conn); +    pa_assert(msg); +    pa_assert(s); + +    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &s->sample_rate); +} + +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { +    pa_dbusiface_stream *s = userdata; +    pa_channel_map *channel_map = NULL; +    dbus_uint32_t channels[PA_CHANNELS_MAX]; +    unsigned i = 0; + +    pa_assert(conn); +    pa_assert(msg); +    pa_assert(s); + +    channel_map = (s->type == STREAM_TYPE_PLAYBACK) ? &s->sink_input->channel_map : &s->source_output->channel_map; + +    for (i = 0; i < channel_map->channels; ++i) +        channels[i] = channel_map->map[i]; + +    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, channel_map->channels); +} + +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, DBusMessageIter *iter, void *userdata) { +    pa_dbusiface_stream *s = userdata; +    DBusMessageIter array_iter; +    int stream_channels = 0; +    dbus_uint32_t *volume = NULL; +    int n_volume_entries = 0; +    pa_cvolume new_vol; +    int i = 0; + +    pa_assert(conn); +    pa_assert(msg); +    pa_assert(iter); +    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; + +    dbus_message_iter_recurse(iter, &array_iter); +    dbus_message_iter_get_fixed_array(&array_iter, &volume, &n_volume_entries); + +    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_mute(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->mute); +} + +static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { +    pa_dbusiface_stream *s = userdata; +    dbus_bool_t mute = FALSE; + +    pa_assert(conn); +    pa_assert(msg); +    pa_assert(iter); +    pa_assert(s); + +    dbus_message_iter_get_basic(iter, &mute); + +    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, mute, TRUE); + +    pa_dbus_send_empty_reply(conn, msg); +}; + +static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) { +    pa_dbusiface_stream *s = userdata; +    dbus_uint64_t buffer_latency = 0; + +    pa_assert(conn); +    pa_assert(msg); +    pa_assert(s); + +    if (s->type == STREAM_TYPE_PLAYBACK) +        buffer_latency = pa_sink_input_get_latency(s->sink_input, NULL); +    else +        buffer_latency = pa_source_output_get_latency(s->source_output, NULL); + +    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &buffer_latency); +} + +static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) { +    pa_dbusiface_stream *s = userdata; +    dbus_uint64_t device_latency = 0; + +    pa_assert(conn); +    pa_assert(msg); +    pa_assert(s); + +    if (s->type == STREAM_TYPE_PLAYBACK) +        pa_sink_input_get_latency(s->sink_input, &device_latency); +    else +        pa_source_output_get_latency(s->source_output, &device_latency); + +    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &device_latency); +} + +static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata) { +    pa_dbusiface_stream *s = userdata; +    const char *resample_method = NULL; + +    pa_assert(conn); +    pa_assert(msg); +    pa_assert(s); + +    if (s->type == STREAM_TYPE_PLAYBACK) +        resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method); +    else +        resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method); + +    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &resample_method); +} + +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 = 0; +    const char *driver = NULL; +    pa_module *owner_module = NULL; +    const char *owner_module_path = NULL; +    pa_client *client = NULL; +    const char *client_path = NULL; +    const char *device = NULL; +    dbus_uint32_t sample_format = 0; +    pa_channel_map *channel_map = NULL; +    dbus_uint32_t channels[PA_CHANNELS_MAX]; +    dbus_uint32_t volume[PA_CHANNELS_MAX]; +    dbus_uint64_t buffer_latency = 0; +    dbus_uint64_t device_latency = 0; +    const char *resample_method = NULL; +    unsigned i = 0; + +    pa_assert(conn); +    pa_assert(msg); +    pa_assert(s); + +    if (s->type == STREAM_TYPE_PLAYBACK) { +        idx = s->sink_input->index; +        driver = s->sink_input->driver; +        owner_module = s->sink_input->module; +        client = s->sink_input->client; +        device = pa_dbusiface_core_get_sink_path(s->core, s->sink); +        sample_format = s->sink_input->sample_spec.format; +        channel_map = &s->sink_input->channel_map; +        for (i = 0; i < s->volume.channels; ++i) +            volume[i] = s->volume.values[i]; +        buffer_latency = pa_sink_input_get_latency(s->sink_input, &device_latency); +        resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method); +    } else { +        idx = s->source_output->index; +        driver = s->source_output->driver; +        owner_module = s->source_output->module; +        client = s->source_output->client; +        device = pa_dbusiface_core_get_source_path(s->core, s->source); +        sample_format = s->source_output->sample_spec.format; +        channel_map = &s->source_output->channel_map; +        buffer_latency = pa_source_output_get_latency(s->source_output, &device_latency); +        resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method); +    } +    if (owner_module) +        owner_module_path = pa_dbusiface_core_get_module_path(s->core, owner_module); +    if (client) +        client_path = pa_dbusiface_core_get_client_path(s->core, client); +    for (i = 0; i < channel_map->channels; ++i) +        channels[i] = channel_map->map[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 (driver) +        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &driver); + +    if (owner_module) +        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module_path); + +    if (client) +        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CLIENT].property_name, DBUS_TYPE_OBJECT_PATH, &client_path); + +    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format); +    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &s->sample_rate); +    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, channel_map->channels); + +    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_MUTE].property_name, DBUS_TYPE_BOOLEAN, &s->mute); +    } + +    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BUFFER_LATENCY].property_name, DBUS_TYPE_UINT64, &buffer_latency); +    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEVICE_LATENCY].property_name, DBUS_TYPE_UINT64, &device_latency); +    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RESAMPLE_METHOD].property_name, DBUS_TYPE_STRING, &resample_method); +    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 handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata) { +    pa_dbusiface_stream *s = userdata; +    const char *device = NULL; + +    pa_assert(conn); +    pa_assert(msg); +    pa_assert(s); + +    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &device, DBUS_TYPE_INVALID)); + +    if (s->type == STREAM_TYPE_PLAYBACK) { +        pa_sink *sink = pa_dbusiface_core_get_sink(s->core, device); + +        if (!sink) { +            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", device); +            return; +        } + +        if (pa_sink_input_move_to(s->sink_input, sink, TRUE) < 0) { +            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, +                               "Moving playback stream %u to sink %s failed.", s->sink_input->index, sink->name); +            return; +        } +    } else { +        pa_source *source = pa_dbusiface_core_get_source(s->core, device); + +        if (!source) { +            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", device); +            return; +        } + +        if (pa_source_output_move_to(s->source_output, source, TRUE) < 0) { +            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, +                               "Moving record stream %u to source %s failed.", s->source_output->index, source->name); +            return; +        } +    } + +    pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_kill(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_PLAYBACK) +        pa_sink_input_kill(s->sink_input); +    else +        pa_source_output_kill(s->source_output); + +    pa_dbus_send_empty_reply(conn, msg); +} + +static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { +    pa_dbusiface_stream *s = userdata; +    DBusMessage *signal = NULL; +    const char *new_device_path = NULL; +    uint32_t new_sample_rate = 0; +    pa_proplist *new_proplist = NULL; +    unsigned i = 0; + +    pa_assert(c); +    pa_assert(s); + +    if ((s->type == STREAM_TYPE_PLAYBACK && idx != s->sink_input->index) +        || (s->type == STREAM_TYPE_RECORD && idx != s->source_output->index)) +        return; + +    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) +        return; + +    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_sink *new_sink = s->sink_input->sink; + +        if (s->sink != new_sink) { +            pa_sink_unref(s->sink); +            s->sink = pa_sink_ref(new_sink); + +            new_device_path = pa_dbusiface_core_get_sink_path(s->core, new_sink); + +            pa_assert_se(signal = dbus_message_new_signal(s->path, +                                                          PA_DBUSIFACE_STREAM_INTERFACE, +                                                          signals[SIGNAL_DEVICE_UPDATED].name)); +            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID)); + +            pa_dbus_protocol_send_signal(s->dbus_protocol, signal); +            dbus_message_unref(signal); +            signal = NULL; +        } +    } else { +        pa_source *new_source = s->source_output->source; + +        if (s->source != new_source) { +            pa_source_unref(s->source); +            s->source = pa_source_ref(new_source); + +            new_device_path = pa_dbusiface_core_get_source_path(s->core, new_source); + +            pa_assert_se(signal = dbus_message_new_signal(s->path, +                                                          PA_DBUSIFACE_STREAM_INTERFACE, +                                                          signals[SIGNAL_DEVICE_UPDATED].name)); +            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID)); + +            pa_dbus_protocol_send_signal(s->dbus_protocol, signal); +            dbus_message_unref(signal); +            signal = NULL; +        } +    } + +    new_sample_rate = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->sample_spec.rate : s->source_output->sample_spec.rate; + +    if (s->sample_rate != new_sample_rate) { +        s->sample_rate = new_sample_rate; + +        pa_assert_se(signal = dbus_message_new_signal(s->path, +                                                      PA_DBUSIFACE_STREAM_INTERFACE, +                                                      signals[SIGNAL_SAMPLE_RATE_UPDATED].name)); +        pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_UINT32, &s->sample_rate, DBUS_TYPE_INVALID)); + +        pa_dbus_protocol_send_signal(s->dbus_protocol, signal); +        dbus_message_unref(signal); +        signal = NULL; +    } + +    if (s->type == STREAM_TYPE_PLAYBACK) { +        pa_cvolume new_volume; +        pa_bool_t new_mute = 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_mute = pa_sink_input_get_mute(s->sink_input); + +        if (s->mute != new_mute) { +            s->mute = new_mute; + +            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->mute, 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; +    } +} + +static pa_hook_result_t send_event_cb(void *hook_data, void *call_data, void *slot_data) { +    pa_dbusiface_stream *s = slot_data; +    DBusMessage *signal = NULL; +    DBusMessageIter msg_iter; +    const char *name = NULL; +    pa_proplist *property_list = NULL; + +    pa_assert(call_data); +    pa_assert(s); + +    if (s->type == STREAM_TYPE_PLAYBACK) { +        pa_sink_input_send_event_hook_data *data = call_data; + +        if (data->sink_input != s->sink_input) +            return PA_HOOK_OK; + +        name = data->event; +        property_list = data->data; +    } else { +        pa_source_output_send_event_hook_data *data = call_data; + +        if (data->source_output != s->source_output) +            return PA_HOOK_OK; + +        name = data->event; +        property_list = data->data; +    } + +    pa_assert_se(signal = dbus_message_new_signal(s->path, +                                                  PA_DBUSIFACE_STREAM_INTERFACE, +                                                  signals[SIGNAL_STREAM_EVENT].name)); +    dbus_message_iter_init_append(signal, &msg_iter); +    pa_assert_se(dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name)); +    pa_dbus_append_proplist(&msg_iter, property_list); + +    pa_dbus_protocol_send_signal(s->dbus_protocol, signal); +    dbus_message_unref(signal); + +    return PA_HOOK_OK; +} + +pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) { +    pa_dbusiface_stream *s; + +    pa_assert(core); +    pa_assert(sink_input); + +    s = pa_xnew(pa_dbusiface_stream, 1); +    s->core = core; +    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); +    s->sink = pa_sink_ref(sink_input->sink); +    s->sample_rate = sink_input->sample_spec.rate; +    pa_sink_input_get_volume(sink_input, &s->volume, TRUE); +    s->mute = 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); +    s->send_event_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT], +                                         PA_HOOK_NORMAL, +                                         send_event_cb, +                                         s); + +    pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0); + +    return s; +} + +pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_source_output *source_output) { +    pa_dbusiface_stream *s; + +    pa_assert(core); +    pa_assert(source_output); + +    s = pa_xnew(pa_dbusiface_stream, 1); +    s->core = core; +    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); +    s->source = pa_source_ref(source_output->source); +    s->sample_rate = source_output->sample_spec.rate; +    pa_cvolume_init(&s->volume); +    s->mute = 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); +    s->send_event_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT], +                                         PA_HOOK_NORMAL, +                                         send_event_cb, +                                         s); + +    pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0); + +    return s; +} + +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); +        pa_sink_unref(s->sink); +    } else { +        pa_source_output_unref(s->source_output); +        pa_source_unref(s->source); +    } + +    pa_proplist_free(s->proplist); +    pa_dbus_protocol_unref(s->dbus_protocol); +    pa_subscription_free(s->subscription); +    pa_hook_slot_free(s->send_event_slot); + +    pa_xfree(s->path); +    pa_xfree(s); +} + +const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s) { +    pa_assert(s); + +    return s->path; +} | 
