753 lines
21 KiB
C
753 lines
21 KiB
C
#include <glib.h>
|
|
#include <farsight/farsight.h>
|
|
#include <farsight/farsight-transport.h>
|
|
#include <dbus/dbus-glib.h>
|
|
#include <glib/gprintf.h>
|
|
#include <string.h>
|
|
#include "tox-session.h"
|
|
#include "tox-marshal.h"
|
|
|
|
G_DEFINE_TYPE(ToxSession, tox_session, G_TYPE_OBJECT)
|
|
|
|
typedef struct _ToxSessionPrivate {
|
|
FarsightSession *session;
|
|
/* for now, one stream is enough */
|
|
FarsightStream *stream;
|
|
|
|
int have_source, have_sink;
|
|
|
|
gboolean dispose_has_run;
|
|
} ToxSessionPrivate;
|
|
|
|
gboolean tox_session_destroy(ToxSession *obj, GError **error);
|
|
gboolean tox_session_set_default_audio_sink(ToxSession *obj, GError **error);
|
|
gboolean tox_session_set_ogg_vorbis_audio_source(ToxSession *obj, char *filename, GError **error);
|
|
gboolean tox_session_add_remote_candidate(ToxSession *obj, GPtrArray *candidate, GError **error);
|
|
gboolean tox_session_set_remote_codecs(ToxSession *obj, GPtrArray *codecs, GError **error);
|
|
gboolean tox_session_get_local_codecs(ToxSession *obj, GPtrArray **codecs, GError **error);
|
|
gboolean tox_session_get_codec_intersection(ToxSession *obj, GPtrArray **codecs, GError **error);
|
|
|
|
/* properties */
|
|
enum {
|
|
DIRECTION = 1
|
|
};
|
|
static void tox_session_set_property(GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec);
|
|
|
|
/* signals */
|
|
enum {
|
|
NEW_NATIVE_CANDIDATE,
|
|
NATIVE_CANDIDATES_PREPARED,
|
|
STATE_CHANGED,
|
|
NEW_ACTIVE_CANDIDATE_PAIR,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint signals[LAST_SIGNAL];
|
|
|
|
#include "tox-session-glue.h"
|
|
|
|
static GstElement *prepare_source(const char *filename);
|
|
static void new_pad(GstElement *, GstPad *, gpointer);
|
|
static GstElement *prepare_sink(void);
|
|
static void stream_done(ToxSession *);
|
|
|
|
static GValueArray * candidate_list_to_dbus_array(const GList *candidates);
|
|
|
|
static void tox_session_native_candidates_prepared(FarsightStream *stream, gpointer user_data);
|
|
static void tox_session_new_native_candidate(FarsightStream *stream, gchar *candidate_id, ToxSession *self);
|
|
static void tox_session_state_changed(FarsightStream *stream, gint state, gint direction, ToxSession *self);
|
|
static void tox_session_new_active_candidate_pair(FarsightStream *stream, gchar *native_candidate_id, gchar *remote_candidate_id, ToxSession *self);
|
|
|
|
void
|
|
tox_session_init(ToxSession *obj)
|
|
{
|
|
ToxSessionPrivate *priv;
|
|
priv = g_new0(ToxSessionPrivate, 1);
|
|
obj->priv = priv;
|
|
|
|
priv->session = farsight_session_factory_make("rtp");
|
|
g_assert(priv->session);
|
|
|
|
/* we need to know the direction to create a stream,
|
|
so we do that when that property is set.
|
|
*/
|
|
|
|
}
|
|
|
|
static GObjectClass *parent_class = NULL;
|
|
|
|
static void
|
|
tox_session_dispose(GObject *obj)
|
|
{
|
|
ToxSession *self = (ToxSession *)obj;
|
|
|
|
if (self->priv->dispose_has_run) {
|
|
return;
|
|
}
|
|
g_debug("in tox_session_dispose\n");
|
|
self->priv->dispose_has_run = TRUE;
|
|
|
|
if (self->priv->stream)
|
|
g_object_unref(self->priv->stream);
|
|
if (self->priv->session)
|
|
g_object_unref(self->priv->session);
|
|
|
|
self->priv->stream = NULL;
|
|
self->priv->session = NULL;
|
|
|
|
G_OBJECT_CLASS(parent_class)->dispose(obj);
|
|
}
|
|
|
|
static void
|
|
tox_session_finalize(GObject *obj)
|
|
{
|
|
ToxSession *self = (ToxSession *)obj;
|
|
|
|
g_debug("in tox_session_finalize\n");
|
|
g_free(self->priv);
|
|
|
|
G_OBJECT_CLASS(parent_class)->finalize(obj);
|
|
}
|
|
|
|
void
|
|
tox_session_class_init(ToxSessionClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
|
|
GParamSpec *direction_param_spec;
|
|
|
|
gobject_class->dispose = tox_session_dispose;
|
|
gobject_class->finalize = tox_session_finalize;
|
|
|
|
gobject_class->set_property = tox_session_set_property;
|
|
|
|
direction_param_spec = g_param_spec_uint("direction",
|
|
"stream direction",
|
|
"1 means send-only, 2 means receive-only, 3 means both",
|
|
1,
|
|
3,
|
|
1,
|
|
G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE);
|
|
g_object_class_install_property(gobject_class,
|
|
DIRECTION,
|
|
direction_param_spec);
|
|
|
|
signals[NEW_NATIVE_CANDIDATE] =
|
|
g_signal_new("new-native-candidate",
|
|
G_OBJECT_CLASS_TYPE(klass),
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
|
|
0,
|
|
NULL, NULL,
|
|
tox_marshal_VOID__BOXED,
|
|
G_TYPE_NONE,
|
|
1,
|
|
G_TYPE_VALUE_ARRAY);
|
|
|
|
signals[NATIVE_CANDIDATES_PREPARED] =
|
|
g_signal_new("native-candidates-prepared",
|
|
G_OBJECT_CLASS_TYPE(klass),
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
|
|
0,
|
|
NULL, NULL,
|
|
tox_marshal_VOID__BOXED,
|
|
G_TYPE_NONE,
|
|
1,
|
|
G_TYPE_VALUE_ARRAY);
|
|
|
|
signals[STATE_CHANGED] =
|
|
g_signal_new("state-changed",
|
|
G_OBJECT_CLASS_TYPE(klass),
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
|
|
0,
|
|
NULL, NULL,
|
|
tox_marshal_VOID__UCHAR_UCHAR,
|
|
G_TYPE_NONE,
|
|
2,
|
|
G_TYPE_UCHAR,
|
|
G_TYPE_UCHAR);
|
|
|
|
signals[NEW_ACTIVE_CANDIDATE_PAIR] =
|
|
g_signal_new("new-active-candidate-pair",
|
|
G_OBJECT_CLASS_TYPE(klass),
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
|
|
0,
|
|
NULL, NULL,
|
|
tox_marshal_VOID__STRING_STRING,
|
|
G_TYPE_NONE,
|
|
2,
|
|
G_TYPE_STRING,
|
|
G_TYPE_STRING);
|
|
|
|
dbus_g_object_type_install_info(TOX_TYPE_SESSION, &dbus_glib_tox_session_object_info);
|
|
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
}
|
|
|
|
static void
|
|
tox_session_set_property(GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec)
|
|
{
|
|
ToxSession *self = (ToxSession *)obj;
|
|
guint dir;
|
|
|
|
switch(property_id) {
|
|
case DIRECTION:
|
|
if (self->priv->stream)
|
|
g_object_unref(self->priv->stream);
|
|
/* Now we know the direction, so we create a stream. */
|
|
dir = g_value_get_uint(value);
|
|
self->priv->stream = farsight_session_create_stream(self->priv->session,
|
|
FARSIGHT_MEDIA_TYPE_AUDIO,
|
|
dir);
|
|
g_assert(self->priv->stream);
|
|
|
|
g_object_set(G_OBJECT(self->priv->stream), "transmitter", "libjingle", NULL);
|
|
|
|
/* XXX: should we set null source/sink here? */
|
|
switch (dir) {
|
|
case 1:
|
|
/* send-only, we need no sink */
|
|
self->priv->have_sink = 1;
|
|
break;
|
|
case 2:
|
|
/* receive-only, we need no source */
|
|
self->priv->have_source = 1;
|
|
break;
|
|
}
|
|
|
|
/* start preparing native candidates */
|
|
g_debug("About to prepare native candidates...\n");
|
|
g_signal_connect(self->priv->stream, "new-native-candidate",
|
|
(GCallback)tox_session_new_native_candidate, (gpointer)self);
|
|
g_signal_connect(self->priv->stream, "native-candidates-prepared",
|
|
(GCallback)tox_session_native_candidates_prepared, (gpointer)self);
|
|
/* but we can't actually do it until we have a pipeline.
|
|
so, we'll call stream_done when we do. */
|
|
|
|
/* Other signals we want to forward */
|
|
g_signal_connect(self->priv->stream, "state-changed",
|
|
(GCallback)tox_session_state_changed, (gpointer)self);
|
|
g_signal_connect(self->priv->stream, "new-active-candidate-pair",
|
|
(GCallback)tox_session_new_active_candidate_pair, (gpointer)self);
|
|
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
tox_session_destroy(ToxSession *obj, GError **error)
|
|
{
|
|
g_object_unref(obj);
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
tox_session_set_default_audio_sink(ToxSession *obj, GError **error)
|
|
{
|
|
GstElement *sink = prepare_sink();
|
|
farsight_stream_set_sink(obj->priv->stream, sink);
|
|
obj->priv->have_sink = 1;
|
|
|
|
if (obj->priv->have_sink && obj->priv->have_source)
|
|
stream_done(obj);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
tox_session_set_ogg_vorbis_audio_source(ToxSession *obj, char *filename, GError **error)
|
|
{
|
|
GstElement *source = prepare_source(filename);
|
|
farsight_stream_set_source(obj->priv->stream, source);
|
|
obj->priv->have_source = 1;
|
|
|
|
if (obj->priv->have_sink && obj->priv->have_source)
|
|
stream_done(obj);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GstElement *
|
|
prepare_source(const char *filename)
|
|
{
|
|
GstElement *bin, *filesrc, *demux, *decode;
|
|
|
|
bin = gst_bin_new("mysource");
|
|
filesrc = gst_element_factory_make("filesrc", "file-source");
|
|
g_object_set(G_OBJECT(filesrc), "location", filename, NULL);
|
|
|
|
demux = gst_element_factory_make("oggdemux", "ogg-parser");
|
|
decode = gst_element_factory_make("vorbisdec", "vorbis-decoder");
|
|
|
|
gst_element_link(filesrc, demux);
|
|
g_signal_connect(demux, "pad-added", G_CALLBACK(new_pad), decode);
|
|
|
|
gst_bin_add_many(GST_BIN(bin), filesrc, demux, decode, NULL);
|
|
|
|
return bin;
|
|
}
|
|
|
|
static void
|
|
new_pad(GstElement *demux, GstPad *pad, gpointer data)
|
|
{
|
|
GstElement *decode = (GstElement*)data;
|
|
GstPad *decoder_pad;
|
|
|
|
decoder_pad = gst_element_get_pad(decode, "sink");
|
|
gst_pad_link(pad, decoder_pad);
|
|
|
|
gst_object_unref(decoder_pad);
|
|
}
|
|
|
|
static GstElement *
|
|
prepare_sink(void)
|
|
{
|
|
GstElement *bin, *converter, *audiosink;
|
|
|
|
bin = gst_bin_new("mysink");
|
|
converter = gst_element_factory_make("audioconvert", "converter");
|
|
audiosink = gst_element_factory_make("autoaudiosink", "audiosink");
|
|
gst_element_link(converter, audiosink);
|
|
|
|
gst_bin_add_many(GST_BIN(bin), converter, audiosink, NULL);
|
|
|
|
return bin;
|
|
}
|
|
|
|
static void
|
|
stream_done(ToxSession *self)
|
|
{
|
|
farsight_stream_prepare_transports(self->priv->stream);
|
|
}
|
|
|
|
gboolean
|
|
tox_session_add_remote_candidate(ToxSession *self, GPtrArray *candidate, GError **error)
|
|
{
|
|
int i;
|
|
guint n;
|
|
GList *candidate_list;
|
|
|
|
candidate_list = NULL;
|
|
|
|
/* Here we convert the array of structs into a GList of
|
|
FarsightTransportInfo. The argument list is described in
|
|
tox-session.xml. */
|
|
for (i = 0; i < candidate->len; i++) {
|
|
GValueArray *component;
|
|
FarsightTransportInfo *info;
|
|
gchar *s;
|
|
|
|
component = g_ptr_array_index(candidate, i);
|
|
info = g_new0(FarsightTransportInfo, 1);
|
|
|
|
info->candidate_id =
|
|
g_value_dup_string(
|
|
g_value_array_get_nth(component, 0));
|
|
info->component =
|
|
g_value_get_uint(
|
|
g_value_array_get_nth(component, 1));
|
|
info->ip =
|
|
g_value_dup_string(
|
|
g_value_array_get_nth(component, 2));
|
|
info->port =
|
|
g_value_get_uint(
|
|
g_value_array_get_nth(component, 3));
|
|
|
|
s = g_value_dup_string(g_value_array_get_nth(component, 4));
|
|
if (strcmp(s, "tcp") == 0)
|
|
info->proto = FARSIGHT_NETWORK_PROTOCOL_TCP;
|
|
else if (strcmp(s, "udp") == 0)
|
|
info->proto = FARSIGHT_NETWORK_PROTOCOL_UDP;
|
|
else {
|
|
g_set_error(error, DBUS_GERROR,
|
|
DBUS_GERROR_REMOTE_EXCEPTION,
|
|
"Unexpected protocol '%s'",
|
|
s);
|
|
g_free(s);
|
|
g_free(info);
|
|
goto fail;
|
|
}
|
|
g_free(s);
|
|
|
|
/* this should be "RTP" */
|
|
info->proto_subtype =
|
|
g_value_dup_string(
|
|
g_value_array_get_nth(component, 5));
|
|
/* this should be "AVP" */
|
|
info->proto_profile =
|
|
g_value_dup_string(
|
|
g_value_array_get_nth(component, 6));
|
|
|
|
info->preference =
|
|
g_value_get_uint(
|
|
g_value_array_get_nth(component, 7))
|
|
* 0.01;
|
|
|
|
n = g_value_get_uint(g_value_array_get_nth(component, 8));
|
|
switch (n) {
|
|
case 0:
|
|
info->type = FARSIGHT_CANDIDATE_TYPE_LOCAL;
|
|
break;
|
|
case 1:
|
|
info->type = FARSIGHT_CANDIDATE_TYPE_DERIVED;
|
|
break;
|
|
case 2:
|
|
info->type = FARSIGHT_CANDIDATE_TYPE_RELAY;
|
|
break;
|
|
default:
|
|
g_set_error(error, DBUS_GERROR,
|
|
DBUS_GERROR_REMOTE_EXCEPTION,
|
|
"Unexpected type %u",
|
|
n);
|
|
g_free(info);
|
|
goto fail;
|
|
}
|
|
|
|
info->username = g_value_dup_string(
|
|
g_value_array_get_nth(component, 9));
|
|
info->password = g_value_dup_string(
|
|
g_value_array_get_nth(component, 10));
|
|
|
|
candidate_list = g_list_append(candidate_list, info);
|
|
}
|
|
|
|
farsight_stream_add_remote_candidate(self->priv->stream, candidate_list);
|
|
return TRUE;
|
|
|
|
fail:
|
|
farsight_transport_list_destroy(candidate_list);
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
tox_session_set_remote_codecs(ToxSession *obj, GPtrArray *codecs, GError **error)
|
|
{
|
|
GList *codec_list;
|
|
int i;
|
|
/* GList *j; */
|
|
|
|
codec_list = NULL;
|
|
|
|
for (i = 0; i < codecs->len; i++) {
|
|
GValueArray *codec_in;
|
|
FarsightCodec *codec_out;
|
|
|
|
codec_in = g_ptr_array_index(codecs, i);
|
|
codec_out = g_new0(FarsightCodec, 1);
|
|
|
|
codec_out->id = g_value_get_int(g_value_array_get_nth(codec_in, 0));
|
|
codec_out->encoding_name = g_value_dup_string(g_value_array_get_nth(codec_in, 1));
|
|
/* maybe check range of media_type... */
|
|
codec_out->media_type = g_value_get_uchar(g_value_array_get_nth(codec_in, 2));
|
|
codec_out->clock_rate = g_value_get_uint(g_value_array_get_nth(codec_in, 3));
|
|
codec_out->channels = g_value_get_uint(g_value_array_get_nth(codec_in, 4));
|
|
|
|
codec_list = g_list_append(codec_list, codec_out);
|
|
}
|
|
|
|
farsight_stream_set_remote_codecs(obj->priv->stream, codec_list);
|
|
|
|
/* should the elements be freed, or just the list itself? */
|
|
/*for (j = codec_list; j; j = g_list_next(j)) {
|
|
g_free(j->data);
|
|
}*/
|
|
g_list_free(codec_list);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
tox_session_get_local_codecs(ToxSession *obj, GPtrArray **codecs, GError **error)
|
|
{
|
|
FarsightStream *stream;
|
|
const GList *codec_list;
|
|
|
|
GType hash_string_string;
|
|
|
|
stream = obj->priv->stream;
|
|
codec_list = farsight_stream_get_local_codecs(stream);
|
|
|
|
hash_string_string = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_STRING);
|
|
|
|
*codecs = g_ptr_array_sized_new(g_list_length(codec_list));
|
|
for (; codec_list; codec_list = g_list_next(codec_list)) {
|
|
GValueArray *codec_struct;
|
|
const FarsightCodec *codec;
|
|
GValue value;
|
|
GHashTable *parameters;
|
|
GList *p;
|
|
|
|
memset(&value, 0, sizeof value);
|
|
|
|
codec = (const FarsightCodec*)codec_list->data;
|
|
codec_struct = g_value_array_new(2);
|
|
|
|
g_value_init(&value, G_TYPE_INT);
|
|
g_value_set_int(&value, codec->id);
|
|
g_value_array_append(codec_struct, &value);
|
|
g_value_unset(&value);
|
|
|
|
g_value_init(&value, G_TYPE_STRING);
|
|
g_value_set_string(&value, codec->encoding_name);
|
|
g_value_array_append(codec_struct, &value);
|
|
g_value_unset(&value);
|
|
|
|
/* XXX: why is this the same as id? */
|
|
g_value_init(&value, G_TYPE_UCHAR);
|
|
g_value_set_uchar(&value, codec->media_type);
|
|
g_value_array_append(codec_struct, &value);
|
|
g_value_unset(&value);
|
|
|
|
g_value_init(&value, G_TYPE_UINT);
|
|
g_value_set_uint(&value, codec->clock_rate);
|
|
g_value_array_append(codec_struct, &value);
|
|
g_value_unset(&value);
|
|
|
|
g_value_init(&value, G_TYPE_UINT);
|
|
g_value_set_uint(&value, codec->channels);
|
|
g_value_array_append(codec_struct, &value);
|
|
g_value_unset(&value);
|
|
|
|
/* optional parameters - this should be a hash table, I think */
|
|
/* parameters = g_hash_table_new(g_str_hash, g_str_equal); */
|
|
parameters = dbus_g_type_specialized_construct(hash_string_string);
|
|
for (p = codec->optional_params; p; p = g_list_next(p)) {
|
|
FarsightCodecParameter *param = p->data;
|
|
|
|
g_hash_table_insert(parameters, param->name, param->value);
|
|
}
|
|
g_value_init(&value, hash_string_string);
|
|
g_value_set_boxed(&value, parameters);
|
|
g_value_array_append(codec_struct, &value);
|
|
g_value_unset(&value);
|
|
g_hash_table_unref(parameters);
|
|
|
|
g_assert(codec_struct->n_values == 6);
|
|
|
|
g_ptr_array_add(*codecs, codec_struct);
|
|
|
|
g_debug("Local codec: %s\n", codec->encoding_name);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
tox_session_get_codec_intersection(ToxSession *obj, GPtrArray **codecs, GError **error)
|
|
{
|
|
FarsightStream *stream;
|
|
GList *codec_list;
|
|
|
|
stream = obj->priv->stream;
|
|
codec_list = farsight_stream_get_codec_intersection(stream);
|
|
|
|
*codecs = g_ptr_array_sized_new(g_list_length(codec_list));
|
|
for (; codec_list; codec_list = g_list_next(codec_list)) {
|
|
GValueArray *codec_struct;
|
|
FarsightCodec *codec;
|
|
GValue *value;
|
|
|
|
codec = (FarsightCodec*)codec_list->data;
|
|
codec_struct = g_value_array_new(6);
|
|
|
|
value = g_new0(GValue, 1);
|
|
g_value_init(value, G_TYPE_INT);
|
|
g_value_set_int(value, codec->id);
|
|
g_value_array_append(codec_struct, value);
|
|
|
|
value = g_new0(GValue, 1);
|
|
g_value_init(value, G_TYPE_STRING);
|
|
g_value_set_string(value, codec->encoding_name);
|
|
g_value_array_append(codec_struct, value);
|
|
|
|
value = g_new0(GValue, 1);
|
|
g_value_init(value, G_TYPE_UCHAR);
|
|
g_value_set_uchar(value, codec->media_type);
|
|
g_value_array_append(codec_struct, value);
|
|
|
|
value = g_new0(GValue, 1);
|
|
g_value_init(value, G_TYPE_UINT);
|
|
g_value_set_uint(value, codec->clock_rate);
|
|
g_value_array_append(codec_struct, value);
|
|
|
|
value = g_new0(GValue, 1);
|
|
g_value_init(value, G_TYPE_UINT);
|
|
g_value_set_uint(value, codec->channels);
|
|
g_value_array_append(codec_struct, value);
|
|
|
|
/* XXX: do something about optional parameters */
|
|
value = g_new0(GValue, 1);
|
|
g_value_init(value, DBUS_TYPE_G_STRING_STRING_HASHTABLE);
|
|
g_value_set_boxed(value, g_hash_table_new(g_str_hash, g_str_equal));
|
|
g_value_array_append(codec_struct, value);
|
|
|
|
g_assert(codec_struct->n_values == 6);
|
|
|
|
g_ptr_array_add(*codecs, codec_struct);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
tox_session_native_candidates_prepared(FarsightStream *stream, gpointer user_data)
|
|
{
|
|
ToxSession *self = (ToxSession *)user_data;
|
|
const GList *candidates;
|
|
GValueArray *array;
|
|
|
|
candidates = farsight_stream_get_native_candidate_list(stream);
|
|
|
|
array = candidate_list_to_dbus_array(candidates);
|
|
|
|
g_debug("Sending signal NativeCandidatesPrepared!\n");
|
|
g_signal_emit(self, signals[NATIVE_CANDIDATES_PREPARED], 0, array);
|
|
}
|
|
|
|
static void
|
|
tox_session_new_native_candidate(FarsightStream *stream, gchar *candidate_id, ToxSession *self)
|
|
{
|
|
GList *candidate =
|
|
farsight_stream_get_native_candidate (stream, candidate_id);
|
|
FarsightTransportInfo *trans = candidate->data;
|
|
GValueArray *array;
|
|
|
|
g_debug ("tox_session_new_native_candidate: New native candidate"
|
|
" with %d components, the first being: "
|
|
"<id: %s, "
|
|
"component: %d, "
|
|
"ip: %s port: %d "
|
|
"proto: %d, "
|
|
"proto_subtype: %s, "
|
|
"proto_profile: %s, "
|
|
"preference: %f, "
|
|
"type: %d "
|
|
"username: %s password: %s>",
|
|
g_list_length(candidate),
|
|
trans->candidate_id, trans->component,
|
|
trans->ip, trans->port, trans->proto, trans->proto_subtype,
|
|
trans->proto_profile, trans->preference,
|
|
trans->type, trans->username, trans->password);
|
|
|
|
array = candidate_list_to_dbus_array(candidate);
|
|
g_debug("Sending signal NewNativeCandidate!\n");
|
|
g_signal_emit(self, signals[NEW_NATIVE_CANDIDATE], 0, array);
|
|
}
|
|
|
|
static GValueArray *
|
|
candidate_list_to_dbus_array(const GList *candidates)
|
|
{
|
|
GValueArray *array;
|
|
|
|
array = g_value_array_new(1);
|
|
|
|
for (; candidates; candidates = g_list_next(candidates)) {
|
|
GValueArray *candidate;
|
|
FarsightTransportInfo *info;
|
|
GValue value;
|
|
|
|
info = (FarsightTransportInfo*)candidates->data;
|
|
candidate = g_value_array_new(11);
|
|
|
|
memset(&value, 0, sizeof value);
|
|
|
|
g_value_init(&value, G_TYPE_STRING);
|
|
g_value_set_string(&value, info->candidate_id);
|
|
g_value_array_append(candidate, &value);
|
|
g_value_unset(&value);
|
|
|
|
g_value_init(&value, G_TYPE_UINT);
|
|
g_value_set_uint(&value, info->component);
|
|
g_value_array_append(candidate, &value);
|
|
g_value_unset(&value);
|
|
|
|
g_value_init(&value, G_TYPE_STRING);
|
|
g_value_set_string(&value, info->ip);
|
|
g_value_array_append(candidate, &value);
|
|
g_value_unset(&value);
|
|
|
|
g_value_init(&value, G_TYPE_UINT);
|
|
g_value_set_uint(&value, info->port);
|
|
g_value_array_append(candidate, &value);
|
|
g_value_unset(&value);
|
|
|
|
g_value_init(&value, G_TYPE_STRING);
|
|
switch(info->proto) {
|
|
case FARSIGHT_NETWORK_PROTOCOL_UDP:
|
|
g_value_set_static_string(&value, "udp");
|
|
break;
|
|
case FARSIGHT_NETWORK_PROTOCOL_TCP:
|
|
g_value_set_static_string(&value, "tcp");
|
|
break;
|
|
default:
|
|
g_error("Unknown protocol value %u\n", info->proto);
|
|
}
|
|
g_value_array_append(candidate, &value);
|
|
g_value_unset(&value);
|
|
|
|
g_value_init(&value, G_TYPE_STRING);
|
|
g_value_set_string(&value, info->proto_subtype);
|
|
g_value_array_append(candidate, &value);
|
|
g_value_unset(&value);
|
|
|
|
g_value_init(&value, G_TYPE_STRING);
|
|
g_value_set_string(&value, info->proto_profile);
|
|
g_value_array_append(candidate, &value);
|
|
g_value_unset(&value);
|
|
|
|
g_value_init(&value, G_TYPE_UINT);
|
|
g_value_set_uint(&value, (guint)(info->preference * 100));
|
|
g_value_array_append(candidate, &value);
|
|
g_value_unset(&value);
|
|
|
|
g_value_init(&value, G_TYPE_UINT);
|
|
switch(info->type) {
|
|
case FARSIGHT_CANDIDATE_TYPE_LOCAL:
|
|
g_value_set_uint(&value, 0);
|
|
break;
|
|
case FARSIGHT_CANDIDATE_TYPE_DERIVED:
|
|
g_value_set_uint(&value, 1);
|
|
break;
|
|
case FARSIGHT_CANDIDATE_TYPE_RELAY:
|
|
g_value_set_uint(&value, 2);
|
|
break;
|
|
default:
|
|
g_error("Unknown candidate type %u\n", info->proto);
|
|
}
|
|
g_value_array_append(candidate, &value);
|
|
g_value_unset(&value);
|
|
|
|
g_value_init(&value, G_TYPE_STRING);
|
|
g_value_set_string(&value, info->username);
|
|
g_value_array_append(candidate, &value);
|
|
g_value_unset(&value);
|
|
|
|
g_value_init(&value, G_TYPE_STRING);
|
|
g_value_set_string(&value, info->password);
|
|
g_value_array_append(candidate, &value);
|
|
g_value_unset(&value);
|
|
|
|
g_assert(candidate->n_values == 11);
|
|
|
|
g_value_init(&value, G_TYPE_VALUE_ARRAY);
|
|
/* apparently GValueArray is a "boxed" type */
|
|
g_value_set_boxed(&value, candidate);
|
|
g_value_array_append(array, &value);
|
|
g_value_unset(&value);
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
static void
|
|
tox_session_state_changed(FarsightStream *stream, gint state, gint direction, ToxSession *self)
|
|
{
|
|
g_signal_emit(self, signals[STATE_CHANGED], 0, state, direction);
|
|
}
|
|
|
|
static void
|
|
tox_session_new_active_candidate_pair(FarsightStream *stream, gchar *native_candidate_id, gchar *remote_candidate_id, ToxSession *self)
|
|
{
|
|
g_signal_emit(self, signals[NEW_ACTIVE_CANDIDATE_PAIR], 0, native_candidate_id, remote_candidate_id);
|
|
}
|