[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