<commit_before>/*
 * Farsight2 - Farsight libnice Stream Transmitter
 *
 * Copyright 2007 Collabora Ltd.
 *  @author: <NAME> <<EMAIL>>
 * Copyright 2007 Nokia Corp.
 *
 * fs-nice-stream-transmitter.c - A Farsight libnice stream transmitter
 *
 * 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-stream-transmitter
 * @short_description: A stream transmitter object for ICE using libnice
 * @see_also: fs-rawudp-stream-transmitter
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "fs-nice-stream-transmitter.h"
#include "fs-nice-transmitter.h"
#include "fs-nice-thread.h"

#include <gst/farsight/fs-conference-iface.h>
#include <gst/farsight/fs-interfaces.h>

#include <gst/gst.h>

#include <string.h>
#include <sys/types.h>

#include <udp-bsd.h>

#define GST_CAT_DEFAULT fs_nice_transmitter_debug

/* Signals */
enum
{
  LAST_SIGNAL
};

/* props */
enum
{
  PROP_0,
  PROP_SENDING,
  PROP_PREFERRED_LOCAL_CANDIDATES,
  PROP_STATE,
  PROP_STUN_IP,
  PROP_STUN_PORT,
  PROP_TURN_IP,
  PROP_TURN_PORT,
  PROP_CONTROLLING_MODE,
  PROP_STREAM_ID,
  PROP_COMPATIBILITY_MODE
};

struct _FsNiceStreamTransmitterPrivate
{
  FsNiceTransmitter *transmitter;

  NiceAgent *agent;

  guint stream_id;

  gboolean sending;

  gchar *stun_ip;
  guint stun_port;
  gchar *turn_ip;
  guint turn_port;

  gboolean controlling_mode;

  guint compatibility_mode;

  GMutex *mutex;

  GList *preferred_local_candidates;

  gulong state_changed_handler_id;
  gulong gathering_done_handler_id;
  gulong new_selected_pair_handler_id;
  gulong new_candidate_handler_id;

  /* Everything below is protected by the mutex */

  gboolean gathered;

  gboolean candidates_added;

  GList *candidates_to_set;

  NiceGstStream *gststream;

  FsStreamState state;
  FsStreamState *component_states;
};

#define FS_NICE_STREAM_TRANSMITTER_GET_PRIVATE(o)  \
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), FS_TYPE_NICE_STREAM_TRANSMITTER, \
                                FsNiceStreamTransmitterPrivate))

#define FS_NICE_STREAM_TRANSMITTER_LOCK(o)   g_mutex_lock ((o)->priv->mutex)
#define FS_NICE_STREAM_TRANSMITTER_UNLOCK(o) g_mutex_unlock ((o)->priv->mutex)


static void fs_nice_stream_transmitter_class_init (FsNiceStreamTransmitterClass *klass);
static void fs_nice_stream_transmitter_init (FsNiceStreamTransmitter *self);
static void fs_nice_stream_transmitter_dispose (GObject *object);
static void fs_nice_stream_transmitter_finalize (GObject *object);

static void fs_nice_stream_transmitter_get_property (GObject *object,
                                                guint prop_id,
                                                GValue *value,
                                                GParamSpec *pspec);
static void fs_nice_stream_transmitter_set_property (GObject *object,
                                                guint prop_id,
                                                const GValue *value,
                                                GParamSpec *pspec);

static gboolean fs_nice_stream_transmitter_add_remote_candidate (
    FsStreamTransmitter *streamtransmitter, FsCandidate *candidate,
    GError **error);
static void fs_nice_stream_transmitter_remote_candidates_added (
    FsStreamTransmitter *streamtransmitter);
static gboolean fs_nice_stream_transmitter_select_candidate_pair (
    FsStreamTransmitter *streamtransmitter,
    const gchar *local_foundation,
    const gchar *remote_foundation,
    GError **error);
static gboolean fs_nice_stream_transmitter_gather_local_candidates (
    FsStreamTransmitter *streamtransmitter,
    GError **error);

static void agent_state_changed (NiceAgent *agent,
    guint stream_id,
    guint component_id,
    guint state,
    gpointer user_data);
static void agent_gathering_done (NiceAgent *agent, gpointer user_data);
static void agent_new_selected_pair (NiceAgent *agent,
    guint stream_id,
    guint component_id,
    const gchar *lfoundation,
    const gchar *rfoundation,
    gpointer user_data);
static void agent_new_candidate (NiceAgent *agent,
    guint stream_id,
    guint component_id,
    const gchar *foundation,
    gpointer user_data);


static GObjectClass *parent_class = NULL;
// static guint signals[LAST_SIGNAL] = { 0 };

static GType type = 0;

/*
 * This is global because its not a ref-counted object
 */
static NiceUDPSocketFactory udpfactory;


GType
fs_nice_stream_transmitter_get_type (void)
{
  return type;
}

GType
fs_nice_stream_transmitter_register_type (FsPlugin *module)
{
  static const GTypeInfo info = {
    sizeof (FsNiceStreamTransmitterClass),
    NULL,
    NULL,
    (GClassInitFunc) fs_nice_stream_transmitter_class_init,
    NULL,
    NULL,
    sizeof (FsNiceStreamTransmitter),
    0,
    (GInstanceInitFunc) fs_nice_stream_transmitter_init
  };

  type = g_type_module_register_type (G_TYPE_MODULE (module),
    FS_TYPE_STREAM_TRANSMITTER, "FsNiceStreamTransmitter", &info, 0);

  return type;
}

