mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-29 05:40:23 -04:00
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.
This commit is contained in:
parent
99d5ec6c93
commit
40b66a0be9
4 changed files with 274 additions and 181 deletions
13
PROTOCOL
13
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)
|
||||
|
|
|
|||
252
src/pulse/def.h
252
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_START_CORKED = 0x0001U,
|
||||
/**< Create the stream corked, requiring an explicit
|
||||
* pa_stream_cork() call to uncork it. */
|
||||
|
||||
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_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_START_MUTED = 4096, /**< Create in muted state. \since 0.9.11 */
|
||||
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_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_flags_t;
|
||||
|
||||
/** \cond fulldocs */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
if (adjust_latency) {
|
||||
pa_usec_t orig_fragsize_usec, fragsize_usec;
|
||||
orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec);
|
||||
|
||||
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;
|
||||
|
||||
s->source_latency = pa_source_output_set_requested_latency(s->source_output, fragsize_usec/2);
|
||||
} else {
|
||||
|
||||
/* Ok, the user didn't ask us to adjust the latency, hence we
|
||||
* don't */
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue