spice-gtk音频播放完整流程笔记

1、获取SpiceAudio句柄,也就是音频播放和录音类对象
1.1、在主通道中获取SpiceAudio句柄
1.1.1、在channel-main.c的 main_agent_handle_msg函数中 能力协商( VD_AGENT_ANNOUNCE_CAPABILITIES )时调用agent同步音频播放获取SpiceAudio句柄
/* coroutine context */
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:
    {
        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;
    }
    ...
}
1.1.2、获取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);
}
1.1.3、最终调用 spice_audio_get获取SpiceAudio句柄
static SpiceAudio *spice_main_get_audio(const SpiceMainChannel *channel)
{
    return spice_audio_get(spice_channel_get_session(SPICE_CHANNEL(channel)), NULL);
}
1.2、在外部new_channel信号的回调函数中启动
1.2.1、spicy.c源文件中 connection_new连接信号与回调函数
static spice_connection *connection_new(void)
{
    spice_connection *conn;
    SpiceUsbDeviceManager *manager;
    conn = g_new0(spice_connection, 1);
    conn->session = spice_session_new();
    conn->gtk_session = spice_gtk_session_get(conn->session);
    g_signal_connect(conn->session, "channel-new",G_CALLBACK(channel_new), conn);
    g_signal_connect(conn->session, "channel-destroy",G_CALLBACK(channel_destroy), conn);
    g_signal_connect(conn->session, "notify::migration-state",G_CALLBACK(migration_state), conn);
    g_signal_connect(conn->session, "disconnected",G_CALLBACK(connection_destroy), conn);
    manager = spice_usb_device_manager_get(conn->session, NULL);
    if (manager) {
        g_signal_connect(manager, "auto-connect-failed",G_CALLBACK(usb_connect_failed), NULL);
        g_signal_connect(manager, "device-error",G_CALLBACK(usb_connect_failed), NULL);
    }
    conn->transfers = g_hash_table_new_full(g_direct_hash, g_direct_equal,g_object_unref,(GDestroyNotify)transfer_task_widgets_free);
    connections++;
    SPICE_DEBUG("%s (%d)", __FUNCTION__, connections);
    return conn;
}
1.2.2、new_channel回调函数实现如下, 如果是音频通道,就获取SpiceAudio句柄
static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
{
    ...
    if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
        SPICE_DEBUG("new audio channel");
        conn->audio = spice_audio_get(s, NULL);
    }
    ...
}
2、连接播放的信号(如:playback-start,playback-stop和playback-data)和录音的信号( 如:record-start, record -stop和 record -data
2.1、 在spice-session.c中获取音频句柄SpiceAudio
SpiceAudio *spice_audio_get(SpiceSession *session, GMainContext *context)
{
    static GMutex mutex;
    SpiceAudio *self;
    g_return_val_if_fail(SPICE_IS_SESSION(session), NULL);
    g_mutex_lock(&mutex);
    self = session->priv->audio_manager;
    if (self == NULL) {
        self = spice_audio_new_priv(session, context, NULL);
        session->priv->audio_manager = self;
    }
    g_mutex_unlock(&mutex);
    return self;
}
2.2、SpiceAudio初始化信号与回调函数,并更新通道
G_GNUC_INTERNAL
SpiceAudio *spice_audio_new_priv(SpiceSession *session, GMainContext *context,const char *name)
{
    SpiceAudio *self = NULL;
    if (context == NULL)
        context = g_main_context_default();
    if (name == NULL)
        name = g_get_application_name();
    self = SPICE_AUDIO(spice_gstaudio_new(session, context, name));
    if (self != NULL) {
        spice_g_signal_connect_object(session, "notify::enable-audio", G_CALLBACK(session_enable_audio), self, 0);
        spice_g_signal_connect_object(session, "channel-new", G_CALLBACK(channel_new), self, G_CONNECT_AFTER);
        update_audio_channels(self, session);
    }
    return self;
}
2.3、channel-new信号的回调函数最终调用到connect_channel函数中
static void channel_new(SpiceSession *session, SpiceChannel *channel, SpiceAudio *self)
{
    connect_channel(self, channel);
}
2.4、更新所有的音频通道
static void update_audio_channels(SpiceAudio *self, SpiceSession *session)
{
    GList *list, *tmp;
    if (!spice_session_get_audio_enabled(session)) {
        SPICE_DEBUG("FIXME: disconnect audio channels");
        return;
    }
    list = spice_session_get_channels(session);
    for (tmp = g_list_first(list); tmp != NULL; tmp = g_list_next(tmp)) {
        connect_channel(self, tmp->data);
    }
    g_list_free(list);
}
2.5、在spice-audio.c的connect_channel函数中调用connect_channel回调函数指针()
static void connect_channel(SpiceAudio *self, SpiceChannel *channel)
{
    if (channel->priv->state != SPICE_CHANNEL_STATE_UNCONNECTED)
        return;
    if (SPICE_AUDIO_GET_CLASS(self)->connect_channel(self, channel))
        spice_channel_connect(channel);
}
2.6、SpiceGstAudio继承自SpiceAudio并在 spice_gstaudio_class_init中初始化 了connect_channel函数指针
static void spice_gstaudio_class_init(SpiceGstaudioClass *klass)
{
    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
    SpiceAudioClass *audio_class = SPICE_AUDIO_CLASS(klass);
    audio_class->connect_channel = connect_channel;
    audio_class->get_playback_volume_info_async = spice_gstaudio_get_playback_volume_info_async;
    audio_class->get_playback_volume_info_finish = spice_gstaudio_get_playback_volume_info_finish;
    audio_class->get_record_volume_info_async = spice_gstaudio_get_record_volume_info_async;
    audio_class->get_record_volume_info_finish = spice_gstaudio_get_record_volume_info_finish;
    gobject_class->dispose = spice_gstaudio_dispose;
}
2.7、最终调用到spice-gstaudio.c中的connect_channel函数,连接各种信号与回调函数
static gboolean connect_channel(SpiceAudio *audio, SpiceChannel *channel)
{
    SpiceGstaudio *gstaudio = SPICE_GSTAUDIO(audio);
    SpiceGstaudioPrivate *p = gstaudio->priv;
    if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
        g_return_val_if_fail(p->pchannel == NULL, FALSE);
        p->pchannel = channel;
        g_object_weak_ref(G_OBJECT(p->pchannel), channel_weak_notified, audio);
        spice_g_signal_connect_object(channel, "playback-start",G_CALLBACK(playback_start), gstaudio, 0);
        spice_g_signal_connect_object(channel, "playback-data",G_CALLBACK(playback_data), gstaudio, 0);
        spice_g_signal_connect_object(channel, "playback-stop",G_CALLBACK(playback_stop), gstaudio, G_CONNECT_SWAPPED);
        spice_g_signal_connect_object(channel, "notify::volume",G_CALLBACK(playback_volume_changed), gstaudio, 0);
        spice_g_signal_connect_object(channel, "notify::mute",G_CALLBACK(playback_mute_changed), gstaudio, 0);
        return TRUE;
    }
    if (SPICE_IS_RECORD_CHANNEL(channel)) {
        g_return_val_if_fail(p->rchannel == NULL, FALSE);
        p->rchannel = channel;
        g_object_weak_ref(G_OBJECT(p->rchannel), channel_weak_notified, audio);
        spice_g_signal_connect_object(channel, "record-start",G_CALLBACK(record_start), gstaudio, 0);
        spice_g_signal_connect_object(channel, "record-stop",G_CALLBACK(record_stop), gstaudio, G_CONNECT_SWAPPED);
        spice_g_signal_connect_object(channel, "notify::volume",G_CALLBACK(record_volume_changed), gstaudio, 0);
        spice_g_signal_connect_object(channel, "notify::mute", G_CALLBACK(record_mute_changed), gstaudio, 0);
        return TRUE;
    }
    return FALSE;
}
3、服务器控制音频开启,停止和数据传输
3.1、以音频播放 开启( playback-start)为例,其他都同比类似
3.1.1、收到网络音频播放开启数据包
static void channel_set_handlers(SpiceChannelClass *klass)
{
    static const spice_msg_handler handlers[] = {
        ...
        [ SPICE_MSG_PLAYBACK_START ]           = playback_handle_start,
        ...
    };
    spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
}
3.1.2、调用SpicePlaybackChannel的SPICE_PLAYBACK_START信号的回调函数,也就是前面spice_g_signal_connect_object中spice-gstaudio.c中的playback_start函数
static void playback_handle_start(SpiceChannel *channel, SpiceMsgIn *in)
{
    SpicePlaybackChannelPrivate *c = SPICE_PLAYBACK_CHANNEL(channel)->priv;
    SpiceMsgPlaybackStart *start = spice_msg_in_parsed(in);
    CHANNEL_DEBUG(channel, "%s: fmt %u channels %u freq %u time %u mode %s", __FUNCTION__,
                  start->format, start->channels, start->frequency, start->time,
                  spice_audio_data_mode_to_string(c->mode));
    c->frame_count = 0;
    c->last_time = start->time;
    c->is_active = TRUE;
    c->min_latency = SPICE_PLAYBACK_DEFAULT_LATENCY_MS;
    snd_codec_destroy(&c->codec);
    if (c->mode != SPICE_AUDIO_DATA_MODE_RAW) {
        if (snd_codec_create(&c->codec, c->mode, start->frequency, SND_CODEC_DECODE) != SND_CODEC_OK) {
            g_warning("create decoder failed");
            return;
        }
    }
    g_coroutine_signal_emit(channel, signals[SPICE_PLAYBACK_START], 0,
                            start->format, start->channels, start->frequency);
}
3.1.3、spice-gstaudio.c中playbakc-start回调函数实现如下,创建播放的pipeline
static void playback_start(SpicePlaybackChannel *channel, gint format, gint channels,
                           gint frequency, gpointer data)
{
    SpiceGstaudio *gstaudio = data;
    SpiceGstaudioPrivate *p = gstaudio->priv;
    g_return_if_fail(p != NULL);
    g_return_if_fail(format == SPICE_AUDIO_FMT_S16);
    if (p->playback.pipe &&
        (p->playback.rate != frequency ||
         p->playback.channels != channels)) {
        playback_stop(gstaudio);
        g_clear_pointer(&p->playback.pipe, gst_object_unref);
    }
    if (!p->playback.pipe) {
        GError *error = NULL;
        gchar *audio_caps =
            g_strdup_printf("audio/x-raw,format=\"S16LE\",channels=%d,rate=%d,"
                            "layout=interleaved", channels, frequency);
        gchar *pipeline = g_strdup (g_getenv("SPICE_GST_AUDIOSINK"));
        if (pipeline == NULL)
            pipeline = g_strdup_printf("appsrc is-live=1 do-timestamp=0 format=time caps=\"%s\" name=\"appsrc\" ! queue ! "
                                       "audioconvert ! audioresample ! autoaudiosink name=\"audiosink\"", audio_caps);
        SPICE_DEBUG("audio pipeline: %s", pipeline);
        p->playback.pipe = gst_parse_launch(pipeline, &error);
        if (error != NULL) {
            g_warning("Failed to create pipeline: %s", error->message);
            goto cleanup;
        }
        p->playback.src = gst_bin_get_by_name(GST_BIN(p->playback.pipe), "appsrc");
        p->playback.sink = gst_bin_get_by_name(GST_BIN(p->playback.pipe), "audiosink");
        p->playback.rate = frequency;
        p->playback.channels = channels;
cleanup:
        if (error != NULL)
            g_clear_pointer(&p->playback.pipe, gst_object_unref);
        g_clear_error(&error);
        g_free(audio_caps);
        g_free(pipeline);
    }
    if (p->playback.pipe)
        gst_element_set_state(p->playback.pipe, GST_STATE_PLAYING);
    if (!p->playback.fake && p->mmtime_id == 0) {
        update_mmtime_timeout_cb(gstaudio);
        p->mmtime_id = g_timeout_add_seconds(1, update_mmtime_timeout_cb, gstaudio);
    }
}

猜你喜欢

转载自blog.csdn.net/cai742925624/article/details/128431838