Logo Search packages:      
Sourcecode: farsight version File versions

rtpgstcodecs.c

/*
 * rtpgstcodecs.c - Source for RTP gstreamer codec setting
 *
 * Farsight RTP/AVP/SAVP/AVPF Module
 * Copyright (C) 2005,2006 Collabora Ltd.
 * Copyright (C) 2005,2006 Nokia Corporation
 *   @author Rob Taylor <rob.taylor@collabora.co.uk>
 *   @author Philippe Kalaf <philippe.kalaf@collabora.co.uk>
 * Copyright (C) 2005 INdT 
 *   @author Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>
 *
 * This program 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 of the License, or
 * (at your option) any later version.
 *
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "rtpgstcodecs.h"
#include "rtpcodecnego.h"

#include <stdlib.h>
#include <string.h>

#define ENABLE_DEBUG_CAPS 0

#define GST_ELEMENTS_CONF_FILE "gstelements.conf"

#define MAX_MTU 100

static gboolean check_for_sink (GList *pipeline);
static gboolean check_for_src (GList *pipeline);

static GList *
codec_cap_list_intersect (GList *list1, GList *list2);

/* the filter function for selecting the elements we can use in
 * autoplugging */
typedef gboolean (FilterFunc) (GstElementFactory *factory);

typedef struct _CodecCap CodecCap;

static gboolean is_payloader (GstElementFactory *factory);
static gboolean is_depayloader (GstElementFactory *factory);
static gboolean is_encoder (GstElementFactory *factory);
static gboolean is_decoder (GstElementFactory *factory);

static void debug_pipeline (GList *pipeline);
#if ENABLE_DEBUG_CAPS
static void debug_codec_cap_list (GList *codec_cap_list);

static void debug_codec_cap (CodecCap *codec_cap);
#endif

static GList *get_plugins_filtered_from_caps (FilterFunc filter,
                                              GstCaps *caps,
                                              GstPadDirection direction);

static gboolean check_caps_compatibility (GstElementFactory *factory,
                                          GstCaps *caps,
                                          GstCaps **matched_caps);

static GList *create_codec_cap_list (GstElementFactory *factory,
                                      GstPadDirection direction,
                                      GList *list,
                                      GstCaps *rtp_caps);

static GList *detect_send_codecs (GstCaps *caps);
static GList *detect_recv_codecs (GstCaps *caps);

static void create_codec_lists (FarsightMediaType media_type,
                                GList *recv_list, GList *send_list);

static void parse_codec_cap_list (GList *list, FarsightMediaType media_type);

static gint compare_media_caps (gconstpointer a, gconstpointer b);
static gint compare_rtp_caps (gconstpointer a, gconstpointer b);

static gboolean extract_field_data (GQuark field_id,
                                    const GValue *value,
                                    gpointer user_data);

static void set_codec_preference_order (GList **list_codecs,
                                        const FarsightCodec *codec,
                                        guint pos);

static void set_options_on_element (GstElementFactory *elem_factory, 
    GstElement *current_element);

static void create_ghost_pad (GstElement *current_element, const gchar *dir,
    GstElement *codec_bin);

static GList *get_list_codec_blueprints (FarsightMediaType media_type);

static GList *add_cn_type (GList *codec_list, GHashTable *codec_associations);
static GList *add_dtmf_type (GList *codec_list,
    GHashTable *new_codec_associations, GHashTable *old_codec_associations,
    const GList *remote_codecs);

static void codec_association_destroy (CodecAssociation *ca);

static gint find_first_empty_dynamic_entry (GHashTable *new_codec_associations,
    GHashTable *old_codec_associations);

static gint codecs_lists_ref[FARSIGHT_MEDIA_TYPE_LAST+1] = { 0 };
static GList *list_codec_blueprints[FARSIGHT_MEDIA_TYPE_LAST+1] = { NULL };
static GKeyFile *elem_config = NULL;

struct _CodecCap
{
  GstCaps *caps; /* media caps */
  GstCaps *rtp_caps; /* RTP caps of given media caps */
  /* 2 list approach so we can have a separation after an intersection is
   * calculted */
  GList *element_list1; /* elements for media, enc, dec, pay, depay */
  GList *element_list2; /* elements for media, enc, dec, pay, depay */
};

static GList *get_list_codec_blueprints (FarsightMediaType media_type)
{
  if (!list_codec_blueprints[media_type])
  {
    load_codecs (media_type);
  }

  return list_codec_blueprints[media_type];
}

static void
codec_cap_free (CodecCap *codec_cap)
{
  GList *walk;

  if (codec_cap->caps)
  {
    gst_caps_unref (codec_cap->caps);
  }
  if (codec_cap->rtp_caps)
  {
    gst_caps_unref (codec_cap->rtp_caps);
  }

  for (walk = codec_cap->element_list1; walk; walk = g_list_next (walk))
  {
    if (walk->data)
    {
      gst_object_unref ((GstElementFactory *)walk->data);
    }
  }
  for (walk = codec_cap->element_list2; walk; walk = g_list_next (walk))
  {
    if (walk->data)
    {
      gst_object_unref ((GstElementFactory *)walk->data);
    }
  }

  if (codec_cap->element_list1)
  {
    g_list_free (codec_cap->element_list1);
  }

  if (codec_cap->element_list2)
  {
    g_list_free (codec_cap->element_list2);
  }
  g_free (codec_cap);
}

static void
codec_cap_list_free (GList *list)
{
  GList *mwalk;
  for (mwalk = list; mwalk; mwalk = g_list_next (mwalk))
  {
    codec_cap_free ((CodecCap *)mwalk->data);
  }

  g_list_free (list);
}

GKeyFile *
load_config_file ()
{
  GKeyFile *key_file;
  gchar *user_conf;
  gboolean found = FALSE;

  key_file = g_key_file_new();

  /*user_conf = g_build_filename (g_get_user_config_dir(), "farsight",
      GST_ELEMENTS_CONF_FILE, NULL); */
  user_conf = g_build_filename (g_get_home_dir(), ".farsight",
      GST_ELEMENTS_CONF_FILE, NULL);
  g_debug ("looking for %s", user_conf);

  found = g_key_file_load_from_file
      (key_file, user_conf, G_KEY_FILE_NONE, NULL);

  g_free (user_conf);
  /* let's try the system paths */
  if (!found)
  {
    gchar **dirs;
    gchar *system_conf;
    int i = 0;

    /* dirs = g_get_system_config_dirs(); */
    dirs = g_malloc0 (2*sizeof (gchar *));
    dirs[0] = g_strdup (SYSCONFDIR);
    while (!found && dirs[i])
    {
      system_conf = g_build_filename (dirs[i], "farsight",
          GST_ELEMENTS_CONF_FILE, NULL);
      g_debug ("looking for %s", system_conf);

      found = g_key_file_load_from_file
        (key_file, system_conf, G_KEY_FILE_NONE, NULL);
      i++;
      g_free (system_conf);
    }
    g_strfreev (dirs);
  }

  if (!found)
  {
    g_warning ("Could not find %s config file", GST_ELEMENTS_CONF_FILE);
    g_key_file_free (key_file);
    return NULL;
  }

  return key_file;
}

/**
 * load_codecs:
 * @media_type: a #FarsightMediaType
 *
 * find all plugins that follow the pattern:
 * input (microphone) -> N* -> rtp payloader -> network
 * network  -> rtp depayloader -> N* -> output (soundcard) 
 * media_type defines if we want audio or video codecs
 *
 * Returns : TRUE if load_codecs suceeded, FALSE otherwsie
 */
gboolean
load_codecs (FarsightMediaType media_type)
{
  GstCaps *caps;
  GList *recv_list = NULL;
  GList *send_list = NULL;

  codecs_lists_ref[media_type]++;

  /* if already computed just return list */
  if (codecs_lists_ref[media_type] > 1)
    return TRUE;


  /* let's not reload if already loaded */
  if (!elem_config)
  {
    elem_config = load_config_file();
  }

  /* caps used to find the payloaders and depayloaders based on media type */
  if (media_type == FARSIGHT_MEDIA_TYPE_AUDIO)
  {
    caps = gst_caps_new_simple ("application/x-rtp",
            "media", G_TYPE_STRING, "audio", NULL);
  }
  else if (media_type == FARSIGHT_MEDIA_TYPE_VIDEO)
  {
    caps = gst_caps_new_simple ("application/x-rtp",
            "media", G_TYPE_STRING, "video", NULL);
  }
  else
  {
    g_warning ("Invalid media type given to load_codecs");
    codecs_lists_ref[media_type]--;
    return FALSE;
  }

  recv_list = detect_recv_codecs (caps);
  send_list = detect_send_codecs (caps);

  gst_caps_unref (caps);
  /* if we can't send or recv let's just stop here */
  if (!recv_list && !send_list)
  {
    codecs_lists_ref[media_type]--;
    g_warning ("No codecs for media type %s detected",
        farsight_media_type_to_string (media_type));

    return FALSE;
  }

  create_codec_lists (media_type, recv_list, send_list);

  codec_cap_list_free (recv_list);
  codec_cap_list_free (send_list);

  return TRUE;
}

/*
 * Return the name template of a pad that outputs audio/CN
 * or NULL if no such pad is found
 */

static const gchar *
element_factory_get_cn_pad_name_template (GstElementFactory *factory)
{
  const GList *static_pad_templates;
  const GList *le;
  GstStaticPadTemplate *staticpadt;
  GstCaps *caps, *mycaps = NULL, *intersect=NULL;
  gchar *result = NULL;


  static_pad_templates = gst_element_factory_get_static_pad_templates (factory);

  if (static_pad_templates == NULL)
    return NULL;

  mycaps =  gst_caps_new_simple ("audio/CN", NULL);

  if (mycaps == NULL) {
    g_warning ("Can't build audio/CN static caps?");
    return NULL;
  }

  for (le = static_pad_templates;
       le;
       le = g_list_next (le)) {
    staticpadt = le->data;

    if (staticpadt->direction == GST_PAD_SRC &&
        ( staticpadt->presence == GST_PAD_ALWAYS ||
            staticpadt->presence == GST_PAD_REQUEST)) {

      caps = gst_static_pad_template_get_caps (staticpadt);

      if (!caps)
        continue;

      intersect = gst_caps_intersect (mycaps, caps);
      if (!gst_caps_is_empty (intersect)) {
        /* found */
        result = staticpadt->name_template;
        gst_caps_unref (intersect);
        break;
      }
      gst_caps_unref (intersect);
    }
  }

  gst_caps_unref (mycaps);

  return result;
}

