[Spice-devel] [PATCH spice-gtk 6/8] spice-pulse: adjust the playback latency when the min-latency property changes
Alon Levy
alevy at redhat.com
Wed Apr 17 08:32:08 PDT 2013
> If the current latency is smaller than the new min-latency
> value, we cork the playback till the target latency is achieved.
>
> Note: I didn't modify the prebuf configuration and used
> pa_stream_prebuf, because pulse updated the prebuf only if
> I set both prebuf and tlength to be target_latency*2. Then,
> due to the tlength growth, each time the playback latency deviated
> from the target latency, an underflow occurred. Since the latency
> that is computed by the server is not exact and is based on its
> current evaluation of the bit-rate, which is dynamic, it is better not
> to change the tlength (in order to avoid unnecessary underflows).
Looks good to me, so ACK
You can remove the FIXME below now I think :)
> ---
> gtk/spice-pulse.c | 67
> ++++++++++++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 59 insertions(+), 8 deletions(-)
>
> diff --git a/gtk/spice-pulse.c b/gtk/spice-pulse.c
> index 107ce7c..7a4e2c6 100644
> --- a/gtk/spice-pulse.c
> +++ b/gtk/spice-pulse.c
> @@ -46,6 +46,8 @@ struct _SpicePulsePrivate {
> int state;
> struct stream playback;
> struct stream record;
> + guint last_delay;
> + guint target_delay;
> };
>
> G_DEFINE_TYPE(SpicePulse, spice_pulse, SPICE_TYPE_AUDIO)
> @@ -185,7 +187,7 @@ static void pulse_flush_cb(pa_stream *pastream, int
> success, void *data)
> s->cork_op = NULL;
> }
>
> -static void pulse_cork_cb(pa_stream *pastream, int success, void *data)
> +static void pulse_cork_flush_cb(pa_stream *pastream, int success, void
> *data)
> {
> struct stream *s = data;
>
> @@ -199,7 +201,19 @@ static void pulse_cork_cb(pa_stream *pastream, int
> success, void *data)
> }
> }
>
> -static void stream_cork(SpicePulse *pulse, struct stream *s)
> +static void pulse_cork_cb(pa_stream *pastream, int success, void *data)
> +{
> + struct stream *s = data;
> +
> + SPICE_DEBUG("%s: cork started", __FUNCTION__);
> + if (!success)
> + g_warning("pulseaudio cork operation failed");
> +
> + pa_operation_unref(s->cork_op);
> + s->cork_op = NULL;
> +}
> +
> +static void stream_cork(SpicePulse *pulse, struct stream *s, gboolean
> with_flush)
> {
> SpicePulsePrivate *p = SPICE_PULSE_GET_PRIVATE(pulse);
> pa_operation *o = NULL;
> @@ -211,7 +225,10 @@ static void stream_cork(SpicePulse *pulse, struct stream
> *s)
> }
>
> if (!pa_stream_is_corked(s->stream) && !s->cork_op) {
> - if (!(o = pa_stream_cork(s->stream, 1, pulse_cork_cb, s))) {
> + if (!(o = pa_stream_cork(s->stream, 1,
> + with_flush ? pulse_cork_flush_cb :
> + pulse_cork_cb,
> + s))) {
> g_warning("pa_stream_cork() failed: %s",
> pa_strerror(pa_context_errno(p->context)));
> }
> @@ -277,11 +294,12 @@ static void stream_underflow_cb(pa_stream *s, void
> *userdata)
>
> static void stream_update_latency_callback(pa_stream *s, void *userdata)
> {
> + SpicePulse *pulse = userdata;
> pa_usec_t usec;
> int negative = 0;
> SpicePulsePrivate *p;
>
> - p = SPICE_PULSE_GET_PRIVATE(userdata);
> + p = SPICE_PULSE_GET_PRIVATE(pulse);
>
> g_return_if_fail(s != NULL);
> g_return_if_fail(p != NULL);
> @@ -295,8 +313,16 @@ static void stream_update_latency_callback(pa_stream *s,
> void *userdata)
> }
>
> g_return_if_fail(negative == FALSE);
> -
> + p->last_delay = usec / PA_USEC_PER_MSEC;
> spice_playback_channel_set_delay(SPICE_PLAYBACK_CHANNEL(p->pchannel),
> usec / 1000);
> + if (pa_stream_is_corked(p->playback.stream)) {
> + if (p->last_delay >= p->target_delay) {
> + SPICE_DEBUG("%s: uncork playback. delay %u target %u",
> __FUNCTION__, p->last_delay, p->target_delay);
> + stream_uncork(pulse, &p->playback);
> + } else {
> + SPICE_DEBUG("%s: still corked. delay %u target %u",
> __FUNCTION__, p->last_delay, p->target_delay);
> + }
> + }
> }
>
> static void create_playback(SpicePulse *pulse)
> @@ -319,7 +345,7 @@ static void create_playback(SpicePulse *pulse)
>
> /* FIXME: we might want customizable latency */
Here.
> buffer_attr.maxlength = -1;
> - buffer_attr.tlength = pa_usec_to_bytes(100 * PA_USEC_PER_MSEC,
> &p->playback.spec);
> + buffer_attr.tlength = pa_usec_to_bytes(p->target_delay *
> PA_USEC_PER_MSEC, &p->playback.spec);
> buffer_attr.prebuf = -1;
> buffer_attr.minreq = -1;
> flags = PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE;
> @@ -337,15 +363,18 @@ static void playback_start(SpicePlaybackChannel
> *channel, gint format, gint chan
> SpicePulse *pulse = data;
> SpicePulsePrivate *p = SPICE_PULSE_GET_PRIVATE(pulse);
> pa_context_state_t state;
> + guint latency;
>
> g_return_if_fail(p != NULL);
>
> p->playback.started = TRUE;
> p->playback.num_underflow = 0;
> + g_object_get(p->pchannel, "min-latency", &latency, NULL);
>
> if (p->playback.stream &&
> (p->playback.spec.rate != frequency ||
> - p->playback.spec.channels != channels)) {
> + p->playback.spec.channels != channels ||
> + p->target_delay != latency)) {
> stream_stop(pulse, &p->playback);
> }
>
> @@ -353,6 +382,8 @@ static void playback_start(SpicePlaybackChannel *channel,
> gint format, gint chan
> p->playback.spec.format = PA_SAMPLE_S16LE;
> p->playback.spec.rate = frequency;
> p->playback.spec.channels = channels;
> + p->target_delay = latency;
> + p->last_delay = 0;
>
> state = pa_context_get_state(p->context);
> switch (state) {
> @@ -421,7 +452,7 @@ static void playback_stop(SpicePlaybackChannel *channel,
> gpointer data)
> if (!p->playback.stream)
> return;
>
> - stream_cork(pulse, &p->playback);
> + stream_cork(pulse, &p->playback, TRUE);
> }
>
> static void stream_read_callback(pa_stream *s, size_t length, void *data)
> @@ -629,6 +660,24 @@ static void playback_mute_changed(GObject *object,
> GParamSpec *pspec, gpointer d
> pa_operation_unref(op);
> }
>
> +static void playback_min_latency_changed(GObject *object, GParamSpec *pspec,
> gpointer data)
> +{
> +
> + SpicePulse *pulse = data;
> + SpicePulsePrivate *p = pulse->priv;
> + guint min_latency;
> +
> + g_object_get(object, "min-latency", &min_latency, NULL);
> + p->target_delay = min_latency;
> +
> + if (p->last_delay < p->target_delay) {
> + spice_debug("%s: corking", __FUNCTION__);
> + stream_cork(pulse, &p->playback, FALSE);
> + } else {
> + spice_debug("%s: not corking. The current delay satisfies the
> requirement", __FUNCTION__);
> + }
> +}
> +
> static void record_mute_changed(GObject *object, GParamSpec *pspec, gpointer
> data)
> {
> SpicePulse *pulse = data;
> @@ -711,6 +760,8 @@ static gboolean connect_channel(SpiceAudio *audio,
> SpiceChannel *channel)
> G_CALLBACK(playback_volume_changed),
> pulse, 0);
> spice_g_signal_connect_object(channel, "notify::mute",
> G_CALLBACK(playback_mute_changed),
> pulse, 0);
> + spice_g_signal_connect_object(channel, "notify::min-latency",
> +
> G_CALLBACK(playback_min_latency_changed),
> pulse, 0);
>
> return TRUE;
> }
> --
> 1.8.1
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
>
More information about the Spice-devel
mailing list