#include <stdlib.h>
#include <unistd.h>
+#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/version.h>
#include <pulse/utf8.h>
#include "protocol-native.h"
/* Kick a client if it doesn't authenticate within this time */
-#define AUTH_TIMEOUT 60
+#define AUTH_TIMEOUT (60 * PA_USEC_PER_SEC)
/* Don't accept more connection than this */
#define MAX_CONNECTIONS 64
static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = NULL,
[PA_COMMAND_SET_CARD_PROFILE] = command_set_card_profile,
+ [PA_COMMAND_SET_SINK_PORT] = command_set_sink_or_source_port,
+ [PA_COMMAND_SET_SOURCE_PORT] = command_set_sink_or_source_port,
+
[PA_COMMAND_EXTENSION] = command_extension
};
return -1;
switch (code) {
+
case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: {
pa_tagstruct *t;
int l = 0;
/* Called from main context */
static void fix_playback_buffer_attr(playback_stream *s) {
- size_t frame_size;
+ size_t frame_size, max_prebuf;
pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec;
pa_assert(s);
if (s->buffer_attr.tlength <= s->buffer_attr.minreq)
s->buffer_attr.tlength = s->buffer_attr.minreq*2 + (uint32_t) frame_size;
- if (s->buffer_attr.prebuf == (uint32_t) -1 || s->buffer_attr.prebuf > s->buffer_attr.tlength)
- s->buffer_attr.prebuf = s->buffer_attr.tlength;
+ max_prebuf = s->buffer_attr.tlength + (uint32_t)frame_size - s->buffer_attr.minreq;
+
+ if (s->buffer_attr.prebuf == (uint32_t) -1 ||
+ s->buffer_attr.prebuf > max_prebuf)
+ s->buffer_attr.prebuf = max_prebuf;
}
/* Called from main context */
m = pa_memblockq_pop_missing(s->memblockq);
+ /* pa_log("request_bytes(%lu) (tlength=%lu minreq=%lu length=%lu)", */
+ /* (unsigned long) m, */
+ /* pa_memblockq_get_tlength(s->memblockq), */
+ /* pa_memblockq_get_minreq(s->memblockq), */
+ /* pa_memblockq_get_length(s->memblockq)); */
+
if (m <= 0)
return;
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
}
-
/* Called from main context */
static void playback_stream_send_killed(playback_stream *p) {
pa_tagstruct *t;
/* pa_log("sink input post: %lu %lli", (unsigned long) chunk->length, (long long) windex); */
if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
- pa_log_warn("Failed to push data into queue");
+
+ if (pa_log_ratelimit())
+ pa_log_warn("Failed to push data into queue");
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL);
pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, TRUE);
}
if (pa_memblockq_is_readable(s->memblockq))
s->is_underrun = FALSE;
else {
- pa_log_debug("Underrun on '%s', %lu bytes in queue.", pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)), (unsigned long) pa_memblockq_get_length(s->memblockq));
+ if (!s->is_underrun)
+ pa_log_debug("Underrun on '%s', %lu bytes in queue.", pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)), (unsigned long) pa_memblockq_get_length(s->memblockq));
if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
s->drain_request = FALSE;
s = PLAYBACK_STREAM(i->userdata);
playback_stream_assert_ref(s);
+ if (!dest)
+ return;
+
fix_playback_buffer_attr(s);
pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr);
pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
s = RECORD_STREAM(o->userdata);
record_stream_assert_ref(s);
+ if (!dest)
+ return;
+
fix_record_buffer_attr_pre(s);
pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength);
pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
(no_move ? PA_SINK_INPUT_DONT_MOVE : 0) |
(variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) |
(dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
- (fail_on_suspend ? PA_SINK_INPUT_FAIL_ON_SUSPEND : 0);
+ (fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0);
/* Only since protocol version 15 there's a seperate muted_set
* flag. For older versions we synthesize it here */
(no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
(variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) |
(dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
- (fail_on_suspend ? PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND : 0);
+ (fail_on_suspend ? PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND|PA_SOURCE_OUTPUT_KILL_ON_SUSPEND : 0);
s = record_stream_new(c, source, &ss, &map, peak_detect, &attr, flags, p, adjust_latency, direct_on_input, early_requests, &ret);
pa_proplist_free(p);
reply = reply_new(tag);
pa_tagstruct_put_usec(reply,
s->current_sink_latency +
- pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sample_spec));
+ pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sink->sample_spec));
pa_tagstruct_put_usec(reply, 0);
pa_tagstruct_put_boolean(reply,
s->playing_for > 0 &&
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
CHECK_VALIDITY(c->pstream, upload_stream_isinstance(s), tag, PA_ERR_NOENTITY);
- if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, s->proplist, &idx) < 0)
+ if (!s->memchunk.memblock)
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_TOOLARGE);
+ else if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, s->proplist, &idx) < 0)
pa_pstream_send_error(c->pstream, tag, PA_ERR_INTERNAL);
else
pa_pstream_send_simple_ack(c->pstream, tag);
pa_tagstruct_putu32(t, sink->n_volume_steps);
pa_tagstruct_putu32(t, sink->card ? sink->card->index : PA_INVALID_INDEX);
}
+
+ if (c->version >= 16) {
+ pa_tagstruct_putu32(t, sink->ports ? pa_hashmap_size(sink->ports) : 0);
+
+ if (sink->ports) {
+ void *state;
+ pa_device_port *p;
+
+ PA_HASHMAP_FOREACH(p, sink->ports, state) {
+ pa_tagstruct_puts(t, p->name);
+ pa_tagstruct_puts(t, p->description);
+ pa_tagstruct_putu32(t, p->priority);
+ }
+ }
+
+ pa_tagstruct_puts(t, sink->active_port ? sink->active_port->name : NULL);
+ }
}
static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
pa_tagstruct_putu32(t, source->n_volume_steps);
pa_tagstruct_putu32(t, source->card ? source->card->index : PA_INVALID_INDEX);
}
+
+ if (c->version >= 16) {
+
+ pa_tagstruct_putu32(t, source->ports ? pa_hashmap_size(source->ports) : 0);
+
+ if (source->ports) {
+ void *state;
+ pa_device_port *p;
+
+ PA_HASHMAP_FOREACH(p, source->ports, state) {
+ pa_tagstruct_puts(t, p->name);
+ pa_tagstruct_puts(t, p->description);
+ pa_tagstruct_putu32(t, p->priority);
+ }
+ }
+
+ pa_tagstruct_puts(t, source->active_port ? source->active_port->name : NULL);
+ }
}
static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) {
static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {
pa_sample_spec fixed_ss;
pa_usec_t sink_latency;
+ pa_cvolume v;
pa_assert(t);
pa_sink_input_assert_ref(s);
pa_tagstruct_putu32(t, s->sink->index);
pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &s->channel_map);
- pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s));
+ pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s, &v, TRUE));
pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency));
pa_tagstruct_put_usec(t, sink_latency);
pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_tagstruct *reply;
- char txt[256];
pa_sink *def_sink;
pa_source *def_source;
pa_sample_spec fixed_ss;
+ char *h, *u;
pa_native_connection_assert_ref(c);
pa_assert(t);
reply = reply_new(tag);
pa_tagstruct_puts(reply, PACKAGE_NAME);
pa_tagstruct_puts(reply, PACKAGE_VERSION);
- pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt)));
- pa_tagstruct_puts(reply, pa_get_host_name(txt, sizeof(txt)));
+
+ u = pa_get_user_name_malloc();
+ pa_tagstruct_puts(reply, u);
+ pa_xfree(u);
+
+ h = pa_get_host_name_malloc();
+ pa_tagstruct_puts(reply, h);
+ pa_xfree(h);
fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec);
pa_tagstruct_put_sample_spec(reply, &fixed_ss);
pa_source *source = NULL;
pa_sink_input *si = NULL;
const char *name = NULL;
+ const char *client_name;
pa_native_connection_assert_ref(c);
pa_assert(t);
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
- if (sink)
+ client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
+
+ if (sink) {
+ pa_log_debug("Client %s changes volume of sink %s.", client_name, sink->name);
pa_sink_set_volume(sink, &volume, TRUE, TRUE);
- else if (source)
- pa_source_set_volume(source, &volume);
- else if (si)
- pa_sink_input_set_volume(si, &volume, TRUE);
+ } else if (source) {
+ pa_log_debug("Client %s changes volume of sink %s.", client_name, source->name);
+ pa_source_set_volume(source, &volume, TRUE);
+ } else if (si) {
+ pa_log_debug("Client %s changes volume of sink %s.",
+ client_name,
+ pa_strnull(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)));
+ pa_sink_input_set_volume(si, &volume, TRUE, TRUE);
+ }
pa_pstream_send_simple_ack(c->pstream, tag);
}
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
if (sink)
- pa_sink_set_mute(sink, mute);
+ pa_sink_set_mute(sink, mute, TRUE);
else if (source)
- pa_source_set_mute(source, mute);
+ pa_source_set_mute(source, mute, TRUE);
else if (si)
pa_sink_input_set_mute(si, mute, TRUE);
pa_log_debug("%s all sinks", b ? "Suspending" : "Resuming");
- if (pa_sink_suspend_all(c->protocol->core, b) < 0) {
+ if (pa_sink_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
- if (pa_sink_suspend(sink, b) < 0) {
+ if (pa_sink_suspend(sink, b, PA_SUSPEND_USER) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
pa_log_debug("%s all sources", b ? "Suspending" : "Resuming");
- if (pa_source_suspend_all(c->protocol->core, b) < 0) {
+ if (pa_source_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
- if (pa_source_suspend(source, b) < 0) {
+ if (pa_source_suspend(source, b, PA_SUSPEND_USER) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
uint32_t idx = PA_INVALID_INDEX;
const char *name = NULL, *profile = NULL;
pa_card *card = NULL;
+ int ret;
pa_native_connection_assert_ref(c);
pa_assert(t);
CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY);
- if (pa_card_set_profile(card, profile, TRUE) < 0) {
- pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ if ((ret = pa_card_set_profile(card, profile, TRUE)) < 0) {
+ pa_pstream_send_error(c->pstream, tag, -ret);
return;
}
pa_pstream_send_simple_ack(c->pstream, tag);
}
+static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+ uint32_t idx = PA_INVALID_INDEX;
+ const char *name = NULL, *port = NULL;
+ int ret;
+
+ pa_native_connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_gets(t, &port) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+ CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+
+ if (command == PA_COMMAND_SET_SINK_PORT) {
+ pa_sink *sink;
+
+ if (idx != PA_INVALID_INDEX)
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+ else
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
+
+ CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+ if ((ret = pa_sink_set_port(sink, port, TRUE)) < 0) {
+ pa_pstream_send_error(c->pstream, tag, -ret);
+ return;
+ }
+ } else {
+ pa_source *source;
+
+ pa_assert(command = PA_COMMAND_SET_SOURCE_PORT);
+
+ if (idx != PA_INVALID_INDEX)
+ source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+ else
+ source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
+
+ CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+ if ((ret = pa_source_set_port(source, port, TRUE)) < 0) {
+ pa_pstream_send_error(c->pstream, tag, -ret);
+ return;
+ }
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
/*** pstream callbacks ***/
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) {
pa_native_connection_assert_ref(c);
if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
- pa_log("client sent block for invalid stream.");
+ pa_log_debug("Client sent block for invalid stream.");
/* Ignoring */
return;
}
/*** module entry points ***/
-static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
+static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_assert(m);
- pa_assert(tv);
pa_native_connection_assert_ref(c);
pa_assert(c->auth_timeout_event == e);
c->authorized = TRUE;
}
- if (!c->authorized) {
- struct timeval tv;
- pa_gettimeofday(&tv);
- tv.tv_sec += AUTH_TIMEOUT;
- c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c);
- } else
+ if (!c->authorized)
+ c->auth_timeout_event = pa_core_rttime_new(p->core, pa_rtclock_now() + AUTH_TIMEOUT, auth_timeout, c);
+ else
c->auth_timeout_event = NULL;
c->is_local = pa_iochannel_socket_is_local(io);
pa_pstream_set_revoke_callback(c->pstream, pstream_revoke_callback, c);
pa_pstream_set_release_callback(c->pstream, pstream_release_callback, c);
- c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX);
+ c->pdispatch = pa_pdispatch_new(p->core->mainloop, TRUE, command_table, PA_COMMAND_MAX);
c->record_streams = pa_idxset_new(NULL, NULL);
c->output_streams = pa_idxset_new(NULL, NULL);