static gboolean
ca_has_cn (gpointer key, gpointer value, gpointer user_data)
{
  CodecAssociation *ca = value;

  if (!ca)
    return FALSE;

  if (ca->codec->media_type != FARSIGHT_MEDIA_TYPE_AUDIO)
    return FALSE;

  if (element_factory_get_cn_pad_name_template (
        GST_ELEMENT_FACTORY (ca->codec_blueprint->send_pipeline_factory->data)))
    return TRUE;
  else
    return FALSE;
}

static GList *
add_cn_type (GList *codec_list, GHashTable *codec_associations)
{
  static FarsightCodec *codec = NULL;
  CodecAssociation *ca = NULL;

  ca = g_hash_table_find (codec_associations, ca_has_cn, NULL);

  if (!ca)
    return codec_list;

  if (!codec) {
    codec = g_new0 (FarsightCodec, 1);

    codec->id = 13; /* Yes ... thats hardcoded */
    codec->encoding_name = "CN";
    codec->media_type = FARSIGHT_MEDIA_TYPE_AUDIO;
    codec->clock_rate = 8000;
    codec->channels = 1;
  }

  codec_list = g_list_append (codec_list, codec);

  return codec_list;
}

static GList *
add_dtmf_type (GList *codec_list, GHashTable *new_codec_associations,
    GHashTable *old_codec_associations, const GList *remote_codecs)
{
  FarsightCodec *dtmf_codec = NULL;
  GstPluginFeature *feature = NULL;
  const GList *rcodec_e = NULL;
  FarsightCodec *rcodec = NULL;

  feature = gst_default_registry_find_feature ("rtpdtmfsrc",
      GST_TYPE_ELEMENT_FACTORY);

  if (!feature)
    return codec_list;

  gst_object_unref (feature);

  if (remote_codecs) {
    for (rcodec_e = remote_codecs;
         rcodec_e;
         rcodec_e = g_list_next (rcodec_e)) {
      rcodec = rcodec_e->data;
      if (rcodec->media_type == FARSIGHT_MEDIA_TYPE_AUDIO &&
          !g_ascii_strcasecmp (rcodec->encoding_name, "telephone-event"))
        break;
    }

    if (!rcodec_e)
      return codec_list;

    if (rcodec->id < 0 && rcodec->id > 127)
      return codec_list;

    dtmf_codec = farsight_codec_copy (rcodec);

  } else {

    dtmf_codec = g_new0 (FarsightCodec, 1);
    dtmf_codec->id = find_first_empty_dynamic_entry (
        new_codec_associations, old_codec_associations);
    if (dtmf_codec->id < 0) {
      g_warning ("We've run out of dynamic payload types, "
          "we won't adverstise telephone-event");
      g_free (dtmf_codec);
      return codec_list;
    }

    dtmf_codec->encoding_name = "telephone-event";
    dtmf_codec->media_type = FARSIGHT_MEDIA_TYPE_AUDIO;
    dtmf_codec->clock_rate = 8000;
    dtmf_codec->channels = 1;
  }

  codec_list = g_list_append (codec_list, dtmf_codec);

  /*
   * Lets insert our id so that its not re-used
   */
  g_hash_table_insert (new_codec_associations, GINT_TO_POINTER (dtmf_codec->id),
      NULL);

  return codec_list;
}

static void
create_codec_lists (FarsightMediaType media_type,
    GList *recv_list, GList *send_list)
{
  GList *duplex_list = NULL;
  list_codec_blueprints[media_type] = NULL;

  /* TODO we should support non duplex as well, as in have some caps that are
   * only sendable or only receivable */
  duplex_list = codec_cap_list_intersect (recv_list, send_list);

#if ENABLE_DEBUG_CAPS
  g_debug ("*******Intersection of send_list and recv_list");
  debug_codec_cap_list(duplex_list);
#endif

  parse_codec_cap_list (duplex_list, media_type);

  codec_cap_list_free (duplex_list);
}

/* Check if any of the element factories in the pipeline that
   has the unique property set */
static gint
pipeline_has_unique (GList *list)
{
  GList *elem;
  gint val;
  const gchar *name;

  if (!elem_config)
    return 0;
  for (elem = list;
       elem;
       elem = g_list_next (elem)) {
    GstElementFactory *elem_factory = elem->data;
    if (elem_factory == NULL)
      g_error ("NULL factory");
    name = gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (elem_factory));
    if (name && g_key_file_has_key (elem_config, name, "unique", NULL)) {
      GError *error = NULL;
      val = g_key_file_get_integer (elem_config, name, "unique", &error);

      if (error) {
        g_error_free (error);
        return 0;
      } else {
        return val;
      }
    }
  }

  return 0;
}


static gboolean
validate_h263_codecs (CodecCap *codec_cap)
{
  /* we assume we have just one structure per caps as it should be */
  GstStructure *media_struct = gst_caps_get_structure (codec_cap->caps, 0);
  const gchar *name = gst_structure_get_name (media_struct);
  const gchar *h263version;
  GstStructure *rtp_struct;
  const gchar *encoding_name;

  /* let's check if it's h263 */
  if (g_ascii_strcasecmp (name, "video/x-h263"))
  {
    return TRUE;
  }

  h263version = gst_structure_get_string (media_struct, "h263version");
  rtp_struct = gst_caps_get_structure (codec_cap->rtp_caps, 0);
  encoding_name = gst_structure_get_string (rtp_struct, "encoding-name");

  if( !g_ascii_strcasecmp (h263version, "h263"))
  {
    /* baseline H263 can only be encoding name H263 or H263-1998 */
    if (g_ascii_strcasecmp (encoding_name, "H263") &&
        g_ascii_strcasecmp (encoding_name, "H263-1998"))
    {
      return FALSE;
    }
  }
  else if (!g_ascii_strcasecmp (h263version, "h263p"))
  {
    /* has to be H263-1998 */
    if (g_ascii_strcasecmp (encoding_name, "H263-1998"))
    {
      return FALSE;
    }
  }
  else if (!g_ascii_strcasecmp (h263version, "h263pp"))
  {
    /* has to be H263-2000 */
    if (g_ascii_strcasecmp (encoding_name, "H263-2000"))
    {
      return FALSE;
    }
  }

  /* if no h263version specified, we assume it's all h263 versions */

  return TRUE;
}

/* Removes all dynamic pts that already have a static pt in the list */
static void
remove_dynamic_duplicates (GList *list)
{
  GList *walk1, *walk2;
  CodecCap *codec_cap, *cur_codec_cap;
  GstStructure *rtp_struct, *cur_rtp_struct;
  const gchar *encoding_name, *cur_encoding_name;

  for (walk1 = list; walk1; walk1 = g_list_next (walk1))
  {
    const GValue *value;
    GType type;

    codec_cap = (CodecCap *)(walk1->data);
    rtp_struct = gst_caps_get_structure (codec_cap->rtp_caps, 0);
    encoding_name = gst_structure_get_string (rtp_struct, "encoding-name");
    if (!encoding_name)
      continue;

    /* let's skip all non static payload types */
    value = gst_structure_get_value (rtp_struct, "payload");
    type = G_VALUE_TYPE (value);
    if (type != G_TYPE_INT)
    {
      continue;
    }
    else
    {
      gint payload_type;
      payload_type = g_value_get_int (value);
      if (payload_type >= 96)
      {
        continue;
      }
    }

    for (walk2 = list; walk2; walk2 = g_list_next (walk2))
    {
      cur_codec_cap = (CodecCap *)(walk2->data);
      cur_rtp_struct = gst_caps_get_structure (cur_codec_cap->rtp_caps, 0);
      cur_encoding_name =
        gst_structure_get_string (cur_rtp_struct, "encoding-name");
      if (!cur_encoding_name)
        continue;
      if (g_ascii_strcasecmp (encoding_name, cur_encoding_name) == 0)
      {
        const GValue *value = gst_structure_get_value (cur_rtp_struct, "payload");
        GType type = G_VALUE_TYPE (value);
        /* this is a dynamic pt that has a static one , let's remove it */
        if (type == GST_TYPE_INT_RANGE)
        {
          list = g_list_remove (list, cur_codec_cap);
          codec_cap_free (cur_codec_cap);
        }
      }
    }
  }
}

/* insert given codec_cap list into list_codecs and list_codec_blueprints */
static void
parse_codec_cap_list (GList *list, FarsightMediaType media_type)
{
  GList *walk;
  CodecCap *codec_cap;
  FarsightCodec *codec;
  CodecBlueprint *codec_blueprint;
  gint i;
  GList *w;

  remove_dynamic_duplicates (list);

  /* go thru all common caps */
  for (walk = list; walk; walk = g_list_next (walk))
  {
    codec_cap = (CodecCap *)(walk->data);

    codec = g_new0 (FarsightCodec, 1);
    codec->id = -1;
    codec->clock_rate = 0;

    for (i = 0; i < gst_caps_get_size (codec_cap->rtp_caps); i++)
    {
      GstStructure *structure = gst_caps_get_structure (codec_cap->rtp_caps, i);

      gst_structure_foreach (structure, extract_field_data,
            (gpointer) codec);
    }

    if (!codec->encoding_name)
    {
      GstStructure *caps = gst_caps_get_structure (codec_cap->rtp_caps, 0);
      const gchar *encoding_name = codec->encoding_name ? codec->encoding_name
        : gst_structure_get_string (caps, "encoding-name");

      g_debug ("skipping codec %s/%s, no encoding name specified"
          " (pt: %d clock_rate:%u",
          media_type == FARSIGHT_MEDIA_TYPE_AUDIO ? "audio" : "video",
          encoding_name ? encoding_name : "unknown", codec->id,
          codec->clock_rate);

      farsight_codec_destroy (codec);
      continue;
    }

    if (codec->media_type == FARSIGHT_MEDIA_TYPE_VIDEO)
    {
      if (!validate_h263_codecs (codec_cap))
      {
        farsight_codec_destroy (codec);
        continue;
      }
    }

  another:

    codec_blueprint = g_new0 (CodecBlueprint, 1);
    codec_blueprint->codec = codec;
    codec_blueprint->media_caps = gst_caps_copy (codec_cap->caps);
    codec_blueprint->rtp_caps = gst_caps_copy (codec_cap->rtp_caps);

    codec_blueprint->send_pipeline_factory =
      g_list_copy (codec_cap->element_list2);
    for (w = codec_blueprint->send_pipeline_factory; w; w = g_list_next (w))
    {
      gst_object_ref (GST_OBJECT (w->data));
    }
    codec_blueprint->receive_pipeline_factory =
      g_list_copy (codec_cap->element_list1);
    for (w = codec_blueprint->receive_pipeline_factory; w; w = g_list_next (w))
    {
      gst_object_ref (GST_OBJECT (w->data));
    }

    codec_blueprint->has_sink =
      check_for_sink (codec_blueprint->receive_pipeline_factory);
    codec_blueprint->has_src =
      check_for_src (codec_blueprint->send_pipeline_factory);

    codec_blueprint->send_has_unique = pipeline_has_unique (
        codec_blueprint->send_pipeline_factory);
    codec_blueprint->receive_has_unique = pipeline_has_unique (
        codec_blueprint->receive_pipeline_factory);

    /* insert new information into tables */
    list_codec_blueprints[media_type] = g_list_append (
        list_codec_blueprints[media_type], codec_blueprint);
    g_debug ("adding codec %s with pt %d, send_pipeline %p, receive_pipeline %p",
        codec->encoding_name, codec->id, 
        codec_blueprint->send_pipeline_factory,
        codec_blueprint->receive_pipeline_factory);
    debug_pipeline (codec_blueprint->send_pipeline_factory);
    debug_pipeline (codec_blueprint->receive_pipeline_factory);

    if (!g_ascii_strcasecmp (codec->encoding_name, "H263-1998")) {
      codec = farsight_codec_copy (codec);
      g_free (codec->encoding_name);
      codec->encoding_name = g_strdup ("H263-N800");
      goto another;
    }
  }
}

