/*
* Farstream - Farstream libnice Transmitter agent object
*
* Copyright 2007-2008 Collabora Ltd.
* @author: Olivier Crete <olivier.crete@collabora.co.uk>
* Copyright 2007-2008 Nokia Corp.
*
* fs-nice-agent.c - A Farstream libnice transmitter agent object
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* SECTION:fs-nice-agent
* @short_description: A transmitter for agents for libnice
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <farstream/fs-conference.h>
#include "fs-nice-transmitter.h"
#include "fs-nice-agent.h"
#include <nice/nice.h>
#include <string.h>
#include <sys/types.h>
#define GST_CAT_DEFAULT fs_nice_transmitter_debug
/* Signals */
enum
{
LAST_SIGNAL
};
/* props */
enum
{
PROP_0,
PROP_COMPATIBILITY_MODE,
PROP_PREFERRED_LOCAL_CANDIDATES,
};
struct _FsNiceAgentPrivate
{
GMainContext *main_context;
GMainLoop *main_loop;
guint compatibility_mode;
GList *preferred_local_candidates;
GMutex mutex;
/* Everything below is protected by the mutex */
GThread *thread;
};
#define FS_NICE_AGENT_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), FS_TYPE_NICE_AGENT, \
FsNiceAgentPrivate))
#define FS_NICE_AGENT_LOCK(o) g_mutex_lock (&(o)->priv->mutex)
#define FS_NICE_AGENT_UNLOCK(o) g_mutex_unlock (&(o)->priv->mutex)
static void fs_nice_agent_class_init (
FsNiceAgentClass *klass);
static void fs_nice_agent_init (FsNiceAgent *self);
static void fs_nice_agent_dispose (GObject *object);
static void fs_nice_agent_finalize (GObject *object);
static void fs_nice_agent_stop_thread (FsNiceAgent *self);
static void fs_nice_agent_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void fs_nice_agent_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static GObjectClass *parent_class = NULL;
/*
* Lets register the plugin
*/
static GType type = 0;
GType
fs_nice_agent_get_type (void)
{
g_assert (type);
return type;
}
GType
fs_nice_agent_register_type (FsPlugin *module G_GNUC_UNUSED)
{
static const GTypeInfo info = {
sizeof (FsNiceAgentClass),
NULL,
NULL,
(GClassInitFunc) fs_nice_agent_class_init,
NULL,
NULL,
sizeof (FsNiceAgent),
0,
(GInstanceInitFunc) fs_nice_agent_init
};
type = g_type_register_static (G_TYPE_OBJECT, "FsNiceAgent", &info, 0);
return type;
}
static void
fs_nice_agent_class_init (FsNiceAgentClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
parent_class = g_type_class_peek_parent (klass);
gobject_class->set_property = fs_nice_agent_set_property;
gobject_class->get_property = fs_nice_agent_get_property;
gobject_class->dispose = fs_nice_agent_dispose;
gobject_class->finalize = fs_nice_agent_finalize;
g_type_class_add_private (klass, sizeof (FsNiceAgentPrivate));
g_object_class_install_property (gobject_class, PROP_COMPATIBILITY_MODE,
g_param_spec_uint (
"compatibility-mode",
"The compability-mode",
"The id of the stream according to libnice",
NICE_COMPATIBILITY_DRAFT19, NICE_COMPATIBILITY_LAST,
NICE_COMPATIBILITY_DRAFT19,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_PREFERRED_LOCAL_CANDIDATES,
g_param_spec_boxed ("preferred-local-candidates",
"The preferred candidates",
"A GList of FsCandidates",
FS_TYPE_CANDIDATE_LIST,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void
fs_nice_agent_init (FsNiceAgent *self)
{
/* member init */
self->priv = FS_NICE_AGENT_GET_PRIVATE (self);
g_mutex_init (&self->priv->mutex);
self->priv->main_context = g_main_context_new ();
self->priv->main_loop = g_main_loop_new (self->priv->main_context, FALSE);
self->priv->compatibility_mode = NICE_COMPATIBILITY_DRAFT19;
}
static void
fs_nice_agent_dispose (GObject *object)
{
FsNiceAgent *self = FS_NICE_AGENT (object);
fs_nice_agent_stop_thread (self);
if (self->agent)
g_object_unref (self->agent);
self->agent = NULL;
parent_class->dispose (object);
}
static void
fs_nice_agent_finalize (GObject *object)
{
FsNiceAgent *self = FS_NICE_AGENT (object);
if (self->priv->main_context)
g_main_context_unref (self->priv->main_context);
self->priv->main_context = NULL;
if (self->priv->main_loop)
g_main_loop_unref (self->priv->main_loop);
self->priv->main_loop = NULL;
fs_candidate_list_destroy (self->priv->preferred_local_candidates);
self->priv->preferred_local_candidates = NULL;
g_mutex_clear (&self->priv->mutex);
parent_class->finalize (object);
}
static void
fs_nice_agent_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
FsNiceAgent *self = FS_NICE_AGENT (object);
switch (prop_id)
{
case PROP_COMPATIBILITY_MODE:
self->priv->compatibility_mode = g_value_get_uint (value);
break;
case PROP_PREFERRED_LOCAL_CANDIDATES:
self->priv->preferred_local_candidates = g_value_dup_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
fs_nice_agent_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
FsNiceAgent *self = FS_NICE_AGENT (object);
switch (prop_id)
{
case PROP_COMPATIBILITY_MODE:
g_value_set_uint (value, self->priv->compatibility_mode);
break;
case PROP_PREFERRED_LOCAL_CANDIDATES:
g_value_set_boxed (value, self->priv->preferred_local_candidates);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
thread_unlock_idler (gpointer data)
{
FsNiceAgent *self = FS_NICE_AGENT (data);
g_main_loop_quit (self->priv->main_loop);
return TRUE;
}
static void
fs_nice_agent_stop_thread (FsNiceAgent *self)
{
GSource *idle_source;
g_main_loop_quit (self->priv->main_loop);
FS_NICE_AGENT_LOCK(self);
if (self->priv->thread == NULL ||
self->priv->thread == g_thread_self ())
{
FS_NICE_AGENT_UNLOCK (self);
return;
}
FS_NICE_AGENT_UNLOCK (self);
idle_source = g_idle_source_new ();
g_source_set_priority (idle_source, G_PRIORITY_HIGH);
g_source_set_callback (idle_source, thread_unlock_idler, self, NULL);
g_source_attach (idle_source, self->priv->main_context);
g_thread_join (self->priv->thread);
g_source_destroy (idle_source);
g_source_unref (idle_source);
FS_NICE_AGENT_LOCK (self);
self->priv->thread = NULL;
FS_NICE_AGENT_UNLOCK (self);
}
static gpointer
fs_nice_agent_main_thread (gpointer data)
{
FsNiceAgent *self = FS_NICE_AGENT (data);
g_main_loop_run (self->priv->main_loop);
return NULL;
}
static gboolean
fs_nice_agent_init_agent (FsNiceAgent *self, GError **error)
{
GList *item;
gboolean set = FALSE;
for (item = self->priv->preferred_local_candidates;
item;
item = g_list_next (item))
{
FsCandidate *cand = item->data;
NiceAddress *addr = nice_address_new ();
if (nice_address_set_from_string (addr, cand->ip))
{
if (!nice_agent_add_local_address (self->agent, addr))
{
g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS,
"Unable to set preferred local candidate: %s", cand->ip);
return FALSE;
}
set = TRUE;
}
else
{
g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS,
"Invalid local address passed: %s", cand->ip);
nice_address_free (addr);
return FALSE;
}
nice_address_free (addr);
}
if (!set)
{
GList *addresses = nice_interfaces_get_local_ips (FALSE);
for (item = addresses;
item;
item = g_list_next (item))
{
NiceAddress *addr = nice_address_new ();
if (nice_address_set_from_string (addr, item->data))
{
if (!nice_agent_add_local_address (self->agent,
addr))
{
g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS,
"Unable to set preferred local candidate");
return FALSE;
}
}
else
{
g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS,
"Invalid local address passed");
nice_address_free (addr);
return FALSE;
}
nice_address_free (addr);
}
g_list_foreach (addresses, (GFunc) g_free, NULL);
g_list_free (addresses);
}
return TRUE;
}
FsNiceAgent *
fs_nice_agent_new (guint compatibility_mode,
GList *preferred_local_candidates,
gboolean reliable,
GError **error)
{
FsNiceAgent *self = NULL;
self = g_object_new (FS_TYPE_NICE_AGENT,
"compatibility-mode", compatibility_mode,
"preferred-local-candidates", preferred_local_candidates,
NULL);
if (reliable)
self->agent = nice_agent_new_reliable (self->priv->main_context,
self->priv->compatibility_mode);
else
self->agent = nice_agent_new (self->priv->main_context,
self->priv->compatibility_mode);
if (self->agent == NULL)
{
g_set_error (error, FS_ERROR, FS_ERROR_INTERNAL,
"Could not make nice agent");
g_object_unref (self);
return NULL;
}
if (!fs_nice_agent_init_agent (self, error))
{
g_object_unref (self);
return NULL;
}
FS_NICE_AGENT_LOCK (self);
self->priv->thread = g_thread_try_new ("libnice agent thread",
fs_nice_agent_main_thread, self, error);
if (!self->priv->thread)
{
FS_NICE_AGENT_UNLOCK (self);
g_object_unref (self);
return NULL;
}
FS_NICE_AGENT_UNLOCK (self);
return self;
}
void
fs_nice_agent_add_idle (FsNiceAgent *agent, GSourceFunc func,
gpointer data, GDestroyNotify destroy_notify)
{
GSource *source;
g_return_if_fail (func != NULL);
source = g_idle_source_new ();
g_source_set_priority (source, G_PRIORITY_HIGH);
g_source_set_callback (source, func, data, destroy_notify);
g_source_attach (source, agent->priv->main_context);
g_source_unref (source);
}