[Spice-devel] [spice-gtk PATCH 3/5] audio: spice-pulse aware of app changes
Victor Toso
victortoso at redhat.com
Thu Mar 19 05:59:52 PDT 2015
Hey,
On Wed, Mar 18, 2015 at 01:52:05PM -0400, Marc-André Lureau wrote:
> 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!)
Okay, I'll fix it. Thanks for the explanation.
>
> > 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