/* find all encoder/payloader combos and build list for them */
static GList *
detect_send_codecs (GstCaps *caps)
{
  GList *payloaders, *encoders;
  GList *send_list = NULL;

  /* find all payloader caps. All payloaders should be from klass
   * Codec/Payloader/Network and have as output a data of the mimetype
   * application/x-rtp */
  payloaders = get_plugins_filtered_from_caps (is_payloader, caps, GST_PAD_SINK);

  /* no payloader found. giving up */
  if (!payloaders)
  {
    g_warning ("No RTP Payloaders found");
    return NULL;
  }
#if ENABLE_DEBUG_CAPS
  else {
    g_debug ("**Payloaders");
    debug_codec_cap_list(payloaders);
  }
#endif

  /* find all encoders based on is_encoder filter */
  encoders = get_plugins_filtered_from_caps (is_encoder, NULL, GST_PAD_SRC);
  if (!encoders)
  {
    codec_cap_list_free (payloaders);
    g_warning ("No encoders found");
    return NULL;
  }
#if ENABLE_DEBUG_CAPS
  else {
    g_debug ("**Encoders");
    debug_codec_cap_list(encoders);
  }
#endif

  /* create intersection list of codecs common
   * to encoders and payloaders lists */
  send_list = codec_cap_list_intersect (payloaders, encoders);

  if (!send_list)
  {
    g_warning ("No compatible encoder/payloader pairs found");
  }
#if ENABLE_DEBUG_CAPS
  else {
    g_debug ("**intersection of payloaders and encoders");
    debug_codec_cap_list(send_list);
  }
#endif


  codec_cap_list_free (payloaders);
  codec_cap_list_free (encoders);

  return send_list;
}

/* find all decoder/depayloader combos and build list for them */
static GList *
detect_recv_codecs (GstCaps *caps)
{
  GList *depayloaders, *decoders;
  GList *recv_list = NULL;

  /* find all depayloader caps. All depayloaders should be from klass
   * Codec/Depayr/Network and have as input a data of the mimetype
   * application/x-rtp */
  depayloaders = get_plugins_filtered_from_caps (is_depayloader, caps,
      GST_PAD_SRC);

  /* no depayloader found. giving up */
  if (!depayloaders)
  {
    g_warning ("No RTP Depayloaders found");
    return NULL;
  }
#if ENABLE_DEBUG_CAPS
  else {
    g_debug ("**Depayloaders");
    debug_codec_cap_list(depayloaders);
  }
#endif

  /* find all decoders based on is_decoder filter */
  decoders = get_plugins_filtered_from_caps (is_decoder, NULL, GST_PAD_SINK);

  if (!decoders)
  {
    codec_cap_list_free (depayloaders);
    g_warning ("No decoders found");
    return NULL;
  }
#if ENABLE_DEBUG_CAPS
  else {
    g_debug ("**Decoders");
    debug_codec_cap_list(decoders);
  }
#endif

  /* create intersection list of codecs common
   * to decoders and depayloaders lists */
  recv_list = codec_cap_list_intersect (depayloaders, decoders);

  if (!recv_list)
  {
    g_warning ("No compatible decoder/depayloader pairs found");
  }
#if ENABLE_DEBUG_CAPS
  else {
    g_debug ("**intersection of depayloaders and decoders");
    debug_codec_cap_list(recv_list);
  }
#endif

  codec_cap_list_free (depayloaders);
  codec_cap_list_free (decoders);

  return recv_list;
}

/* returns the intersection of two lists */
static GList *
codec_cap_list_intersect (GList *list1, GList *list2)
{
  GList *walk1, *walk2;
  CodecCap *codec_cap1, *codec_cap2;
  GstCaps *caps1, *caps2;
  GstCaps *rtp_caps1, *rtp_caps2;
  GList *intersection_list = NULL;

  for (walk1 = list1; walk1; walk1 = g_list_next (walk1))
  {
    codec_cap1 = (CodecCap *)(walk1->data);
    caps1 = codec_cap1->caps;
    rtp_caps1 = codec_cap1->rtp_caps;
    for (walk2 = list2; walk2; walk2 = g_list_next (walk2))
    {
      GstCaps *intersection;
      GstCaps *rtp_intersection = NULL;

      codec_cap2 = (CodecCap *)(walk2->data);
      caps2 = codec_cap2->caps;
      rtp_caps2 = codec_cap2->rtp_caps;

      //g_debug ("intersecting %s AND %s", gst_caps_to_string (caps1), gst_caps_to_string (caps2));
      intersection = gst_caps_intersect (caps1, caps2);
      if (rtp_caps1 && rtp_caps2)
      {
        //g_debug ("RTP intersecting %s AND %s", gst_caps_to_string (rtp_caps1), gst_caps_to_string (rtp_caps2));
        rtp_intersection = gst_caps_intersect (rtp_caps1, rtp_caps2);
      }
      if (!gst_caps_is_empty (intersection) &&
          (rtp_intersection == NULL || !gst_caps_is_empty (rtp_intersection)))
      {
        CodecCap *item = g_new0 (CodecCap, 1);
        GList *swalk;
        item->caps = intersection;

        if (rtp_caps1 && rtp_caps2)
        {
          item->rtp_caps = rtp_intersection;
        }
        else if (rtp_caps1)
        {
          item->rtp_caps = rtp_caps1;
          gst_caps_ref (rtp_caps1);
        }
        else if (rtp_caps2)
        {
          item->rtp_caps = rtp_caps2;
          gst_caps_ref (rtp_caps2);
        }

        /* during an intersect, we concat/copy previous lists together and put them
         * into 1 and 2 */
        for (swalk = codec_cap1->element_list1; swalk;
            swalk = g_list_next(swalk))
        {
          item->element_list1 = g_list_prepend (item->element_list1,
              gst_object_ref(swalk->data));
        }
        for (swalk = codec_cap1->element_list2; swalk;
            swalk = g_list_next(swalk))
        {
          item->element_list1 = g_list_prepend (item->element_list1,
              gst_object_ref(swalk->data));
        }
        for (swalk = codec_cap2->element_list1; swalk;
            swalk = g_list_next(swalk))
        {
          item->element_list2 = g_list_prepend (item->element_list2,
              gst_object_ref(swalk->data));
        }
        for (swalk = codec_cap2->element_list2; swalk;
            swalk = g_list_next(swalk))
        {
          item->element_list2 = g_list_prepend (item->element_list2,
              gst_object_ref(swalk->data));
        }

        intersection_list = g_list_prepend (intersection_list, item);
        break;
      }
      gst_caps_unref (intersection);
      if (rtp_intersection)
        gst_caps_unref (rtp_intersection);
    }
  }

  return intersection_list;
}

void
unload_codecs (FarsightMediaType media_type)
{
  codecs_lists_ref[media_type]--;
  if (!codecs_lists_ref[media_type])
  {
    if (list_codec_blueprints[media_type])
    {
      GList *item;
      for (item = list_codec_blueprints[media_type];
           item;
           item = g_list_next (item)) {
        codec_blueprint_destroy (item->data);
      }
      g_list_free (list_codec_blueprints[media_type]);
      list_codec_blueprints[media_type] = NULL;
    }
  }
}

/**
 * sort_codecs:
 * @local_codecs: Codec list returned by #load_codecs
 * @codec_preference: An array of #FarsightCodecPreference for sorting
 * @size: Size of codec_preference array
 *
 * Sorts the given codec list based on the codec_preference array.
 */
void
sort_codecs (GList **list_codecs,
             const GArray *codec_preference)
{
  guint8 i = 0, pos = 0;
  GList *lc;
  FarsightCodec *codec;
  guint8 count;
  FarsightCodecPreference *cur_codec_pref;

  count = codec_preference->len;
  for (; i < count; ++i)
  {
    for (lc = *list_codecs; lc; lc = g_list_next (lc))
    {
      codec = (FarsightCodec *) lc->data;
      cur_codec_pref =
        &g_array_index (codec_preference, FarsightCodecPreference, i);

      /* only match clock_rate if it is different than 0 */
      if ((g_ascii_strcasecmp (codec->encoding_name,
              cur_codec_pref->encoding_name) == 0) &&
              (codec->clock_rate == 0 || 
               codec->clock_rate == cur_codec_pref->clock_rate))
      {
        /* HACK ALERT this hack is to compensate for the fact that GTalk jingle
         * does not send us any clock-rates */
        if (codec->clock_rate == 0)
        {
          codec->clock_rate = cur_codec_pref->clock_rate;
        }
        set_codec_preference_order (list_codecs, codec, pos++);
        break;
      }
    }
  }
}

