spice的录音和播放声音同步

1、主通道vdagent能力协商时会同步录音和播放声音,下面以播放声音为例
static void main_agent_handle_msg(SpiceChannel *channel,
                                  VDAgentMessage *msg, gpointer payload)
{
    SpiceMainChannel *self = SPICE_MAIN_CHANNEL(channel);
    SpiceMainChannelPrivate *c = self->priv;
    guint8 selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
    guint32 serial;
    g_return_if_fail(msg->protocol == VD_AGENT_PROTOCOL);
    switch (msg->type) {
    case VD_AGENT_CLIPBOARD_RELEASE:
    case VD_AGENT_CLIPBOARD_REQUEST:
    case VD_AGENT_CLIPBOARD_GRAB:
    case VD_AGENT_CLIPBOARD:
        if (test_agent_cap(self, VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
            selection = *((guint8*)payload);
            payload = ((guint8*)payload) + 4;
            msg->size -= 4;
        }
        break;
    default:
        break;
    }

    switch (msg->type) {
    case VD_AGENT_ANNOUNCE_CAPABILITIES: //vdagent能力协商
    {
        VDAgentAnnounceCapabilities *caps = payload;
        int i, size;
        size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(msg->size);
        if (size > VD_AGENT_CAPS_SIZE)
            size = VD_AGENT_CAPS_SIZE;
        memset(c->agent_caps, 0, sizeof(c->agent_caps));
        for (i = 0; i < size * 32; i++) {
            if (!VD_AGENT_HAS_CAPABILITY(caps->caps, size, i))
                continue;
            SPICE_DEBUG("%s: cap: %d (%s)", __FUNCTION__, i, NAME(agent_caps, i));
            VD_AGENT_SET_CAPABILITY(c->agent_caps, i);
        }
        c->agent_caps_received = true;
        g_coroutine_signal_emit(self, signals[SPICE_MAIN_AGENT_UPDATE], 0);
        update_display_timer(SPICE_MAIN_CHANNEL(channel), 0);
        if (caps->request)
            agent_announce_caps(self);
        if (test_agent_cap(self, VD_AGENT_CAP_DISPLAY_CONFIG) &&
            !c->agent_display_config_sent) {
            agent_display_config(self);
            c->agent_display_config_sent = true;
        }
        agent_sync_audio_playback(self);
        agent_sync_audio_record(self);
        agent_max_clipboard(self);
        agent_send_msg_queue(self);
        break;
    }
...
}
2、spice_main_get_audio获取核心音频处理类SpiceAudio
static void agent_sync_audio_playback(SpiceMainChannel *main_channel)
{
    SpiceAudio *audio = spice_main_get_audio(main_channel);
    SpiceMainChannelPrivate *c = main_channel->priv;
    if (audio == NULL ||
        !test_agent_cap(main_channel, VD_AGENT_CAP_AUDIO_VOLUME_SYNC) ||
        c->agent_volume_playback_sync == TRUE) {
        SPICE_DEBUG("%s - is not going to sync audio with guest", __func__);
        return;
    }
    /* only one per connection */
    g_cancellable_reset(c->cancellable_volume_info);
    c->agent_volume_playback_sync = TRUE;
    spice_audio_get_playback_volume_info_async(audio, c->cancellable_volume_info, main_channel,
                                               audio_playback_volume_info_cb, main_channel);
}
3、建立异步调用获取播放音量和静音的任务
static void spice_gstaudio_get_playback_volume_info_async(SpiceAudio *audio,
                                                          GCancellable *cancellable,
                                                          SpiceMainChannel *main_channel,
                                                          GAsyncReadyCallback callback,
                                                          gpointer user_data)
{
    GTask *task = g_task_new(audio, cancellable, callback, user_data);
    g_task_return_boolean(task, TRUE);
    g_object_unref(task);
}
4、异步调用audio_playback_volume_info_cb,从而会,获取完成后将结果发送
VD_AGENT_AUDIO_VOLUME_SYNC消息给spice服务器
static void audio_playback_volume_info_cb(GObject *object, GAsyncResult *res, gpointer user_data)
{
    SpiceMainChannel *main_channel = user_data;
    SpiceAudio *audio = spice_main_get_audio(main_channel);
    VDAgentAudioVolumeSync *avs;
    guint16 *volume;
    guint8 nchannels;
    gboolean mute, ret;
    gsize array_size;
    GError *error = NULL;
    ret = spice_audio_get_playback_volume_info_finish(audio, res, &mute, &nchannels, &volume, &error);
    if (ret == FALSE || volume == NULL || nchannels == 0) {
        if (error != NULL) {
            SPICE_DEBUG("Failed to get playback async volume info: %s", error->message);
            g_error_free(error);
        } else {
            SPICE_DEBUG("Failed to get playback async volume info");
        }
        main_channel->priv->agent_volume_playback_sync = FALSE;
        return;
    }
    array_size = sizeof(uint16_t) * nchannels;
    avs = g_malloc0(sizeof(VDAgentAudioVolumeSync) + array_size);
    avs->is_playback = TRUE;
    avs->mute = mute;
    avs->nchannels = nchannels;
    memcpy(avs->volume, volume, array_size);
    SPICE_DEBUG("%s mute=%s nchannels=%u volume[0]=%u", __func__, spice_yes_no(mute), nchannels, volume[0]);
    g_free(volume);
    agent_msg_queue(main_channel, VD_AGENT_AUDIO_VOLUME_SYNC, sizeof(VDAgentAudioVolumeSync) + array_size, avs);
    g_free (avs);
}
5、调用SpiceAudio的spice_audio_get_playback_volume_info_finish获取音量和静音,从而调用到SpiceGstAudio的spice_gstaudio_get_playback_volume_info_finish
gboolean spice_audio_get_playback_volume_info_finish(SpiceAudio *audio,
                                                     GAsyncResult *res,
                                                     gboolean *mute,
                                                     guint8 *nchannels,
                                                     guint16 **volume,
                                                     GError **error)
{
    g_return_val_if_fail(audio != NULL, FALSE);
    return SPICE_AUDIO_GET_CLASS(audio)->get_playback_volume_info_finish(audio,
            res, mute, nchannels, volume, error);
}

6、SpiceGstAudio获取音量的具体实现
static gboolean spice_gstaudio_get_playback_volume_info_finish(SpiceAudio *audio,
                                                               GAsyncResult *res,
                                                               gboolean *mute,
                                                               guint8 *nchannels,
                                                               guint16 **volume,
                                                               GError **error)
{
    SpiceGstaudioPrivate *p = SPICE_GSTAUDIO(audio)->priv;
    GstElement *e = NULL;
    gboolean lmute;
    gdouble vol;
    GTask *task = G_TASK(res);
    g_return_val_if_fail(g_task_is_valid(task, audio), FALSE);
    if (g_task_had_error(task)) {
        /* set out args that should have new alloc'ed memory to NULL */
        if (volume != NULL) {
            *volume = NULL;
        }
        return g_task_propagate_boolean(task, error);
    }

    if (p->playback.sink == NULL || p->playback.channels == 0) {
        SPICE_DEBUG("PlaybackChannel not created yet, force start");
        p->playback.fake = TRUE;
        /* In order to get system volume, we start the pipeline */
        playback_start(NULL, SPICE_AUDIO_FMT_S16, 2, 48000, audio);
    }

    if (GST_IS_BIN(p->playback.sink))
        e = gst_bin_get_by_interface(GST_BIN(p->playback.sink), GST_TYPE_STREAM_VOLUME);
    if (!e)
        e = g_object_ref(p->playback.sink);

    if (GST_IS_STREAM_VOLUME(e)) {
        vol = gst_stream_volume_get_volume(GST_STREAM_VOLUME(e), GST_STREAM_VOLUME_FORMAT_CUBIC);
        lmute = gst_stream_volume_get_mute(GST_STREAM_VOLUME(e));
    } else {
        g_object_get(e, "volume", &vol, "mute", &lmute, NULL);
    }
    g_object_unref(e);

    if (p->playback.fake) {
        SPICE_DEBUG("Stop faked PlaybackChannel");
        playback_stop(SPICE_GSTAUDIO(audio));
        p->playback.fake = FALSE;
    }

    if (mute != NULL) {
        *mute = lmute;
    }

    if (nchannels != NULL) {
        *nchannels = p->playback.channels;
    }

    if (volume != NULL) {
        gint i;
        *volume = g_new(guint16, p->playback.channels);
        for (i = 0; i < p->playback.channels; i++) {
            (*volume)[i] = (guint16) (vol * VOLUME_NORMAL);
            SPICE_DEBUG("(playback) volume at %d is %u (%0.2f%%)", i, (*volume)[i], 100*vol);
        }
    }
    return g_task_propagate_boolean(task, error);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值