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)
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)
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);
}
purple_conversation_set_name(chat->conv, name);
}
+#if 0
static void audio_joined(GObject *source, GAsyncResult *result, gpointer _chat)
{
ChimeConnection *cxn = CHIME_CONNECTION(source);
}
#endif
}
-
+#endif
struct chime_chat *do_join_chat(PurpleConnection *conn, ChimeConnection *cxn, ChimeObject *obj, JsonNode *first_msg, ChimeMeeting *meeting)
{
if (!obj)
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
}
}
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;
}
#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);
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;
}
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;
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)
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;
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)
#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;
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);
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;
}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
+}
#include "chime-connection-private.h"
#include "chime-call.h"
+#include "chime-call-audio.h"
#include <glib/gi18n.h>
CHIME_PROPS_VARS
- ChimeConnection *cxn;
GHashTable *participants;
+ ChimeCallAudio *audio;
guint opens;
};
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);
{
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)
{
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;
}
}
}
-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);
}
}
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
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);
{
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);