static void
fs_nice_stream_transmitter_class_init (FsNiceStreamTransmitterClass *klass)
{
  GObjectClass *gobject_class = (GObjectClass *) klass;
  FsStreamTransmitterClass *streamtransmitterclass =
    FS_STREAM_TRANSMITTER_CLASS (klass);

  parent_class = g_type_class_peek_parent (klass);

  gobject_class->set_property = fs_nice_stream_transmitter_set_property;
  gobject_class->get_property = fs_nice_stream_transmitter_get_property;
  gobject_class->dispose = fs_nice_stream_transmitter_dispose;
  gobject_class->finalize = fs_nice_stream_transmitter_finalize;

  streamtransmitterclass->add_remote_candidate =
    fs_nice_stream_transmitter_add_remote_candidate;
  streamtransmitterclass->remote_candidates_added =
    fs_nice_stream_transmitter_remote_candidates_added;
  streamtransmitterclass->select_candidate_pair =
    fs_nice_stream_transmitter_select_candidate_pair;
  streamtransmitterclass->gather_local_candidates =
    fs_nice_stream_transmitter_gather_local_candidates;

  g_type_class_add_private (klass, sizeof (FsNiceStreamTransmitterPrivate));

  g_object_class_override_property (gobject_class, PROP_SENDING, "sending");
  g_object_class_override_property (gobject_class,
      PROP_PREFERRED_LOCAL_CANDIDATES, "preferred-local-candidates");
  g_object_class_override_property (gobject_class,
      PROP_STATE, "state");

  g_object_class_install_property (gobject_class, PROP_STUN_IP,
      g_param_spec_string (
          "stun-ip",
          "STUN server",
          "The STUN server used to obtain server-reflexive candidates",
          NULL,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

  g_object_class_install_property (gobject_class, PROP_STUN_PORT,
      g_param_spec_uint (
          "stun-port",
          "STUN server port",
          "The STUN server used to obtain server-reflexive candidates",
          1, 65536,
          3478,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_TURN_IP,
      g_param_spec_string (
          "turn-ip",
          "TURN server",
          "The TURN server used to obtain relay candidates",
          NULL,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_TURN_PORT,
      g_param_spec_uint (
          "turn-port",
          "TURN server port",
          "The TURN server used to obtain relay candidates",
          1, 65536,
          3478,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_CONTROLLING_MODE,
      g_param_spec_boolean (
          "controlling-mode",
          "ICE controlling mode",
          "Whether the agent is in controlling mode",
          TRUE,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_STREAM_ID,
      g_param_spec_uint (
          "stream-id",
          "The id of the stream",
          "The id of the stream according to libnice",
          0, G_MAXINT,
          0,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

  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_ID19, NICE_COMPATIBILITY_LAST,
          NICE_COMPATIBILITY_ID19,
          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));

  nice_udp_bsd_socket_factory_init (&udpfactory);

}

static void
fs_nice_stream_transmitter_init (FsNiceStreamTransmitter *self)
{
  /* member init */
  self->priv = FS_NICE_STREAM_TRANSMITTER_GET_PRIVATE (self);

  self->priv->sending = TRUE;
  self->priv->state = FS_STREAM_STATE_DISCONNECTED;
  self->priv->mutex = g_mutex_new ();

  self->priv->controlling_mode = TRUE;
}

static void
fs_nice_stream_transmitter_dispose (GObject *object)
{
  FsNiceStreamTransmitter *self = FS_NICE_STREAM_TRANSMITTER (object);

  FS_NICE_STREAM_TRANSMITTER_LOCK (self);
  if (self->priv->gststream)
    fs_nice_transmitter_free_gst_stream (self->priv->transmitter,
        self->priv->gststream);
  self->priv->gststream = NULL;

  if (self->priv->stream_id)
    nice_agent_remove_stream (self->priv->agent,
        self->priv->stream_id);
  self->priv->stream_id = 0;

  if (self->priv->state_changed_handler_id)
    g_signal_handler_disconnect (self->priv->agent,
        self->priv->state_changed_handler_id);
  self->priv->state_changed_handler_id = 0;

  if (self->priv->gathering_done_handler_id)
    g_signal_handler_disconnect (self->priv->agent,
        self->priv->gathering_done_handler_id);
  self->priv->gathering_done_handler_id = 0;

  if (self->priv->new_selected_pair_handler_id)
    g_signal_handler_disconnect (self->priv->agent,
        self->priv->new_selected_pair_handler_id);
  self->priv->new_selected_pair_handler_id = 0;

  if (self->priv->new_candidate_handler_id)
    g_signal_handler_disconnect (self->priv->agent,
        self->priv->new_candidate_handler_id);
  self->priv->new_candidate_handler_id = 0;

  if (self->priv->agent)
  {
    g_object_unref (self->priv->agent);
    self->priv->agent = NULL;
  }
  FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);

  if (self->priv->transmitter)
  {
    g_object_unref (self->priv->transmitter);
    self->priv->transmitter = NULL;
  }

  parent_class->dispose (object);
}

static void
fs_nice_stream_transmitter_finalize (GObject *object)
{
  FsNiceStreamTransmitter *self = FS_NICE_STREAM_TRANSMITTER (object);

  fs_candidate_list_destroy (self->priv->preferred_local_candidates);

  g_free (self->priv->component_states);

  g_free (self->priv->stun_ip);
  g_free (self->priv->turn_ip);

  g_mutex_free (self->priv->mutex);

  parent_class->finalize (object);
}

static void
fs_nice_stream_transmitter_get_property (GObject *object,
                                           guint prop_id,
                                           GValue *value,
                                           GParamSpec *pspec)
{
  FsNiceStreamTransmitter *self = FS_NICE_STREAM_TRANSMITTER (object);

  switch (prop_id)
  {
    case PROP_SENDING:
      g_value_set_boolean (value, self->priv->sending);
      break;
    case PROP_PREFERRED_LOCAL_CANDIDATES:
      g_value_set_boxed (value, self->priv->preferred_local_candidates);
      break;
    case PROP_STATE:
      FS_NICE_STREAM_TRANSMITTER_LOCK (self);
      g_value_set_enum (value, self->priv->state);
      FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);
      break;
    case PROP_STUN_IP:
      if (self->priv->agent)
        g_object_get_property (G_OBJECT (self->priv->agent),
            g_param_spec_get_name (pspec), value);
      else
        g_value_set_string (value, self->priv->stun_ip);
      break;
    case PROP_STUN_PORT:
      if (self->priv->agent)
        g_object_get_property (G_OBJECT (self->priv->agent),
            g_param_spec_get_name (pspec), value);
      else
        g_value_set_uint (value, self->priv->stun_port);
      break;
    case PROP_TURN_IP:
      if (self->priv->agent)
        g_object_get_property (G_OBJECT (self->priv->agent),
            g_param_spec_get_name (pspec), value);
      else
        g_value_set_string (value, self->priv->turn_ip);
      break;
    case PROP_TURN_PORT:
      if (self->priv->agent)
        g_object_get_property (G_OBJECT (self->priv->agent),
            g_param_spec_get_name (pspec), value);
      else
        g_value_set_uint (value, self->priv->turn_port);

      break;
    case PROP_CONTROLLING_MODE:
      if (self->priv->agent)
        g_object_get_property (G_OBJECT (self->priv->agent),
            g_param_spec_get_name (pspec), value);
      else
        g_value_set_boolean (value, self->priv->controlling_mode);
      break;
    case PROP_STREAM_ID:
      FS_NICE_STREAM_TRANSMITTER_LOCK (self);
      g_value_set_uint (value, self->priv->stream_id);
      FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);
      break;
    case PROP_COMPATIBILITY_MODE:
      g_value_set_uint (value, self->priv->compatibility_mode);
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
fs_nice_stream_transmitter_set_property (GObject *object,
                                           guint prop_id,
                                           const GValue *value,
                                           GParamSpec *pspec)
{
  FsNiceStreamTransmitter *self = FS_NICE_STREAM_TRANSMITTER (object);

  switch (prop_id)
  {
    case PROP_SENDING:
      self->priv->sending = g_value_get_boolean (value);
      break;
    case PROP_PREFERRED_LOCAL_CANDIDATES:
      self->priv->preferred_local_candidates = g_value_dup_boxed (value);
      break;
    case PROP_STUN_IP:
      self->priv->stun_ip = g_value_dup_string (value);
      break;
    case PROP_STUN_PORT:
      self->priv->stun_port = g_value_get_uint (value);
      break;
    case PROP_TURN_IP:
      self->priv->turn_ip = g_value_dup_string (value);
      break;
    case PROP_TURN_PORT:
      self->priv->turn_port = g_value_get_uint (value);
      break;
    case PROP_CONTROLLING_MODE:
      self->priv->controlling_mode = g_value_get_boolean (value);
      if (self->priv->transmitter && self->priv->agent)
        g_object_set_property (G_OBJECT (self->priv->agent),
            g_param_spec_get_name (pspec), value);
      break;
    case PROP_COMPATIBILITY_MODE:
      self->priv->compatibility_mode = g_value_get_uint (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}


static NiceCandidateType
fs_candidate_type_to_nice_candidate_type (FsCandidateType type)
{
  switch (type)
  {
    case FS_CANDIDATE_TYPE_HOST:
      return NICE_CANDIDATE_TYPE_HOST;
    case FS_CANDIDATE_TYPE_SRFLX:
      return NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
    case FS_CANDIDATE_TYPE_PRFLX:
      return NICE_CANDIDATE_TYPE_PEER_REFLEXIVE;
    case FS_CANDIDATE_TYPE_RELAY:
      return NICE_CANDIDATE_TYPE_RELAYED;
    default:
      GST_WARNING ("Invalid candidate type %d, defaulting to type host", type);
      return NICE_CANDIDATE_TYPE_HOST;
  }
}

static NiceCandidateTransport
fs_network_protocol_to_nice_candidate_protocol (FsNetworkProtocol proto)
{
  switch (proto)
  {
    case FS_NETWORK_PROTOCOL_UDP:
      return NICE_CANDIDATE_TRANSPORT_UDP;
    default:
      GST_WARNING ("Invalid Fs network protocol type %u", proto);
      return NICE_CANDIDATE_TRANSPORT_UDP;
  }
}

static NiceCandidate *
fs_candidate_to_nice_candidate (FsNiceStreamTransmitter *self,
    FsCandidate *candidate)
{
  NiceCandidate *nc = nice_candidate_new (
      fs_candidate_type_to_nice_candidate_type (candidate->type));

  nc->transport =
    fs_network_protocol_to_nice_candidate_protocol (candidate->proto);
  nc->priority = candidate->priority;
  nc->stream_id = self->priv->stream_id;
  nc->component_id = candidate->component_id;
  strncpy (nc->foundation, candidate->foundation,
      NICE_CANDIDATE_MAX_FOUNDATION);

  nc->username = g_strdup(candidate->username);
  nc->password = g_strdup(candidate->password);


  g_warning ("%s %s", nc->username, nc->password);

  if (candidate->ip == NULL || candidate->port == 0)
    goto error;
  if (!nice_address_set_from_string (&nc->addr, candidate->ip))
    goto error;
  nice_address_set_port (&nc->addr, candidate->port);

  if (candidate->base_ip && candidate->base_port)
  {
    if (!nice_address_set_from_string (&nc->base_addr, candidate->base_ip))
      goto error;
    nice_address_set_port (&nc->base_addr, candidate->base_port);
  }

  return nc;

 error:
  g_free (nc);
  return NULL;
}

/**
 * fs_nice_stream_transmitter_add_remote_candidate
 * @streamtransmitter: a #FsStreamTransmitter
 * @candidate: a remote #FsCandidate to add
 * @error: location of a #GError, or NULL if no error occured
 *
 * This function is used to add remote candidates to the transmitter
 *
 * Returns: TRUE of the candidate could be added, FALSE if it couldnt
 *   (and the #GError will be set)
 */

static gboolean
fs_nice_stream_transmitter_add_remote_candidate (
    FsStreamTransmitter *streamtransmitter, FsCandidate *candidate,
    GError **error)
{
  FsNiceStreamTransmitter *self =
    FS_NICE_STREAM_TRANSMITTER (streamtransmitter);
  GSList *list = NULL;
  NiceCandidate *cand = NULL;

  FS_NICE_STREAM_TRANSMITTER_LOCK (self);
  if (!self->priv->candidates_added)
  {
    self->priv->candidates_to_set = g_list_append (
        self->priv->candidates_to_set,
        fs_candidate_copy (candidate));
    FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);
    return TRUE;
  }
  FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);

  cand = fs_candidate_to_nice_candidate (self, candidate);

  if (!cand)
  {
    g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS,
        "Invalid candidate added");
    goto error;
  }

  list = g_slist_prepend (NULL, cand);

  nice_agent_set_remote_candidates (self->priv->agent,
      self->priv->stream_id, candidate->component_id, list);

  g_slist_foreach (list, (GFunc)nice_candidate_free, NULL);
  g_slist_free (list);

  return TRUE;

 error:

  FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);
  return FALSE;
}


static void
fs_nice_stream_transmitter_remote_candidates_added (
    FsStreamTransmitter *streamtransmitter)
{
  FsNiceStreamTransmitter *self =
    FS_NICE_STREAM_TRANSMITTER (streamtransmitter);
  GList *candidates = NULL, *item;
  GSList *nice_candidates = NULL;
  gint c;

  FS_NICE_STREAM_TRANSMITTER_LOCK (self);
  if (self->priv->candidates_added)
  {
    FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);
    GST_LOG ("remote_candidates_added already called, ignoring");
    return;
  }

  self->priv->candidates_added = TRUE;
  candidates = self->priv->candidates_to_set;
  self->priv->candidates_to_set = NULL;
  FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);

  /*
  if (candidates)
  {
    FsCandidate *cand = candidates->data;
    nice_agent_set_remote_credentials (self->priv->agent,
        self->priv->stream_id, cand->username, cand->password);
  }
  else
  {
    GST_DEBUG ("Candidates added called before any candidate set,"
        " assuming we're in ice-6 with dribble, so every candidate has"
        " its own password");
  }
  */

  for (c = 1; c <= self->priv->transmitter->components; c++)
  {
    for (item = candidates;
         item;
         item = g_list_next (item))
    {
      FsCandidate *candidate = item->data;

      if (candidate->component_id == c)
      {
        NiceCandidate *nc = fs_candidate_to_nice_candidate (self, candidate);

        if (!nc)
          goto error;

        nice_candidates = g_slist_append (nice_candidates, nc);
      }
    }

    nice_agent_set_remote_candidates (self->priv->agent,
        self->priv->stream_id, c, nice_candidates);

    g_slist_foreach (nice_candidates, (GFunc)nice_candidate_free, NULL);
    g_slist_free (nice_candidates);
    nice_candidates = NULL;
  }

  fs_candidate_list_destroy (candidates);

  return;
 error:
  fs_stream_transmitter_emit_error (FS_STREAM_TRANSMITTER (self),
      FS_ERROR_INVALID_ARGUMENTS,
      "Invalid remote candidate passed",
      "Remote candidate passed in previous add_remote_candidate() call invalid");
  g_slist_foreach (nice_candidates, (GFunc) nice_candidate_free, NULL);
  g_slist_free (nice_candidates);
  fs_candidate_list_destroy (candidates);
}

static gboolean
fs_nice_stream_transmitter_select_candidate_pair (
    FsStreamTransmitter *streamtransmitter,
    const gchar *local_foundation,
    const gchar *remote_foundation,
    GError **error)
{
  FsNiceStreamTransmitter *self =
    FS_NICE_STREAM_TRANSMITTER (streamtransmitter);
  gint c;
  gboolean res = TRUE;

  if (self->priv->stream_id == 0)
  {
    g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS,
        "Can not call this function before gathering local candidates");
    return FALSE;
  }

  for (c = 1; c <= self->priv->transmitter->components; c++)
    if (!nice_agent_set_selected_pair (self->priv->agent,
            self->priv->stream_id, c, local_foundation, remote_foundation))
      res = FALSE;

  if (!res)
    g_set_error (error, FS_ERROR, FS_ERROR_INTERNAL,
        "Unknown error while selecting pairs");

  return res;
}

