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@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/spice-devel > _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel