[farsight2/master] Import for the codec negotiation code from Farsight 1

Olivier Crête olivier.crete at collabora.co.uk
Tue Dec 23 15:19:52 PST 2008


---
 gst/fsrtpconference/Makefile.am                |    6 +-
 gst/fsrtpconference/fs-rtp-codec-negotiation.c |  196 +++++++++++++
 gst/fsrtpconference/fs-rtp-codec-negotiation.h |    3 -
 gst/fsrtpconference/fs-rtp-specific-nego.c     |  347 ++++++++++++++++++++++++
 gst/fsrtpconference/fs-rtp-specific-nego.h     |   40 +++
 5 files changed, 587 insertions(+), 5 deletions(-)
 create mode 100644 gst/fsrtpconference/fs-rtp-specific-nego.c
 create mode 100644 gst/fsrtpconference/fs-rtp-specific-nego.h

diff --git a/gst/fsrtpconference/Makefile.am b/gst/fsrtpconference/Makefile.am
index 998285c..9c7da5d 100644
--- a/gst/fsrtpconference/Makefile.am
+++ b/gst/fsrtpconference/Makefile.am
@@ -7,7 +7,8 @@ libfsrtpconference_la_SOURCES = gstfsrtpconference.c \
 	fs-rtp-stream.c \
 	fs-rtp-discover-codecs.c \
 	fs-rtp-codec-cache.c \
