]> www.infradead.org Git - pidgin-chime.git/commitdiff
Call cleanup
authorDavid Woodhouse <dwmw@amazon.co.uk>
Wed, 6 Dec 2017 16:37:41 +0000 (16:37 +0000)
committerDavid Woodhouse <dwmw@amazon.co.uk>
Wed, 6 Dec 2017 16:37:41 +0000 (16:37 +0000)
Split out chime-call-transport.c so we can add DTLS more easily. Deal with
opening/closing the "Call Participants" window by adding a chat menu, which
will also be the basis for starting an audio call.

Makefile.am
chat.c
chime-call-audio.c
chime-call-audio.h [new file with mode: 0644]
chime-call-transport.c [new file with mode: 0644]
chime-call.c
chime-call.h
chime-connection-private.h
chime-meeting.c

index 88466c8ec178a63d18204aca502c2b6aaf38a886..d4be919ccb4193847a33537d28e978f4a6107c55 100644 (file)
@@ -19,7 +19,8 @@ libchime_la_SOURCES = chime.c buddy.c rooms.c chat.c messages.c conversations.c
        chime-call.c chime-call.h chime-call-audio.c attachments.c \
        protobuf/auth_message.pb-c.c protobuf/auth_message.pb-c.h \
        protobuf/data_message.pb-c.c protobuf/data_message.pb-c.h \
-       protobuf/rt_message.pb-c.c protobuf/rt_message.pb-c.h
+       protobuf/rt_message.pb-c.c protobuf/rt_message.pb-c.h \
+       chime-call-audio.h chime-call-transport.c
 
 libchime_la_CFLAGS = $(PURPLE_CFLAGS) $(SOUP_CFLAGS) $(JSON_CFLAGS) $(LIBXML_CFLAGS) $(PROTOBUF_CFLAGS) $(OPUS_CFLAGS) $(FARSTREAM_CFLAGS)
 libchime_la_LIBADD = $(PURPLE_LIBS) $(SOUP_LIBS) $(JSON_LIBS) $(LIBXML_LIBS) $(PROTOBUF_LIBS) $(OPUS_LIBS) $(FARSTREAM_LIBS)
diff --git a/chat.c b/chat.c
index 848a345b28d7aaf62209db908314d0c9538d04b6..6973c143b5aec491e7ca594f773d79935b432f21 100644 (file)
--- a/chat.c
+++ b/chat.c
@@ -215,11 +215,14 @@ static PurpleNotifySearchResults *generate_sr_participants(GHashTable *participa
        return results;
 
 }
+static void on_call_participants(ChimeCall *call, GHashTable *participants, struct chime_chat *chat);
 
 static void participants_closed_cb(gpointer _chat)
 {
        struct chime_chat *chat = _chat;
        chat->participants_ui = NULL;
+       g_signal_handlers_disconnect_matched(chat->call, G_SIGNAL_MATCH_FUNC|G_SIGNAL_MATCH_DATA,
+                                            0, 0, NULL, G_CALLBACK(on_call_participants), chat);
 }
 
 static void on_call_participants(ChimeCall *call, GHashTable *participants, struct chime_chat *chat)
@@ -295,10 +298,12 @@ void chime_destroy_chat(struct chime_chat *chat)
                                                          chat->media);
                        chat->media = NULL;
                }
+#if 0
                if (chat->audio) {
                        chime_connection_call_audio_close(chat->audio);
                        chat->audio = NULL;
                }
+#endif
                chime_connection_close_meeting(cxn, chat->meeting);
                g_object_unref(chat->meeting);
        }
@@ -319,6 +324,7 @@ static void on_chat_name(ChimeObject *obj, GParamSpec *ignored, struct chime_cha
                purple_conversation_set_name(chat->conv, name);
 }
 
+#if 0
 static void audio_joined(GObject *source, GAsyncResult *result, gpointer _chat)
 {
        ChimeConnection *cxn = CHIME_CONNECTION(source);
@@ -354,7 +360,7 @@ static void audio_joined(GObject *source, GAsyncResult *result, gpointer _chat)
        }
 #endif
 }
