summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorg Chini <georg@chini.tk>2021-01-01 00:27:24 +0100
committerPulseAudio Marge Bot <pulseaudio-maintainers@lists.freedesktop.org>2021-11-03 18:37:31 +0000
commitd55fde2fed663e46409dd2e3b4af279d97190f73 (patch)
tree39d7c910c5707c36e98f2e7d8c2a60af0afaeb2f
parenta275a0b8111071ac1937f0c0f3c1703e3831beeb (diff)
source-output: Fix rewinding
If the output implements a process_rewind() callback, the resampler delay is not taken into account. This leads to glitches during volume changes when source and source output rates differ. This patch fixes the problem. Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/120>
-rw-r--r--src/pulsecore/source-output.c59
1 files changed, 47 insertions, 12 deletions
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index cf16c400b..2e2b7a274 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -29,6 +29,7 @@
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulse/internal.h>
+#include <pulse/timeval.h>
#include <pulsecore/core-format.h>
#include <pulsecore/mix.h>
@@ -237,6 +238,7 @@ int pa_source_output_new(
pa_channel_map volume_map;
int r;
char *pt;
+ size_t resampler_history;
pa_assert(_o);
pa_assert(core);
@@ -499,6 +501,11 @@ int pa_source_output_new(
0,
&o->source->silence);
+ resampler_history = (uint64_t) PA_RESAMPLER_MAX_DELAY_USEC * o->source->sample_spec.rate / PA_USEC_PER_SEC;
+ resampler_history *= pa_frame_size(&o->source->sample_spec);
+
+ pa_memblockq_set_maxrewind(o->thread_info.delay_memblockq, resampler_history + pa_source_get_max_rewind(o->source));
+
pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0);
pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0);
@@ -865,21 +872,36 @@ void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in so
return;
if (o->process_rewind) {
- pa_assert(pa_memblockq_get_length(o->thread_info.delay_memblockq) == 0);
+ size_t source_output_nbytes;
+ size_t length;
- if (o->thread_info.resampler)
- nbytes = pa_resampler_result(o->thread_info.resampler, nbytes);
+ /* The length of the memblockq may be non-zero if pa_source_output_rewind() is called twice
+ * without pa_source_output_push() called in between. In that case, the resampler has already
+ * been reset and we can skip that part. */
+ length = pa_memblockq_get_length(o->thread_info.delay_memblockq);
- pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) nbytes);
+ pa_memblockq_rewind(o->thread_info.delay_memblockq, nbytes);
- if (nbytes > 0)
- o->process_rewind(o, nbytes);
+ source_output_nbytes = pa_resampler_result(o->thread_info.resampler, nbytes);
- if (o->thread_info.resampler)
- pa_resampler_rewind(o->thread_info.resampler, nbytes, NULL, 0);
+ pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) source_output_nbytes);
- } else
- pa_memblockq_seek(o->thread_info.delay_memblockq, - ((int64_t) nbytes), PA_SEEK_RELATIVE, true);
+ if (source_output_nbytes > 0)
+ o->process_rewind(o, source_output_nbytes);
+
+ if (o->thread_info.resampler && length == 0) {
+ size_t resampler_bytes;
+
+ /* Round down to full frames */
+ resampler_bytes = (size_t) pa_resampler_get_delay(o->thread_info.resampler, false) * pa_frame_size(&o->source->sample_spec);
+ if (resampler_bytes > 0)
+ pa_memblockq_rewind(o->thread_info.delay_memblockq, resampler_bytes);
+
+ pa_resampler_rewind(o->thread_info.resampler, source_output_nbytes, NULL, 0);
+ }
+ }
+
+ pa_memblockq_seek(o->thread_info.delay_memblockq, - ((int64_t) nbytes), PA_SEEK_RELATIVE, true);
}
/* Called from thread context */
@@ -887,18 +909,25 @@ size_t pa_source_output_get_max_rewind(pa_source_output *o) {
pa_source_output_assert_ref(o);
pa_source_output_assert_io_context(o);
- return o->thread_info.resampler ? pa_resampler_request(o->thread_info.resampler, o->source->thread_info.max_rewind) : o->source->thread_info.max_rewind;
+ return pa_resampler_result(o->thread_info.resampler, o->source->thread_info.max_rewind);
}
/* Called from thread context */
void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) {
+ size_t resampler_history;
+
pa_source_output_assert_ref(o);
pa_source_output_assert_io_context(o);
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
+ resampler_history = (uint64_t) PA_RESAMPLER_MAX_DELAY_USEC * o->source->sample_spec.rate / PA_USEC_PER_SEC;
+ resampler_history *= pa_frame_size(&o->source->sample_spec);
+
+ pa_memblockq_set_maxrewind(o->thread_info.delay_memblockq, resampler_history + nbytes);
+
if (o->update_max_rewind)
- o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes);
+ o->update_max_rewind(o, pa_resampler_result(o->thread_info.resampler, nbytes));
}
/* Called from thread context */
@@ -1785,6 +1814,7 @@ finish:
int pa_source_output_update_resampler(pa_source_output *o) {
pa_resampler *new_resampler;
char *memblockq_name;
+ size_t resampler_history;
pa_source_output_assert_ref(o);
pa_assert_ctl_context();
@@ -1842,6 +1872,11 @@ int pa_source_output_update_resampler(pa_source_output *o) {
&o->source->silence);
pa_xfree(memblockq_name);
+ resampler_history = (uint64_t) PA_RESAMPLER_MAX_DELAY_USEC * o->source->sample_spec.rate / PA_USEC_PER_SEC;
+ resampler_history *= pa_frame_size(&o->source->sample_spec);
+
+ pa_memblockq_set_maxrewind(o->thread_info.delay_memblockq, resampler_history + pa_source_get_max_rewind(o->source));
+
o->actual_resample_method = new_resampler ? pa_resampler_get_method(new_resampler) : PA_RESAMPLER_INVALID;
pa_log_debug("Updated resampler for source output %d", o->index);