static void
codec_association_to_pt_caps (gpointer key, gpointer value, gpointer user_data)
{
  CodecAssociation *ca = value;
  GHashTable *pt_map = user_data;
  GstCaps *caps;
  gchar *tmp;

  if (!value)
    return;

  if (ca->codec->clock_rate == 0)
  {
    g_warning ("skipping pt %d because clock-rate is 0. "
        "We will not be able to receive this payload type", ca->codec->id);
    return;
  }
  /*
   * HACK ALERT: Massive hack because we can't receive CN or DTMF yet
   */
  if (ca->codec->id == 13 ||
      !g_ascii_strcasecmp (ca->codec->encoding_name, "CN") ||
      !g_ascii_strcasecmp (ca->codec->encoding_name, "telephone-event"))
    return;

  caps = farsight_codec_to_gst_caps (ca->codec);

  tmp = gst_caps_to_string (caps);
  g_debug ("caps are: %s\n", tmp);
  g_free (tmp);

  g_hash_table_insert(pt_map, GINT_TO_POINTER(ca->codec->id), (gpointer) caps);
}

/**
 * create_pt_caps_hashtable
 * @codecs: A #GHashTable of #CodecAssociation elements
 *
 * Creates a pt caps hash table for use by rtpbin
 *
 * Returns: the new #GHashTable for the given codec list
 */
GHashTable *
create_pt_caps_hashtable (GHashTable *codecs)
{
  GHashTable *pt_map = g_hash_table_new_full (g_direct_hash,
          g_direct_equal,
          NULL,
          (GDestroyNotify) gst_caps_unref);

  g_hash_table_foreach (codecs, codec_association_to_pt_caps, pt_map);

  return pt_map;
}

gboolean
merge_hashtables (gpointer key, gpointer value,
    gpointer user_data)
{
  GHashTable *dest_hashtable = (GHashTable *)user_data;

  g_hash_table_insert (dest_hashtable,
      (gpointer) key, (gpointer) value);

  return TRUE;
}


static void
set_codec_preference_order (GList **list_codecs,
                            const FarsightCodec *codec,
                            guint pos)
{
  gint index = 0;
  GList *lc;
  GList *codec_item = NULL;

  g_return_if_fail (codec != NULL);
  g_return_if_fail (pos <= g_list_length (*list_codecs));


  for (lc = *list_codecs; lc; lc = g_list_next (lc), ++index)
  {
    if (lc->data == codec) 
    {
      codec_item = lc;
    }
  }

  if (!codec_item)
  {
    g_print ("%s (%d): codec not supported\n", __FUNCTION__, __LINE__);
    return;
  }

  // do nothing, codec already sorted
  if (index == pos)
  {
    return;
  }

  *list_codecs = g_list_delete_link (*list_codecs, codec_item);
  *list_codecs = g_list_insert_before (*list_codecs,
          g_list_nth (*list_codecs, pos),
          (gpointer) codec);
}

static gboolean
klass_contains (const gchar *klass, const gchar *needle)
{
  gchar *found = strstr (klass, needle);
   
  if(!found)
    return FALSE;
  if (found != klass && *(found-1) != '/')
    return FALSE;
  if (found[strlen (needle)] != 0 &&
      found[strlen (needle)] != '/')
    return FALSE;
  return TRUE;
}
 
static gboolean
is_payloader (GstElementFactory *factory)
{
  const gchar *klass = gst_element_factory_get_klass (factory);
  return (klass_contains (klass, "Payloader") &&
          klass_contains (klass, "Network"));
}

static gboolean
is_depayloader (GstElementFactory *factory)
{
  const gchar *klass = gst_element_factory_get_klass (factory);
  return (klass_contains (klass, "Network") &&
          (klass_contains (klass, "Depayloader") ||
           klass_contains (klass, "Depayr")));
  
}

static gboolean
is_encoder (GstElementFactory *factory)
{
  const gchar *klass = gst_element_factory_get_klass (factory);
  /* we might have some sources that provide a non raw stream */
  return (klass_contains (klass, "Encoder") ||
          klass_contains (klass, "Source"));
}

static gboolean
is_decoder (GstElementFactory *factory)
{
  const gchar *klass = gst_element_factory_get_klass (factory);
  /* we might have some sinks that provide decoding */
  return (klass_contains (klass, "Decoder") ||
          klass_contains (klass, "Parser") ||
          klass_contains (klass, "Sink"));
}

/* checks if there is a Source element in the give 
 * list of GstElementFactories */
static gboolean
check_for_src (GList *pipeline)
{
  GList *walk;
  const gchar *class;

  for (walk = pipeline; walk; walk = g_list_next (walk))
  {
    class = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (walk->data));
    if (g_strrstr (class, "Source") != NULL)
    {
      return TRUE;
    }
  }
  return FALSE;
}

/* checks if there is a Sink element in the give 
 * list of GstElementFactories */
static gboolean
check_for_sink (GList *pipeline)
{
  GList *walk;
  const gchar *class;

  for (walk = pipeline; walk; walk = g_list_next (walk))
  {
      class = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (walk->data));
      if (g_strrstr (class, "Sink") != NULL)
      {
          return TRUE;
      }
  }
  return FALSE;
}

static void
debug_pipeline (GList *pipeline)
{
  GList *walk;

  g_debug ("pipeline: ");
  for (walk = pipeline; walk; walk = g_list_next (walk))
  {
    g_debug ("%p:%d:%s ", walk->data,
        GST_OBJECT_REFCOUNT_VALUE(GST_OBJECT (walk->data)),
        gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (walk->data)));
  }
  g_debug ("\n");
}

#if ENABLE_DEBUG_CAPS
static void
debug_codec_cap_list (GList *codec_cap_list)
{
  GList *walk;
  g_debug ("size of codec_cap list is %d", g_list_length (codec_cap_list));
  for (walk = codec_cap_list; walk; walk = g_list_next (walk))
  {
    debug_codec_cap ((CodecCap *)walk->data);
  }
}

static void
debug_codec_cap (CodecCap *codec_cap)
{
  gchar *caps;
  if (codec_cap->caps)
  {
    g_assert (gst_caps_get_size (codec_cap->caps) == 1);
    caps = gst_caps_to_string (codec_cap->caps);
    g_debug ("%p:%d:media_caps %s\n", codec_cap->caps,
        GST_CAPS_REFCOUNT_VALUE(codec_cap->caps),
        caps);
    g_free (caps);
  }

  if (codec_cap->rtp_caps)
  {
    g_assert (gst_caps_get_size (codec_cap->rtp_caps) == 1);
    caps = gst_caps_to_string (codec_cap->rtp_caps);
    g_debug ("%p:%d:rtp_caps %s\n", codec_cap->rtp_caps,
        GST_CAPS_REFCOUNT_VALUE(codec_cap->rtp_caps), caps);
    g_free (caps);
  }

  g_debug ("element_list1 -> ");
  debug_pipeline (codec_cap->element_list1);
  g_debug ("element_list2 -> ");
  debug_pipeline (codec_cap->element_list2);
}

#endif

/* creates/returns a list of CodecCap based on given filter function and caps */
static GList *
get_plugins_filtered_from_caps (FilterFunc filter,
                                GstCaps *caps,
                                GstPadDirection direction)
{
  GList *walk, *result;
  GstElementFactory *factory;
  GList *list = NULL;
  gboolean is_valid;
  GstCaps *matched_caps = NULL;

  result = gst_registry_get_feature_list (gst_registry_get_default (),
          GST_TYPE_ELEMENT_FACTORY);

  walk = result;
  while (walk)
  {
    factory = GST_ELEMENT_FACTORY (walk->data);
    is_valid = FALSE;

    if (!filter (factory))
    {
      goto next;
    }

    if (caps)
    {
      if (check_caps_compatibility (factory, caps, &matched_caps))
      {
        is_valid = TRUE;
      }
    }

    if (is_valid || !caps)
    {
      if (!matched_caps)
      {
        list = create_codec_cap_list (factory, direction, list, NULL);
      }
      else
      {
        gint i;
        for (i = 0; i < gst_caps_get_size (matched_caps); i++)
        {
          GstStructure *structure = gst_caps_get_structure (matched_caps, i);
          GstCaps *cur_caps =
            gst_caps_new_full (gst_structure_copy (structure), NULL);

          list = create_codec_cap_list (factory, direction, list, cur_caps);
          gst_caps_unref (cur_caps);
        }
        gst_caps_unref (matched_caps);
      }
    }

next:
    walk = g_list_next (walk);
  }

  /*
  walk = result;
  while (walk)
  {
    factory = GST_ELEMENT_FACTORY (walk->data);
    g_debug ("new refcnt is %d", GST_OBJECT_REFCOUNT_VALUE (GST_OBJECT (factory)));
    walk = g_list_next (walk);
  }
  */

  gst_plugin_feature_list_free (result);

  return list;
}

/* check if caps are found on given element */
static gboolean
check_caps_compatibility (GstElementFactory *factory,
                          GstCaps *caps, GstCaps **matched_caps)
{
  const GList *pads;
  GstStaticPadTemplate *padtemplate;
  GstCaps *padtemplate_caps = NULL;

  if (!factory->numpadtemplates)
  {
    return FALSE;
  }

  pads = factory->staticpadtemplates;
  while (pads)
  {
    padtemplate = (GstStaticPadTemplate *) (pads->data);
    pads = g_list_next (pads);

    padtemplate_caps = gst_static_caps_get (&padtemplate->static_caps);
    if (gst_caps_is_any (padtemplate_caps))
    {
      goto next;
    }

    if (caps)
    {
      GstCaps *intersection = gst_caps_intersect (padtemplate_caps, caps);
      gboolean have_intersection = !gst_caps_is_empty (intersection);

      if (have_intersection)
      {
        *matched_caps = intersection;
        gst_caps_unref (padtemplate_caps);
        return TRUE;
      }

      gst_caps_unref (intersection);
    }

next:
    if (padtemplate_caps)
    {
      gst_caps_unref (padtemplate_caps);
    }
  }

  *matched_caps = NULL;
  return FALSE;
}

/* adds the given element to a list of CodecCap */
/* if element has several caps, several CodecCap elements will be added */
/* if element caps already in list, will make sure Transform elements have
 * priority and replace old ones */