-
+#endif
 struct chime_chat *do_join_chat(PurpleConnection *conn, ChimeConnection *cxn, ChimeObject *obj, JsonNode *first_msg, ChimeMeeting *meeting)
 {
        if (!obj)
@@ -372,9 +378,11 @@ struct chime_chat *do_join_chat(PurpleConnection *conn, ChimeConnection *cxn, Ch
                chat->call = chime_meeting_get_call(meeting);
                if (chat->call) {
                        g_signal_connect(chat->call, "participants-changed", G_CALLBACK(on_call_participants), chat);
+#if 0
                        /* Don't try this at home, kids! (yet) */
                        if (getenv("CHIME_AUDIO"))
                                chime_connection_join_call_audio_async(cxn, chat->call, NULL, audio_joined, chat);
+#endif
                }
        }
 
@@ -655,7 +663,48 @@ void chime_purple_chat_invite(PurpleConnection *conn, int id, const char *messag
        chime_connection_autocomplete_contact_async(pc->cxn, who, NULL, autocomplete_mad_cb, mad);
 }
 
-GList *chime_purple_chat_menu(PurpleChat *chat)
+static void show_participants (PurpleBuddy *buddy, gpointer _chat)
+{
+       struct chime_chat *chat = _chat;
+       if (chat->call) {
+               g_signal_handlers_disconnect_matched(chat->call, G_SIGNAL_MATCH_FUNC|G_SIGNAL_MATCH_DATA,
+                                                    0, 0, NULL, G_CALLBACK(on_call_participants), chat);
+               g_signal_connect(chat->call, "participants-changed", G_CALLBACK(on_call_participants), chat);
+               chime_call_emit_participants(chat->call);
+       }
+}
+
+GList *chime_purple_chat_menu(PurpleChat *pchat)
 {
-       return NULL;
+
+       if (!pchat->components)
+               return NULL;
+
+       const gchar *roomid = g_hash_table_lookup(pchat->components, (char *)"RoomId");
+       if (!roomid)
+               return NULL;
+
+       purple_debug_info("chime", "Chat menu for %s\n", roomid);
+
+       PurpleConnection *conn = pchat->account->gc;
+       if (!conn)
+               return NULL;
+
+       struct purple_chime *pc = purple_connection_get_protocol_data(conn);
+       ChimeRoom *room = chime_connection_room_by_id(pc->cxn, roomid);
+       if (!room)
+               return NULL;
+
+       struct chime_chat *chat = g_hash_table_lookup(pc->chats_by_room, room);
+       if (!chat)
+               return NULL;
+
+       GList *items = NULL;
+       if (chat->call) {
+               items = g_list_append(items,
+                                     purple_menu_action_new(_("Show participants"),
+                                                            PURPLE_CALLBACK(show_participants), chat, NULL));
+       }
+
+       return items;
 }
index d5cb7b0d2f6f7396178c2f43b253a3e760367385..13cde9b37a539348903345429de89f207ce811e6 100644 (file)
@@ -18,6 +18,7 @@
 #include "chime-connection.h"
 #include "chime-call.h"
 #include "chime-connection-private.h"
+#include "chime-call-audio.h"
 
 #include <libsoup/soup.h>
 
 #include <string.h>
 #include <ctype.h>
 
-#ifdef AUDIO_HACKS
-#include <opus.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#endif
-
-struct _ChimeCallAudio {
-       ChimeCall *call;
-       SoupWebsocketConnection *ws;
-       guint data_ack_source;
-       guint32 data_next_seq;
-       guint64 data_ack_mask;
-       gint32 data_next_logical_msg;
-       GSList *data_messages;
-       GHashTable *profiles;
-#ifdef AUDIO_HACKS
-       OpusDecoder *opus_dec;
-       int audio_fd;
-#endif
-       guint send_rt_source;
-       gint64 last_server_time_offset;
-       gboolean echo_server_time;
-       RTMessage rt_msg;
-       AudioMessage audio_msg;
-};
-
-struct xrp_header {
-       guint16 type;
-       guint16 len;
-};
-
-enum xrp_pkt_type {
-       XRP_RT_MESSAGE = 2,
-       XRP_AUTH_MESSAGE= 3,
-       XRP_DATA_MESSAGE = 4,
-       XRP_STREAM_MESSAGE = 5,
-};
-
-static void hexdump(const void *buf, int len)
-{
-       char linechars[17];
-       int i;
-
-       memset(linechars, 0, sizeof(linechars));
-       for (i=0; i < len; i++) {
-               unsigned char c = ((unsigned char *)buf)[i];
-               if (!(i & 15)) {
-                       if (i)
-                               printf("   %s", linechars);
-                       printf("\n%04x:", i);
-               }
-               printf(" %02x", c);
-               linechars[i & 15] = isprint(c) ? c : '.';
-       }
-       if (i & 15) {
-               linechars[i & 15] = 0;
-               printf("   %s", linechars);
-       }
-       printf("\n");
-}
-
-
-static void audio_send_packet(ChimeCallAudio *audio, enum xrp_pkt_type type, const ProtobufCMessage *message)
-{
-       size_t len = protobuf_c_message_get_packed_size(message);
-
-       len += sizeof(struct xrp_header);
-       struct xrp_header *hdr = g_malloc0(len);
-       hdr->type = htons(type);
-       hdr->len = htons(len);
-       protobuf_c_message_pack(message, (void *)(hdr + 1));
-       if (getenv("CHIME_AUDIO_DEBUG")) {
-               printf("sending protobuf of len %zd\n", len);
-               hexdump(hdr, len);
-       }
-       soup_websocket_connection_send_binary(audio->ws, hdr, len);
-       g_free(hdr);
-}
-
-static void audio_send_auth_packet(ChimeCallAudio *audio)
-{
-       ChimeConnection *cxn = chime_call_get_connection(audio->call);
-       if (!cxn)
-               return;
-
-       ChimeConnectionPrivate *priv = CHIME_CONNECTION_GET_PRIVATE (cxn);
-       AuthMessage msg;
-       auth_message__init(&msg);
-       msg.message_type = AUTH_MESSAGE_TYPE__REQUEST;
-       msg.has_message_type = TRUE;
-
-       msg.call_id = 0;
-       msg.has_call_id = TRUE;
-
-       msg.call_uuid = (char *)chime_call_get_uuid(audio->call);
-
-       msg.service_type = SERVICE_TYPE__FULL_DUPLEX;
-       msg.has_service_type = TRUE;
-
-       msg.profile_id = 0;
-       msg.has_profile_id = TRUE;
 
-       msg.profile_uuid = (char *)chime_connection_get_profile_id(cxn);
 
-       /* XX: What if it *just* expired? We'll need to renew it and try again? */
-       msg.session_token = priv->session_token;
-
-       msg.codec = 7; /* Opus Med. Later... */
-       msg.has_codec = TRUE;
-
-       msg.flags = FLAGS__FLAG_HAS_PROFILE_TABLE;
-#ifndef AUDIO_HACKS
-       msg.flags |= FLAGS__FLAG_MUTE;
-#endif
-       msg.has_flags = TRUE;
-
-       audio_send_packet(audio, XRP_AUTH_MESSAGE, &msg.base);
-}
 static gboolean audio_receive_rt_msg(ChimeCallAudio *audio, gconstpointer pkt, gsize len)
 {
        RTMessage *msg = rtmessage__unpack(NULL, len, pkt);
@@ -220,7 +104,7 @@ static gboolean do_send_rt_packet(ChimeCallAudio *audio)
        audio->audio_msg.has_audio = TRUE;
        audio->audio_msg.audio.len = 0;
 
-       audio_send_packet(audio, XRP_RT_MESSAGE, &audio->rt_msg.base);
+       chime_call_transport_send_packet(audio, XRP_RT_MESSAGE, &audio->rt_msg.base);
 
        return TRUE;
 }
@@ -231,12 +115,13 @@ static gboolean audio_receive_auth_msg(ChimeCallAudio *audio, gconstpointer pkt,
                return FALSE;
 
        chime_debug("Got AuthMessage authorised %d %d\n", msg->has_authorized, msg->authorized);
-       if (msg->has_authorized && msg->authorized)
+       if (msg->has_authorized && msg->authorized) {
                do_send_rt_packet(audio);
 
-       audio->send_rt_source = g_timeout_add(100, (GSourceFunc)do_send_rt_packet, audio);
+               audio->send_rt_source = g_timeout_add(100, (GSourceFunc)do_send_rt_packet, audio);
 
-       g_signal_emit_by_name(audio->call, "call-connected");
+               g_signal_emit_by_name(audio->call, "call-connected");
+       }
 
        auth_message__free_unpacked(msg, NULL);
        return TRUE;
@@ -303,7 +188,7 @@ static void do_send_ack(ChimeCallAudio *audio)
                audio->data_ack_mask = 0;
        }
 
-       audio_send_packet(audio, XRP_DATA_MESSAGE, &msg.base);
+       chime_call_transport_send_packet(audio, XRP_DATA_MESSAGE, &msg.base);
 
 }
 static gboolean idle_send_ack(gpointer _audio)
