-/* $Id$ */
-
/***
This file is part of PulseAudio.
#define MAX_MEMBLOCKQ_LENGTH (4*1024*1024) /* 4MB */
#define DEFAULT_TLENGTH_MSEC 2000 /* 2s */
+#define DEFAULT_PROCESS_MSEC 20 /* 20ms */
#define DEFAULT_FRAGSIZE_MSEC DEFAULT_TLENGTH_MSEC
typedef struct connection connection;
pa_sink_input *sink_input;
pa_memblockq *memblockq;
- pa_bool_t drain_request;
+ pa_bool_t is_underrun:1;
+ pa_bool_t drain_request:1;
uint32_t drain_tag;
uint32_t syncid;
- pa_bool_t underrun;
pa_atomic_t missing;
size_t minreq;
struct connection {
pa_msgobject parent;
- pa_bool_t authorized;
+ pa_bool_t authorized:1;
+ pa_bool_t is_local:1;
uint32_t version;
pa_protocol_native *protocol;
pa_client *client;
PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, /* data requested from sink input from the main loop */
PLAYBACK_STREAM_MESSAGE_UNDERFLOW,
PLAYBACK_STREAM_MESSAGE_OVERFLOW,
- PLAYBACK_STREAM_MESSAGE_DRAIN_ACK
+ PLAYBACK_STREAM_MESSAGE_DRAIN_ACK,
+ PLAYBACK_STREAM_MESSAGE_STARTED
};
enum {
static void sink_input_kill_cb(pa_sink_input *i);
static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend);
static void sink_input_moved_cb(pa_sink_input *i);
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
+
static void send_memblock(connection *c);
static void request_bytes(struct playback_stream*s);
return 0;
}
+static void fix_record_buffer_attr_pre(record_stream *s, pa_bool_t adjust_latency, uint32_t *maxlength, uint32_t *fragsize) {
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(fragsize);
+
+ if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
+ *maxlength = MAX_MEMBLOCKQ_LENGTH;
+ if (*maxlength <= 0)
+ *maxlength = pa_frame_size(&s->source_output->sample_spec);
+
+ if (*fragsize == (uint32_t) -1)
+ *fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
+ if (*fragsize <= 0)
+ *fragsize = pa_frame_size(&s->source_output->sample_spec);
+
+ if (adjust_latency) {
+ pa_usec_t fragsize_usec;
+
+ /* So, the user asked us to adjust the latency according to
+ * what the source can provide. Half the latency will be
+ * spent on the hw buffer, half of it in the async buffer
+ * queue we maintain for each client. */
+
+ fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec);
+
+ s->source_latency = pa_source_output_set_requested_latency(s->source_output, fragsize_usec/2);
+
+ if (fragsize_usec >= s->source_latency*2)
+ fragsize_usec -= s->source_latency;
+ else
+ fragsize_usec = s->source_latency;
+
+ *fragsize = pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
+ } else
+ s->source_latency = 0;
+}
+
+static void fix_record_buffer_attr_post(record_stream *s, uint32_t *maxlength, uint32_t *fragsize) {
+ size_t base;
+
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(fragsize);
+
+ *maxlength = pa_memblockq_get_maxlength(s->memblockq);
+
+ base = pa_frame_size(&s->source_output->sample_spec);
+
+ s->fragment_size = (*fragsize/base)*base;
+ if (s->fragment_size <= 0)
+ s->fragment_size = base;
+
+ if (s->fragment_size > *maxlength)
+ s->fragment_size = *maxlength;
+
+ *fragsize = s->fragment_size;
+}
+
static record_stream* record_stream_new(
connection *c,
pa_source *source,
pa_sample_spec *ss,
pa_channel_map *map,
- const char *name,
+ pa_bool_t peak_detect,
uint32_t *maxlength,
uint32_t *fragsize,
pa_source_output_flags_t flags,
pa_proplist *p,
- pa_bool_t adjust_latency) {
+ pa_bool_t adjust_latency,
+ pa_sink_input *direct_on_input) {
record_stream *s;
pa_source_output *source_output;
pa_assert(c);
pa_assert(ss);
- pa_assert(name);
pa_assert(maxlength);
pa_assert(p);
data.module = c->protocol->module;
data.client = c->client;
data.source = source;
+ data.direct_on_input = direct_on_input;
pa_source_output_new_data_set_sample_spec(&data, ss);
pa_source_output_new_data_set_channel_map(&data, map);
+ if (peak_detect)
+ data.resample_method = PA_RESAMPLER_PEAKS;
source_output = pa_source_output_new(c->protocol->core, &data, flags);
s->source_output->suspend = source_output_suspend_cb;
s->source_output->userdata = s;
- if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
- *maxlength = MAX_MEMBLOCKQ_LENGTH;
- if (*fragsize <= 0)
- *fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*1000, &source_output->sample_spec);
-
- if (adjust_latency) {
- pa_usec_t fragsize_usec;
-
- /* So, the user asked us to adjust the latency according to
- * the what the source can provide. Half the latency will be
- * spent on the hw buffer, half of it in the async buffer
- * queue we maintain for each client. */
-
- fragsize_usec = pa_bytes_to_usec(*fragsize, &source_output->sample_spec);
-
- s->source_latency = pa_source_output_set_requested_latency(source_output, fragsize_usec/2);
-
- if (fragsize_usec >= s->source_latency*2)
- fragsize_usec -= s->source_latency;
- else
- fragsize_usec = s->source_latency;
-
- *fragsize = pa_usec_to_bytes(fragsize_usec, &source_output->sample_spec);
- }
+ fix_record_buffer_attr_pre(s, adjust_latency, maxlength, fragsize);
s->memblockq = pa_memblockq_new(
0,
0,
NULL);
- *maxlength = pa_memblockq_get_maxlength(s->memblockq);
-
- s->fragment_size = (*fragsize/base)*base;
- if (s->fragment_size <= 0)
- s->fragment_size = base;
-
- if (s->fragment_size > *maxlength)
- s->fragment_size = *maxlength;
-
- *fragsize = s->fragment_size;
+ fix_record_buffer_attr_post(s, maxlength, fragsize);
*ss = s->source_output->sample_spec;
*map = s->source_output->channel_map;
pa_idxset_put(c->record_streams, s, &s->index);
+ pa_log_info("Final latency %0.2f ms = %0.2f ms + %0.2f ms",
+ ((double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) + (double) s->source_latency) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) / PA_USEC_PER_MSEC,
+ (double) s->source_latency / PA_USEC_PER_MSEC);
+
pa_source_output_put(s->source_output);
return s;
}
uint32_t l = 0;
for (;;) {
- int32_t k;
-
- if ((k = pa_atomic_load(&s->missing)) <= 0)
- break;
-
- l += k;
-
- if (l < s->minreq)
+ if ((l = pa_atomic_load(&s->missing)) <= 0)
break;
- if (pa_atomic_sub(&s->missing, k) <= k)
+ if (pa_atomic_cmpxchg(&s->missing, l, 0))
break;
}
- if (l < s->minreq)
+ if (l <= 0)
break;
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, l);
pa_pstream_send_tagstruct(s->connection->pstream, t);
-/* pa_log("Requesting %u bytes", l); */
+/* pa_log("Requesting %lu bytes", (unsigned long) l); */
break;
}
break;
}
+ case PLAYBACK_STREAM_MESSAGE_STARTED:
+
+ if (s->connection->version >= 13) {
+ pa_tagstruct *t;
+
+ /* Notify the user we're overflowed*/
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_STARTED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+ }
+
+ break;
+
case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
break;
-
}
return 0;
}
+static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_latency, uint32_t *maxlength, uint32_t *tlength, uint32_t* prebuf, uint32_t* minreq) {
+ size_t frame_size;
+ pa_usec_t tlength_usec, minreq_usec, sink_usec;
+
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(tlength);
+ pa_assert(prebuf);
+ pa_assert(minreq);
+
+ frame_size = pa_frame_size(&s->sink_input->sample_spec);
+
+ if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
+ *maxlength = MAX_MEMBLOCKQ_LENGTH;
+ if (*maxlength <= 0)
+ *maxlength = frame_size;
+
+ if (*tlength == (uint32_t) -1)
+ *tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+ if (*tlength <= 0)
+ *tlength = frame_size;
+
+ if (*minreq == (uint32_t) -1)
+ *minreq = pa_usec_to_bytes(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+ if (*minreq <= 0)
+ *minreq = frame_size;
+
+ if (*tlength < *minreq+frame_size)
+ *tlength = *minreq+frame_size;
+
+ tlength_usec = pa_bytes_to_usec(*tlength, &s->sink_input->sample_spec);
+ minreq_usec = pa_bytes_to_usec(*minreq, &s->sink_input->sample_spec);
+
+ pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms",
+ (double) tlength_usec / PA_USEC_PER_MSEC,
+ (double) minreq_usec / PA_USEC_PER_MSEC);
+
+ if (adjust_latency) {
+
+ /* So, the user asked us to adjust the latency of the stream
+ * buffer according to the what the sink can provide. The
+ * tlength passed in shall be the overall latency. Roughly
+ * half the latency will be spent on the hw buffer, the other
+ * half of it in the async buffer queue we maintain for each
+ * client. In between we'll have a safety space of size
+ * 2*minreq. Why the 2*minreq? When the hw buffer is completey
+ * empty and needs to be filled, then our buffer must have
+ * enough data to fulfill this request immediatly and thus
+ * have at least the same tlength as the size of the hw
+ * buffer. It additionally needs space for 2 times minreq
+ * because if the buffer ran empty and a partial fillup
+ * happens immediately on the next iteration we need to be
+ * able to fulfill it and give the application also minreq
+ * time to fill it up again for the next request Makes 2 times
+ * minreq in plus.. */
+
+ if (tlength_usec > minreq_usec*2)
+ sink_usec = (tlength_usec - minreq_usec*2)/2;
+ else
+ sink_usec = 0;
+
+ } else {
+
+ /* Ok, the user didn't ask us to adjust the latency, but we
+ * still need to make sure that the parameters from the user
+ * do make sense. */
+
+ if (tlength_usec > minreq_usec*2)
+ sink_usec = (tlength_usec - minreq_usec*2);
+ else
+ sink_usec = 0;
+ }
+
+ s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
+
+ if (adjust_latency) {
+ /* Ok, we didn't necessarily get what we were asking for, so
+ * let's subtract from what we asked for for the remaining
+ * buffer space */
+
+ if (tlength_usec >= s->sink_latency)
+ tlength_usec -= s->sink_latency;
+ }
+
+ if (tlength_usec < s->sink_latency + 2*minreq_usec)
+ tlength_usec = s->sink_latency + 2*minreq_usec;
+
+ *tlength = pa_usec_to_bytes(tlength_usec, &s->sink_input->sample_spec);
+ *minreq = pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
+
+ if (*minreq <= 0) {
+ *minreq += frame_size;
+ *tlength += frame_size*2;
+ }
+
+ if (*tlength <= *minreq)
+ *tlength = *minreq*2 + frame_size;
+
+ if (*prebuf == (uint32_t) -1 || *prebuf > *tlength)
+ *prebuf = *tlength;
+}
+
+static void fix_playback_buffer_attr_post(playback_stream *s, uint32_t *maxlength, uint32_t *tlength, uint32_t* prebuf, uint32_t* minreq) {
+ pa_assert(s);
+ pa_assert(maxlength);
+ pa_assert(tlength);
+ pa_assert(prebuf);
+ pa_assert(minreq);
+
+ *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
+ *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
+ *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
+ *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
+
+ s->minreq = *minreq;
+}
+
static playback_stream* playback_stream_new(
connection *c,
pa_sink *sink,
pa_sample_spec *ss,
pa_channel_map *map,
- const char *name,
uint32_t *maxlength,
uint32_t *tlength,
uint32_t *prebuf,
playback_stream *s, *ssync;
pa_sink_input *sink_input;
- pa_memblock *silence;
+ pa_memchunk silence;
uint32_t idx;
int64_t start_index;
pa_sink_input_new_data data;
pa_assert(c);
pa_assert(ss);
- pa_assert(name);
pa_assert(maxlength);
pa_assert(tlength);
pa_assert(prebuf);
s->connection = c;
s->syncid = syncid;
s->sink_input = sink_input;
- s->underrun = TRUE;
+ s->is_underrun = TRUE;
+ s->drain_request = FALSE;
+ pa_atomic_store(&s->missing, 0);
s->sink_input->parent.process_msg = sink_input_process_msg;
s->sink_input->pop = sink_input_pop_cb;
+ s->sink_input->process_rewind = sink_input_process_rewind_cb;
+ s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
s->sink_input->kill = sink_input_kill_cb;
s->sink_input->moved = sink_input_moved_cb;
s->sink_input->suspend = sink_input_suspend_cb;
start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
- if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
- *maxlength = MAX_MEMBLOCKQ_LENGTH;
- if (*tlength <= 0)
- *tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*1000, &sink_input->sample_spec);
- if (*minreq <= 0)
- *minreq = (*tlength*9)/10;
- if (*prebuf <= 0)
- *prebuf = *tlength;
-
- if (adjust_latency) {
- pa_usec_t tlength_usec, minreq_usec;
-
- /* So, the user asked us to adjust the latency according to
- * the what the sink can provide. Half the latency will be
- * spent on the hw buffer, half of it in the async buffer
- * queue we maintain for each client. */
-
- tlength_usec = pa_bytes_to_usec(*tlength, &sink_input->sample_spec);
- minreq_usec = pa_bytes_to_usec(*minreq, &sink_input->sample_spec);
-
- s->sink_latency = pa_sink_input_set_requested_latency(sink_input, tlength_usec/2);
-
- if (tlength_usec >= s->sink_latency*2)
- tlength_usec -= s->sink_latency;
- else
- tlength_usec = s->sink_latency;
-
- if (minreq_usec >= s->sink_latency*2)
- minreq_usec -= s->sink_latency;
- else
- minreq_usec = s->sink_latency;
-
- *tlength = pa_usec_to_bytes(tlength_usec, &sink_input->sample_spec);
- *minreq = pa_usec_to_bytes(minreq_usec, &sink_input->sample_spec);
- }
-
- silence = pa_silence_memblock_new(c->protocol->core->mempool, &sink_input->sample_spec, 0);
+ fix_playback_buffer_attr_pre(s, adjust_latency, maxlength, tlength, prebuf, minreq);
+ pa_sink_input_get_silence(sink_input, &silence);
s->memblockq = pa_memblockq_new(
start_index,
*prebuf,
*minreq,
0,
- silence);
+ &silence);
- pa_memblock_unref(silence);
+ pa_memblock_unref(silence.memblock);
+ fix_playback_buffer_attr_post(s, maxlength, tlength, prebuf, minreq);
- *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
- *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
- *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
- *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
*missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
*ss = s->sink_input->sample_spec;
*map = s->sink_input->channel_map;
- s->minreq = pa_memblockq_get_minreq(s->memblockq);
- pa_atomic_store(&s->missing, 0);
- s->drain_request = FALSE;
-
pa_idxset_put(c->output_streams, s, &s->index);
+ pa_log_info("Final latency %0.2f ms = %0.2f ms + 2*%0.2f ms + %0.2f ms",
+ ((double) pa_bytes_to_usec(*tlength, &sink_input->sample_spec) + (double) s->sink_latency) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(*tlength-*minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(*minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+ (double) s->sink_latency / PA_USEC_PER_MSEC);
+
pa_sink_input_put(s->sink_input);
return s;
}
if (m <= 0)
return;
-/* pa_log("request_bytes(%u)", m); */
+/* pa_log("request_bytes(%lu)", (unsigned long) m); */
previous_missing = pa_atomic_add(&s->missing, m);
- if (previous_missing < s->minreq && previous_missing+m >= s->minreq) {
- pa_assert(pa_thread_mq_get());
+
+ if (pa_memblockq_prebuf_active(s->memblockq) ||
+ (previous_missing < s->minreq && previous_missing+m >= s->minreq))
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
- }
}
static void send_memblock(connection *c) {
/*** sink input callbacks ***/
+static void handle_seek(playback_stream *s, int64_t indexw) {
+ playback_stream_assert_ref(s);
+
+/* pa_log("handle_seek: %llu -- %i", (unsigned long long) s->sink_input->thread_info.underrun_for, pa_memblockq_is_readable(s->memblockq)); */
+
+ if (s->sink_input->thread_info.underrun_for > 0) {
+
+/* pa_log("%lu vs. %lu", (unsigned long) pa_memblockq_get_length(s->memblockq), (unsigned long) pa_memblockq_get_prebuf(s->memblockq)); */
+
+ if (pa_memblockq_is_readable(s->memblockq)) {
+
+ /* We just ended an underrun, let's ask the sink
+ * for a complete rewind rewrite */
+
+ pa_log_debug("Requesting rewind due to end of underrun.");
+ pa_sink_input_request_rewind(s->sink_input,
+ s->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : s->sink_input->thread_info.underrun_for,
+ FALSE, TRUE);
+ }
+
+ } else {
+ int64_t indexr;
+
+ indexr = pa_memblockq_get_read_index(s->memblockq);
+
+ if (indexw < indexr) {
+ /* OK, the sink already asked for this data, so
+ * let's have it usk us again */
+
+ pa_log_debug("Requesting rewind due to rewrite.");
+ pa_sink_input_request_rewind(s->sink_input, indexr - indexw, TRUE, FALSE);
+ }
+ }
+
+ request_bytes(s);
+}
+
/* Called from thread context */
static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
pa_sink_input *i = PA_SINK_INPUT(o);
switch (code) {
- case SINK_INPUT_MESSAGE_SEEK:
+ case SINK_INPUT_MESSAGE_SEEK: {
+ int64_t windex;
+
+ windex = pa_memblockq_get_write_index(s->memblockq);
pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata));
- request_bytes(s);
+
+ handle_seek(s, windex);
return 0;
+ }
case SINK_INPUT_MESSAGE_POST_DATA: {
+ int64_t windex;
+
pa_assert(chunk);
-/* pa_log("sink input post: %u", chunk->length); */
+ windex = pa_memblockq_get_write_index(s->memblockq);
- if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
+/* 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");
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL);
pa_memblockq_seek(s->memblockq, chunk->length, PA_SEEK_RELATIVE);
}
- request_bytes(s);
-
- s->underrun = FALSE;
- return 0;
- }
+ handle_seek(s, windex);
- case SINK_INPUT_MESSAGE_DRAIN: {
-
- pa_memblockq_prebuf_disable(s->memblockq);
-
- if (!pa_memblockq_is_readable(s->memblockq))
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, userdata, 0, NULL, NULL);
- else {
- s->drain_tag = PA_PTR_TO_UINT(userdata);
- s->drain_request = TRUE;
- }
- request_bytes(s);
+/* pa_log("sink input post2: %lu", (unsigned long) pa_memblockq_get_length(s->memblockq)); */
return 0;
}
+ case SINK_INPUT_MESSAGE_DRAIN:
case SINK_INPUT_MESSAGE_FLUSH:
case SINK_INPUT_MESSAGE_PREBUF_FORCE:
case SINK_INPUT_MESSAGE_TRIGGER: {
+ int64_t windex;
pa_sink_input *isync;
void (*func)(pa_memblockq *bq);
switch (code) {
case SINK_INPUT_MESSAGE_FLUSH:
- func = pa_memblockq_flush;
+ func = pa_memblockq_flush_write;
break;
case SINK_INPUT_MESSAGE_PREBUF_FORCE:
func = pa_memblockq_prebuf_force;
break;
+ case SINK_INPUT_MESSAGE_DRAIN:
case SINK_INPUT_MESSAGE_TRIGGER:
func = pa_memblockq_prebuf_disable;
break;
pa_assert_not_reached();
}
+ windex = pa_memblockq_get_write_index(s->memblockq);
func(s->memblockq);
- s->underrun = FALSE;
- request_bytes(s);
+ handle_seek(s, windex);
/* Do the same for all other members in the sync group */
for (isync = i->sync_prev; isync; isync = isync->sync_prev) {
playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
+ windex = pa_memblockq_get_write_index(ssync->memblockq);
func(ssync->memblockq);
- ssync->underrun = FALSE;
- request_bytes(ssync);
+ handle_seek(ssync, windex);
}
for (isync = i->sync_next; isync; isync = isync->sync_next) {
playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
+ windex = pa_memblockq_get_write_index(ssync->memblockq);
func(ssync->memblockq);
- ssync->underrun = FALSE;
- request_bytes(ssync);
+ handle_seek(ssync, windex);
+ }
+
+ if (code == SINK_INPUT_MESSAGE_DRAIN) {
+ if (!pa_memblockq_is_readable(s->memblockq))
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, userdata, 0, NULL, NULL);
+ else {
+ s->drain_tag = PA_PTR_TO_UINT(userdata);
+ s->drain_request = TRUE;
+ }
}
return 0;
s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq);
return 0;
- case PA_SINK_INPUT_MESSAGE_SET_STATE:
+ case PA_SINK_INPUT_MESSAGE_SET_STATE: {
+ int64_t windex;
+
+ windex = pa_memblockq_get_write_index(s->memblockq);
pa_memblockq_prebuf_force(s->memblockq);
- request_bytes(s);
+
+ handle_seek(s, windex);
+
+ /* Fall through to the default handler */
break;
+ }
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
pa_usec_t *r = userdata;
}
/* Called from thread context */
-static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
playback_stream *s;
pa_sink_input_assert_ref(i);
playback_stream_assert_ref(s);
pa_assert(chunk);
- if (pa_memblockq_peek(s->memblockq, chunk) < 0) {
+/* pa_log("%s, pop(): %lu", pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME), (unsigned long) pa_memblockq_get_length(s->memblockq)); */
+
+ if (pa_memblockq_is_readable(s->memblockq))
+ s->is_underrun = FALSE;
+ else {
+/* pa_log("%s, UNDERRUN: %lu", 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;
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL);
- } else if (!s->underrun) {
- s->underrun = TRUE;
+ } else if (!s->is_underrun)
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL);
- }
- return -1;
+ s->is_underrun = TRUE;
+
+ request_bytes(s);
}
+ /* This call will not fail with prebuf=0, hence we check for
+ underrun explicitly above */
+ if (pa_memblockq_peek(s->memblockq, chunk) < 0)
+ return -1;
+
+ chunk->length = PA_MIN(nbytes, chunk->length);
+
+ if (i->thread_info.underrun_for > 0)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_STARTED, NULL, 0, NULL, NULL);
+
pa_memblockq_drop(s->memblockq, chunk->length);
request_bytes(s);
return 0;
}
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ playback_stream *s;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ /* If we are in an underrun, then we don't rewind */
+ if (i->thread_info.underrun_for > 0)
+ return;
+
+ pa_memblockq_rewind(s->memblockq, nbytes);
+}
+
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+ playback_stream *s;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ pa_memblockq_set_maxrewind(s->memblockq, nbytes);
+}
+
/* Called from main context */
static void sink_input_kill_cb(pa_sink_input *i) {
playback_stream *s;
static void sink_input_moved_cb(pa_sink_input *i) {
playback_stream *s;
pa_tagstruct *t;
+ uint32_t maxlength, tlength, prebuf, minreq;
pa_sink_input_assert_ref(i);
s = PLAYBACK_STREAM(i->userdata);
playback_stream_assert_ref(s);
+ maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
+ tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
+ prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
+ minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
+
+ fix_playback_buffer_attr_pre(s, TRUE, &maxlength, &tlength, &prebuf, &minreq);
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ pa_memblockq_set_tlength(s->memblockq, tlength);
+ pa_memblockq_set_prebuf(s->memblockq, prebuf);
+ pa_memblockq_set_minreq(s->memblockq, minreq);
+ fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
+
if (s->connection->version < 12)
return;
pa_tagstruct_putu32(t, i->sink->index);
pa_tagstruct_puts(t, i->sink->name);
pa_tagstruct_put_boolean(t, pa_sink_get_state(i->sink) == PA_SINK_SUSPENDED);
+
+ if (s->connection->version >= 13) {
+ pa_tagstruct_putu32(t, maxlength);
+ pa_tagstruct_putu32(t, tlength);
+ pa_tagstruct_putu32(t, prebuf);
+ pa_tagstruct_putu32(t, minreq);
+ pa_tagstruct_put_usec(t, s->sink_latency);
+ }
+
pa_pstream_send_tagstruct(s->connection->pstream, t);
}
static void source_output_moved_cb(pa_source_output *o) {
record_stream *s;
pa_tagstruct *t;
+ uint32_t maxlength, fragsize;
pa_source_output_assert_ref(o);
s = RECORD_STREAM(o->userdata);
record_stream_assert_ref(s);
+ fragsize = (uint32_t) s->fragment_size;
+ maxlength = (uint32_t) pa_memblockq_get_length(s->memblockq);
+
+ fix_record_buffer_attr_pre(s, TRUE, &maxlength, &fragsize);
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+
if (s->connection->version < 12)
return;
pa_tagstruct_putu32(t, o->source->index);
pa_tagstruct_puts(t, o->source->name);
pa_tagstruct_put_boolean(t, pa_source_get_state(o->source) == PA_SOURCE_SUSPENDED);
+
+ if (s->connection->version >= 13) {
+ pa_tagstruct_putu32(t, maxlength);
+ pa_tagstruct_putu32(t, fragsize);
+ pa_tagstruct_put_usec(t, s->source_latency);
+ }
+
pa_pstream_send_tagstruct(s->connection->pstream, t);
}
(no_move ? PA_SINK_INPUT_DONT_MOVE : 0) |
(variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0);
- s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, muted, syncid, &missing, flags, p, adjust_latency);
+ s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, &volume, muted, syncid, &missing, flags, p, adjust_latency);
pa_proplist_free(p);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
record_stream *s;
uint32_t maxlength, fragment_size;
uint32_t source_index;
- const char *name, *source_name;
+ const char *name = NULL, *source_name;
pa_sample_spec ss;
pa_channel_map map;
pa_tagstruct *reply;
fix_channels = FALSE,
no_move = FALSE,
variable_rate = FALSE,
- adjust_latency = FALSE;
+ adjust_latency = FALSE,
+ peak_detect = FALSE;
pa_source_output_flags_t flags = 0;
pa_proplist *p;
+ uint32_t direct_on_input_idx = PA_INVALID_INDEX;
+ pa_sink_input *direct_on_input = NULL;
connection_assert_ref(c);
pa_assert(t);
if (c->version >= 13) {
- if (pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
- pa_tagstruct_get_proplist(t, p) < 0) {
+ if (pa_tagstruct_get_boolean(t, &peak_detect) < 0 ||
+ pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
+ pa_tagstruct_get_proplist(t, p) < 0 ||
+ pa_tagstruct_getu32(t, &direct_on_input_idx) < 0) {
protocol_error(c);
pa_proplist_free(p);
return;
}
}
+ if (direct_on_input_idx != PA_INVALID_INDEX) {
+
+ if (!(direct_on_input = pa_idxset_get_by_index(c->protocol->core->sink_inputs, direct_on_input_idx))) {
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
flags =
(corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) |
(no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) |
(no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
(variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0);
- s = record_stream_new(c, source, &ss, &map, name, &maxlength, &fragment_size, flags, p, adjust_latency);
+ s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input);
pa_proplist_free(p);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
connection *c = CONNECTION(userdata);
const void*cookie;
pa_tagstruct *reply;
- char tmp[16];
+ pa_bool_t shm_on_remote, do_shm;
connection_assert_ref(c);
pa_assert(t);
return;
}
- pa_snprintf(tmp, sizeof(tmp), "%u", c->version);
- pa_proplist_sets(c->client->proplist, "native-protocol.version", tmp);
+ /* Starting with protocol version 13 the MSB of the version tag
+ reflects if shm is available for this connection or
+ not. */
+ if (c->version >= 13) {
+ shm_on_remote = !!(c->version & 0x80000000U);
+ c->version &= 0x7FFFFFFFU;
+ }
+
+ pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
+
+ pa_proplist_setf(c->client->proplist, "native-protocol.version", "%u", c->version);
if (!c->authorized) {
- int success = 0;
+ pa_bool_t success = FALSE;
#ifdef HAVE_CREDS
const pa_creds *creds;
if ((creds = pa_pdispatch_creds(pd))) {
if (creds->uid == getuid())
- success = 1;
+ success = TRUE;
else if (c->protocol->auth_group) {
int r;
gid_t gid;
if ((gid = pa_get_gid_of_group(c->protocol->auth_group)) == (gid_t) -1)
- pa_log_warn("failed to get GID of group '%s'", c->protocol->auth_group);
+ pa_log_warn("Failed to get GID of group '%s'", c->protocol->auth_group);
else if (gid == creds->gid)
- success = 1;
+ success = TRUE;
if (!success) {
if ((r = pa_uid_in_group(creds->uid, c->protocol->auth_group)) < 0)
- pa_log_warn("failed to check group membership.");
+ pa_log_warn("Failed to check group membership.");
else if (r > 0)
- success = 1;
+ success = TRUE;
}
}
pa_log_info("Got credentials: uid=%lu gid=%lu success=%i",
(unsigned long) creds->uid,
(unsigned long) creds->gid,
- success);
-
- if (c->version >= 10 &&
- pa_mempool_is_shared(c->protocol->core->mempool) &&
- creds->uid == getuid()) {
-
- pa_pstream_use_shm(c->pstream, 1);
- pa_log_info("Enabled SHM for new connection");
- }
-
+ (int) success);
}
#endif
if (!success && memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) == 0)
- success = 1;
+ success = TRUE;
if (!success) {
pa_log_warn("Denied access to client with invalid authorization data.");
}
}
+ /* Enable shared memory support if possible */
+ do_shm =
+ pa_mempool_is_shared(c->protocol->core->mempool) &&
+ c->is_local;
+
+ pa_log_debug("SHM possible: %s", pa_yes_no(do_shm));
+
+ if (do_shm)
+ if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
+ do_shm = FALSE;
+
+ if (do_shm) {
+ /* Only enable SHM if both sides are owned by the same
+ * user. This is a security measure because otherwise data
+ * private to the user might leak. */
+
+ const pa_creds *creds;
+ if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
+ do_shm = FALSE;
+ }
+
+ pa_log_debug("Negotiated SHM: %s", pa_yes_no(do_shm));
+ pa_pstream_enable_shm(c->pstream, do_shm);
+
reply = reply_new(tag);
- pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION);
+ pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION | (do_shm ? 0x80000000 : 0));
#ifdef HAVE_CREDS
{
pa_tagstruct_put_usec(reply, latency);
pa_tagstruct_put_usec(reply, 0);
- pa_tagstruct_put_boolean(reply, pa_sink_input_get_state(s->sink_input) == PA_SINK_INPUT_RUNNING);
+ pa_tagstruct_put_boolean(reply, s->sink_input->thread_info.playing_for > 0);
pa_tagstruct_put_timeval(reply, &tv);
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
pa_tagstruct_puts64(reply, s->write_index);
pa_tagstruct_puts64(reply, s->read_index);
+
+ if (c->version >= 13) {
+ pa_tagstruct_putu64(reply, s->sink_input->thread_info.underrun_for);
+ pa_tagstruct_putu64(reply, s->sink_input->thread_info.playing_for);
+ }
+
pa_pstream_send_tagstruct(c->pstream, reply);
}
reply = reply_new(tag);
pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0);
pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source));
- pa_tagstruct_put_boolean(reply, FALSE);
+ pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING);
pa_tagstruct_put_timeval(reply, &tv);
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
connection_assert_ref(c);
pa_assert(t);
- if ((c->version < 13 && pa_tagstruct_gets(t, &name) < 0) ||
+ if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
pa_tagstruct_get_channel_map(t, &map) < 0 ||
pa_tagstruct_getu32(t, &length) < 0) {
CHECK_VALIDITY(c->pstream, (length % pa_frame_size(&ss)) == 0 && length > 0, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, length <= PA_SCACHE_ENTRY_SIZE_MAX, tag, PA_ERR_TOOLARGE);
- if (c->version < 13)
- CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
-
p = pa_proplist_new();
if (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) {
if (c->version < 13)
pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
+ else if (!name)
+ if (!(name = pa_proplist_gets(p, PA_PROP_EVENT_ID)))
+ name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME);
+
+ CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
s = upload_stream_new(c, &ss, &map, name, length, p);
pa_proplist_free(p);
static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_input *s) {
pa_sample_spec fixed_ss;
+ pa_usec_t sink_latency;
pa_assert(t);
pa_sink_input_assert_ref(s);
pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &s->channel_map);
pa_tagstruct_put_cvolume(t, &s->volume);
- pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s));
- pa_tagstruct_put_usec(t, pa_sink_get_latency(s->sink));
+ 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)));
pa_tagstruct_puts(t, s->driver);
if (c->version >= 11)
static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source_output *s) {
pa_sample_spec fixed_ss;
+ pa_usec_t source_latency;
pa_assert(t);
pa_source_output_assert_ref(s);
pa_tagstruct_putu32(t, s->source->index);
pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &s->channel_map);
- pa_tagstruct_put_usec(t, pa_source_output_get_latency(s));
- pa_tagstruct_put_usec(t, pa_source_get_latency(s->source));
+ pa_tagstruct_put_usec(t, pa_source_output_get_latency(s, &source_latency));
+ pa_tagstruct_put_usec(t, source_latency);
pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s)));
pa_tagstruct_puts(t, s->driver);
pa_assert(t);
pa_assert(e);
- fixup_sample_spec(c, &fixed_ss, &e->sample_spec);
+ if (e->memchunk.memblock)
+ fixup_sample_spec(c, &fixed_ss, &e->sample_spec);
+ else
+ memset(&fixed_ss, 0, sizeof(fixed_ss));
pa_tagstruct_putu32(t, e->index);
pa_tagstruct_puts(t, e->name);
pa_tagstruct_put_cvolume(t, &e->volume);
- pa_tagstruct_put_usec(t, pa_bytes_to_usec(e->memchunk.length, &e->sample_spec));
+ pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0);
pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &e->channel_map);
pa_tagstruct_putu32(t, e->memchunk.length);
s = pa_idxset_get_by_index(c->record_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- pa_memblockq_flush(s->memblockq);
+ pa_memblockq_flush_read(s->memblockq);
pa_pstream_send_simple_ack(c->pstream, tag);
}
if (command == PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR) {
playback_stream *s;
+ pa_bool_t adjust_latency = FALSE;
s = pa_idxset_get_by_index(c->output_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
PA_TAG_U32, &prebuf,
PA_TAG_U32, &minreq,
PA_TAG_INVALID) < 0 ||
+ (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
- if (maxlength <= 0 || maxlength > MAX_MEMBLOCKQ_LENGTH)
- maxlength = MAX_MEMBLOCKQ_LENGTH;
- if (tlength <= 0)
- tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*1000, &s->sink_input->sample_spec);
- if (minreq <= 0)
- minreq = (tlength*9)/10;
- if (prebuf <= 0)
- prebuf = tlength;
-
+ fix_playback_buffer_attr_pre(s, adjust_latency, &maxlength, &tlength, &prebuf, &minreq);
pa_memblockq_set_maxlength(s->memblockq, maxlength);
pa_memblockq_set_tlength(s->memblockq, tlength);
pa_memblockq_set_prebuf(s->memblockq, prebuf);
pa_memblockq_set_minreq(s->memblockq, minreq);
+ fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
reply = reply_new(tag);
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq));
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_tlength(s->memblockq));
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_prebuf(s->memblockq));
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_minreq(s->memblockq));
+ pa_tagstruct_putu32(reply, maxlength);
+ pa_tagstruct_putu32(reply, tlength);
+ pa_tagstruct_putu32(reply, prebuf);
+ pa_tagstruct_putu32(reply, minreq);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_usec(reply, s->sink_latency);
} else {
record_stream *s;
- size_t base;
+ pa_bool_t adjust_latency = FALSE;
pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR);
s = pa_idxset_get_by_index(c->record_streams, idx);
PA_TAG_U32, &maxlength,
PA_TAG_U32, &fragsize,
PA_TAG_INVALID) < 0 ||
+ (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
- if (maxlength <= 0 || maxlength > MAX_MEMBLOCKQ_LENGTH)
- maxlength = MAX_MEMBLOCKQ_LENGTH;
- if (fragsize <= 0)
- fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*1000, &s->source_output->sample_spec);
-
+ fix_record_buffer_attr_pre(s, adjust_latency, &maxlength, &fragsize);
pa_memblockq_set_maxlength(s->memblockq, maxlength);
-
- base = pa_frame_size(&s->source_output->sample_spec);
- s->fragment_size = (fragsize/base)*base;
- if (s->fragment_size <= 0)
- s->fragment_size = base;
-
- if (s->fragment_size > pa_memblockq_get_maxlength(s->memblockq))
- s->fragment_size = pa_memblockq_get_maxlength(s->memblockq);
+ fix_record_buffer_attr_post(s, &maxlength, &fragsize);
reply = reply_new(tag);
- pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq));
- pa_tagstruct_putu32(reply, s->fragment_size);
+ pa_tagstruct_putu32(reply, maxlength);
+ pa_tagstruct_putu32(reply, fragsize);
+
+ if (c->version >= 13)
+ pa_tagstruct_put_usec(reply, s->source_latency);
}
pa_pstream_send_tagstruct(c->pstream, reply);
CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY);
- if (pa_sink_input_move_to(si, sink, 0) < 0) {
+ if (pa_sink_input_move_to(si, sink) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
connection *c;
char cname[256], pname[128];
- pa_assert(s);
pa_assert(io);
pa_assert(p);
} else
c->auth_timeout_event = NULL;
+ c->is_local = pa_iochannel_socket_is_local(io);
c->version = 8;
c->protocol = p;
pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
pa_snprintf(cname, sizeof(cname), "Native client (%s)", pname);
c->client = pa_client_new(p->core, __FILE__, cname);
+ pa_proplist_sets(c->client->proplist, "native-protocol.peer", pname);
c->client->kill = client_kill_cb;
c->client->userdata = c;
c->client->module = p->module;
#ifdef HAVE_CREDS
if (pa_iochannel_creds_supported(io))
pa_iochannel_creds_enable(io);
-
#endif
}
#ifdef HAVE_CREDS
{
- pa_bool_t a = 1;
+ pa_bool_t a = TRUE;
if (pa_modargs_get_value_boolean(ma, "auth-group-enabled", &a) < 0) {
pa_log("auth-group-enabled= expects a boolean argument.");
return NULL;
}
- p->auth_group = a ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", c->is_system_instance ? PA_ACCESS_GROUP : NULL)) : NULL;
+ p->auth_group = a ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", pa_in_system_mode() ? PA_ACCESS_GROUP : NULL)) : NULL;
if (p->auth_group)
pa_log_info("Allowing access to group '%s'.", p->auth_group);
if (!(p = protocol_new_internal(core, m, ma)))
return NULL;
- p->server = server;
+ p->server = pa_socket_server_ref(server);
pa_socket_server_set_callback(p->server, on_connection, p);
if (pa_socket_server_get_address(p->server, t, sizeof(t))) {