static FsCandidateType
nice_candidate_type_to_fs_candidate_type (NiceCandidateType type)
{
  switch (type)
  {
    case NICE_CANDIDATE_TYPE_HOST:
      return FS_CANDIDATE_TYPE_HOST;
    case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
      return FS_CANDIDATE_TYPE_SRFLX;
    case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
      return FS_CANDIDATE_TYPE_PRFLX;
    case NICE_CANDIDATE_TYPE_RELAYED:
      return FS_CANDIDATE_TYPE_RELAY;
    default:
      GST_WARNING ("Invalid candidate type %d, defaulting to type host", type);
      return FS_CANDIDATE_TYPE_HOST;
  }
}

static FsNetworkProtocol
nice_candidate_transport_to_fs_network_protocol (NiceCandidateTransport trans)
{
  switch (trans)
  {
    case NICE_CANDIDATE_TRANSPORT_UDP:
      return FS_NETWORK_PROTOCOL_UDP;
    default:
      GST_WARNING ("Invalid Nice network transport type %u", trans);
      return FS_NETWORK_PROTOCOL_UDP;
  }
}

static FsCandidate *
nice_candidate_to_fs_candidate (NiceAgent *agent, NiceCandidate *nicecandidate)
{
  FsCandidate *fscandidate;
  gchar *ipaddr = g_malloc (INET_ADDRSTRLEN);

  nice_address_to_string (&nicecandidate->addr, ipaddr);

  fscandidate = fs_candidate_new (
      nicecandidate->foundation,
      nicecandidate->component_id,
      nice_candidate_type_to_fs_candidate_type (nicecandidate->type),
      nice_candidate_transport_to_fs_network_protocol (
          nicecandidate->transport),
      ipaddr,
      nice_address_get_port (&nicecandidate->addr));

  nice_address_to_string (&nicecandidate->base_addr, ipaddr);

  fscandidate->base_ip = ipaddr;
  fscandidate->base_port = nice_address_get_port (&nicecandidate->base_addr);

  fscandidate->username = g_strdup (nicecandidate->username);
  fscandidate->password = g_strdup (nicecandidate->password);
  fscandidate->priority = nicecandidate->priority;

  if (fscandidate->username == NULL && fscandidate->password == NULL)
  {
    const gchar *username = NULL, *password = NULL;
    nice_agent_get_local_credentials (agent, nicecandidate->stream_id,
        &username, &password);
    fscandidate->username = g_strdup (username);
    fscandidate->password = g_strdup (password);

    if (username == NULL || password == NULL)
    {
      GST_WARNING ("The stream has no credentials??");
    }
  }

<commit_msg>Only try to add base_addr if it is valid<commit_after>/*
 * Farsight2 - Farsight libnice Stream Transmitter
 *
 * Copyright 2007 Collabora Ltd.
 *  @author: <NAME> <<EMAIL>>
 * Copyright 2007 Nokia Corp.
 *
 * fs-nice-stream-transmitter.c - A Farsight libnice stream transmitter
 *
 * 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-stream-transmitter
 * @short_description: A stream transmitter object for ICE using libnice
 * @see_also: fs-rawudp-stream-transmitter
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "fs-nice-stream-transmitter.h"
#include "fs-nice-transmitter.h"
#include "fs-nice-thread.h"

#include <gst/farsight/fs-conference-iface.h>
#include <gst/farsight/fs-interfaces.h>

#include <gst/gst.h>

#include <string.h>
#include <sys/types.h>

#include <udp-bsd.h>

#define GST_CAT_DEFAULT fs_nice_transmitter_debug

/* Signals */
enum
{
  LAST_SIGNAL
};

/* props */
enum
{
  PROP_0,
  PROP_SENDING,
  PROP_PREFERRED_LOCAL_CANDIDATES,
  PROP_STATE,
  PROP_STUN_IP,
  PROP_STUN_PORT,
  PROP_TURN_IP,
  PROP_TURN_PORT,
  PROP_CONTROLLING_MODE,
  PROP_STREAM_ID,
  PROP_COMPATIBILITY_MODE
};

struct _FsNiceStreamTransmitterPrivate
{
  FsNiceTransmitter *transmitter;

  NiceAgent *agent;

  guint stream_id;

  gboolean sending;

  gchar *stun_ip;
  guint stun_port;
  gchar *turn_ip;
  guint turn_port;

  gboolean controlling_mode;

  guint compatibility_mode;

  GMutex *mutex;

  GList *preferred_local_candidates;

  gulong state_changed_handler_id;
  gulong gathering_done_handler_id;
  gulong new_selected_pair_handler_id;
  gulong new_candidate_handler_id;

  /* Everything below is protected by the mutex */

  gboolean gathered;

  gboolean candidates_added;

  GList *candidates_to_set;

  NiceGstStream *gststream;

  FsStreamState state;
  FsStreamState *component_states;
};

#define FS_NICE_STREAM_TRANSMITTER_GET_PRIVATE(o)  \
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), FS_TYPE_NICE_STREAM_TRANSMITTER, \
                                FsNiceStreamTransmitterPrivate))