static GList *
create_codec_cap_list (GstElementFactory *factory,
                       GstPadDirection direction,
                       GList *list,
                       GstCaps *rtp_caps)
{
  const GList *pads = factory->staticpadtemplates;
  gint i;


  /* Let us look at each pad for stuff to add*/
  while (pads)
  {
    GstCaps *caps = NULL;
    GstStaticPadTemplate *padtemplate = NULL;

    padtemplate = (GstStaticPadTemplate *) (pads->data);
    pads = g_list_next (pads);

    if (padtemplate->direction != direction)
      continue;

    if (GST_PAD_TEMPLATE_PRESENCE(padtemplate) != GST_PAD_ALWAYS) {
      continue;
    }

    caps = gst_static_caps_get (&padtemplate->static_caps);
    /*
      g_debug ("%s caps are %s", gst_plugin_feature_get_name (GST_PLUGIN_FEATURE
      (factory)), gst_caps_to_string (caps));
    */

    /* skips caps ANY */
    if (!caps || gst_caps_is_any (caps))
    {
      goto done;
    }

    /* let us add one entry to the list per media type */
    for (i = 0; i < gst_caps_get_size (caps); i++)
    {
      CodecCap *entry = NULL;
      GList *found_item = NULL;
      GstStructure *structure = gst_caps_get_structure (caps, i);
      GstCaps *cur_caps =
          gst_caps_new_full (gst_structure_copy (structure), NULL);

      /* FIXME fix this in gstreamer! The rtpdepay element is bogus, it claims to
       * be a depayloader yet has application/x-rtp on both sides and does
       * absolutely nothing */
      /* Let's check if media caps are really media caps, this is to deal with
       * wierd elements such as rtpdepay that says it's a depayloader but has
       * application/x-rtp on src and sink pads */
      const gchar *name = gst_structure_get_name (structure);
      if (g_ascii_strcasecmp (name, "application/x-rtp") == 0)
      {
        g_debug ("skipping %s", gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
        continue;
      }

      /* let's check if this caps is already in the list, if so let's replace
       * that CodecCap list instead of creating a new one */
      /* we need to compare both media caps and rtp caps */
      found_item = g_list_find_custom (list, cur_caps,
          (GCompareFunc)compare_media_caps);
      if (found_item)
      {
        entry = (CodecCap *)found_item->data;
        /* if RTP caps exist and don't match nullify entry */
        if (rtp_caps && compare_rtp_caps (found_item->data, rtp_caps))
        {
          entry = NULL;
        }
      }

      if (!entry)
      {
        entry = g_new0 (CodecCap, 1);

        entry->caps = cur_caps;
        if (rtp_caps)
        {
          entry->rtp_caps = rtp_caps;
          gst_caps_ref (rtp_caps);
        }
        list = g_list_prepend (list, entry);
        entry->element_list1 = g_list_prepend (NULL, factory);
        gst_object_ref (factory);
      }
      else
      {
        const gchar *name;

        gst_caps_unref (cur_caps);
        /* FIXME Should we unionize RTP info from several elements even if they
         * will not be used? For now won't do it... */

        /* already exists, let's check if this element is in our config file, if
         * so give it priority */
        name = gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory));
        if (elem_config &&
            g_key_file_get_boolean (elem_config, name, "prioritize", NULL))
        {
          /* should be only one element, let's free it */
          if (entry->element_list1)
          {
            gst_object_unref (entry->element_list1->data);
            g_list_free (entry->element_list1);
          }
          entry->element_list1 = g_list_prepend (NULL, factory);
          gst_object_ref (factory);
        }
        else
        {
          const gchar *prev_klass, *cur_klass, *prev_name;

          /* if previous element is a Source/Sink and current element is not,
           * and if the previous element has no priority, let's replace the
           * previous element with the current element */
          GstElementFactory *prev_factory = NULL;
          if (entry->element_list1)
          {
            prev_factory = entry->element_list1->data;
          }

          if (!prev_factory)
          {
            continue;
          }

          prev_klass = gst_element_factory_get_klass(prev_factory);
          if (g_strrstr (prev_klass, "Sink") == NULL &&
              g_strrstr (prev_klass, "Source") == NULL)
          {
            /* previous element is not a sink/source, don't do anything */
            continue;
          }

          cur_klass = gst_element_factory_get_klass (factory);
          if (g_strrstr (cur_klass, "Sink") != NULL ||
              g_strrstr (cur_klass, "Source") != NULL)
          {
            /* current element is a sink/source, don't do anything */
            continue;
          }

          prev_name = gst_plugin_feature_get_name
              (GST_PLUGIN_FEATURE (prev_factory));
          if (elem_config &&
              !g_key_file_get_boolean (elem_config, prev_name, "prioritize",
                  NULL))
          {
            /* previous element is not prioritised, make the switch */
            gst_object_unref (prev_factory);
            g_list_free (entry->element_list1);
            entry->element_list1 = g_list_prepend (NULL, factory);
            gst_object_ref (factory);
          }
        }
      }
    }
  done:
    if (caps != NULL) {
      gst_caps_unref (caps);
    }

  }

  return list;
}

/* GCompareFunc for list_find_custom */
/* compares caps and returns 0 if they intersect */
static gint
compare_media_caps (gconstpointer a, gconstpointer b)
{
  CodecCap *element = (CodecCap *)a;
  GstCaps *c_caps = (GstCaps *)b;
  GstCaps *intersect = gst_caps_intersect (element->caps, c_caps);
  if (!gst_caps_is_empty (intersect))
  {
    /* found */
    gst_caps_unref (intersect);
    return 0;
  }
  else
  {
    /* not found */
    gst_caps_unref (intersect);
    return 1;
  }
}

static gint
compare_rtp_caps (gconstpointer a, gconstpointer b)
{
  CodecCap *element = (CodecCap *)a;
  GstCaps *c_caps = (GstCaps *)b;
  GstCaps *intersect = gst_caps_intersect (element->rtp_caps, c_caps);
  if (!gst_caps_is_empty (intersect))
  {
    /* found */
    gst_caps_unref (intersect);
    return 0;
  }
  else
  {
    /* not found */
    gst_caps_unref (intersect);
    return 1;
  }
}

/**
 *  fill FarsightCodec fields based on payloader capabilities 
 *  TODO: optimise using quarks
 */
static gboolean
extract_field_data (GQuark field_id,
                    const GValue *value, 
                    gpointer user_data)
{
  /* TODO : This can be called several times from different rtp caps for the
   * same codec, it would be good to make sure any duplicate values are the
   * same, if not then we have several rtp elements that are giving different
   * caps information, therefore they need to be fixed */

  FarsightCodec *codec = (FarsightCodec *) user_data;
  GType type = G_VALUE_TYPE (value);
  const gchar *field_name = g_quark_to_string (field_id);
  const gchar *tmp;

  if (0 == strcmp (field_name, "media"))
  {
    if (type != G_TYPE_STRING)
    {
      return FALSE;
    }
    tmp = g_value_get_string (value);
    if (strcmp (tmp, "audio") == 0)
    {
      codec->media_type = FARSIGHT_MEDIA_TYPE_AUDIO;
    }
    else if (strcmp (tmp, "video") == 0)
    {
      codec->media_type = FARSIGHT_MEDIA_TYPE_VIDEO;
    }

  }
  else if (0 == strcmp (field_name, "payload"))
  {
    if (type == GST_TYPE_INT_RANGE)
    {
      if (gst_value_get_int_range_min (value) < 96 ||
          gst_value_get_int_range_max (value) > 255)
      {
        return FALSE;
      }
    }
    else if (type == G_TYPE_INT)
    {
      int id;
      id = g_value_get_int (value);
      if (id > 96) 
      {
        /* Dynamic id that was explicitelly set ?? shouldn't happen */
        return FALSE;
      }
      codec->id = id;
    }
    else
    {
      return FALSE;
    }
  }
  else if (0 == strcmp (field_name, "clock-rate"))
  {
    if (type == GST_TYPE_INT_RANGE)
    {
      /* set to 0, this should be checked by the optional parameters code later
       * in Farsight */
      codec->clock_rate = 0;
      return TRUE;
    }
    else if (type != G_TYPE_INT)
    {
      return FALSE;
    }
    codec->clock_rate = g_value_get_int (value);
  }
  else if (0 == strcmp (field_name, "ssrc") ||
      0 == strcmp (field_name, "clock-base") ||
      0 == strcmp (field_name, "seqnum-base"))
  {
    // ignore these fields for now
    ;
  }
  else if (0 == strcmp (field_name, "encoding-name"))
  {
    if (type != G_TYPE_STRING)
    {
      return FALSE;
    }
    if (!codec->encoding_name)
    {
      codec->encoding_name = g_value_dup_string (value);
    }
  }
  else if (0 == strcmp (field_name, "encoding-params"))
  {
    if (type != G_TYPE_STRING)
    {
      return FALSE;
    }
    codec->channels = (guint) g_ascii_strtoull (
                                       g_value_get_string (value), NULL, 10);
  }
  else
  {
    if (type == G_TYPE_STRING)
    {
      FarsightCodecParameter *optional_param =
        g_new (FarsightCodecParameter, 1);

      optional_param->name = g_strdup (field_name);
      optional_param->value = g_strdup (g_value_get_string (value));
      codec->optional_params = g_list_append (codec->optional_params,
          optional_param);
    }
  }

  return TRUE;
}

void
codec_blueprint_destroy (gpointer data)
{
  CodecBlueprint *codec_blueprint = data;
  GList *walk;

  if (codec_blueprint->codec)
  {
    farsight_codec_destroy (codec_blueprint->codec);
  }

  if (codec_blueprint->media_caps)
  {
    gst_caps_unref (codec_blueprint->media_caps);
  }

  if (codec_blueprint->rtp_caps)
  {
    gst_caps_unref (codec_blueprint->rtp_caps);
  }

  for (walk = codec_blueprint->send_pipeline_factory;
      walk; walk = g_list_next (walk))
  {
    if (walk->data)
    {
      gst_object_unref ((GstElementFactory *)walk->data);
    }
  }
  for (walk = codec_blueprint->receive_pipeline_factory;
      walk; walk = g_list_next (walk))
  {
    if (walk->data)
    {
      gst_object_unref ((GstElementFactory *)walk->data);
    }
  }
  g_list_free (codec_blueprint->send_pipeline_factory);
  g_list_free (codec_blueprint->receive_pipeline_factory);


  g_free (codec_blueprint);
}

static FarsightCodec *
find_codec_for_type (const GList *codec_list, FarsightMediaType media_type,
    gchar *encoding_name, guint clock_rate)
{
  const GList *codec_item = NULL;
  FarsightCodec *codec = NULL;

  for (codec_item = codec_list;
       codec_item;
       codec_item = g_list_next (codec_item)) {
    codec = codec_item->data;

    if (codec->media_type == media_type && codec->clock_rate == clock_rate &&
        !g_ascii_strcasecmp (encoding_name, codec->encoding_name))
      return codec;
  }

  return NULL;
}