-	fs-rtp-codec-negotiation.c
+	fs-rtp-codec-negotiation.c \
+	fs-rtp-specific-nego.c
 libfsrtpconference_la_CFLAGS = $(FS2_INTERNAL_CFLAGS) $(FS2_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
 libfsrtpconference_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 libfsrtpconference_la_LIBADD = \
@@ -22,4 +23,5 @@ noinst_HEADERS = \
 	fs-rtp-stream.h \
 	fs-rtp-discover-codecs.h \
 	fs-rtp-codec-cache.h \
-	fs-rtp-codec-negotiation.h
+	fs-rtp-codec-negotiation.h \
+	fs-rtp-specific-nego.h
diff --git a/gst/fsrtpconference/fs-rtp-codec-negotiation.c b/gst/fsrtpconference/fs-rtp-codec-negotiation.c
index b4a5c05..a04b7f1 100644
--- a/gst/fsrtpconference/fs-rtp-codec-negotiation.c
+++ b/gst/fsrtpconference/fs-rtp-codec-negotiation.c
@@ -24,6 +24,7 @@
 
 #include "fs-rtp-codec-negotiation.h"
 
+#include "fs-rtp-specific-nego.h"
 
 /**
  * validate_codecs_configuration:
@@ -422,3 +423,198 @@ GHashTable *create_local_codec_associations (FsMediaType media_type,
   return codec_associations;
 }
 
+
+
+
+
+struct SDPNegoData {
+  /* in  */
+  FsCodec *remote_codec;
+  /* out */
+  CodecAssociation *local_ca;
+  FsCodec *nego_codec;
+};
+
+
+static gboolean
+_do_sdp_codec_nego (gpointer key, gpointer value, gpointer user_data)
+{
+  struct SDPNegoData *tmpdata = user_data;
+  CodecAssociation *local_ca = value;
+  FsCodec *nego_codec = NULL;
+
+  if (local_ca == NULL)
+    return FALSE;
+
+  nego_codec = sdp_is_compat (local_ca->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)) {
+    FsCodec *remote_codec = rcodec_e->data;
+    FsCodec *nego_codec = NULL;
+    CodecAssociation *local_ca = NULL;
+
+    gchar *tmp = fs_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_association_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->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 = fs_codec_copy (nego_codec);
+      new_ca->blueprint = local_ca->blueprint;
+      tmp = fs_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 = fs_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_association_by_pt (local_codec_associations, i);
+    if (local_ca) {
+      CodecAssociation *new_ca = g_new0 (CodecAssociation, 1);
+      new_ca->codec = fs_codec_copy (local_ca->codec);
+      new_ca->blueprint = local_ca->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);
+    }
+
+  }
+
+#if 0
+  /*
+   * 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);
+  }
+#endif
+
+  *negotiated_codecs_out = new_negotiated_codecs;
+  return new_codec_associations;
+}
+
+
+CodecAssociation *
+lookup_codec_association_by_pt (GHashTable *codec_associations, gint pt)
+{
+  if (!codec_associations)
+    return NULL;
+
+  return g_hash_table_lookup (codec_associations, GINT_TO_POINTER (pt));
+}
diff --git a/gst/fsrtpconference/fs-rtp-codec-negotiation.h b/gst/fsrtpconference/fs-rtp-codec-negotiation.h
index 1f6fd8f..4da310a 100644
--- a/gst/fsrtpconference/fs-rtp-codec-negotiation.h
+++ b/gst/fsrtpconference/fs-rtp-codec-negotiation.h
@@ -42,8 +42,6 @@ GHashTable *create_local_codec_associations (FsMediaType media_type,
   GList *blueprints, GList *codec_prefs, GHashTable *current_codec_associations,
   GList **local_codecs_list);
 
-#if 0
-
 GHashTable *negotiate_codecs (const GList *remote_codecs,
     GHashTable *current_negotiated_codec_associations,
     GHashTable *local_codec_associations, GList *local_codecs,
@@ -52,7 +50,6 @@ GHashTable *negotiate_codecs (const GList *remote_codecs,
 CodecAssociation *lookup_codec_association_by_pt (
     GHashTable *codec_associations, gint pt);
 
-#endif
 
 
 G_END_DECLS
diff --git a/gst/fsrtpconference/fs-rtp-specific-nego.c b/gst/fsrtpconference/fs-rtp-specific-nego.c
new file mode 100644
index 0000000..d871e8f
--- /dev/null
+++ b/gst/fsrtpconference/fs-rtp-specific-nego.c
@@ -0,0 +1,347 @@
+/*
+ * fs-rtp-specific-nego.c - Per-codec SDP negociation
+ *
+ * Farsight RTP/AVP/SAVP/AVPF Module
+ * Copyright (C) 2007 Collabora Ltd.
+ * Copyright (C) 2007 Nokia Corporation
+ *   @author Olivier Crete <olivier.crete at collabora.co.uk>
+ *
+ * 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 <string.h>
+
+#include <glib.h>
+#include <gst/gst.h>
+
+#include "fs-rtp-specific-nego.h"
+
+struct SdpCompatCheck {
+  FsMediaType media_type;
+  const gchar *encoding_name;
+  FsCodec * (* sdp_is_compat) (GstCaps *rtp_caps, FsCodec *local_codec,
+      FsCodec *remote_codec);
+};
+
+
+static FsCodec *
+sdp_is_compat_ilbc (GstCaps *rtp_caps, FsCodec *local_codec,
+    FsCodec *remote_codec);
+static FsCodec *
+sdp_is_compat_h263_1998 (GstCaps *rtp_caps, FsCodec *local_codec,
+    FsCodec *remote_codec);
+
+struct SdpCompatCheck sdp_compat_checks[] = {
+  {FS_MEDIA_TYPE_AUDIO, "iLBC", sdp_is_compat_ilbc},
+  {FS_MEDIA_TYPE_VIDEO, "H263-1998", sdp_is_compat_h263_1998},
+  {0, NULL, NULL}
+};
+
+
+static FsCodec *
+sdp_is_compat_default (GstCaps *rtp_caps, FsCodec *local_codec,
+    FsCodec *remote_codec);
+
+FsCodec *
+sdp_is_compat (GstCaps *rtp_caps, FsCodec *local_codec,
+    FsCodec *remote_codec)
+{
+  gint i;
+
+  g_assert (local_codec);
+  g_assert (remote_codec);
+  g_assert (rtp_caps);
+
+  if (local_codec->media_type != remote_codec->media_type) {
+    g_debug ("Wrong media type, local: %s, remote: %s",
+        fs_media_type_to_string (local_codec->media_type),
+        fs_media_type_to_string (remote_codec->media_type));
+    return NULL;
+  }
+  if (g_ascii_strcasecmp (local_codec->encoding_name,
+        remote_codec->encoding_name)) {
+    g_debug ("Encoding names dont match, local: %s, remote: %s",
+        local_codec->encoding_name, remote_codec->encoding_name);
+    return NULL;
+  }
+
+  for (i = 0; sdp_compat_checks[i].sdp_is_compat; i++) {
+    if (sdp_compat_checks[i].media_type == remote_codec->media_type &&
+        !g_ascii_strcasecmp (sdp_compat_checks[i].encoding_name,
+            remote_codec->encoding_name)) {
+      return sdp_compat_checks[i].sdp_is_compat (rtp_caps, local_codec,
+          remote_codec);
+    }
+  }
+
+  return sdp_is_compat_default (rtp_caps, local_codec, remote_codec);
+}
+
+static FsCodec *
+sdp_is_compat_default (GstCaps *rtp_caps, FsCodec *local_codec,
+    FsCodec *remote_codec)
+{
+  FsCodec *negotiated_codec = NULL;
+  GList *local_param_list = NULL, *negotiated_param_list = NULL;
+
+  g_debug ("Using default codec negotiation function");
+
+  if (remote_codec->clock_rate &&
+      local_codec->clock_rate != remote_codec->clock_rate) {
+    g_debug ("Clock rates differ local=%u remote=%u", local_codec->clock_rate,
+        remote_codec->clock_rate);
+    return NULL;
+  }
+
+  if (local_codec->channels && remote_codec->channels &&
+      local_codec->channels != remote_codec->channels) {
+    g_debug ("Channel counts differ local=%u remote=%u", local_codec->channels,
+        remote_codec->channels);
+    return NULL;
+  }
+
+  negotiated_codec = fs_codec_copy (remote_codec);
+
+  /* Lets fix here missing clock rates and channels counts */
+  if (negotiated_codec->channels == 0 && local_codec->channels)
+    negotiated_codec->channels = local_codec->channels;
+  if (negotiated_codec->clock_rate == 0)
+    negotiated_codec->clock_rate = local_codec->clock_rate;
+
+  for (local_param_list = local_codec->optional_params;
+       local_param_list;
+       local_param_list = g_list_next (local_param_list)) {
+    FsCodecParameter *local_param = local_param_list->data;
+
+    for (negotiated_param_list = negotiated_codec->optional_params;
+         negotiated_param_list;
+         negotiated_param_list = g_list_next (negotiated_param_list)) {
+      FsCodecParameter *negotiated_param = negotiated_param_list->data;
+      if (!g_ascii_strcasecmp (local_param->name, negotiated_param->name)) {
+        if (!strcmp (local_param->value, negotiated_param->value)) {
+          break;
+        } else {
+          g_debug ("Different values for %s, local=%s remote=%s",
+              local_param->name, local_param->value, negotiated_param->value);
+          fs_codec_destroy (negotiated_codec);
+          return NULL;
+        }
+      }
+    }
+
+    /* Let's add the local param to the negotiated codec if it does not exist in
+     * the remote codec */
+    if (!negotiated_param_list) {
+      FsCodecParameter *newparam = g_new (FsCodecParameter, 1);
+      newparam->name = g_strdup (local_param->name);
+      newparam->value = g_strdup (local_param->value);
+      negotiated_codec->optional_params = g_list_append (
+          negotiated_codec->optional_params, newparam);
+    }
+  }
+
+  return negotiated_codec;
+}
+
+static FsCodec *
+sdp_is_compat_ilbc (GstCaps *rtp_caps, FsCodec *local_codec,
+    FsCodec *remote_codec)
+{
+  FsCodec *negotiated_codec = NULL;
+  GList *mylistitem = NULL, *negotiated_param_list = NULL;
+  gboolean has_mode = FALSE;
+
+  g_debug ("Using ilbc negotiation function");
+
+  if (remote_codec->clock_rate &&
+      local_codec->clock_rate != remote_codec->clock_rate) {
+    g_debug ("Clock rates differ local=%u remote=%u", local_codec->clock_rate,
+        remote_codec->clock_rate);
+    return NULL;
+  }
+
+  if (local_codec->channels && remote_codec->channels &&
+      local_codec->channels != remote_codec->channels) {
+    g_debug ("Channel counts differ local=%u remote=%u", local_codec->channels,
+        remote_codec->channels);
+    return NULL;
+  }
+
+  negotiated_codec = fs_codec_copy (remote_codec);
+
+  /* Lets fix here missing clock rates and channels counts */
+  if (negotiated_codec->channels == 0 && local_codec->channels)
+    negotiated_codec->channels = local_codec->channels;
+  if (negotiated_codec->clock_rate == 0)
+    negotiated_codec->clock_rate = local_codec->clock_rate;
+
+  for (mylistitem = local_codec->optional_params;
+       mylistitem;
+       mylistitem = g_list_next (mylistitem)) {
+    FsCodecParameter *local_param = mylistitem->data;
+
+    for (negotiated_param_list = negotiated_codec->optional_params;
+         negotiated_param_list;
+         negotiated_param_list = g_list_next (negotiated_param_list)) {
+      FsCodecParameter *negotiated_param = negotiated_param_list->data;
+
+      if (!g_ascii_strcasecmp (local_param->name, negotiated_param->name)) {
+        if (!g_ascii_strcasecmp (local_param->name, "mode")) {
+          gint local_mode = atoi (local_param->value);
+          gint remote_mode = atoi (negotiated_param->value);
+
+          has_mode = TRUE;
+
+          if (remote_mode != 20 && remote_mode != 30) {
+            g_debug ("Invalid mode on ilbc");
+            goto failure;
+          }
+          if (local_mode != remote_mode) {
+            g_free (negotiated_param->value);
+            negotiated_param->value = g_strdup ("30");
+            break;
+          }
+        } else {
+          if (!strcmp (local_param->value, negotiated_param->value)) {
+            break;
+          } else {
+            g_debug ("Different values for %s, local=%s remote=%s",
+                local_param->name, local_param->value, negotiated_param->value);
+            goto failure;
+          }
+        }
+      }
+    }
+
+    /* Let's add the local param to the negotiated codec if it does not exist in
+     * the remote codec */
+    if (!negotiated_param_list) {
+      FsCodecParameter *newparam = g_new (FsCodecParameter, 1);
+      newparam->name = g_strdup (local_param->name);
+      newparam->value = g_strdup (local_param->value);
+      negotiated_codec->optional_params = g_list_append (
+          negotiated_codec->optional_params, newparam);
+
+      if (!g_ascii_strcasecmp (local_param->name, "mode")) {
+        has_mode = TRUE;
+      }
+    }
+  }
+
+  /* If more has not be found in the local codec, let's check if it's in the
+   * remote codec */
+  if (!has_mode) {
+    for (negotiated_param_list = negotiated_codec->optional_params;
+         negotiated_param_list;
+         negotiated_param_list = g_list_next (negotiated_param_list)) {
+      FsCodecParameter *negotiated_param = negotiated_param_list->data;
+      if (!g_ascii_strcasecmp (negotiated_param->name, "mode")) {
+        has_mode = TRUE;
+        break;
+      }
+    }
+  }
+
+  /* If we still can't find the mode anywhere, let's add it since it's mandatory
+   * and use default value of 30 ms */
+  if (!has_mode) {
+    FsCodecParameter *newparam = g_new0 (FsCodecParameter, 1);
+    newparam->name = g_strdup ("mode");
+    newparam->value = g_strdup ("30");
+    negotiated_codec->optional_params = g_list_append (
+        negotiated_codec->optional_params, newparam);
+  }
+
+  return negotiated_codec;
+
+ failure:
+  if (negotiated_codec)
+    fs_codec_destroy (negotiated_codec);
+  return NULL;
+
+}
+
+
+
+static FsCodec *
+sdp_is_compat_h263_1998 (GstCaps *rtp_caps, FsCodec *local_codec,
+    FsCodec *remote_codec)
+{
+  FsCodec *negotiated_codec = NULL;
+  GList *mylistitem = NULL, *remote_param_list = NULL;
+  FsCodecParameter *profile = NULL;
+
+  g_debug ("Using H263-1998 negotiation function");
+
+  if (remote_codec->clock_rate != 90000) {
+    g_debug ("Remote clock rate is %d which is not 90000",
+        remote_codec->clock_rate);
+    return NULL;
+  }
+
+
+  if (remote_codec->channels > 1) {
+    g_debug ("Channel count  %d > 1", remote_codec->channels);
+    return NULL;
+  }
+
+  /* First lets check if there is a profile */
+
+  for (remote_param_list = remote_codec->optional_params;
+       remote_param_list;
+       remote_param_list = g_list_next (remote_param_list)) {
+    FsCodecParameter *remote_param = remote_param_list->data;
+
+    if (!g_ascii_strcasecmp (remote_param->name, "profile")) {
+
+      if (profile) {
+        g_debug ("The remote codecs contain the profile item more than once, ignoring");
+        return NULL;
+      } else {
+        profile = remote_param;
+      }
+
+      for (mylistitem = local_codec->optional_params;
+           mylistitem;
+           mylistitem = g_list_next (mylistitem)) {
+        FsCodecParameter *local_param = mylistitem->data;
+
+        if (!g_ascii_strcasecmp (local_param->name, "profile")) {
+
+          if (g_ascii_strcasecmp (local_param->value, remote_param->value)) {
+            g_debug ("Local (%s) and remote (%s) profiles are different",
+                local_param->value, remote_param->value);
+            return NULL;
+          } else {
+            g_debug ("We have the same profile, lets return our local codec");
+
+            negotiated_codec = fs_codec_copy (local_codec);
+
+            negotiated_codec->id = remote_codec->id;
+
+            return negotiated_codec;
+          }
+        }
+      }
+        g_debug ("Profile (%s) is unknown locally, rejecting",
+            remote_param->value);
+            return NULL;
+    }
+  }
+
+
+  negotiated_codec = fs_codec_copy (local_codec);
+  return negotiated_codec;
+}
diff --git a/gst/fsrtpconference/fs-rtp-specific-nego.h b/gst/fsrtpconference/fs-rtp-specific-nego.h
new file mode 100644
index 0000000..4d214d1
--- /dev/null
+++ b/gst/fsrtpconference/fs-rtp-specific-nego.h
@@ -0,0 +1,40 @@
+/*
+ * fs-rtp-specific-nego.h - Per-codec SDP negociation
+ *
+ * Farsight RTP/AVP/SAVP/AVPF Module
+ * Copyright (C) 2007 Collabora Ltd.
+ * Copyright (C) 2007 Nokia Corporation
+ *   @author Olivier Crete <olivier.crete at collabora.co.uk>
+ *
+ * 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
+ */
+
+#ifndef __FS_RTP_SPECIFIC_NEGO_H__
+#define __FS_RTP_SPECIFIC_NEGO_H__
+
+#include <glib.h>
+#include <gst/gst.h>
+
+#include <gst/farsight/fs-codec.h>
+
+G_BEGIN_DECLS
+
+FsCodec *
+sdp_is_compat (GstCaps *rtp_caps, FsCodec *local_codec,
+    FsCodec *remote_codec);
+
+G_END_DECLS
+
+#endif
-- 
1.5.6.5




More information about the farsight-commits mailing list