#define FS_NICE_STREAM_TRANSMITTER_LOCK(o)   g_mutex_lock ((o)->priv->mutex)
#define FS_NICE_STREAM_TRANSMITTER_UNLOCK(o) g_mutex_unlock ((o)->priv->mutex)


static void fs_nice_stream_transmitter_class_init (FsNiceStreamTransmitterClass *klass);
static void fs_nice_stream_transmitter_init (FsNiceStreamTransmitter *self);
static void fs_nice_stream_transmitter_dispose (GObject *object);
static void fs_nice_stream_transmitter_finalize (GObject *object);

static void fs_nice_stream_transmitter_get_property (GObject *object,
                                                guint prop_id,
                                                GValue *value,
                                                GParamSpec *pspec);
static void fs_nice_stream_transmitter_set_property (GObject *object,
                                                guint prop_id,
                                                const GValue *value,
                                                GParamSpec *pspec);

static gboolean fs_nice_stream_transmitter_add_remote_candidate (
    FsStreamTransmitter *streamtransmitter, FsCandidate *candidate,
    GError **error);
static void fs_nice_stream_transmitter_remote_candidates_added (
    FsStreamTransmitter *streamtransmitter);
static gboolean fs_nice_stream_transmitter_select_candidate_pair (
    FsStreamTransmitter *streamtransmitter,
    const gchar *local_foundation,
    const gchar *remote_foundation,
    GError **error);