/* Creates a codec bin with payloader/encoder or depayloader/decoder based on
 * dir */
GstElement *
create_codec_bin (GHashTable *codec_associations, gint codec_id, gboolean dir,
    const GList *remote_codecs)
{
  GstElement *codec_bin = NULL;
  GstElement *current_element = NULL;
  GstElement *previous_element = NULL;
  GList *walk;
  CodecAssociation *codec_association;
  GList *pipeline_factory = NULL;
  gchar *name = NULL;

  codec_association = lookup_codec_by_pt (codec_associations, codec_id);

  if (!codec_association)
  {
    return NULL;
  }

  if (dir == DIR_SEND)
  {
    pipeline_factory =
        codec_association->codec_blueprint->send_pipeline_factory;
  }
  else
  {
    pipeline_factory =
        codec_association->codec_blueprint->receive_pipeline_factory;
  }

  g_debug ("creating %s codec bin for id %d, pipeline_factory %p",
      (dir==DIR_SEND)?"send":"recv", codec_id, pipeline_factory);
  name = g_strdup_printf ((dir == DIR_SEND)?"send%d":"recv%d",
          codec_id);
  codec_bin = gst_bin_new (name);
  g_free(name);

  if (dir == DIR_RECV) {
    if (codec_association->codec_blueprint->receive_has_unique) {
      if (codec_association->codec_blueprint->receive_unique_bin) {
        gst_object_unref (codec_bin);
        return NULL;
      }

      g_object_add_weak_pointer (G_OBJECT (codec_bin),
          (gpointer) &codec_association->codec_blueprint->receive_unique_bin);
      codec_association->codec_blueprint->receive_unique_bin = codec_bin;

    }
  } else if (dir == DIR_SEND) {
    if (codec_association->codec_blueprint->send_has_unique) {
      if (codec_association->codec_blueprint->send_unique_bin) {
        gst_object_unref (codec_bin);
        return NULL;
      }

      g_object_add_weak_pointer (G_OBJECT (codec_bin),
          (gpointer) &codec_association->codec_blueprint->send_unique_bin);
      codec_association->codec_blueprint->send_unique_bin = codec_bin;
    }
  }

  for (walk = pipeline_factory; walk; walk = g_list_next (walk))
  {
    current_element =
        gst_element_factory_create ((GstElementFactory *) walk->data, NULL);
    if (!current_element)
    {
      gst_object_unref (codec_bin);

      return NULL;
    }

    gst_bin_add (GST_BIN (codec_bin), current_element);

    set_options_on_element (walk->data, current_element);

    /* queue delay to 0 on all depayloaders until I remove that property
     * all-together */
    if (dir == DIR_RECV)
    {
      if (is_depayloader (walk->data))
      {
        g_debug ("setting queue-delay on depayloader");
        g_object_set (G_OBJECT (current_element), "queue-delay", 0, NULL);

        /* let's create our bin sink ghostpad and link it to the depayloader */
        create_ghost_pad (current_element, "sink", codec_bin);
      }
      else if (is_decoder (walk->data))
      {
        /* let's create our bin src ghostpad and link it to the decoder */
        create_ghost_pad (current_element, "src", codec_bin);
      }
    }
    else if (dir == DIR_SEND)
    {
      if (is_payloader (walk->data))
      {
        g_object_set (current_element, "pt", codec_association->codec->id,
            NULL);
        /* TODO find a better way to set the MTU, RTCP perhaps? */
        /*g_object_set (current_element, "mtu", MAX_MTU, NULL);*/
        /* FIXME please don't hardcode me */
        g_object_set (current_element, "max_ptime", 20 * GST_MSECOND, NULL);

        /* let's create our bin src ghostpad and link it to the payloader */
        create_ghost_pad (current_element, "src", codec_bin);
      }
      else if (is_encoder (walk->data))
      {
        const gchar *cnpadname;
        
        /* let's create our bin sink ghostpad and link it to the depayloader */
        create_ghost_pad (current_element, "sink", codec_bin);

        cnpadname = element_factory_get_cn_pad_name_template (walk->data);

        if (cnpadname) {
          FarsightCodec *cncodec;
          cncodec = find_codec_for_type (remote_codecs,
              codec_association->codec->media_type, "CN",
              codec_association->codec_blueprint->codec->clock_rate);

          if (cncodec) {
            GstPad *cnsrc;
            gchar *realname;

            g_debug ("Found audio/CN with pt %d", cncodec->id);

            realname = g_strdup_printf (cnpadname, 0);
            cnsrc = gst_element_get_request_pad (current_element, realname);
            g_free (realname);

            if (cnsrc) {
              GstElement *cnpayloader;

              cnpayloader = gst_element_factory_make ("rtpcnpay", NULL);

              if (cnpayloader) {

                g_object_set (G_OBJECT (cnpayloader), "pt", cncodec->id, NULL);

                gst_bin_add (GST_BIN (codec_bin), cnpayloader);

                if (gst_element_link_pads (current_element, "cnsrc0",
                        cnpayloader, "sink")) {
                  GstPad *ghostpad;
                  GstPad *pad = gst_element_get_pad (cnpayloader, "src");
                  if (pad) {
                    g_debug ("Added CN source %d to pipeline", cncodec->id);
                    ghostpad = gst_ghost_pad_new ("cnsrc", pad);
                    gst_pad_set_active (ghostpad, TRUE);
                    gst_element_add_pad (codec_bin, ghostpad);
                    gst_object_unref (GST_OBJECT (pad));
                  }
                } else {
                  gst_bin_remove (GST_BIN (codec_bin), cnpayloader);
                }
              }
            }
          }
      }
      }
    }

    /* let's link them together using the specified media_caps if any
     * this will ensure that multi-codec encoders/decoders will select the
     * appropriate codec based on caps negotiation */
    if (previous_element)
    {
      GstElement *src_element;
      GstElement *sink_element;
      if (gst_element_factory_can_sink_caps (walk->data,
              codec_association->codec_blueprint->media_caps))
      {
        src_element = previous_element;
        sink_element = current_element;
      }
      else
      {
        src_element = current_element;
        sink_element = previous_element;
      }

      g_debug ("linking %s and %s with caps %s", gst_element_get_name
          (src_element), gst_element_get_name (sink_element),
          gst_caps_to_string (codec_association->codec_blueprint->media_caps));
      if (!gst_element_link_pads_filtered (src_element, "src",
              sink_element, "sink",
              //codec_association->codec_blueprint->media_caps))
              NULL))
      {
        gchar *src_element_name = gst_element_get_name (src_element);
        gchar *sink_element_name = gst_element_get_name (sink_element);
        gchar *caps_as_string = gst_caps_to_string (
            codec_association->codec_blueprint->media_caps);
        g_debug ("Could not link %s and %s with caps %s", src_element_name,
            sink_element_name, caps_as_string);
        g_free (src_element_name);
        g_free (sink_element_name);
        g_free (caps_as_string);

        gst_object_unref (codec_bin);
        return NULL;
      }
    }
    previous_element = current_element;
  }

  return codec_bin;
}

static void
set_options_on_element (GstElementFactory *elem_factory, GstElement
    *current_element)
{
  /* let's check if this plugin has any user defined parameters to set */
  const gchar *element_name = gst_plugin_feature_get_name
    (GST_PLUGIN_FEATURE (elem_factory));
  g_debug ("checking if %s is in config list", element_name);
  if (elem_config && g_key_file_has_group (elem_config,
        element_name))
  {
    gchar **keys;
    gint i;

    g_debug ("Found config for %s", element_name);
    keys = g_key_file_get_keys (elem_config, element_name, NULL, NULL);

    for (i = 0; keys[i]; i++)
    {
      GParamSpec *param_spec;
      GValue key_value = { 0 };
      GValue prop_value = { 0 };

      gchar *str_key_value;
      gboolean bool_key_value;
      gint int_key_value;
      gdouble double_key_value;
      glong long_key_value;
      gulong ulong_key_value;

      g_debug ("getting %s", keys[i]);
      param_spec = g_object_class_find_property
        (G_OBJECT_GET_CLASS(current_element), keys[i]);
      if (!param_spec)
      {
        continue;
      }

      g_value_init (&prop_value, param_spec->value_type);

      switch (param_spec->value_type)
      {
        case G_TYPE_STRING:
          str_key_value = g_key_file_get_value (elem_config, element_name,
              keys[i], NULL);
          g_value_init (&key_value, G_TYPE_STRING);
          g_value_set_string (&key_value, str_key_value);
          g_debug ("%s is a string: %s", keys[i], str_key_value);
          g_free (str_key_value);
          break;
        case G_TYPE_BOOLEAN:
          bool_key_value = g_key_file_get_boolean (elem_config, element_name,
              keys[i], NULL);
          g_value_init (&key_value, G_TYPE_BOOLEAN);
          g_value_set_boolean (&key_value, bool_key_value);
          g_debug ("%s is a boolean: %d", keys[i], bool_key_value);
          break;
        case G_TYPE_UINT64:
        case G_TYPE_INT64:
        case G_TYPE_DOUBLE:
          /* FIXME it seems get_double is only in 2.12, so for now get a
           * string and convert it to double */
#if 1
          str_key_value = g_key_file_get_value (elem_config, element_name, keys[i],
              NULL);
          double_key_value = g_strtod(str_key_value, NULL);
#else
          double_key_value = g_key_file_get_double (elem_config, element_name,
              keys[i], NULL);
#endif
          g_value_init (&key_value, G_TYPE_DOUBLE);
          g_value_set_double (&key_value, double_key_value);
          g_debug ("%s is a uint64", keys[i]);
          g_debug ("%s is a int64", keys[i]);
          g_debug ("%s is a double: %f", keys[i], double_key_value);
          break;
        case G_TYPE_ULONG:
          str_key_value = g_key_file_get_value (elem_config, element_name, keys[i],
              NULL);
          ulong_key_value = strtoul(str_key_value, NULL, 10);
          g_value_init (&key_value, G_TYPE_ULONG);
          g_value_set_ulong (&key_value, ulong_key_value);
          g_debug ("%s is a ulong: %lu", keys[i], ulong_key_value);
          break;
        case G_TYPE_LONG:
          str_key_value = g_key_file_get_value (elem_config, element_name, keys[i],
              NULL);
          long_key_value = strtol(str_key_value, NULL, 10);
          g_value_init (&key_value, G_TYPE_LONG);
          g_value_set_long (&key_value, long_key_value);
          g_debug ("%s is a long: %ld", keys[i], long_key_value);
          break;
        case G_TYPE_INT:
        case G_TYPE_UINT:
        case G_TYPE_ENUM:
        default:
          int_key_value = g_key_file_get_integer (elem_config, element_name,
              keys[i], NULL);
          g_value_init (&key_value, G_TYPE_INT);
          g_value_set_int (&key_value, int_key_value);
          g_debug ("%s is a int: %d", keys[i], int_key_value);
          g_debug ("%s is a uint", keys[i]);
          g_debug ("%s is an enum", keys[i]);
          g_debug ("%s is something else, attempting to int conv", keys[i]);
          break;
      }

      if (!g_value_transform (&key_value, &prop_value))
      {
        g_debug ("Could not transform gvalue pair");
        continue;
      }

      g_debug ("Setting %s to on %s", keys[i], element_name);
      g_object_set_property (G_OBJECT(current_element), keys[i], &prop_value);
    }

    g_strfreev(keys);
  }
}

