From 40b66a0be9579fff095cba77f3ea29c390d966f2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Sep 2008 18:31:46 +0200 Subject: Implement "early requests" mode. PA_STREAM_EARLY_REQUESTS is a new flag that will modify buffering metric selection behaviour a bit. This code is good for broken ALSA/OSS clients that ignore 'readability' on the fds in question and schedule audio via usleep() instead. --- PROTOCOL | 13 +- src/pulse/def.h | 260 ++++++++++++++++++---------------------- src/pulse/stream.c | 18 +-- src/pulsecore/protocol-native.c | 172 ++++++++++++++++++++------ 4 files changed, 278 insertions(+), 185 deletions(-) diff --git a/PROTOCOL b/PROTOCOL index 581eeefa..1e2a832f 100644 --- a/PROTOCOL +++ b/PROTOCOL @@ -126,7 +126,7 @@ New field for PA_COMMAND_CREATE_PLAYBACK_STREAM at the end: Buffer attributes for PA_COMMAND_CREATE_PLAYBACK_STREAM and PA_COMMAND_CREATE_RECORD_STREAM may now be 0 for default values. -New filed for PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR, +New field for PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR, PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR at the end: adjust_latency (bool) @@ -141,6 +141,15 @@ new message: PA_COMMAND_EXTENSION -PA_COMMAND_CREATE_RECORD_STREAM, PA_COMMAND_CREATE_PLAYBACK_STREAM: +PA_COMMAND_CREATE_PLAYBACK_STREAM: bool volume_set at the end + +PA_COMMAND_CREATE_RECORD_STREAM, PA_COMMAND_CREATE_PLAYBACK_STREAM: + + bool early_requests at the end + +New field for PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR, +PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR at the end: + + early_requests (bool) diff --git a/src/pulse/def.h b/src/pulse/def.h index 53bea3bd..02da9f70 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -96,148 +96,124 @@ typedef enum pa_stream_direction { /** Some special flags for stream connections. */ typedef enum pa_stream_flags { - PA_STREAM_START_CORKED = 1, /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */ - PA_STREAM_INTERPOLATE_TIMING = 2, /**< Interpolate the latency for - * this stream. When enabled, - * pa_stream_get_latency() and - * pa_stream_get_time() will try - * to estimate the current - * record/playback time based on - * the local time that passed - * since the last timing info - * update. Using this option - * has the advantage of not - * requiring a whole roundtrip - * when the current - * playback/recording time is - * needed. Consider using this - * option when requesting - * latency information - * frequently. This is - * especially useful on long - * latency network - * connections. It makes a lot - * of sense to combine this - * option with - * PA_STREAM_AUTO_TIMING_UPDATE. */ - PA_STREAM_NOT_MONOTONIC = 4, /**< Don't force the time to - * increase monotonically. If - * this option is enabled, - * pa_stream_get_time() will not - * necessarily return always - * monotonically increasing time - * values on each call. This may - * confuse applications which - * cannot deal with time going - * 'backwards', but has the - * advantage that bad transport - * latency estimations that - * caused the time to to jump - * ahead can be corrected - * quickly, without the need to - * wait. (Please note that this - * flag was named - * PA_STREAM_NOT_MONOTONOUS in - * releases prior to 0.9.11. The - * old name is still defined too, - * for compatibility reasons. */ - PA_STREAM_AUTO_TIMING_UPDATE = 8, /**< If set timing update requests - * are issued periodically - * automatically. Combined with - * PA_STREAM_INTERPOLATE_TIMING - * you will be able to query the - * current time and latency with - * pa_stream_get_time() and - * pa_stream_get_latency() at - * all times without a packet - * round trip.*/ - PA_STREAM_NO_REMAP_CHANNELS = 16, /**< Don't remap channels by - * their name, instead map them - * simply by their - * index. Implies - * PA_STREAM_NO_REMIX_CHANNELS. Only - * supported when the server is - * at least PA 0.9.8. It is - * ignored on older - * servers.\since 0.9.8 */ - PA_STREAM_NO_REMIX_CHANNELS = 32, /**< When remapping channels by - * name, don't upmix or downmix - * them to related - * channels. Copy them into - * matching channels of the - * device 1:1. Only supported - * when the server is at least - * PA 0.9.8. It is ignored on - * older servers. \since - * 0.9.8 */ - PA_STREAM_FIX_FORMAT = 64, /**< Use the sample format of the - * sink/device this stream is being - * connected to, and possibly ignore - * the format the sample spec contains - * -- but you still have to pass a - * valid value in it as a hint to - * PulseAudio what would suit your - * stream best. If this is used you - * should query the used sample format - * after creating the stream by using - * pa_stream_get_sample_spec(). Also, - * if you specified manual buffer - * metrics it is recommended to update - * them with - * pa_stream_set_buffer_attr() to - * compensate for the changed frame - * sizes. Only supported when the - * server is at least PA 0.9.8. It is - * ignored on older servers. \since - * 0.9.8 */ - - PA_STREAM_FIX_RATE = 128, /**< Use the sample rate of the sink, - * and possibly ignore the rate the - * sample spec contains. Usage similar - * to PA_STREAM_FIX_FORMAT.Only - * supported when the server is at least - * PA 0.9.8. It is ignored on older - * servers. \since 0.9.8 */ - - PA_STREAM_FIX_CHANNELS = 256, /**< Use the number of channels and - * the channel map of the sink, and - * possibly ignore the number of - * channels and the map the sample spec - * and the passed channel map - * contains. Usage similar to - * PA_STREAM_FIX_FORMAT. Only supported - * when the server is at least PA - * 0.9.8. It is ignored on older - * servers. \since 0.9.8 */ - PA_STREAM_DONT_MOVE = 512, /**< Don't allow moving of this stream to - * another sink/device. Useful if you use - * any of the PA_STREAM_FIX_ flags and - * want to make sure that resampling - * never takes place -- which might - * happen if the stream is moved to - * another sink/source whith a different - * sample spec/channel map. Only - * supported when the server is at least - * PA 0.9.8. It is ignored on older - * servers. \since 0.9.8 */ - PA_STREAM_VARIABLE_RATE = 1024, /**< Allow dynamic changing of the - * sampling rate during playback - * with - * pa_stream_update_sample_rate(). Only - * supported when the server is at - * least PA 0.9.8. It is ignored - * on older servers. \since - * 0.9.8 */ - PA_STREAM_PEAK_DETECT = 2048, /**< Find peaks instead of - * resampling. \since 0.9.11 */ - - PA_STREAM_START_MUTED = 4096, /**< Create in muted state. \since 0.9.11 */ - - PA_STREAM_ADJUST_LATENCY = 8192, /**< Try to adjust the latency of - * the sink/source based on the - * requested buffer metrics and - * adjust buffer metrics - * accordingly. See pa_buffer_attr \since 0.9.11 */ + + PA_STREAM_START_CORKED = 0x0001U, + /**< Create the stream corked, requiring an explicit + * pa_stream_cork() call to uncork it. */ + + PA_STREAM_INTERPOLATE_TIMING = 0x0002U, + /**< Interpolate the latency for this stream. When enabled, + * pa_stream_get_latency() and pa_stream_get_time() will try to + * estimate the current record/playback time based on the local + * time that passed since the last timing info update. Using this + * option has the advantage of not requiring a whole roundtrip + * when the current playback/recording time is needed. Consider + * using this option when requesting latency information + * frequently. This is especially useful on long latency network + * connections. It makes a lot of sense to combine this option + * with PA_STREAM_AUTO_TIMING_UPDATE. */ + + PA_STREAM_NOT_MONOTONIC = 0x0004U, + /**< Don't force the time to increase monotonically. If this + * option is enabled, pa_stream_get_time() will not necessarily + * return always monotonically increasing time values on each + * call. This may confuse applications which cannot deal with time + * going 'backwards', but has the advantage that bad transport + * latency estimations that caused the time to to jump ahead can + * be corrected quickly, without the need to wait. (Please note + * that this flag was named PA_STREAM_NOT_MONOTONOUS in releases + * prior to 0.9.11. The old name is still defined too, for + * compatibility reasons. */ + + PA_STREAM_AUTO_TIMING_UPDATE = 0x0008U, + /**< If set timing update requests are issued periodically + * automatically. Combined with PA_STREAM_INTERPOLATE_TIMING you + * will be able to query the current time and latency with + * pa_stream_get_time() and pa_stream_get_latency() at all times + * without a packet round trip.*/ + + PA_STREAM_NO_REMAP_CHANNELS = 0x0010U, + /**< Don't remap channels by their name, instead map them simply + * by their index. Implies PA_STREAM_NO_REMIX_CHANNELS. Only + * supported when the server is at least PA 0.9.8. It is ignored + * on older servers.\since 0.9.8 */ + + PA_STREAM_NO_REMIX_CHANNELS = 0x0020U, + /**< When remapping channels by name, don't upmix or downmix them + * to related channels. Copy them into matching channels of the + * device 1:1. Only supported when the server is at least PA + * 0.9.8. It is ignored on older servers. \since 0.9.8 */ + + PA_STREAM_FIX_FORMAT = 0x0040U, + /**< Use the sample format of the sink/device this stream is being + * connected to, and possibly ignore the format the sample spec + * contains -- but you still have to pass a valid value in it as a + * hint to PulseAudio what would suit your stream best. If this is + * used you should query the used sample format after creating the + * stream by using pa_stream_get_sample_spec(). Also, if you + * specified manual buffer metrics it is recommended to update + * them with pa_stream_set_buffer_attr() to compensate for the + * changed frame sizes. Only supported when the server is at least + * PA 0.9.8. It is ignored on older servers. \since 0.9.8 */ + + PA_STREAM_FIX_RATE = 0x0080U, + /**< Use the sample rate of the sink, and possibly ignore the rate + * the sample spec contains. Usage similar to + * PA_STREAM_FIX_FORMAT.Only supported when the server is at least + * PA 0.9.8. It is ignored on older servers. \since 0.9.8 */ + + PA_STREAM_FIX_CHANNELS = 0x0100, + /**< Use the number of channels and the channel map of the sink, + * and possibly ignore the number of channels and the map the + * sample spec and the passed channel map contains. Usage similar + * to PA_STREAM_FIX_FORMAT. Only supported when the server is at + * least PA 0.9.8. It is ignored on older servers. \since 0.9.8 */ + + PA_STREAM_DONT_MOVE = 0x0200U, + /**< Don't allow moving of this stream to another + * sink/device. Useful if you use any of the PA_STREAM_FIX_ flags + * and want to make sure that resampling never takes place -- + * which might happen if the stream is moved to another + * sink/source whith a different sample spec/channel map. Only + * supported when the server is at least PA 0.9.8. It is ignored + * on older servers. \since 0.9.8 */ + + PA_STREAM_VARIABLE_RATE = 0x0400U, + /**< Allow dynamic changing of the sampling rate during playback + * with pa_stream_update_sample_rate(). Only supported when the + * server is at least PA 0.9.8. It is ignored on older + * servers. \since 0.9.8 */ + + PA_STREAM_PEAK_DETECT = 0x0800U, + /**< Find peaks instead of resampling. \since 0.9.11 */ + + PA_STREAM_START_MUTED = 0x1000U, + /**< Create in muted state. \since 0.9.11 */ + + PA_STREAM_ADJUST_LATENCY = 0x2000U, + /**< Try to adjust the latency of the sink/source based on the + * requested buffer metrics and adjust buffer metrics + * accordingly. Also see pa_buffer_attr. This option may not be + * specified at the same time as PA_STREAM_EARLY_REQUESTS. \since + * 0.9.11 */ + + PA_STREAM_EARLY_REQUESTS = 0x4000U + /**< Enable compatibility mode for legacy clients that rely on a + * "classic" hardware device fragment-style playback model. If + * this option is set, the minreq value of the buffer metrics gets + * a new meaning: instead of just specifying that no requests + * asking for less new data than this value will be made to the + * client it will also guarantee that requests are generated as + * early as this limit is reached. This flag should only be set in + * very few situations where compatiblity with a fragment-based + * playback model needs to be kept and the client applications + * cannot deal with data requests that are delayed to the latest + * moment possible. (Usually these are programs that use usleep() + * or a similar call in their playback loops instead of sleeping + * on the device itself.) Also see pa_buffer_attr. This option may + * not be specified at the same time as + * PA_STREAM_ADJUST_LATENCY. \since 0.9.12 */ + } pa_stream_flags_t; /** \cond fulldocs */ diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 6a497b70..d0c7d67e 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -886,7 +886,8 @@ static int create_stream( PA_STREAM_VARIABLE_RATE| PA_STREAM_PEAK_DETECT| PA_STREAM_START_MUTED| - PA_STREAM_ADJUST_LATENCY)), PA_ERR_INVALID); + PA_STREAM_ADJUST_LATENCY| + PA_STREAM_EARLY_REQUESTS)), PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED); PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED); @@ -899,6 +900,7 @@ static int create_stream( PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_RECORD || !(flags & (PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, (flags & (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS)) != (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS), PA_ERR_INVALID); pa_stream_ref(s); @@ -997,13 +999,12 @@ static int create_stream( pa_tagstruct_putu32(t, s->direct_on_input); } - if (s->context->version >= 14 && - s->direction == PA_STREAM_PLAYBACK) { + if (s->context->version >= 14) { - pa_tagstruct_put( - t, - PA_TAG_BOOLEAN, volume_set, - PA_TAG_INVALID); + if (s->direction == PA_STREAM_PLAYBACK) + pa_tagstruct_put_boolean(t, volume_set); + + pa_tagstruct_put_boolean(t, flags & PA_STREAM_EARLY_REQUESTS); } pa_pstream_send_tagstruct(s->context->pstream, t); @@ -2079,6 +2080,9 @@ pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr if (s->context->version >= 13) pa_tagstruct_put_boolean(t, !!(s->flags & PA_STREAM_ADJUST_LATENCY)); + if (s->context->version >= 14) + pa_tagstruct_put_boolean(t, !!(s->flags & PA_STREAM_EARLY_REQUESTS)); + pa_pstream_send_tagstruct(s->context->pstream, t); pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_set_buffer_attr_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 8ceea809..06245d1b 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -469,47 +469,95 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i return 0; } -static void fix_record_buffer_attr_pre(record_stream *s, pa_bool_t adjust_latency, uint32_t *maxlength, uint32_t *fragsize) { +static void fix_record_buffer_attr_pre( + record_stream *s, + pa_bool_t adjust_latency, + pa_bool_t early_requests, + uint32_t *maxlength, + uint32_t *fragsize) { + + size_t frame_size; + pa_usec_t orig_fragsize_usec, fragsize_usec, source_usec; + pa_assert(s); pa_assert(maxlength); pa_assert(fragsize); + frame_size = pa_frame_size(&s->source_output->sample_spec); + if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH) *maxlength = MAX_MEMBLOCKQ_LENGTH; if (*maxlength <= 0) - *maxlength = (uint32_t) pa_frame_size(&s->source_output->sample_spec); + *maxlength = (uint32_t) frame_size; if (*fragsize == (uint32_t) -1) *fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec); if (*fragsize <= 0) - *fragsize = (uint32_t) pa_frame_size(&s->source_output->sample_spec); + *fragsize = (uint32_t) frame_size; + + orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec); - if (adjust_latency) { - pa_usec_t orig_fragsize_usec, fragsize_usec; + if (early_requests) { + + /* In early request mode we need to emulate the classic + * fragment-based playback model. We do this setting the source + * latency to the fragment size. */ + + source_usec = fragsize_usec; + + } else if (adjust_latency) { /* 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. */ - orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec); + source_usec = fragsize_usec/2; + + } else { + + /* Ok, the user didn't ask us to adjust the latency, hence we + * don't */ - s->source_latency = pa_source_output_set_requested_latency(s->source_output, fragsize_usec/2); + source_usec = 0; + } + + if (source_usec > 0) + s->source_latency = pa_source_output_set_requested_latency(s->source_output, source_usec); + else + s->source_latency = 0; + + if (early_requests) { + + /* Ok, we didn't necessarily get what we were asking for, so + * let's tell the user */ + + fragsize_usec = s->source_latency; + + } else if (adjust_latency) { + + /* Now subtract what we actually got */ if (fragsize_usec >= s->source_latency*2) fragsize_usec -= s->source_latency; else fragsize_usec = s->source_latency; + } - if (pa_usec_to_bytes(orig_fragsize_usec, &s->source_output->sample_spec) != - pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec)) + if (pa_usec_to_bytes(orig_fragsize_usec, &s->source_output->sample_spec) != + pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec)) - *fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec); - } else - s->source_latency = 0; + *fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec); + + if (*fragsize <= 0) + *fragsize = (uint32_t) frame_size; } -static void fix_record_buffer_attr_post(record_stream *s, uint32_t *maxlength, uint32_t *fragsize) { +static void fix_record_buffer_attr_post( + record_stream *s, + uint32_t *maxlength, + uint32_t *fragsize) { + size_t base; pa_assert(s); @@ -541,7 +589,8 @@ static record_stream* record_stream_new( pa_source_output_flags_t flags, pa_proplist *p, pa_bool_t adjust_latency, - pa_sink_input *direct_on_input) { + pa_sink_input *direct_on_input, + pa_bool_t early_requests) { record_stream *s; pa_source_output *source_output; @@ -587,7 +636,7 @@ static record_stream* record_stream_new( s->source_output->suspend = source_output_suspend_cb; s->source_output->userdata = s; - fix_record_buffer_attr_pre(s, adjust_latency, maxlength, fragsize); + fix_record_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, fragsize); s->memblockq = pa_memblockq_new( 0, @@ -693,6 +742,8 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, case PLAYBACK_STREAM_MESSAGE_UNDERFLOW: { pa_tagstruct *t; +/* pa_log("signalling underflow"); */ + /* Report that we're empty */ t = pa_tagstruct_new(NULL, 0); pa_tagstruct_putu32(t, PA_COMMAND_UNDERFLOW); @@ -737,7 +788,15 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, 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) { +static void fix_playback_buffer_attr_pre( + playback_stream *s, + pa_bool_t adjust_latency, + pa_bool_t early_requests, + uint32_t *maxlength, + uint32_t *tlength, + uint32_t* prebuf, + uint32_t* minreq) { + size_t frame_size; pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec; @@ -774,7 +833,17 @@ static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_la (double) tlength_usec / PA_USEC_PER_MSEC, (double) minreq_usec / PA_USEC_PER_MSEC); - if (adjust_latency) { + if (early_requests) { + + /* In early request mode we need to emulate the classic + * fragment-based playback model. We do this setting the sink + * latency to the fragment size. */ + + sink_usec = minreq_usec; + + pa_log_debug("Early requests mode enabled, configuring sink latency to minreq."); + + } else 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 @@ -798,6 +867,8 @@ static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_la else sink_usec = 0; + pa_log_debug("Adjust latency mode enabled, configuring sink latency to half of overall latency."); + } else { /* Ok, the user didn't ask us to adjust the latency, but we @@ -808,11 +879,21 @@ static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_la sink_usec = (tlength_usec - minreq_usec*2); else sink_usec = 0; + + pa_log_debug("Traditional mode enabled, modifying sink usec only for compat with minreq."); } s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec); - if (adjust_latency) { + if (early_requests) { + + /* Ok, we didn't necessarily get what we were asking for, so + * let's tell the user */ + + minreq_usec = s->sink_latency; + + } else 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 */ @@ -835,7 +916,7 @@ static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_la *minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec); if (*minreq <= 0) { - *minreq += (uint32_t) frame_size; + *minreq = (uint32_t) frame_size; *tlength += (uint32_t) frame_size*2; } @@ -846,7 +927,13 @@ static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_la *prebuf = *tlength; } -static void fix_playback_buffer_attr_post(playback_stream *s, uint32_t *maxlength, uint32_t *tlength, uint32_t* prebuf, uint32_t* minreq) { +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); @@ -876,7 +963,8 @@ static playback_stream* playback_stream_new( uint32_t *missing, pa_sink_input_flags_t flags, pa_proplist *p, - pa_bool_t adjust_latency) { + pa_bool_t adjust_latency, + pa_bool_t early_requests) { playback_stream *s, *ssync; pa_sink_input *sink_input; @@ -957,7 +1045,7 @@ static playback_stream* playback_stream_new( start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0; - fix_playback_buffer_attr_pre(s, adjust_latency, maxlength, tlength, prebuf, minreq); + fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, tlength, prebuf, minreq); pa_sink_input_get_silence(sink_input, &silence); s->memblockq = pa_memblockq_new( @@ -1433,7 +1521,7 @@ static void sink_input_moved_cb(pa_sink_input *i) { 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); + fix_playback_buffer_attr_pre(s, TRUE, FALSE, &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); @@ -1532,7 +1620,7 @@ static void source_output_moved_cb(pa_source_output *o) { fragsize = (uint32_t) s->fragment_size; maxlength = (uint32_t) pa_memblockq_get_length(s->memblockq); - fix_record_buffer_attr_pre(s, TRUE, &maxlength, &fragsize); + fix_record_buffer_attr_pre(s, TRUE, FALSE, &maxlength, &fragsize); pa_memblockq_set_maxlength(s->memblockq, maxlength); fix_record_buffer_attr_post(s, &maxlength, &fragsize); @@ -1599,7 +1687,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u no_move = FALSE, variable_rate = FALSE, muted = FALSE, - adjust_latency = FALSE; + adjust_latency = FALSE, + early_requests = FALSE; pa_sink_input_flags_t flags = 0; pa_proplist *p; @@ -1672,7 +1761,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u if (c->version >= 14) { - if (pa_tagstruct_get_boolean(t, &volume_set) < 0) { + if (pa_tagstruct_get_boolean(t, &volume_set) < 0 || + pa_tagstruct_get_boolean(t, &early_requests) < 0) { protocol_error(c); pa_proplist_free(p); return; @@ -1712,7 +1802,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) | (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0); - s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, syncid, &missing, flags, p, adjust_latency); + s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, syncid, &missing, flags, p, adjust_latency, early_requests); pa_proplist_free(p); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); @@ -1832,7 +1922,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin no_move = FALSE, variable_rate = FALSE, adjust_latency = FALSE, - peak_detect = FALSE; + peak_detect = FALSE, + early_requests = FALSE; pa_source_output_flags_t flags = 0; pa_proplist *p; uint32_t direct_on_input_idx = PA_INVALID_INDEX; @@ -1895,6 +1986,15 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin } } + if (c->version >= 14) { + + if (pa_tagstruct_get_boolean(t, &early_requests) < 0) { + protocol_error(c); + pa_proplist_free(p); + return; + } + } + if (!pa_tagstruct_eof(t)) { protocol_error(c); pa_proplist_free(p); @@ -1937,7 +2037,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) | (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0); - s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input); + s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests); pa_proplist_free(p); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); @@ -1997,7 +2097,7 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); const void*cookie; pa_tagstruct *reply; - pa_bool_t shm_on_remote, do_shm; + pa_bool_t shm_on_remote = FALSE, do_shm; pa_native_connection_assert_ref(c); pa_assert(t); @@ -3174,7 +3274,7 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u if (command == PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR) { playback_stream *s; - pa_bool_t adjust_latency = FALSE; + pa_bool_t adjust_latency = FALSE, early_requests = FALSE; s = pa_idxset_get_by_index(c->output_streams, idx); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); @@ -3188,12 +3288,13 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u PA_TAG_U32, &minreq, PA_TAG_INVALID) < 0 || (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) || + (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) || !pa_tagstruct_eof(t)) { protocol_error(c); return; } - fix_playback_buffer_attr_pre(s, adjust_latency, &maxlength, &tlength, &prebuf, &minreq); + fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, &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); @@ -3211,7 +3312,7 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u } else { record_stream *s; - pa_bool_t adjust_latency = FALSE; + pa_bool_t adjust_latency = FALSE, early_requests = FALSE; pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR); s = pa_idxset_get_by_index(c->record_streams, idx); @@ -3223,12 +3324,13 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u PA_TAG_U32, &fragsize, PA_TAG_INVALID) < 0 || (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) || + (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) || !pa_tagstruct_eof(t)) { protocol_error(c); return; } - fix_record_buffer_attr_pre(s, adjust_latency, &maxlength, &fragsize); + fix_record_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &fragsize); pa_memblockq_set_maxlength(s->memblockq, maxlength); fix_record_buffer_attr_post(s, &maxlength, &fragsize); @@ -3954,6 +4056,8 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o return; } +/* pa_log("got %lu bytes", (unsigned long) chunk->length); */ + if (playback_stream_isinstance(stream)) { playback_stream *ps = PLAYBACK_STREAM(stream); -- cgit