static gboolean fs_nice_stream_transmitter_gather_local_candidates (
    FsStreamTransmitter *streamtransmitter,
    GError **error);

static void agent_state_changed (NiceAgent *agent,
    guint stream_id,
    guint component_id,
    guint state,
    gpointer user_data);
static void agent_gathering_done (NiceAgent *agent, gpointer user_data);
static void agent_new_selected_pair (NiceAgent *agent,
    guint stream_id,
    guint component_id,
    const gchar *lfoundation,
    const gchar *rfoundation,
    gpointer user_data);
static void agent_new_candidate (NiceAgent *agent,
    guint stream_id,
    guint component_id,
    const gchar *foundation,
    gpointer user_data);


static GObjectClass *parent_class = NULL;
// static guint signals[LAST_SIGNAL] = { 0 };

static GType type = 0;

/*
 * This is global because its not a ref-counted object
 */
static NiceUDPSocketFactory udpfactory;


GType
fs_nice_stream_transmitter_get_type (void)
{
  return type;
}

GType
fs_nice_stream_transmitter_register_type (FsPlugin *module)
{
  static const GTypeInfo info = {
    sizeof (FsNiceStreamTransmitterClass),
    NULL,
    NULL,
    (GClassInitFunc) fs_nice_stream_transmitter_class_init,
    NULL,
    NULL,
    sizeof (FsNiceStreamTransmitter),
    0,
    (GInstanceInitFunc) fs_nice_stream_transmitter_init
  };

  type = g_type_module_register_type (G_TYPE_MODULE (module),
    FS_TYPE_STREAM_TRANSMITTER, "FsNiceStreamTransmitter", &info, 0);

  return type;
}

