summaryrefslogtreecommitdiff
path: root/src/pulsecore/sink.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulsecore/sink.c')
-rw-r--r--src/pulsecore/sink.c106
1 files changed, 54 insertions, 52 deletions
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 905e1db7b..01a94eda0 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -40,6 +40,7 @@
#include <pulsecore/namereg.h>
#include <pulsecore/core-util.h>
#include <pulsecore/sample-util.h>
+#include <pulsecore/stream-util.h>
#include <pulsecore/mix.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
@@ -338,6 +339,7 @@ pa_sink* pa_sink_new(
s->thread_info.soft_muted = s->muted;
s->thread_info.state = s->state;
s->thread_info.rewind_nbytes = 0;
+ s->thread_info.last_rewind_nbytes = 0;
s->thread_info.rewind_requested = false;
s->thread_info.max_rewind = 0;
s->thread_info.max_request = 0;
@@ -1073,6 +1075,9 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
pa_sink_volume_change_rewind(s, nbytes);
}
+ /* Save rewind value */
+ s->thread_info.last_rewind_nbytes = nbytes;
+
PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
pa_sink_input_assert_ref(i);
pa_sink_input_process_rewind(i, nbytes);
@@ -1557,13 +1562,26 @@ void pa_sink_reconfigure(pa_sink *s, pa_sample_spec *spec, bool passthrough) {
PA_IDXSET_FOREACH(i, s->inputs, idx) {
if (i->state == PA_SINK_INPUT_CORKED)
- pa_sink_input_update_resampler(i);
+ pa_sink_input_update_resampler(i, true);
}
pa_sink_suspend(s, false, PA_SUSPEND_INTERNAL);
}
/* Called from main thread */
+size_t pa_sink_get_last_rewind(pa_sink *s) {
+ size_t rewind_bytes;
+
+ pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LAST_REWIND, &rewind_bytes, 0, NULL) == 0);
+
+ return rewind_bytes;
+}
+
+/* Called from main thread */
pa_usec_t pa_sink_get_latency(pa_sink *s) {
int64_t usec = 0;
@@ -2669,59 +2687,21 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
pa_assert(!i->thread_info.sync_prev);
if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
- pa_usec_t usec = 0;
- size_t sink_nbytes, total_nbytes;
/* The old sink probably has some audio from this
* stream in its buffer. We want to "take it back" as
* much as possible and play it to the new sink. We
* don't know at this point how much the old sink can
- * rewind. We have to pick something, and that
- * something is the full latency of the old sink here.
- * So we rewind the stream buffer by the sink latency
- * amount, which may be more than what we should
- * rewind. This can result in a chunk of audio being
- * played both to the old sink and the new sink.
- *
- * FIXME: Fix this code so that we don't have to make
- * guesses about how much the sink will actually be
- * able to rewind. If someone comes up with a solution
- * for this, something to note is that the part of the
- * latency that the old sink couldn't rewind should
- * ideally be compensated after the stream has moved
- * to the new sink by adding silence. The new sink
- * most likely can't start playing the moved stream
- * immediately, and that gap should be removed from
- * the "compensation silence" (at least at the time of
- * writing this, the move finish code will actually
- * already take care of dropping the new sink's
- * unrewindable latency, so taking into account the
- * unrewindable latency of the old sink is the only
- * problem).
- *
- * The render_memblockq contents are discarded,
- * because when the sink changes, the format of the
- * audio stored in the render_memblockq may change
- * too, making the stored audio invalid. FIXME:
- * However, the read and write indices are moved back
- * the same amount, so if they are not the same now,
- * they won't be the same after the rewind either. If
- * the write index of the render_memblockq is ahead of
- * the read index, then the render_memblockq will feed
- * the new sink some silence first, which it shouldn't
- * do. The write index should be flushed to be the
- * same as the read index. */
-
- /* Get the latency of the sink */
- usec = pa_sink_get_latency_within_thread(s, false);
- sink_nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
- total_nbytes = sink_nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq);
-
- if (total_nbytes > 0) {
- i->thread_info.rewrite_nbytes = i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, total_nbytes) : total_nbytes;
- i->thread_info.rewrite_flush = true;
- pa_sink_input_process_rewind(i, sink_nbytes);
- }
+ * rewind, so we just save some values and reconstruct
+ * the render memblockq in finish_move(). */
+
+ /* Save some current values for restore_render_memblockq() */
+ i->thread_info.origin_sink_latency = pa_sink_get_latency_within_thread(s, false);
+ i->thread_info.move_start_time = pa_rtclock_now();
+ i->thread_info.resampler_delay_frames = 0;
+ if (i->thread_info.resampler)
+ /* Round down */
+ i->thread_info.resampler_delay_frames = pa_resampler_get_delay(i->thread_info.resampler, false);
}
pa_sink_input_detach(i);
@@ -2754,7 +2734,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
pa_usec_t usec = 0;
- size_t nbytes;
+ size_t nbytes, delay_bytes;
/* In the ideal case the new sink would start playing
* the stream immediately. That requires the sink to
@@ -2778,8 +2758,20 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
usec = pa_sink_get_latency_within_thread(s, false);
nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
- if (nbytes > 0)
- pa_sink_input_drop(i, nbytes);
+ /* Calculate number of samples that have been played during the move */
+ delay_bytes = 0;
+ if (i->thread_info.move_start_time > 0) {
+ usec = pa_rtclock_now() - i->thread_info.move_start_time;
+ pa_log_debug("Move took %lu usec", usec);
+ delay_bytes = pa_usec_to_bytes(usec, &s->sample_spec);
+ }
+
+ /* max_rewind must be updated for the sink input because otherwise
+ * the data in the render memblockq will get lost */
+ pa_sink_input_update_max_rewind(i, nbytes);
+
+ if (nbytes + delay_bytes > 0)
+ pa_sink_input_drop(i, nbytes + delay_bytes);
pa_log_debug("Requesting rewind due to finished move");
pa_sink_request_rewind(s, nbytes);
@@ -2796,6 +2788,11 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
pa_sink_input_update_max_request(i, s->thread_info.max_request);
+ /* Reset move variables */
+ i->thread_info.move_start_time = 0;
+ i->thread_info.resampler_delay_frames = 0;
+ i->thread_info.origin_sink_latency = 0;
+
return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
}
@@ -2942,6 +2939,11 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
*((size_t*) userdata) = s->thread_info.max_rewind;
return 0;
+ case PA_SINK_MESSAGE_GET_LAST_REWIND:
+
+ *((size_t*) userdata) = s->thread_info.last_rewind_nbytes;
+ return 0;
+
case PA_SINK_MESSAGE_GET_MAX_REQUEST:
*((size_t*) userdata) = s->thread_info.max_request;