[Spice-devel] [spice-gtk PATCH 3/5] audio: spice-pulse aware of app changes

Marc-André Lureau mlureau at redhat.com
Wed Mar 18 10:52:05 PDT 2015


Hi

----- Original Message -----
> If there are changes in the audio stream like mute or volume,
> spice-pulse should be aware of this changes and propagate this
> changes to channel-playback and channel-record.

No, the channel state should only reflect the guest state. The audio backend state must stay independant or we will most likely run some conflicts situation.

You should rather have get_volume() and get_mute() on SpiceAudio as I suggested in another patch. Also that would avoid useless notifications of volume changes (it's only needed once for sync!)


> This patch subscribe a callback for changes in sink-input and
> source-output of pulse and keep track of volume and mute changes.
> ---
>  gtk/spice-pulse.c | 156
>  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 156 insertions(+)
> 
> diff --git a/gtk/spice-pulse.c b/gtk/spice-pulse.c
> index dd7f309..b9ced66 100644
> --- a/gtk/spice-pulse.c
> +++ b/gtk/spice-pulse.c
> @@ -37,6 +37,7 @@ struct stream {
>      pa_operation            *cork_op;
>      gboolean                started;
>      guint                   num_underflow;
> +    gboolean                client_volume_change;
>  };
>  
>  struct _SpicePulsePrivate {
> @@ -50,6 +51,7 @@ struct _SpicePulsePrivate {
>      struct stream           record;
>      guint                   last_delay;
>      guint                   target_delay;
> +    gboolean                context_subscribed;
>  };
>  
>  G_DEFINE_TYPE(SpicePulse, spice_pulse, SPICE_TYPE_AUDIO)
> @@ -589,6 +591,14 @@ static void playback_volume_changed(GObject *object,
> GParamSpec *pspec, gpointer
>      pa_cvolume v;
>      guint i;
>  
> +    if (p->playback.client_volume_change) {
> +        /* signal volume-changed emitted by client changes (not guest).
> +         * this avoid infinite volume changes as the volume in the guest is
> +         * already updated */
> +        p->playback.client_volume_change = FALSE;
> +        return;
> +    }
> +
>      g_object_get(object,
>                   "volume", &volume,
>                   "nchannels", &nchannels,
> @@ -692,6 +702,14 @@ static void record_volume_changed(GObject *object,
> GParamSpec *pspec, gpointer d
>      pa_cvolume v;
>      guint i;
>  
> +    if (p->record.client_volume_change) {
> +        /* signal volume-changed emitted by client changes (not guest).
> +         * this avoid infinite volume changes as the volume in the guest is
> +         * already updated */
> +        p->record.client_volume_change = FALSE;
> +        return;
> +    }
> +
>      g_object_get(object,
>                   "volume", &volume,
>                   "nchannels", &nchannels,
> @@ -780,6 +798,126 @@ static gboolean connect_channel(SpiceAudio *audio,
> SpiceChannel *channel)
>      return FALSE;
>  }
>  
> +static void sink_input_info_cb(pa_context *context,
> +                               const pa_sink_input_info *info,
> +                               int eol,
> +                               void *userdata)
> +{
> +    SpicePulse *pulse = userdata;
> +    SpicePulsePrivate *p = pulse->priv;
> +    gboolean sink_mute;
> +    guint16 *volume;
> +    gint i;
> +
> +    if (eol)
> +        return;
> +
> +    /* volume in pa_cvolume is _stored_ as guint32 */
> +    volume = g_new(guint16, info->volume.channels);
> +    for (i = 0; i < info->volume.channels; i++) {
> +        volume[i] = (guint16) info->volume.values[i];
> +        SPICE_DEBUG("playback volume changed (client-side) %u", volume[i]);
> +    }
> +
> +    sink_mute = (info->mute) ? TRUE : FALSE;
> +    SPICE_DEBUG("playback mute changed (client-side) %u", sink_mute);
> +
> +    p->playback.client_volume_change = TRUE;
> +    g_object_set(p->pchannel,
> +                 "mute", sink_mute,
> +                 "volume", volume,
> +                 NULL);
> +    g_free (volume);
> +}
> +
> +static void source_output_info_cb(pa_context *context,
> +                                  const pa_source_output_info *info,
> +                                  int eol,
> +                                  void *userdata)
> +{
> +    SpicePulse *pulse = userdata;
> +    SpicePulsePrivate *p = pulse->priv;
> +    gboolean source_mute;
> +    guint16 *volume;
> +    gint i;
> +
> +    if (eol)
> +        return;
> +
> +    /* volume in pa_cvolume is _stored_ as guint32 */
> +    volume = g_new(guint16, info->volume.channels);
> +    for (i = 0; i < info->volume.channels; i++) {
> +        volume[i] = (guint16) info->volume.values[i];
> +        SPICE_DEBUG("record volume changed (client-side) %u", volume[i]);
> +    }
> +
> +    source_mute = (info->mute) ? TRUE : FALSE;
> +    SPICE_DEBUG("record mute changed (client-side) %u", source_mute);
> +
> +    p->record.client_volume_change = TRUE;
> +    g_object_set(p->rchannel,
> +                 "mute", source_mute,
> +                 "volume", volume,
> +                 NULL);
> +    g_free (volume);
> +}
> +
> +static void context_subscribe_callback(pa_context *c,
> +                                       pa_subscription_event_type_t event,
> +                                       uint32_t index,
> +                                       void *userdata)
> +{
> +    SpicePulse *pulse = userdata;
> +    SpicePulsePrivate *p = pulse->priv;
> +    pa_subscription_event_type_t type, facility;
> +
> +    type = event & PA_SUBSCRIPTION_EVENT_TYPE_MASK;
> +    if (type != PA_SUBSCRIPTION_EVENT_CHANGE &&
> +        type != PA_SUBSCRIPTION_EVENT_NEW)
> +        return;
> +
> +    facility = event & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
> +    if (p->playback.stream != NULL &&
> +        facility == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
> +        pa_operation *op;
> +        guint32 stream_index;
> +
> +        stream_index = pa_stream_get_index(p->playback.stream);
> +        if (stream_index != index) {
> +            SPICE_DEBUG ("Playback stream %d differs from sink-input %d",
> +                         stream_index, index);
> +            return;
> +        }
> +        op = pa_context_get_sink_input_info(p->context, stream_index,
> +                                            sink_input_info_cb, pulse);
> +        if (!op)
> +            spice_warning("get_sink_input_info failed: %s",
> +                          pa_strerror(pa_context_errno(p->context)));
> +        else
> +            pa_operation_unref(op);
> +    }
> +
> +    if (p->record.stream != NULL &&
> +        facility == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
> +        pa_operation *op;
> +        guint32 stream_index;
> +
> +        stream_index = pa_stream_get_index(p->record.stream);
> +        if (stream_index != index) {
> +            SPICE_DEBUG ("Record stream %d differs from sink-input %d",
> +                         stream_index, index);
> +            return;
> +        }
> +        op = pa_context_get_source_output_info(p->context, stream_index,
> +                                               source_output_info_cb,
> pulse);
> +        if (!op)
> +            spice_warning("get_source_output_info failed: %s",
> +                          pa_strerror(pa_context_errno(p->context)));
> +        else
> +            pa_operation_unref(op);
> +    }
> +}
> +
>  static void context_state_callback(pa_context *c, void *userdata)
>  {
>      SpicePulse *pulse = userdata;
> @@ -802,6 +940,24 @@ static void context_state_callback(pa_context *c, void
> *userdata)
>  
>          if (!p->playback.stream && p->playback.started)
>              create_playback(SPICE_PULSE(userdata));
> +
> +        if (p->context_subscribed == FALSE) {
> +            pa_operation *op;
> +            pa_context_set_subscribe_callback(p->context,
> +                                              context_subscribe_callback,
> +                                              pulse);
> +            op = pa_context_subscribe(p->context,
> +                                      PA_SUBSCRIPTION_MASK_SINK_INPUT|
> +                                        PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT,
> +                                      NULL, NULL);
> +            if (op) {
> +                pa_operation_unref(op);
> +                p->context_subscribed = TRUE;
> +            } else {
> +                spice_warning("context_subscribe failed: %s",
> +                              pa_strerror(pa_context_errno(p->context)));
> +            }
> +        }
>          break;
>      }
>  
> --
> 2.1.0
> 
> _______________________________________________
> 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