const gchar *profile_id = g_hash_table_lookup(audio->profiles,
GUINT_TO_POINTER(msg->profiles[i]->stream_id));
- if (!profile_id)
+ if (!profile_id) {
+ chime_debug("no profile for stream id %d\n",
+ msg->profiles[i]->stream_id);
continue;
+ }
int vol;
if (msg->profiles[i]->has_muted && msg->profiles[i]->muted)
return TRUE;
}
+static gboolean audio_reconnect(gpointer _audio)
+{
+ ChimeCallAudio *audio = _audio;
+
+ audio->timeout_source = 0;
+
+ chime_call_transport_disconnect(audio, TRUE);
+ chime_call_transport_connect(audio, audio->silent);
+
+ return G_SOURCE_REMOVE;
+}
+
static gboolean timed_send_rt_packet(ChimeCallAudio *audio);
static void do_send_rt_packet(ChimeCallAudio *audio, GstBuffer *buffer)
{
g_mutex_lock(&audio->rt_lock);
gint64 now = g_get_monotonic_time();
+ if (!audio->timeout_source && audio->last_rx + 10000000 < now) {
+ chime_debug("RX timeout, reconnect audio\n");
+ audio->timeout_source = g_timeout_add(0, audio_reconnect, audio);
+ }
audio->audio_msg.seq = (audio->audio_msg.seq + 1) & 0xffff;
if (audio->last_server_time_offset) {
static gboolean timed_send_rt_packet(ChimeCallAudio *audio)
{
- do_send_rt_packet(audio, NULL);
+ if (audio->state >= CHIME_AUDIO_STATE_AUDIOLESS)
+ do_send_rt_packet(audio, NULL);
return TRUE;
}
do_send_rt_packet(audio, NULL);
chime_call_audio_set_state(audio, audio->silent ? CHIME_AUDIO_STATE_AUDIOLESS :
(audio->local_mute ? CHIME_AUDIO_STATE_AUDIO_MUTED : CHIME_AUDIO_STATE_AUDIO));
- if (audio->silent || audio->local_mute)
+ if ((audio->silent || audio->local_mute) &&
+ !audio->send_rt_source)
audio->send_rt_source = g_timeout_add(100, (GSourceFunc)timed_send_rt_packet, audio);
}
g_free(m);
}
+void chime_call_audio_cleanup_datamsgs(ChimeCallAudio *audio)
+{
+ if (audio->data_ack_source) {
+ g_source_remove(audio->data_ack_source);
+ audio->data_ack_source = 0;
+ }
+
+ g_slist_free_full(audio->data_messages, (GDestroyNotify) free_msgbuf);
+ audio->data_messages = NULL;
+
+ audio->data_next_seq = 0;
+ audio->data_ack_mask = 0;
+ audio->data_next_logical_msg = 0;
+}
+
static void do_send_ack(ChimeCallAudio *audio)
{
DataMessage msg = DATA_MESSAGE__INIT;
if (!audio->data_ack_source)
audio->data_ack_source = g_idle_add(idle_send_ack, audio);
-
/* Now process the incoming data packet. First, drop packets
that look like replays and are too old. */
if (msg->msg_id < audio->data_next_logical_msg)
if (len != ntohs(hdr->len))
return FALSE;
+ audio->last_rx = g_get_monotonic_time();
+
/* Point to the payload, without (void *) arithmetic */
pkt = hdr + 1;
len -= 4;
if (audio->audio_sink)
gst_app_sink_set_callbacks(audio->audio_sink, &no_appsink_callbacks, NULL, NULL);
- if (audio->data_ack_source)
- g_source_remove(audio->data_ack_source);
- if (audio->send_rt_source)
- g_source_remove(audio->send_rt_source);
-
- g_hash_table_destroy(audio->profiles);
- g_slist_free_full(audio->data_messages, (GDestroyNotify) free_msgbuf);
chime_call_transport_disconnect(audio, hangup);
chime_call_audio_set_state(audio, CHIME_AUDIO_STATE_HANGUP);
+
+ g_hash_table_destroy(audio->profiles);
g_free(audio);
}
ChimeCallAudio *chime_call_audio_open(ChimeConnection *cxn, ChimeCall *call, gboolean silent)
{
ChimeCallAudio *audio = g_new0(ChimeCallAudio, 1);
+
audio->call = call;
audio->profiles = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
g_mutex_init(&audio->transport_lock);
g_mutex_init(&audio->rt_lock);
+ audio->session_id = ((guint64)g_random_int() << 32) | g_random_int();
+
rtmessage__init(&audio->rt_msg);
audio_message__init(&audio->audio_msg);
client_status_message__init(&audio->client_status_msg);
audio->audio_msg.sample_time = g_random_int();
chime_call_transport_connect(audio, silent);
- chime_call_audio_set_state(audio, CHIME_AUDIO_STATE_CONNECTING);
return audio;
}
{
chime_call_audio_local_mute(audio, silent);
if (silent != audio->silent) {
- if (audio->send_rt_source) {
- g_source_remove(audio->send_rt_source);
- audio->send_rt_source = 0;
- }
- if (audio->data_ack_source) {
- g_source_remove(audio->data_ack_source);
- audio->data_ack_source = 0;
- }
chime_call_transport_disconnect(audio, TRUE);
chime_call_transport_connect(audio, silent);
- chime_call_audio_set_state(audio, CHIME_AUDIO_STATE_CONNECTING);
}
}
static void on_audiows_closed(SoupWebsocketConnection *ws, gpointer _audio)
{
- /* XXX: Reconnect it */
+ ChimeCallAudio *audio = _audio;
+
+ chime_call_transport_disconnect(audio, FALSE);
+ chime_call_transport_connect(audio, audio->silent);
}
static void on_audiows_message(SoupWebsocketConnection *ws, gint type,
msg.profile_id = 0;
msg.has_profile_id = TRUE;
+ msg.session_id = audio->session_id;
+ msg.has_session_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? */
GError *error = NULL;
SoupWebsocketConnection *ws = chime_connection_websocket_connect_finish(cxn, res, &error);
if (!ws) {
+ /* If it was cancelled, 'audio' may have been freed. */
if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
chime_debug("audio ws error %s\n", error->message);
audio->state = CHIME_AUDIO_STATE_FAILED;
static gboolean
read_timeout_cb (gpointer user_data)
{
- gboolean *timed_out = user_data;
+ gboolean *timed_out = user_data;
- *timed_out = TRUE;
+ *timed_out = TRUE;
- return G_SOURCE_REMOVE;
+ return G_SOURCE_REMOVE;
}
static int
return 0;
}
+static gboolean dtls_timeout(ChimeCallAudio *audio);
static gboolean dtls_src_cb(GDatagramBased *dgram, GIOCondition condition, ChimeCallAudio *audio)
{
if (!audio->dtls_handshaked) {
int ret = gnutls_handshake(audio->dtls_sess);
- if (ret == GNUTLS_E_AGAIN)
+ if (ret == GNUTLS_E_AGAIN) {
+ if (audio->timeout_source)
+ g_source_remove(audio->timeout_source);
+
+ int timeo = gnutls_dtls_get_timeout(audio->dtls_sess);
+ audio->timeout_source = g_timeout_add(timeo, (GSourceFunc)dtls_timeout, audio);
+
return G_SOURCE_CONTINUE;
+ }
if (ret) {
gnutls_deinit(audio->dtls_sess);
audio->dtls_source = NULL;
g_object_unref(audio->dtls_sock);
audio->dtls_sock = NULL;
+ if (audio->timeout_source)
+ g_source_remove(audio->timeout_source);
+ audio->timeout_source = 0;
chime_call_transport_connect_ws(audio);
return G_SOURCE_REMOVE;
}
chime_debug("DTLS established\n");
+ g_source_remove(audio->timeout_source);
+ audio->timeout_source = 0;
audio->dtls_handshaked = TRUE;
audio_send_auth_packet(audio);
/* Fall through and receive data, not that it should be there */
unsigned char pkt[CHIME_DTLS_MTU];
ssize_t len = gnutls_record_recv(audio->dtls_sess, pkt, sizeof(pkt));
- if (len > 0)
+ if (len > 0) {
+ if (getenv("CHIME_AUDIO_DEBUG")) {
+ printf("incoming:\n");
+ hexdump(pkt, len);
+ }
audio_receive_packet(audio, pkt, len);
+ }
return G_SOURCE_CONTINUE;
}
+static gboolean dtls_timeout(ChimeCallAudio *audio)
+{
+ audio->timeout_source = 0;
+
+ dtls_src_cb(NULL, 0, audio);
+
+ return G_SOURCE_REMOVE;
+}
+
static void connect_dtls(ChimeCallAudio *audio, GSocket *s)
{
/* Not that "connected" means anything except that we think we can route to it. */
gnutls_set_default_priority(audio->dtls_sess);
gnutls_session_set_ptr(audio->dtls_sess, audio);
+ /* We can't rely on the length argument to gnutls_server_name_set().
+ https://bugs.launchpad.net/ubuntu/+bug/1762710 */
gchar *hostname = g_strdup(chime_call_get_media_host(audio->call));
if (!hostname)
goto err;
*colon = 0;
gnutls_server_name_set(audio->dtls_sess, GNUTLS_NAME_DNS, hostname, colon - hostname);
g_free(hostname);
+
if (!audio->dtls_cred) {
gnutls_certificate_allocate_credentials(&audio->dtls_cred);
gnutls_certificate_set_x509_system_trust(audio->dtls_cred);
g_tls_connection_gnutls_pull_timeout_func);
gnutls_transport_set_vec_push_function (audio->dtls_sess,
g_tls_connection_gnutls_vec_push_func);
+ gnutls_dtls_set_timeouts(audio->dtls_sess, 250, 2500);
gnutls_dtls_set_mtu(audio->dtls_sess, CHIME_DTLS_MTU);
- gnutls_handshake(audio->dtls_sess);
+
+ if (gnutls_handshake(audio->dtls_sess) != GNUTLS_E_AGAIN) {
+ chime_debug("Initial DTLS handshake failed\n");
+
+ gnutls_deinit(audio->dtls_sess);
+ audio->dtls_sess = NULL;
+
+ if (audio->dtls_source) {
+ g_source_destroy(audio->dtls_source);
+ audio->dtls_source = NULL;
+ }
+ goto err;
+ }
+
+ int timeo = gnutls_dtls_get_timeout(audio->dtls_sess);
+ audio->timeout_source = g_timeout_add(timeo, (GSourceFunc)dtls_timeout, audio);
return;
err:
- g_object_unref(s);
- audio->dtls_sock = NULL;
+ g_clear_object(&audio->dtls_sock);
chime_call_transport_connect_ws(audio);
}
GSocketAddress *addr = g_socket_address_enumerator_next_finish(enumerator, res, &error);
if (!addr) {
+ /* If it was cancelled, 'audio' may have been freed. */
if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
chime_call_transport_connect_ws(audio);
g_clear_error(&error);
void chime_call_transport_connect(ChimeCallAudio *audio, gboolean silent)
{
-
audio->silent = silent;
audio->cancel = g_cancellable_new();
audio->dtls_handshaked = FALSE;
+ chime_call_audio_set_state(audio, CHIME_AUDIO_STATE_CONNECTING);
+
GSocketConnectable *addr = g_network_address_parse(chime_call_get_media_host(audio->call),
0, NULL);
if (!addr) {
void chime_call_transport_disconnect(ChimeCallAudio *audio, gboolean hangup)
{
- if (hangup)
+ if (audio->send_rt_source) {
+ g_source_remove(audio->send_rt_source);
+ audio->send_rt_source = 0;
+ }
+
+ g_hash_table_remove_all(audio->profiles);
+
+ chime_call_audio_cleanup_datamsgs(audio);
+
+ if (hangup && audio->state >= CHIME_AUDIO_STATE_AUDIOLESS)
audio_send_hangup_packet(audio);
g_mutex_lock(&audio->transport_lock);
audio->cancel = NULL;
}
if (audio->ws) {
- 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);
g_signal_connect(G_OBJECT(audio->ws), "closed", G_CALLBACK(on_final_audiows_close), NULL);
+ soup_websocket_connection_close(audio->ws, 0, NULL);
audio->ws = NULL;
} else if (audio->dtls_sess) {
gnutls_deinit(audio->dtls_sess);
g_clear_object(&audio->dtls_sock);
}
- g_mutex_unlock(&audio->transport_lock);
-}
+ if (audio->timeout_source) {
+ g_source_remove(audio->timeout_source);
+ audio->timeout_source = 0;
+ }
+ if (hangup && audio->dtls_cred) {
+ gnutls_certificate_free_credentials(audio->dtls_cred);
+ audio->dtls_cred = NULL;
+ }
+ g_mutex_unlock(&audio->transport_lock);
+}
void chime_call_transport_send_packet(ChimeCallAudio *audio, enum xrp_pkt_type type, const ProtobufCMessage *message)
{