diff options
author | Lennart Poettering <lennart@poettering.net> | 2008-04-23 18:11:57 +0000 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2008-04-23 18:11:57 +0000 |
commit | ed0af46e69a207249bfbade2babfbc93b3f89fc9 (patch) | |
tree | da25ac83c86af97939974525b154fd2d99c75b38 | |
parent | af03dd4e4fe668c988e1d9e781212763d3e9d56e (diff) |
unify code that fixes up buffering metrics
git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2302 fefdeb5f-60dc-0310-8127-8f9354f1896f
-rw-r--r-- | src/pulsecore/protocol-native.c | 345 |
1 files changed, 188 insertions, 157 deletions
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index cde113df..ca14b955 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -470,11 +470,65 @@ 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) { + pa_assert(s); + pa_assert(maxlength); + pa_assert(fragsize); + + if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH) + *maxlength = MAX_MEMBLOCKQ_LENGTH; + + if (*fragsize <= 0) + *fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->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, &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); + } +} + +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, + pa_bool_t peak_detect, uint32_t *maxlength, uint32_t *fragsize, pa_source_output_flags_t flags, @@ -501,6 +555,8 @@ static record_stream* record_stream_new( data.source = source; 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); @@ -522,30 +578,7 @@ static record_stream* record_stream_new( 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*PA_USEC_PER_MSEC, &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, @@ -557,16 +590,7 @@ static record_stream* record_stream_new( 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; @@ -674,6 +698,114 @@ 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) { + 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); + + if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH) + *maxlength = MAX_MEMBLOCKQ_LENGTH; + if (*tlength <= 0) + *tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec); + if (*minreq <= 0) + *minreq = pa_usec_to_bytes(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec); + + frame_size = pa_frame_size(&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 <= 0) + *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); +} + static playback_stream* playback_stream_new( connection *c, pa_sink *sink, @@ -697,8 +829,6 @@ static playback_stream* playback_stream_new( uint32_t idx; int64_t start_index; pa_sink_input_new_data data; - pa_usec_t tlength_usec, minreq_usec, sink_usec; - size_t frame_size; pa_assert(c); pa_assert(ss); @@ -769,90 +899,7 @@ static playback_stream* playback_stream_new( 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*PA_USEC_PER_MSEC, &sink_input->sample_spec); - if (*minreq <= 0) - *minreq = pa_usec_to_bytes(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &sink_input->sample_spec); - - frame_size = pa_frame_size(&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, &sink_input->sample_spec); - minreq_usec = pa_bytes_to_usec(*minreq, &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(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, &sink_input->sample_spec); - *minreq = pa_usec_to_bytes(minreq_usec, &sink_input->sample_spec); - - if (*minreq <= 0) { - *minreq += frame_size; - *tlength += frame_size*2; - } - - if (*tlength <= *minreq) - *tlength = *minreq*2 + frame_size; - - if (*prebuf <= 0) - *prebuf = *tlength; - + 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( @@ -866,17 +913,14 @@ static playback_stream* playback_stream_new( &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); + s->minreq = *minreq; pa_atomic_store(&s->missing, 0); s->drain_request = FALSE; @@ -1671,7 +1715,8 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ 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; @@ -1720,7 +1765,8 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ if (c->version >= 13) { - if (pa_tagstruct_get_boolean(t, &adjust_latency) < 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) { protocol_error(c); pa_proplist_free(p); @@ -1761,7 +1807,7 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) | (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0); - s = record_stream_new(c, source, &ss, &map, &maxlength, &fragment_size, flags, p, adjust_latency); + s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency); pa_proplist_free(p); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); @@ -2940,6 +2986,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; s = pa_idxset_get_by_index(c->output_streams, idx); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); @@ -2952,34 +2999,28 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u 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); } 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); @@ -2990,29 +3031,19 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u 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); } pa_pstream_send_tagstruct(c->pstream, reply); |