[vlc-commits] PulseAudio: resync and keep in sync with PulseAudio resampling
Rémi Denis-Courmont
git at videolan.org
Sun Jul 31 21:31:42 CEST 2011
vlc/vlc-1.1 | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Wed Jul 20 23:55:00 2011 +0300| [cf96d40a26097e1b59c746a0608ac8b6141f9798] | committer: Rémi Denis-Courmont
PulseAudio: resync and keep in sync with PulseAudio resampling
(cherry picked from commit ccb049c1f3e71a5303181e690ac5b06339021bd9)
Conflicts:
modules/audio_output/pulse.c
> http://git.videolan.org/gitweb.cgi/vlc/vlc-1.1.git/?a=commit;h=cf96d40a26097e1b59c746a0608ac8b6141f9798
---
modules/audio_output/pulse.c | 114 +++++++++++++++++++++++++++++++++++++++--
1 files changed, 108 insertions(+), 6 deletions(-)
diff --git a/modules/audio_output/pulse.c b/modules/audio_output/pulse.c
index b2f426b..2534ae6 100644
--- a/modules/audio_output/pulse.c
+++ b/modules/audio_output/pulse.c
@@ -58,7 +58,9 @@ struct aout_sys_t
pa_stream *stream; /**< PulseAudio playback stream object */
pa_context *context; /**< PulseAudio connection context */
pa_threaded_mainloop *mainloop; /**< PulseAudio event loop */
- //uint32_t byterate; /**< bytes per second */
+ mtime_t pts; /**< Play time of buffer write offset */
+ mtime_t desync; /**< Measured desynchronization */
+ unsigned rate; /**< Current stream sample rate */
};
/* Context helpers */
@@ -94,6 +96,20 @@ static void error(aout_instance_t *aout, const char *msg, pa_context *context)
}
/* Stream helpers */
+static void stream_reset_sync(pa_stream *s, aout_instance_t *aout)
+{
+ aout_sys_t *sys = aout->output.p_sys;
+ const unsigned rate = aout->output.output.i_rate;
+
+ sys->pts = VLC_TS_INVALID;
+ sys->desync = 0;
+ pa_operation *op = pa_stream_update_sample_rate(s, rate, NULL, NULL);
+ if (unlikely(op == NULL))
+ return;
+ pa_operation_unref(op);
+ sys->rate = rate;
+}
+
static void stream_state_cb(pa_stream *s, void *userdata)
{
pa_threaded_mainloop *mainloop = userdata;
@@ -108,6 +124,83 @@ static void stream_state_cb(pa_stream *s, void *userdata)
}
}
+/* Latency management and lip synchronization */
+/* Values from EBU R37 */
+#define AOUT_EARLY_TOLERANCE 40000
+#define AOUT_LATE_TOLERANCE 60000
+
+static void stream_latency_cb(pa_stream *s, void *userdata)
+{
+ aout_instance_t *aout = userdata;
+ aout_sys_t *sys = aout->output.p_sys;
+ mtime_t delta, change;
+
+ if (sys->pts == VLC_TS_INVALID)
+ {
+ msg_Dbg(aout, "missing latency from input");
+ return;
+ }
+
+ /* Compute lip desynchronization */
+ {
+ pa_usec_t latency;
+ int negative;
+
+ if (pa_stream_get_latency(s, &latency, &negative)) {
+ error(aout, "missing latency", sys->context);
+ return;
+ }
+ delta = sys->pts - mdate();
+ if (unlikely(negative))
+ delta += latency;
+ else
+ delta -= latency;
+ }
+
+ change = delta - sys->desync;
+ sys->desync = delta;
+ //msg_Dbg(aout, "desync: %+"PRId64" us (variation: %+"PRId64" us)",
+ // delta, change);
+
+ /* Compute playback sample rate */
+ const unsigned inrate = aout->output.output.i_rate;
+ /* NOTE: AOUT_MAX_RESAMPLING (10%) is way too high... */
+ const int limit = inrate >> 6;
+
+ if (delta < -AOUT_LATE_TOLERANCE) {
+ msg_Warn(aout, "too late by %"PRId64" us", -delta);
+ if (change < 0)
+ delta += change; /* be more severe if really out of sync */
+ } else if (delta > +AOUT_EARLY_TOLERANCE) {
+ msg_Warn(aout, "too early by %"PRId64" us", delta);
+ if (change > 0)
+ delta += change;
+ }
+
+ /* This is empirical. Feel free to define something smarter. */
+ int outrate = inrate * delta / -(CLOCK_FREQ * 10);
+ if (16 * abs(outrate) < limit)
+ outrate = inrate; /* favor native rate to avoid resampling */
+ else if (outrate > limit)
+ outrate = inrate + limit;
+ else if (outrate < -limit)
+ outrate = inrate - limit;
+ else
+ outrate += inrate;
+
+ /* Apply adjusted sample rate */
+ if (outrate == (int)sys->rate)
+ return;
+ pa_operation *op = pa_stream_update_sample_rate(s, outrate, NULL, NULL);
+ if (unlikely(op == NULL)) {
+ error(aout, "cannot change sample rate", sys->context);
+ return;
+ }
+ pa_operation_unref(op);
+ msg_Dbg(aout, "changed sample rate to %d Hz", outrate);
+ sys->rate = outrate;
+}
+
static void stream_moved_cb(pa_stream *s, void *userdata)
{
vlc_object_t *obj = userdata;
@@ -138,7 +231,7 @@ static void stream_suspended_cb(pa_stream *s, void *userdata)
aout_instance_t *aout = userdata;
msg_Dbg(aout, "suspended");
- (void) s;
+ stream_reset_sync(s, aout);
}
static void stream_underflow_cb(pa_stream *s, void *userdata)
@@ -150,6 +243,7 @@ static void stream_underflow_cb(pa_stream *s, void *userdata)
op = pa_stream_cork(s, 1, NULL, NULL);
if (op != NULL)
pa_operation_unref(op);
+ stream_reset_sync(s, aout);
}
static int stream_wait(pa_threaded_mainloop *mainloop, pa_stream *stream)
@@ -205,7 +299,7 @@ static void Play(aout_instance_t *aout)
return;
size_t len = block->i_buffer;
- //mtime_t pts = block->i_pts + block->i_length;
+ mtime_t pts = block->i_pts + block->i_length;
/* Note: The core already holds the output FIFO lock at this point.
* Therefore we must not under any circumstances (try to) acquire the
@@ -241,8 +335,10 @@ static void Play(aout_instance_t *aout)
msg_Dbg(aout, "prepending %zu zeroes", nb);
if (likely(zeroes != NULL))
+#if 1 /* Fault injection: remove this to be too early */
if (pa_stream_write(s, zeroes, nb * size, free, 0,
PA_SEEK_RELATIVE) < 0)
+#endif
free(zeroes);
}
@@ -255,11 +351,11 @@ static void Play(aout_instance_t *aout)
msg_Dbg(aout, "uncorking");
}
-#if 0 /* Fault injector to test underrun recovery */
+#if 0 /* Fault injector to be too late / test underrun recovery */
static unsigned u = 0;
if ((++u % 500) == 0) {
msg_Err(aout, "fault injection");
- msleep(CLOCK_FREQ*2);
+ pa_operation_unref(pa_stream_flush(s, NULL, NULL));
}
#endif
@@ -267,6 +363,7 @@ static void Play(aout_instance_t *aout)
error(aout, "cannot write", sys->context);
block_Release(block);
}
+ sys->pts = pts;
pa_threaded_mainloop_unlock(sys->mainloop);
}
@@ -390,7 +487,8 @@ static int Open(vlc_object_t *obj)
/* Stream parameters */
const pa_stream_flags_t flags = PA_STREAM_START_CORKED
//| PA_STREAM_INTERPOLATE_TIMING
- | PA_STREAM_AUTO_TIMING_UPDATE;
+ | PA_STREAM_AUTO_TIMING_UPDATE
+ | PA_STREAM_VARIABLE_RATE;
const uint32_t byterate = pa_bytes_per_second(&ss);
struct pa_buffer_attr attr;
@@ -438,6 +536,9 @@ static int Open(vlc_object_t *obj)
if (unlikely(ctx == NULL))
goto fail;
sys->context = ctx;
+ sys->pts = VLC_TS_INVALID;
+ sys->desync = 0;
+ sys->rate = ss.rate;
pa_context_set_state_callback(ctx, context_state_cb, mainloop);
if (pa_context_connect(ctx, NULL, 0, NULL) < 0
@@ -454,6 +555,7 @@ static int Open(vlc_object_t *obj)
}
sys->stream = s;
pa_stream_set_state_callback(s, stream_state_cb, mainloop);
+ pa_stream_set_latency_update_callback(s, stream_latency_cb, aout);
pa_stream_set_moved_callback(s, stream_moved_cb, aout);
pa_stream_set_overflow_callback(s, stream_overflow_cb, aout);
pa_stream_set_started_callback(s, stream_started_cb, aout);
More information about the vlc-commits
mailing list