static void
create_ghost_pad (GstElement *current_element, const gchar *dir, GstElement
    *codec_bin)
{
  GstPad *ghostpad;
  GstPad *pad = gst_element_get_pad (current_element, dir);
  if (pad)
  {
    ghostpad = gst_ghost_pad_new (dir, pad);
    gst_pad_set_active (ghostpad, TRUE);
    gst_element_add_pad (codec_bin, ghostpad);
    gst_object_unref (GST_OBJECT (pad));
  }
}

CodecAssociation *
lookup_codec_by_pt (GHashTable *codec_associations, gint pt)
{
  if (!codec_associations)
    return NULL;

  return g_hash_table_lookup (codec_associations, GINT_TO_POINTER (pt));
}

static void
codec_association_destroy (CodecAssociation *ca)
{
  if (!ca)
    return;

  farsight_codec_destroy (ca->codec);
  g_free (ca);
}

static CodecBlueprint *
find_matching_blueprint (FarsightCodec *codec)
{
  GList *item = get_list_codec_blueprints (codec->media_type);
  GstCaps *caps = NULL;

  if (item == NULL)
    return NULL;

  caps = farsight_codec_to_gst_caps (codec);

  if (!caps)
    return NULL;

  for (; item; item = g_list_next (item)) {
    CodecBlueprint *bp = item->data;
    GstCaps *intersectedcaps = NULL;
    gboolean ok = FALSE;

    intersectedcaps = gst_caps_intersect (caps, bp->rtp_caps);

    if (!gst_caps_is_empty (intersectedcaps))
      ok = TRUE;

    gst_caps_unref (intersectedcaps);

    if (ok)
      break;
  }

  gst_caps_unref (caps);

  if (item)
    return item->data;
  else
    return NULL;
}

static gint
find_first_empty_dynamic_entry (GHashTable *new_codec_associations,
    GHashTable *old_codec_associations)
{
  int id;

  for (id = 96; id < 128; id++) {
    if (new_codec_associations &&
        g_hash_table_lookup_extended (new_codec_associations,
            GINT_TO_POINTER (id), NULL, NULL))
      continue;
    if (old_codec_associations &&
        g_hash_table_lookup_extended (old_codec_associations,
            GINT_TO_POINTER (id), NULL, NULL))
      continue;
    return id;
  }

  return -1;
}

static gboolean
ht_has_codec_blueprint (gpointer key, gpointer value, gpointer user_data)
{
  CodecAssociation *ca = value;

  if (ca->codec_blueprint == user_data)
    return TRUE;
  else
    return FALSE;
}

GHashTable *
create_local_codec_associations (FarsightMediaType media_type,
    GList *codec_prefs, GHashTable *current_codec_associations,
    GList **local_codecs_list)
{
  GHashTable *codec_associations = NULL;
  GList *bp_e = get_list_codec_blueprints (media_type);
  GList *local_codecs = NULL;
  GList *local_codec_association_list = NULL;
  GList *codec_pref_e = NULL;
  GList *lca_e = NULL;

  *local_codecs_list = NULL;

  if (bp_e == NULL)
    return NULL;

  codec_associations = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
      (GDestroyNotify) codec_association_destroy);

  /* First, lets create the original table by looking at our prefered codecs */
  for (codec_pref_e = codec_prefs;
       codec_pref_e;
       codec_pref_e = g_list_next (codec_pref_e)) {
    FarsightCodec *codec_pref = codec_pref_e->data;
    CodecBlueprint *bp = find_matching_blueprint (codec_pref);
    CodecAssociation *ca = NULL;
    GList *bp_param_e = NULL;

    /* No matching blueprint, can't use this codec */
    if (!bp)
      continue;

    ca = g_new0 (CodecAssociation, 1);
    ca->codec_blueprint = bp;
    ca->codec = farsight_codec_copy (codec_pref);

    /* Codec pref does not come with a number, but
     * The blueprint has its own id, lets use it */
    if (ca->codec->id < 0 &&
        (bp->codec->id >= 0 || bp->codec->id < 128)) {
        ca->codec->id = bp->codec->id;
    }

    if (ca->codec->clock_rate <= 0) {
      ca->codec->clock_rate = bp->codec->clock_rate;
    }

    if (ca->codec->channels == 0) {
      ca->codec->channels = bp->codec->channels;
    }

    for (bp_param_e = bp->codec->optional_params;
         bp_param_e;
         bp_param_e = g_list_next (bp_param_e)) {
      GList *pref_param_e = NULL;
      FarsightCodecParameter *bp_param = bp_param_e->data;
      for (pref_param_e = ca->codec->optional_params;
           pref_param_e;
           pref_param_e = g_list_next (pref_param_e)) {
        FarsightCodecParameter *pref_param = pref_param_e->data;
        if (!g_ascii_strcasecmp (bp_param->name, pref_param->name))
          break;
      }
      if (!pref_param_e) {
        FarsightCodecParameter *newparam = g_new0 (FarsightCodecParameter, 1);
        newparam->name = g_strdup (bp_param->name);
        newparam->value = g_strdup (bp_param->value);
        ca->codec->optional_params = g_list_append (ca->codec->optional_params,
            newparam);
      }
    }

    local_codec_association_list = g_list_append (local_codec_association_list,
        ca);
  }

  /* Now, only codecs with specified ids are here,
   * the rest are dynamic
   * Lets attribute them here */
  lca_e = local_codec_association_list;
  while (lca_e) {
    CodecAssociation *lca = lca_e->data;
    GList *next = g_list_next (lca_e);

    if (g_hash_table_lookup_extended (codec_associations,
            GINT_TO_POINTER (lca->codec->id), NULL, NULL) ||
        lca->codec->id < 0) {
      lca->codec->id = find_first_empty_dynamic_entry (
          current_codec_associations, codec_associations);
      if (lca->codec->id < 0) {
        g_warning ("We've run out of dynamic payload types");
        goto out;
      }
    }

    local_codecs = g_list_append (local_codecs, lca->codec);
    g_hash_table_insert (codec_associations, GINT_TO_POINTER (lca->codec->id),
        lca);

    local_codec_association_list = g_list_delete_link (
        local_codec_association_list, lca_e);
    lca_e = next;
  }

  /* Now, lets add all other codecs from the blueprints */
  for (; bp_e; bp_e = g_list_next (bp_e)) {
    CodecBlueprint *bp = bp_e->data;
    CodecAssociation *ca = NULL;

    /* Lets skip codecs that dont have all of the required informations */
    if (bp->codec->clock_rate <= 0) {
      continue;
    }

    /* Check if its already used */
    if (g_hash_table_find (codec_associations, ht_has_codec_blueprint, bp))
      continue;

    ca = g_new0 (CodecAssociation, 1);
    ca->codec_blueprint = bp;
    ca->codec = farsight_codec_copy (bp->codec);

    if (ca->codec->id < 0) {
      ca->codec->id = find_first_empty_dynamic_entry (
          current_codec_associations, codec_associations);
      if (ca->codec->id < 0) {
        g_warning ("We've run out of dynamic payload types");
        goto out;
      }
    }

    g_hash_table_insert (codec_associations, GINT_TO_POINTER (ca->codec->id),
        ca);
    local_codecs = g_list_append (local_codecs, ca->codec);
  }

 out:

  g_list_foreach (local_codec_association_list, (GFunc) codec_association_destroy,
      NULL);
  g_list_free (local_codec_association_list);

  /*
   * BIG hack, we have to manually add CN
   * because we can send it, but not receive it yet
   * Do the same for DTMF for the same reason
   * This is because there is no blueprint for them
   */
  if (media_type == FARSIGHT_MEDIA_TYPE_AUDIO && local_codecs) {
    local_codecs = add_cn_type (local_codecs, codec_associations);
    local_codecs = add_dtmf_type (local_codecs, codec_associations,
        current_codec_associations, NULL);
  }

  *local_codecs_list = local_codecs;

  /* If no local codecs where detected */
  if (!local_codecs) {
    g_hash_table_destroy (codec_associations);
    codec_associations = NULL;
    g_debug ("There are no local codecs for this stream of media type %s",
        farsight_media_type_to_string (media_type));
  }

  return codec_associations;
}


GstCaps *
farsight_codec_to_gst_caps (FarsightCodec *codec)
{
  GstCaps *caps;
  GstStructure *structure;
  GList *item;

  gchar *encoding_name = g_ascii_strup (codec->encoding_name, -1);
  if (!g_ascii_strcasecmp (encoding_name, "H263-N800")) {
    g_free (encoding_name);
    encoding_name = g_strdup ("H263-1998");
  }
  structure = gst_structure_new ("application/x-rtp",
      "encoding-name", G_TYPE_STRING, encoding_name,
      NULL);
  g_free (encoding_name);

  if (codec->clock_rate)
    gst_structure_set (structure,
        "clock-rate", G_TYPE_INT, codec->clock_rate, NULL);

  if (codec->media_type == FARSIGHT_MEDIA_TYPE_AUDIO)
    gst_structure_set (structure, "media", G_TYPE_STRING, "audio", NULL);
  else if (codec->media_type == FARSIGHT_MEDIA_TYPE_VIDEO)
    gst_structure_set (structure, "media", G_TYPE_STRING, "video", NULL);

  if (codec->id >= 0 && codec->id < 128)
    gst_structure_set (structure, "payload", G_TYPE_INT, codec->id, NULL);

  if (codec->channels)
    gst_structure_set (structure, "channels", G_TYPE_INT, codec->channels,
        NULL);

  for (item = codec->optional_params;
       item;
       item = g_list_next (item)) {
    FarsightCodecParameter *param = item->data;
    gchar *lower_name = g_ascii_strdown (param->name, -1);
    gst_structure_set (structure, lower_name, G_TYPE_STRING, param->value,
        NULL);
    g_free (lower_name);
  }

  caps = gst_caps_new_full (structure, NULL);

  return caps;
}