static void
fs_nice_stream_transmitter_class_init (FsNiceStreamTransmitterClass *klass)
{
  GObjectClass *gobject_class = (GObjectClass *) klass;
  FsStreamTransmitterClass *streamtransmitterclass =
    FS_STREAM_TRANSMITTER_CLASS (klass);

  parent_class = g_type_class_peek_parent (klass);

  gobject_class->set_property = fs_nice_stream_transmitter_set_property;
  gobject_class->get_property = fs_nice_stream_transmitter_get_property;
  gobject_class->dispose = fs_nice_stream_transmitter_dispose;
  gobject_class->finalize = fs_nice_stream_transmitter_finalize;

  streamtransmitterclass->add_remote_candidate =
    fs_nice_stream_transmitter_add_remote_candidate;
  streamtransmitterclass->remote_candidates_added =
    fs_nice_stream_transmitter_remote_candidates_added;
  streamtransmitterclass->select_candidate_pair =
    fs_nice_stream_transmitter_select_candidate_pair;
  streamtransmitterclass->gather_local_candidates =
    fs_nice_stream_transmitter_gather_local_candidates;

  g_type_class_add_private (klass, sizeof (FsNiceStreamTransmitterPrivate));

  g_object_class_override_property (gobject_class, PROP_SENDING, "sending");
  g_object_class_override_property (gobject_class,
      PROP_PREFERRED_LOCAL_CANDIDATES, "preferred-local-candidates");
  g_object_class_override_property (gobject_class,
      PROP_STATE, "state");

  g_object_class_install_property (gobject_class, PROP_STUN_IP,
      g_param_spec_string (
          "stun-ip",
          "STUN server",
          "The STUN server used to obtain server-reflexive candidates",
          NULL,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

  g_object_class_install_property (gobject_class, PROP_STUN_PORT,
      g_param_spec_uint (
          "stun-port",
          "STUN server port",
          "The STUN server used to obtain server-reflexive candidates",
          1, 65536,
          3478,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_TURN_IP,
      g_param_spec_string (
          "turn-ip",
          "TURN server",
          "The TURN server used to obtain relay candidates",
          NULL,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_TURN_PORT,
      g_param_spec_uint (
          "turn-port",
          "TURN server port",
          "The TURN server used to obtain relay candidates",
          1, 65536,
          3478,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_CONTROLLING_MODE,
      g_param_spec_boolean (
          "controlling-mode",
          "ICE controlling mode",
          "Whether the agent is in controlling mode",
          TRUE,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_STREAM_ID,
      g_param_spec_uint (
          "stream-id",
          "The id of the stream",
          "The id of the stream according to libnice",
          0, G_MAXINT,
          0,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

  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_ID19, NICE_COMPATIBILITY_LAST,
          NICE_COMPATIBILITY_ID19,
          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));

  nice_udp_bsd_socket_factory_init (&udpfactory);

}

static void
fs_nice_stream_transmitter_init (FsNiceStreamTransmitter *self)
{
  /* member init */
  self->priv = FS_NICE_STREAM_TRANSMITTER_GET_PRIVATE (self);

  self->priv->sending = TRUE;
  self->priv->state = FS_STREAM_STATE_DISCONNECTED;
  self->priv->mutex = g_mutex_new ();

  self->priv->controlling_mode = TRUE;
}

static void
fs_nice_stream_transmitter_dispose (GObject *object)
{
  FsNiceStreamTransmitter *self = FS_NICE_STREAM_TRANSMITTER (object);

  FS_NICE_STREAM_TRANSMITTER_LOCK (self);
  if (self->priv->gststream)
    fs_nice_transmitter_free_gst_stream (self->priv->transmitter,
        self->priv->gststream);
  self->priv->gststream = NULL;

  if (self->priv->stream_id)
    nice_agent_remove_stream (self->priv->agent,
        self->priv->stream_id);
  self->priv->stream_id = 0;

  if (self->priv->state_changed_handler_id)
    g_signal_handler_disconnect (self->priv->agent,
        self->priv->state_changed_handler_id);
  self->priv->state_changed_handler_id = 0;

  if (self->priv->gathering_done_handler_id)
    g_signal_handler_disconnect (self->priv->agent,
        self->priv->gathering_done_handler_id);
  self->priv->gathering_done_handler_id = 0;

  if (self->priv->new_selected_pair_handler_id)
    g_signal_handler_disconnect (self->priv->agent,
        self->priv->new_selected_pair_handler_id);
  self->priv->new_selected_pair_handler_id = 0;

  if (self->priv->new_candidate_handler_id)
    g_signal_handler_disconnect (self->priv->agent,
        self->priv->new_candidate_handler_id);
  self->priv->new_candidate_handler_id = 0;

  if (self->priv->agent)
  {
    g_object_unref (self->priv->agent);
    self->priv->agent = NULL;
  }
  FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);

  if (self->priv->transmitter)
  {
    g_object_unref (self->priv->transmitter);
    self->priv->transmitter = NULL;
  }

  parent_class->dispose (object);
}

static void
fs_nice_stream_transmitter_finalize (GObject *object)
{
  FsNiceStreamTransmitter *self = FS_NICE_STREAM_TRANSMITTER (object);

  fs_candidate_list_destroy (self->priv->preferred_local_candidates);

  g_free (self->priv->component_states);

  g_free (self->priv->stun_ip);
  g_free (self->priv->turn_ip);

  g_mutex_free (self->priv->mutex);

  parent_class->finalize (object);
}

static void
fs_nice_stream_transmitter_get_property (GObject *object,
                                           guint prop_id,
                                           GValue *value,
                                           GParamSpec *pspec)
{
  FsNiceStreamTransmitter *self = FS_NICE_STREAM_TRANSMITTER (object);

  switch (prop_id)
  {
    case PROP_SENDING:
      g_value_set_boolean (value, self->priv->sending);
      break;
    case PROP_PREFERRED_LOCAL_CANDIDATES:
      g_value_set_boxed (value, self->priv->preferred_local_candidates);
      break;
    case PROP_STATE:
      FS_NICE_STREAM_TRANSMITTER_LOCK (self);
      g_value_set_enum (value, self->priv->state);
      FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);
      break;
    case PROP_STUN_IP:
      if (self->priv->agent)
        g_object_get_property (G_OBJECT (self->priv->agent),
            g_param_spec_get_name (pspec), value);
      else
        g_value_set_string (value, self->priv->stun_ip);
      break;
    case PROP_STUN_PORT:
      if (self->priv->agent)
        g_object_get_property (G_OBJECT (self->priv->agent),
            g_param_spec_get_name (pspec), value);
      else
        g_value_set_uint (value, self->priv->stun_port);
      break;
    case PROP_TURN_IP:
      if (self->priv->agent)
        g_object_get_property (G_OBJECT (self->priv->agent),
            g_param_spec_get_name (pspec), value);
      else
        g_value_set_string (value, self->priv->turn_ip);
      break;
    case PROP_TURN_PORT:
      if (self->priv->agent)
        g_object_get_property (G_OBJECT (self->priv->agent),
            g_param_spec_get_name (pspec), value);
      else
        g_value_set_uint (value, self->priv->turn_port);

      break;
    case PROP_CONTROLLING_MODE:
      if (self->priv->agent)
        g_object_get_property (G_OBJECT (self->priv->agent),
            g_param_spec_get_name (pspec), value);
      else
        g_value_set_boolean (value, self->priv->controlling_mode);
      break;
    case PROP_STREAM_ID:
      FS_NICE_STREAM_TRANSMITTER_LOCK (self);
      g_value_set_uint (value, self->priv->stream_id);
      FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);
      break;
    case PROP_COMPATIBILITY_MODE:
      g_value_set_uint (value, self->priv->compatibility_mode);
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
fs_nice_stream_transmitter_set_property (GObject *object,
                                           guint prop_id,
                                           const GValue *value,
                                           GParamSpec *pspec)
{
  FsNiceStreamTransmitter *self = FS_NICE_STREAM_TRANSMITTER (object);

  switch (prop_id)
  {
    case PROP_SENDING:
      self->priv->sending = g_value_get_boolean (value);
      break;
    case PROP_PREFERRED_LOCAL_CANDIDATES:
      self->priv->preferred_local_candidates = g_value_dup_boxed (value);
      break;
    case PROP_STUN_IP:
      self->priv->stun_ip = g_value_dup_string (value);
      break;
    case PROP_STUN_PORT:
      self->priv->stun_port = g_value_get_uint (value);
      break;
    case PROP_TURN_IP:
      self->priv->turn_ip = g_value_dup_string (value);
      break;
    case PROP_TURN_PORT:
      self->priv->turn_port = g_value_get_uint (value);
      break;
    case PROP_CONTROLLING_MODE:
      self->priv->controlling_mode = g_value_get_boolean (value);
      if (self->priv->transmitter && self->priv->agent)
        g_object_set_property (G_OBJECT (self->priv->agent),
            g_param_spec_get_name (pspec), value);
      break;
    case PROP_COMPATIBILITY_MODE:
      self->priv->compatibility_mode = g_value_get_uint (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}


static NiceCandidateType
fs_candidate_type_to_nice_candidate_type (FsCandidateType type)
{
  switch (type)
  {
    case FS_CANDIDATE_TYPE_HOST:
      return NICE_CANDIDATE_TYPE_HOST;
    case FS_CANDIDATE_TYPE_SRFLX:
      return NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
    case FS_CANDIDATE_TYPE_PRFLX:
      return NICE_CANDIDATE_TYPE_PEER_REFLEXIVE;
    case FS_CANDIDATE_TYPE_RELAY:
      return NICE_CANDIDATE_TYPE_RELAYED;
    default:
      GST_WARNING ("Invalid candidate type %d, defaulting to type host", type);
      return NICE_CANDIDATE_TYPE_HOST;
  }
}

static NiceCandidateTransport
fs_network_protocol_to_nice_candidate_protocol (FsNetworkProtocol proto)
{
  switch (proto)
  {
    case FS_NETWORK_PROTOCOL_UDP:
      return NICE_CANDIDATE_TRANSPORT_UDP;
    default:
      GST_WARNING ("Invalid Fs network protocol type %u", proto);
      return NICE_CANDIDATE_TRANSPORT_UDP;
  }
}

static NiceCandidate *
fs_candidate_to_nice_candidate (FsNiceStreamTransmitter *self,
    FsCandidate *candidate)
{
  NiceCandidate *nc = nice_candidate_new (
      fs_candidate_type_to_nice_candidate_type (candidate->type));

  nc->transport =
    fs_network_protocol_to_nice_candidate_protocol (candidate->proto);
  nc->priority = candidate->priority;
  nc->stream_id = self->priv->stream_id;
  nc->component_id = candidate->component_id;
  strncpy (nc->foundation, candidate->foundation,
      NICE_CANDIDATE_MAX_FOUNDATION);

  nc->username = g_strdup(candidate->username);
  nc->password = g_strdup(candidate->password);


  g_warning ("%s %s", nc->username, nc->password);

  if (candidate->ip == NULL || candidate->port == 0)
    goto error;
  if (!nice_address_set_from_string (&nc->addr, candidate->ip))
    goto error;
  nice_address_set_port (&nc->addr, candidate->port);

  if (candidate->base_ip && candidate->base_port)
  {
    if (!nice_address_set_from_string (&nc->base_addr, candidate->base_ip))
      goto error;
    nice_address_set_port (&nc->base_addr, candidate->base_port);
  }

  return nc;

 error:
  g_free (nc);
  return NULL;
}

/**
 * fs_nice_stream_transmitter_add_remote_candidate
 * @streamtransmitter: a #FsStreamTransmitter
 * @candidate: a remote #FsCandidate to add
 * @error: location of a #GError, or NULL if no error occured
 *
 * This function is used to add remote candidates to the transmitter
 *
 * Returns: TRUE of the candidate could be added, FALSE if it couldnt
 *   (and the #GError will be set)
 */

static gboolean
fs_nice_stream_transmitter_add_remote_candidate (
    FsStreamTransmitter *streamtransmitter, FsCandidate *candidate,
    GError **error)
{
  FsNiceStreamTransmitter *self =
    FS_NICE_STREAM_TRANSMITTER (streamtransmitter);
  GSList *list = NULL;
  NiceCandidate *cand = NULL;

  FS_NICE_STREAM_TRANSMITTER_LOCK (self);
  if (!self->priv->candidates_added)
  {
    self->priv->candidates_to_set = g_list_append (
        self->priv->candidates_to_set,
        fs_candidate_copy (candidate));
    FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);
    return TRUE;
  }
  FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);

  cand = fs_candidate_to_nice_candidate (self, candidate);

  if (!cand)
  {
    g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS,
        "Invalid candidate added");
    goto error;
  }

  list = g_slist_prepend (NULL, cand);

  nice_agent_set_remote_candidates (self->priv->agent,
      self->priv->stream_id, candidate->component_id, list);

  g_slist_foreach (list, (GFunc)nice_candidate_free, NULL);
  g_slist_free (list);

  return TRUE;

 error:

  FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);
  return FALSE;
}


static void
fs_nice_stream_transmitter_remote_candidates_added (
    FsStreamTransmitter *streamtransmitter)
{
  FsNiceStreamTransmitter *self =
    FS_NICE_STREAM_TRANSMITTER (streamtransmitter);
  GList *candidates = NULL, *item;
  GSList *nice_candidates = NULL;
  gint c;

  FS_NICE_STREAM_TRANSMITTER_LOCK (self);
  if (self->priv->candidates_added)
  {
    FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);
    GST_LOG ("remote_candidates_added already called, ignoring");
    return;
  }

  self->priv->candidates_added = TRUE;
  candidates = self->priv->candidates_to_set;
  self->priv->candidates_to_set = NULL;
  FS_NICE_STREAM_TRANSMITTER_UNLOCK (self);

  /*
  if (candidates)
  {
    FsCandidate *cand = candidates->data;
    nice_agent_set_remote_credentials (self->priv->agent,
        self->priv->stream_id, cand->username, cand->password);
  }
  else
  {
    GST_DEBUG ("Candidates added called before any candidate set,"
        " assuming we're in ice-6 with dribble, so every candidate has"
        " its own password");
  }
  */

  for (c = 1; c <= self->priv->transmitter->components; c++)
  {
    for (item = candidates;
         item;
         item = g_list_next (item))
    {
      FsCandidate *candidate = item->data;

      if (candidate->component_id == c)
      {
        NiceCandidate *nc = fs_candidate_to_nice_candidate (self, candidate);

        if (!nc)
          goto error;

        nice_candidates = g_slist_append (nice_candidates, nc);
      }
    }

    nice_agent_set_remote_candidates (self->priv->agent,
        self->priv->stream_id, c, nice_candidates);

    g_slist_foreach (nice_candidates, (GFunc)nice_candidate_free, NULL);
    g_slist_free (nice_candidates);
    nice_candidates = NULL;
  }

  fs_candidate_list_destroy (candidates);

  return;
 error:
  fs_stream_transmitter_emit_error (FS_STREAM_TRANSMITTER (self),
      FS_ERROR_INVALID_ARGUMENTS,
      "Invalid remote candidate passed",
      "Remote candidate passed in previous add_remote_candidate() call invalid");
  g_slist_foreach (nice_candidates, (GFunc) nice_candidate_free, NULL);
  g_slist_free (nice_candidates);
  fs_candidate_list_destroy (candidates);
}

static gboolean
fs_nice_stream_transmitter_select_candidate_pair (
    FsStreamTransmitter *streamtransmitter,
    const gchar *local_foundation,
    const gchar *remote_foundation,
    GError **error)
{
  FsNiceStreamTransmitter *self =
    FS_NICE_STREAM_TRANSMITTER (streamtransmitter);
  gint c;
  gboolean res = TRUE;

  if (self->priv->stream_id == 0)
  {
    g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS,
        "Can not call this function before gathering local candidates");
    return FALSE;
  }

  for (c = 1; c <= self->priv->transmitter->components; c++)
    if (!nice_agent_set_selected_pair (self->priv->agent,
            self->priv->stream_id, c, local_foundation, remote_foundation))
      res = FALSE;

  if (!res)
    g_set_error (error, FS_ERROR, FS_ERROR_INTERNAL,
        "Unknown error while selecting pairs");

  return res;
}

static FsCandidateType
nice_candidate_type_to_fs_candidate_type (NiceCandidateType type)
{
  switch (type)
  {
    case NICE_CANDIDATE_TYPE_HOST:
      return FS_CANDIDATE_TYPE_HOST;
    case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
      return FS_CANDIDATE_TYPE_SRFLX;
    case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
      return FS_CANDIDATE_TYPE_PRFLX;
    case NICE_CANDIDATE_TYPE_RELAYED:
      return FS_CANDIDATE_TYPE_RELAY;
    default:
      GST_WARNING ("Invalid candidate type %d, defaulting to type host", type);
      return FS_CANDIDATE_TYPE_HOST;
  }
}

static FsNetworkProtocol
nice_candidate_transport_to_fs_network_protocol (NiceCandidateTransport trans)
{
  switch (trans)
  {
    case NICE_CANDIDATE_TRANSPORT_UDP:
      return FS_NETWORK_PROTOCOL_UDP;
    default:
      GST_WARNING ("Invalid Nice network transport type %u", trans);
      return FS_NETWORK_PROTOCOL_UDP;
  }
}

static FsCandidate *
nice_candidate_to_fs_candidate (NiceAgent *agent, NiceCandidate *nicecandidate)
{
  FsCandidate *fscandidate;
  gchar *ipaddr = g_malloc (INET_ADDRSTRLEN);

  nice_address_to_string (&nicecandidate->addr, ipaddr);

  fscandidate = fs_candidate_new (
      nicecandidate->foundation,
      nicecandidate->component_id,
      nice_candidate_type_to_fs_candidate_type (nicecandidate->type),
      nice_candidate_transport_to_fs_network_protocol (
          nicecandidate->transport),
      ipaddr,
      nice_address_get_port (&nicecandidate->addr));

  if (nice_address_is_valid (&nicecandidate->base_addr))
  {
    nice_address_to_string (&nicecandidate->base_addr, ipaddr);
    fscandidate->base_ip = ipaddr;
    fscandidate->base_port = nice_address_get_port (&nicecandidate->base_addr);
  }
  else
  {
    g_free (ipaddr);
    ipaddr = NULL;
  }

  fscandidate->username = g_strdup (nicecandidate->username);
  fscandidate->password = g_strdup (nicecandidate->password);
  fscandidate->priority = nicecandidate->priority;

  if (fscandidate->username == NULL && fscandidate->password == NULL)
  {
    const gchar *username = NULL, *password = NULL;
    nice_agent_get_local_credentials (agent, nicecandidate->stream_id,
        &username, &password);
    fscandidate->username = g_strdup (username);
    fscandidate->password = g_strdup (password);

    if (username == NULL || password == NULL)
    {
      GST_WARNING ("The stream has no credentials??");
    }
  }


{'fraction_non_alphanumeric': 0.08808183722936609, 'fraction_numerical': 0.0025882256275420075, 'mean_word_length': 3.686465152098575, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 522, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 48, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6337314', 'n_tokens_mistral': 16959, 'n_tokens_neox': 16100, 'n_words': 3136}