@@ -450,7 +335,7 @@ static gboolean audio_receive_data_msg(ChimeCallAudio *audio, gconstpointer pkt,
        return TRUE;
 }
 
-static gboolean audio_receive_packet(ChimeCallAudio *audio, gconstpointer pkt, gsize len)
+gboolean audio_receive_packet(ChimeCallAudio *audio, gconstpointer pkt, gsize len)
 {
        if (len < sizeof(struct xrp_header))
                return FALSE;
@@ -474,29 +359,8 @@ static gboolean audio_receive_packet(ChimeCallAudio *audio, gconstpointer pkt, g
        return FALSE;
 }
 
-static void on_audiows_closed(SoupWebsocketConnection *ws, gpointer _audio)
-{
-       /* XXX: Reconnect it */
-}
-
-static void on_audiows_message(SoupWebsocketConnection *ws, gint type,
-                              GBytes *message, gpointer _audio)
-{
-       gsize s;
-       gconstpointer d = g_bytes_get_data(message, &s);
-
-       if (getenv("CHIME_AUDIO_DEBUG")) {
-               printf("incoming:\n");
-               hexdump(d, s);
-       }
-
-       audio_receive_packet(_audio, d, s);
-}
-
-static void free_audio(gpointer _audio)
+void chime_call_audio_close(ChimeCallAudio *audio, gboolean hangup)
 {
-       ChimeCallAudio *audio = _audio;
-
        g_signal_handlers_disconnect_matched(G_OBJECT(audio->call), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, audio);
 
        if (audio->data_ack_source)
@@ -511,29 +375,15 @@ static void free_audio(gpointer _audio)
 #endif
        g_hash_table_destroy(audio->profiles);
        g_slist_free_full(audio->data_messages, (GDestroyNotify) free_msgbuf);
-       soup_websocket_connection_close(audio->ws, 0, NULL);
-       g_object_unref(audio->ws);
+       chime_call_transport_disconnect(audio, hangup);
        g_signal_emit_by_name(audio->call, "call-disconnected");
-       g_object_unref(audio->call);
        g_free(audio);
 }
 
-static void audio_ws_connect_cb(GObject *obj, GAsyncResult *res, gpointer _task)
+ChimeCallAudio *chime_call_audio_open(ChimeConnection *cxn, ChimeCall *call, gboolean muted)
 {
-       GTask *task = G_TASK(_task);
-       ChimeCall *call = CHIME_CALL(g_task_get_task_data(task));
-
-       GError *error = NULL;
-       SoupWebsocketConnection *ws = chime_connection_websocket_connect_finish(CHIME_CONNECTION(obj), res, &error);
-       if (!ws) {
-               chime_debug("audio ws error %s\n", error->message);
-               g_task_return_error(task, error);
-               return;
-       }
-       chime_debug("audio ws connected!\n");
        ChimeCallAudio *audio = g_new0(ChimeCallAudio, 1);
-       audio->ws = ws;
-       audio->call = g_object_ref(call);
+       audio->call = call;
        audio->profiles = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
 #ifdef AUDIO_HACKS
        int opuserr;
@@ -542,8 +392,6 @@ static void audio_ws_connect_cb(GObject *obj, GAsyncResult *res, gpointer _task)
        audio->audio_fd = open(fname, O_WRONLY|O_TRUNC|O_CREAT, 0644);
        g_free(fname);
 #endif
-       g_signal_connect(G_OBJECT(ws), "closed", G_CALLBACK(on_audiows_closed), audio);
-       g_signal_connect(G_OBJECT(ws), "message", G_CALLBACK(on_audiows_message), audio);
 
        rtmessage__init(&audio->rt_msg);
        audio_message__init(&audio->audio_msg);
@@ -553,49 +401,8 @@ static void audio_ws_connect_cb(GObject *obj, GAsyncResult *res, gpointer _task)
        audio->audio_msg.has_sample_time = 1;
        audio->audio_msg.sample_time = g_random_int();
 
-       audio_send_auth_packet(audio);
-       g_task_return_pointer(task, audio, free_audio);
-       g_object_unref(task);
-}
-
-void chime_connection_call_audio_close(ChimeCallAudio *audio)
-{
-       free_audio(audio);
-}
-
-void chime_connection_join_call_audio_async(ChimeConnection *cxn,
-                                           ChimeCall *call,
-                                           GCancellable *cancellable,
-                                           GAsyncReadyCallback callback,
-                                           gpointer user_data)
-{
-       g_return_if_fail(CHIME_IS_CONNECTION(cxn));
-       g_return_if_fail(CHIME_IS_CALL(call));
-
-       GTask *task = g_task_new(cxn, cancellable, callback, user_data);
-       g_task_set_task_data(task, g_object_ref(call), g_object_unref);
-
-       /* Grrr, GDtlsClientConnection doesn't actually exist yet. Let's stick
-          with the WebSocket for now... */
-       SoupURI *uri = soup_uri_new_printf(chime_call_get_audio_ws_url(call), "/audio");
-       SoupMessage *msg = soup_message_new_from_uri("GET", uri);
-
-       char *protocols[] = { (char *)"opus-med", NULL };
-       gchar *origin = g_strdup_printf("http://%s", soup_uri_get_host(uri));
-       soup_uri_free(uri);
-
-       chime_connection_websocket_connect_async(cxn, msg, origin, protocols, NULL,
-                                                audio_ws_connect_cb, task);
-       g_free(origin);
-}
-
-ChimeCallAudio *chime_connection_join_call_audio_finish(ChimeConnection *self,
-                                                       GAsyncResult *result,
-                                                       GError **error)
-{
-       g_return_val_if_fail(CHIME_IS_CONNECTION(self), FALSE);
-       g_return_val_if_fail(g_task_is_valid(result, self), FALSE);
+       chime_call_transport_connect(audio, muted);
 
-       return g_task_propagate_pointer(G_TASK(result), error);
+       return audio;
 }
 
diff --git a/chime-call-audio.h b/chime-call-audio.h
new file mode 100644 (file)
index 0000000..338f31f
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Pidgin/libpurple Chime client plugin
+ *
+ * Copyright © 2017 Amazon.com, Inc. or its affiliates.
+ *
+ * Authors: David Woodhouse <dwmw2@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ */
+
+
+#include "chime-connection.h"
+#include "chime-call.h"
+#include "chime-connection-private.h"
+
+#include <libsoup/soup.h>
+
+#include "protobuf/auth_message.pb-c.h"
+#include "protobuf/rt_message.pb-c.h"
+#include "protobuf/data_message.pb-c.h"
+
+enum audio_state {
+       AUDIO_STATE_CONNECTING = 0,
+       AUDIO_STATE_FAILED = 1,
+       AUDIO_STATE_MUTED = 2,
+       AUDIO_STATE_AUDIO = 3,
+       AUDIO_STATE_HANGUP = 4,
+       AUDIO_STATE_DISCONNECTED = 5,
+};
+
+struct _ChimeCallAudio {
+       ChimeCall *call;
+       enum audio_state state;
+       gboolean muted;
+       SoupWebsocketConnection *ws;
+       guint data_ack_source;
+       guint32 data_next_seq;
+       guint64 data_ack_mask;
+       gint32 data_next_logical_msg;
+       GSList *data_messages;
+       GHashTable *profiles;
+#ifdef AUDIO_HACKS
+       OpusDecoder *opus_dec;
+       int audio_fd;
+#endif
+       guint send_rt_source;
+       gint64 last_server_time_offset;
+       gboolean echo_server_time;
+       RTMessage rt_msg;
+       AudioMessage audio_msg;
+};
+
+struct xrp_header {
+       guint16 type;
+       guint16 len;
+};
+
+enum xrp_pkt_type {
+       XRP_RT_MESSAGE = 2,
+       XRP_AUTH_MESSAGE= 3,
+       XRP_DATA_MESSAGE = 4,
+       XRP_STREAM_MESSAGE = 5,
+};
+
+/* Called from ChimeMeeting */
+ChimeCallAudio *chime_call_audio_open(ChimeConnection *cxn, ChimeCall *call, gboolean muted);
+void chime_call_audio_close(ChimeCallAudio *audio, gboolean hangup);
+
+/* Called from audio code */
+void chime_call_transport_connect(ChimeCallAudio *audio, gboolean muted);
+void chime_call_transport_disconnect(ChimeCallAudio *audio, gboolean hangup);
+void chime_call_transport_send_packet(ChimeCallAudio *audio, enum xrp_pkt_type type, const ProtobufCMessage *message);
+
+/* Callbacks into audio code from transport */
+gboolean audio_receive_packet(ChimeCallAudio *audio, gconstpointer pkt, gsize len);
diff --git a/chime-call-transport.c b/chime-call-transport.c
new file mode 100644 (file)
index 0000000..ec92515
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Pidgin/libpurple Chime client plugin
+ *
+ * Copyright © 2017 Amazon.com, Inc. or its affiliates.
+ *
+ * Authors: David Woodhouse <dwmw2@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#include "chime-connection-private.h"
+#include "chime-call.h"
+#include "chime-call-audio.h"
+
+#include <arpa/inet.h>
+#include <string.h>
+#include <ctype.h>
+
+static void hexdump(const void *buf, int len)
+{
+       char linechars[17];
+       int i;
+
+       memset(linechars, 0, sizeof(linechars));
+       for (i=0; i < len; i++) {
+               unsigned char c = ((unsigned char *)buf)[i];
+               if (!(i & 15)) {
+                       if (i)
+                               printf("   %s", linechars);
+                       printf("\n%04x:", i);
+               }
+               printf(" %02x", c);
+               linechars[i & 15] = isprint(c) ? c : '.';
+       }
+       if (i & 15) {
+               linechars[i & 15] = 0;
+               printf("   %s", linechars);
+       }
+       printf("\n");
+}
+
+static void on_audiows_closed(SoupWebsocketConnection *ws, gpointer _audio)
+{
+       /* XXX: Reconnect it */
+}
+
+static void on_audiows_message(SoupWebsocketConnection *ws, gint type,
+                              GBytes *message, gpointer _audio)
+{
+       gsize s;
+       gconstpointer d = g_bytes_get_data(message, &s);
+
+       if (getenv("CHIME_AUDIO_DEBUG")) {
+               printf("incoming:\n");
+               hexdump(d, s);
+       }
+
+       audio_receive_packet(_audio, d, s);
+}
+
+static void audio_send_auth_packet(ChimeCallAudio *audio)
+{
+       ChimeConnection *cxn = chime_call_get_connection(audio->call);
+       if (!cxn)
+               return;
+
+       ChimeConnectionPrivate *priv = CHIME_CONNECTION_GET_PRIVATE (cxn);
+       AuthMessage msg;
+       auth_message__init(&msg);
+       msg.message_type = AUTH_MESSAGE_TYPE__REQUEST;
+       msg.has_message_type = TRUE;
+
+       msg.call_id = 0;
+       msg.has_call_id = TRUE;
+
+       msg.call_uuid = (char *)chime_call_get_uuid(audio->call);
+
+       msg.service_type = SERVICE_TYPE__FULL_DUPLEX;
+       msg.has_service_type = TRUE;
+
+       msg.profile_id = 0;
+       msg.has_profile_id = TRUE;
+
+       msg.profile_uuid = (char *)chime_connection_get_profile_id(cxn);
+
+       /* XX: What if it *just* expired? We'll need to renew it and try again? */
+       msg.session_token = priv->session_token;
+
+       msg.codec = 7; /* Opus Med. Later... */
+       msg.has_codec = TRUE;
+
+       msg.flags = FLAGS__FLAG_HAS_PROFILE_TABLE;
+       if (audio->muted)
+               msg.flags |= FLAGS__FLAG_MUTE;
+       msg.has_flags = TRUE;
+
+       chime_call_transport_send_packet(audio, XRP_AUTH_MESSAGE, &msg.base);
+}
+
+static void audio_send_hangup_packet(ChimeCallAudio *audio)
+{
+       ChimeConnection *cxn = chime_call_get_connection(audio->call);
+       if (!cxn)
+               return;
+
+       ChimeConnectionPrivate *priv = CHIME_CONNECTION_GET_PRIVATE (cxn);
+       AuthMessage msg;
+       auth_message__init(&msg);
+       msg.message_type = AUTH_MESSAGE_TYPE__HANGUP;
+       msg.has_message_type = TRUE;
+
+       msg.call_id = 0;
+       msg.has_call_id = TRUE;
+
+       msg.call_uuid = (char *)chime_call_get_uuid(audio->call);
+
+       msg.service_type = SERVICE_TYPE__FULL_DUPLEX;
+       msg.has_service_type = TRUE;
+
+       msg.profile_id = 0;
+       msg.has_profile_id = TRUE;
+
+       msg.profile_uuid = (char *)chime_connection_get_profile_id(cxn);
+
+       /* XX: What if it *just* expired? We'll need to renew it and try again? */
+       msg.session_token = priv->session_token;
+
+       msg.codec = 7; /* Opus Med. Later... */
+       msg.has_codec = TRUE;
+
+       msg.flags = FLAGS__FLAG_HAS_PROFILE_TABLE;
+       if (audio->muted)
+               msg.flags |= FLAGS__FLAG_MUTE;
+       msg.has_flags = TRUE;
+
+       chime_call_transport_send_packet(audio, XRP_AUTH_MESSAGE, &msg.base);
+}
+
+static void audio_ws_connect_cb(GObject *obj, GAsyncResult *res, gpointer _audio)
+{
+       ChimeCallAudio *audio = _audio;
+       ChimeConnection *cxn = CHIME_CONNECTION(obj);
+       GError *error = NULL;
+       SoupWebsocketConnection *ws = chime_connection_websocket_connect_finish(cxn, res, &error);
+       if (!ws) {
+               chime_debug("audio ws error %s\n", error->message);
+               audio->state = AUDIO_STATE_FAILED;
+               g_object_unref(cxn);
+               return;
+       }
+       chime_debug("audio ws connected!\n");
+       g_signal_connect(G_OBJECT(ws), "closed", G_CALLBACK(on_audiows_closed), audio);
+       g_signal_connect(G_OBJECT(ws), "message", G_CALLBACK(on_audiows_message), audio);
+       audio->ws = ws;
+
+       audio_send_auth_packet(audio);
+       g_object_unref(cxn);
+}
+
+
+void chime_call_transport_connect(ChimeCallAudio *audio, gboolean muted)
+{
+       /* Grrr, GDtlsClientConnection doesn't actually exist yet. Let's stick
+          with the WebSocket for now... */
+       SoupURI *uri = soup_uri_new_printf(chime_call_get_audio_ws_url(audio->call), "/audio");
+       SoupMessage *msg = soup_message_new_from_uri("GET", uri);
+
+       audio->muted = muted;
+
+       char *protocols[] = { (char *)"opus-med", NULL };
+       gchar *origin = g_strdup_printf("http://%s", soup_uri_get_host(uri));
+       soup_uri_free(uri);
+
+       ChimeConnection *cxn = chime_call_get_connection(audio->call);
+       chime_connection_websocket_connect_async(g_object_ref(cxn), msg, origin, protocols, NULL,
+                                                audio_ws_connect_cb, audio);
+       g_free(origin);
+}
+
+void chime_call_transport_disconnect(ChimeCallAudio *audio, gboolean hangup)
+{
+       if (hangup)
+               audio_send_hangup_packet(audio);
+       soup_websocket_connection_close(audio->ws, 0, NULL);
+       g_signal_handlers_disconnect_matched(G_OBJECT(audio->ws), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, audio);
+       audio->ws = NULL;
+}
+
+
+
+void chime_call_transport_send_packet(ChimeCallAudio *audio, enum xrp_pkt_type type, const ProtobufCMessage *message)
+{
+       size_t len = protobuf_c_message_get_packed_size(message);
+
+       len += sizeof(struct xrp_header);
+       struct xrp_header *hdr = g_malloc0(len);
+       hdr->type = htons(type);
+       hdr->len = htons(len);
+       protobuf_c_message_pack(message, (void *)(hdr + 1));
+       if (getenv("CHIME_AUDIO_DEBUG")) {
+               printf("sending protobuf of len %zd\n", len);
+               hexdump(hdr, len);
+       }
+       soup_websocket_connection_send_binary(audio->ws, hdr, len);
+       g_free(hdr);
+}
index 228b49b337db487ebfdc30fca3891fdbb5613abe..46e54d4f1522f774c3bdaed8d83c0ae5faa286fb 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "chime-connection-private.h"
 #include "chime-call.h"
+#include "chime-call-audio.h"
 
 #include <glib/gi18n.h>
 
@@ -66,9 +67,9 @@ struct _ChimeCall {
 
        CHIME_PROPS_VARS
 
-       ChimeConnection *cxn;
        GHashTable *participants;
 
+       ChimeCallAudio *audio;
        guint opens;
 };
 
@@ -94,7 +95,9 @@ chime_call_dispose(GObject *object)
 
        chime_debug("Call disposed: %p\n", self);
 
-       unsub_call(NULL, self, NULL);
+       if (self->opens)
+               unsub_call(NULL, self, NULL);
+
        g_signal_emit(self, signals[ENDED], 0, NULL);
 
        g_clear_pointer(&self->participants, g_hash_table_destroy);
@@ -187,7 +190,7 @@ ChimeConnection *chime_call_get_connection(ChimeCall *self)
 {
        g_return_val_if_fail(CHIME_IS_CALL(self), NULL);
 
-       return self->cxn;
+       return chime_object_get_connection(CHIME_OBJECT(self));
 }
 
 gboolean chime_call_get_ongoing(ChimeCall *self)
@@ -458,10 +461,13 @@ static void unsub_call(gpointer key, gpointer val, gpointer data)
 {
        ChimeCall *call = CHIME_CALL (val);
 
-       if (call->cxn) {
-               chime_jugg_unsubscribe(call->cxn, call->channel, "Call", call_jugg_cb, NULL);
-               chime_jugg_unsubscribe(call->cxn, call->roster_channel, "Roster", call_roster_cb, call);
-               call->cxn = NULL;
+       ChimeConnection *cxn = chime_object_get_connection(CHIME_OBJECT(call));
+       chime_jugg_unsubscribe(cxn, call->channel, "Call", call_jugg_cb, NULL);
+       chime_jugg_unsubscribe(cxn, call->roster_channel, "Roster", call_roster_cb, call);
+
+       if (call->audio) {
+               chime_call_audio_close(call->audio, TRUE);
+               call->audio = NULL;
        }
 }
 
@@ -476,11 +482,15 @@ void chime_connection_close_call(ChimeConnection *cxn, ChimeCall *call)
 }
 
 
-void chime_connection_open_call(ChimeConnection *cxn, ChimeCall *call)
+
+void chime_connection_open_call(ChimeConnection *cxn, ChimeCall *call, gboolean muted)
 {
+       g_return_if_fail(CHIME_IS_CONNECTION(cxn));
+       g_return_if_fail(CHIME_IS_CALL(call));
+
        if (!call->opens++) {
-               call->cxn = cxn;
                chime_jugg_subscribe(cxn, call->channel, "Call", call_jugg_cb, NULL);
                chime_jugg_subscribe(cxn, call->roster_channel, "Roster", call_roster_cb, call);
+               call->audio = chime_call_audio_open(cxn, call, muted);
        }
 }
index 79e54557bd32f3b25f7c990c753b61be1c3253bb..3d4cf7e8c78e29918b17356cb59a5f6fafbc310f 100644 (file)
@@ -84,17 +84,6 @@ GList *chime_call_get_participants(ChimeCall *self);
 struct _ChimeCallAudio;
 typedef struct _ChimeCallAudio ChimeCallAudio;
 
-void chime_connection_join_call_audio_async(ChimeConnection *cxn,
-                                           ChimeCall *call,
-                                           GCancellable *cancellable,
-                                           GAsyncReadyCallback callback,
-                                           gpointer user_data);
-
-ChimeCallAudio *chime_connection_join_call_audio_finish(ChimeConnection *self,
-                                                       GAsyncResult *result,
-                                                       GError **error);
-void chime_connection_call_audio_close(ChimeCallAudio *audio);
-
 
 G_END_DECLS
 
index 951f282aa16c13e81c1b79ce4f64d55aa3d3d864..01e589a3d5f50d2b1ea5cc649a884fe78d1b9c0f 100644 (file)
@@ -247,7 +247,7 @@ ChimeCall *chime_connection_parse_call(ChimeConnection *cxn, JsonNode *node,
 ChimeConnection *chime_call_get_connection(ChimeCall *self);
 /* Implicitly from open/close meeting */
 void chime_connection_close_call(ChimeConnection *cxn, ChimeCall *call);
-void chime_connection_open_call(ChimeConnection *cxn, ChimeCall *call);
+void chime_connection_open_call(ChimeConnection *cxn, ChimeCall *call, gboolean muted);
 
 gboolean chime_call_participant_audio_stats(ChimeCall *call, const gchar *profile_id, int vol, int signal_strength);
 void chime_call_emit_participants(ChimeCall *call);
index 524e408064257f549059493104f4112496420c69..722e1187081de12a5e0af700678561082f8a78bb 100644 (file)
@@ -781,7 +781,7 @@ static void chime_connection_open_meeting(ChimeConnection *cxn, ChimeMeeting *me
 {
        if (!meeting->opens++) {
                meeting->cxn = cxn;
-               chime_connection_open_call(cxn, meeting->call);
+               chime_connection_open_call(cxn, meeting->call, TRUE);
        }
 
        g_task_return_pointer(task, g_object_ref(meeting), g_object_unref);