GstElement *
get_unique_bin (FarsightMediaType media_type, FarsightStreamDirection direction,
                gint unique_id)
{
  GList *item = NULL;

  for (item = list_codec_blueprints[media_type];
       item;
       item = g_list_next (item)) {
    CodecBlueprint *bp = item->data;

    if (direction == DIR_RECV) {
      if (bp->receive_has_unique == unique_id &&
          bp->receive_unique_bin) {
        return bp->receive_unique_bin;
      }
    } else if (direction == DIR_SEND) {
      if (bp->send_has_unique == unique_id &&
          bp->send_unique_bin) {
        return bp->send_unique_bin;
      }
    }

  }

  return NULL;
}

/*
 * This function validates a GList of passed FarsightCodec structures
 * against the valid discovered payloaders
 * It removes all "invalid" codecs from the list
 */
GList *
validate_codecs_configuration (FarsightMediaType media_type, GList *codecs)
{
  GList *codec_e = codecs;

  while (codec_e) {
    FarsightCodec *codec = codec_e->data;
    GList *blueprint_e = NULL;

    /* Check if codec is for the wrong media_type.. this would be wrong
     */
    if (media_type != codec->media_type) {
      goto remove_this_codec;
    }

    for (blueprint_e = list_codec_blueprints[media_type];
         blueprint_e;
         blueprint_e = g_list_next (blueprint_e)) {
      CodecBlueprint *blueprint = blueprint_e->data;
      GList *codecparam_e = NULL;

      /* First, lets check the encoding name */
      if (g_ascii_strcasecmp (blueprint->codec->encoding_name,
              codec->encoding_name))
        continue;
      /* If both have a clock_rate, it must be the same */
      if (blueprint->codec->clock_rate && codec->clock_rate &&
          blueprint->codec->clock_rate != codec->clock_rate) {
        continue;
        /* At least one needs to have a clockrate */
      } else if (!blueprint->codec->clock_rate && !codec->clock_rate) {
        continue;
      }

      /* Now lets check that all params that are present in both
       * match
       */
      for (codecparam_e = codec->optional_params;
           codecparam_e;
           codecparam_e = g_list_next (codecparam_e)) {
        FarsightCodecParameter *codecparam = codecparam_e->data;
        GList *bpparam_e = NULL;
        for (bpparam_e = blueprint->codec->optional_params;
             bpparam_e;
             bpparam_e = g_list_next (bpparam_e)) {
          FarsightCodecParameter *bpparam = bpparam_e->data;
          if (!g_ascii_strcasecmp (codecparam->name, bpparam->name)) {
            /* If the blueprint and the codec specify the value
             * of a parameter, they should be the same
             */
            if (g_ascii_strcasecmp (codecparam->value, bpparam->value)) {
              goto next_blueprint;
            }
            break;
          }
        }
      }
      break;
    next_blueprint:
      continue;
    }

    /* If no blueprint was found */
    if (blueprint_e == NULL) {
      goto remove_this_codec;
    }

    codec_e = g_list_next (codec_e);

    continue;
 remove_this_codec:
    {
      GList *nextcodec_e = g_list_next (codec_e);
      gchar *tmp = farsight_codec_to_string (codec);
      g_debug ("Prefered codec %s could not be matched with an installed "
          "encoder/decoder/payloader/depayloader quatuor", tmp);
      g_free (tmp);
      farsight_codec_destroy (codec);
      codecs = g_list_delete_link (codecs, codec_e);
      codec_e = nextcodec_e;
    }
  }

  return codecs;
}

struct SDPNegoData {
  /* in  */
  FarsightCodec *remote_codec;
  /* out */
  CodecAssociation *local_ca;
  FarsightCodec *nego_codec;
};

static gboolean
do_sdp_codec_nego (gpointer key, gpointer value, gpointer user_data)
{
  struct SDPNegoData *tmpdata = user_data;
  CodecAssociation *local_ca = value;
  FarsightCodec *nego_codec = NULL;

  if (local_ca == NULL)
    return FALSE;

  nego_codec = sdp_is_compat (local_ca->codec_blueprint->rtp_caps,
      local_ca->codec, tmpdata->remote_codec);

  if (nego_codec) {
    tmpdata->nego_codec = nego_codec;
    tmpdata->local_ca = local_ca;
    return TRUE;
  }

  return FALSE;
}

GHashTable *
negotiate_codecs (const GList *remote_codecs,
    GHashTable *negotiated_codec_associations,
    GHashTable *local_codec_associations, GList *local_codecs,
    GList **negotiated_codecs_out)
{
  GHashTable *new_codec_associations = NULL;
  GList *new_negotiated_codecs = NULL;
  const GList *rcodec_e = NULL;
  int i;

  g_return_val_if_fail (remote_codecs, NULL);
  g_return_val_if_fail (local_codec_associations, NULL);
  g_return_val_if_fail (local_codecs, NULL);

  new_codec_associations = g_hash_table_new_full (g_direct_hash,
      g_direct_equal, NULL, (GDestroyNotify) codec_association_destroy);

  for (rcodec_e = remote_codecs;
       rcodec_e;
       rcodec_e = g_list_next (rcodec_e)) {
    FarsightCodec *remote_codec = rcodec_e->data;
    FarsightCodec *nego_codec = NULL;
    CodecAssociation *local_ca = NULL;

    gchar *tmp = farsight_codec_to_string (remote_codec);
    g_debug ("Remote codec %s", tmp);
    g_free (tmp);

    /* First lets try the codec that is in the same PT */

    local_ca = lookup_codec_by_pt (local_codec_associations, remote_codec->id);

    if (local_ca) {
      g_debug ("Have local codec in the same PT, lets try it first");
      nego_codec = sdp_is_compat (local_ca->codec_blueprint->rtp_caps,
          local_ca->codec, remote_codec);
    }

    if (!nego_codec) {
      struct SDPNegoData tmpdata;
      tmpdata.remote_codec = remote_codec;
      tmpdata.local_ca = NULL;
      tmpdata.nego_codec = NULL;

      g_hash_table_find (local_codec_associations, do_sdp_codec_nego,
          &tmpdata);

      if (tmpdata.local_ca && tmpdata.nego_codec) {
        local_ca = tmpdata.local_ca;
        nego_codec = tmpdata.nego_codec;
      }
    }

    if (nego_codec) {
      CodecAssociation *new_ca = g_new0 (CodecAssociation, 1);
      gchar *tmp;

      new_ca->codec = farsight_codec_copy (nego_codec);
      new_ca->codec_blueprint = local_ca->codec_blueprint;
      tmp = farsight_codec_to_string (nego_codec);
      g_debug ("Negotiated codec %s", tmp);
      g_free (tmp);

      g_hash_table_insert (new_codec_associations,
          GINT_TO_POINTER (remote_codec->id), new_ca);
      new_negotiated_codecs = g_list_append (new_negotiated_codecs, new_ca->codec);
    } else {
      gchar *tmp = farsight_codec_to_string (remote_codec);
      g_debug ("Could not find a valid intersection... for codec %s",
                 tmp);
      g_free (tmp);
      g_hash_table_insert (new_codec_associations,
          GINT_TO_POINTER (remote_codec->id), NULL);
    }
  }

  /* If no intersection was found, lets return NULL */
  if (g_hash_table_size (new_codec_associations) == 0) {
    g_hash_table_destroy (new_codec_associations);
    return NULL;
  }

  /* Now, lets fill all of the PTs that were previously used in the session
   * even if they are not currently used, so they can't be re-used
   */
  for (i=0; i < 128; i++) {
    CodecAssociation *local_ca = NULL;

    /* We can skip those currently in use */
    if (g_hash_table_lookup_extended (new_codec_associations,
            GINT_TO_POINTER (i), NULL, NULL))
      continue;

    /* We check if our local table (our offer) and if we offered
       something, we add it. Some broken implementation (like Tandberg's)
       send packets on PTs that they did not put in their response
    */
    local_ca = lookup_codec_by_pt (local_codec_associations, i);
    if (local_ca) {
      CodecAssociation *new_ca = g_new0 (CodecAssociation, 1);
      new_ca->codec = farsight_codec_copy (local_ca->codec);
      new_ca->codec_blueprint = local_ca->codec_blueprint;

      g_hash_table_insert (new_codec_associations,
          GINT_TO_POINTER (i), new_ca);
      /*
       * We dont insert it into the list, because the list is used for offers
       * and answers.. and we shouldn't offer/answer with codecs that
       * were not in the remote codecs
       */
      //new_negotiated_codecs = g_list_append (new_negotiated_codecs, new_ca->codec);
      continue;
    }

    /* We check in our local table (our offer) and in the old negotiated
     * table (the result of previous negotiations). And kill all of the
     * PTs used in there
     */
    if (g_hash_table_lookup_extended (local_codec_associations,
                GINT_TO_POINTER (i), NULL, NULL)
        || (negotiated_codec_associations &&
            g_hash_table_lookup_extended (negotiated_codec_associations,
                GINT_TO_POINTER (i), NULL, NULL))) {
      g_hash_table_insert (new_codec_associations,
          GINT_TO_POINTER (i), NULL);
    }

  }

  /*
   * BIG hack, we have to manually add CN
   * because we can send it, but not receive it yet
   * Do the same for DTMF for the same reason
   * This is because there is no blueprint for them
   */

  if (new_negotiated_codecs) {
    new_negotiated_codecs = add_cn_type (new_negotiated_codecs,
        new_codec_associations);
    new_negotiated_codecs = add_dtmf_type (new_negotiated_codecs,
        local_codec_associations, negotiated_codec_associations, remote_codecs);
  }

  *negotiated_codecs_out = new_negotiated_codecs;
  return new_codec_associations;
}

Generated by  Doxygen 1.6.0   Back to index