[farsight2/master] Import the codec discovery and caching stuff from Farsight 1
Olivier Crête
olivier.crete at collabora.co.uk
Tue Dec 23 15:19:47 PST 2008
---
gst/fsrtpconference/Makefile.am | 8 +-
gst/fsrtpconference/fs-rtp-codec-cache.c | 525 +++++++++
gst/fsrtpconference/fs-rtp-codec-cache.h | 40 +
gst/fsrtpconference/fs-rtp-discover-codecs.c | 1475 ++++++++++++++++++++++++++
gst/fsrtpconference/fs-rtp-discover-codecs.h | 57 +
5 files changed, 2103 insertions(+), 2 deletions(-)
create mode 100644 gst/fsrtpconference/fs-rtp-codec-cache.c
create mode 100644 gst/fsrtpconference/fs-rtp-codec-cache.h
create mode 100644 gst/fsrtpconference/fs-rtp-discover-codecs.c
create mode 100644 gst/fsrtpconference/fs-rtp-discover-codecs.h
diff --git a/gst/fsrtpconference/Makefile.am b/gst/fsrtpconference/Makefile.am
index 23fb083..3e00dab 100644
--- a/gst/fsrtpconference/Makefile.am
+++ b/gst/fsrtpconference/Makefile.am
@@ -4,7 +4,9 @@ libfsrtpconference_la_SOURCES = gstfsrtpconference.c \
fs-rtp-conference.c \
fs-rtp-participant.c \
fs-rtp-session.c \
- fs-rtp-stream.c
+ fs-rtp-stream.c \
+ fs-rtp-discover-codecs.c \
+ fs-rtp-codec-cache.c
libfsrtpconference_la_CFLAGS = $(FS2_INTERNAL_CFLAGS) $(FS2_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
libfsrtpconference_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libfsrtpconference_la_LIBADD = \
@@ -16,4 +18,6 @@ noinst_HEADERS = \
fs-rtp-conference.h \
fs-rtp-participant.h \
fs-rtp-session.h \
- fs-rtp-stream.h
+ fs-rtp-stream.h \
+ fs-rtp-discover-codecs.h \
+ fs-rtp-codec-cache.h
diff --git a/gst/fsrtpconference/fs-rtp-codec-cache.c b/gst/fsrtpconference/fs-rtp-codec-cache.c
new file mode 100644
index 0000000..1a2a338
--- /dev/null
+++ b/gst/fsrtpconference/fs-rtp-codec-cache.c
@@ -0,0 +1,525 @@
+/*
+ * Farsight2 - Farsight RTP Discovered Codecs cache
+ *
+ * Copyright 2007 Collabora Ltd.
+ * @author: Olivier Crete <olivier.crete at collabora.co.uk>
+ * Copyright 2007 Nokia Corp.
+ *
+ * fs-rtp-codec-cache.c - A Farsight RTP Codec Caching gobject
+ *
+ * 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
+ */
+
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "fs-rtp-codec-cache.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#ifndef GST_MAJORMINOR
+#define GST_MAJORMINOR "0.10"
+#endif
+
+#define HOST_CPU "x86"
+
+static gboolean codecs_cache_valid(gchar *cache_path) {
+ time_t cache_ts = 0;
+ time_t registry_ts = 0;
+ struct stat cache_stat;
+ struct stat registry_stat;
+ gchar *registry_xml_path;
+ gchar *registry_bin_path;
+
+ return FALSE;
+
+ registry_xml_path = g_strdup (g_getenv ("GST_REGISTRY"));
+ if (registry_xml_path == NULL) {
+ registry_bin_path = g_build_filename (g_get_home_dir (),
+ ".gstreamer-" GST_MAJORMINOR, "registry." HOST_CPU ".bin", NULL);
+ registry_xml_path = g_build_filename (g_get_home_dir (),
+ ".gstreamer-" GST_MAJORMINOR, "registry." HOST_CPU ".xml", NULL);
+ } else {
+ registry_bin_path = g_strdup(registry_xml_path);
+ }
+
+ if (stat(registry_xml_path, ®istry_stat) == 0) {
+ registry_ts = registry_stat.st_mtime;
+ }
+
+ if (stat(registry_bin_path, ®istry_stat) == 0) {
+ if (registry_ts < registry_stat.st_mtime) {
+ registry_ts = registry_stat.st_mtime;
+ }
+ }
+
+ if (stat(cache_path, &cache_stat) == 0) {
+ cache_ts = cache_stat.st_mtime;
+ }
+
+ g_free(registry_bin_path);
+ g_free(registry_xml_path);
+
+ return (registry_ts != 0 && cache_ts > registry_ts);
+}
+
+static gchar *
+get_codecs_cache_path(FsMediaType media_type) {
+ gchar *cache_path;
+
+ if (media_type == FS_MEDIA_TYPE_AUDIO) {
+ cache_path = g_strdup (g_getenv ("FS_AUDIO_CODECS_CACHE"));
+ if (cache_path == NULL) {
+ cache_path = g_build_filename (g_get_home_dir (), ".farsight",
+ "codecs.audio." HOST_CPU ".cache", NULL);
+ }
+ } else if(media_type == FS_MEDIA_TYPE_VIDEO) {
+ cache_path = g_strdup (g_getenv ("FS_VIDEO_CODECS_CACHE"));
+ if (cache_path == NULL) {
+ cache_path = g_build_filename (g_get_home_dir (), ".farsight",
+ "codecs.video." HOST_CPU ".cache", NULL);
+ }
+ } else {
+ g_warning ("Unknown media type %d for cache loading", media_type);
+ return NULL;
+ }
+
+ return cache_path;
+}
+
+
+static gboolean
+read_codec_blueprint_uint (gchar **in, gsize *size, guint *val) {
+ if (*size < sizeof(guint))
+ return FALSE;
+
+ *val = *((guint *) *in);
+ *in += sizeof(guint);
+ *size -= sizeof(guint);
+ return TRUE;
+}
+
+static gboolean
+read_codec_blueprint_int (gchar **in, gsize *size, gint *val) {
+ if (*size < sizeof(gint))
+ return FALSE;
+
+ *val = *((gint *) *in);
+ *in += sizeof(gint);
+ *size -= sizeof(gint);
+ return TRUE;
+}
+
+static gboolean
+read_codec_blueprint_string (gchar **in, gsize *size, gchar **str) {
+ gint str_length;
+
+ if (!read_codec_blueprint_int (in, size, &str_length))
+ return FALSE;
+
+ if (*size < str_length)
+ return FALSE;
+
+ *str = g_new0 (gchar, str_length +1);
+ memcpy (*str, *in, str_length);
+ *in += str_length;
+ *size -= str_length;
+
+ return TRUE;
+}
+
+
+#define READ_CHECK(x) if(!x) return FALSE;
+
+static gboolean
+load_codec_blueprint (FsMediaType media_type, gchar **in, gsize *size) {
+ CodecBlueprint *codec_blueprint = g_new0 (CodecBlueprint, 1);
+ gchar *tmp;
+ gint tmp_size;
+ int i;
+
+ codec_blueprint->codec = g_new0 (FsCodec, 1);
+ codec_blueprint->codec->media_type = media_type;
+
+ READ_CHECK (read_codec_blueprint_int
+ (in, size, &(codec_blueprint->codec->id)));
+ READ_CHECK (read_codec_blueprint_string
+ (in, size, &(codec_blueprint->codec->encoding_name)));
+ READ_CHECK (read_codec_blueprint_uint
+ (in, size, &(codec_blueprint->codec->clock_rate)));
+ READ_CHECK (read_codec_blueprint_uint
+ (in, size, &(codec_blueprint->codec->channels)));
+
+
+ READ_CHECK (read_codec_blueprint_int (in, size, &tmp_size));
+ for(i = 0; i < tmp_size; i++) {
+ FsCodecParameter *param = g_new0 (FsCodecParameter, 1);
+ READ_CHECK (read_codec_blueprint_string (in, size, &(param->name)));
+ READ_CHECK (read_codec_blueprint_string (in, size, &(param->value)));
+ codec_blueprint->codec->optional_params =
+ g_list_append (codec_blueprint->codec->optional_params, param);
+ }
+
+ READ_CHECK (read_codec_blueprint_string (in, size, &tmp));
+ codec_blueprint->media_caps = gst_caps_from_string (tmp);
+ g_free (tmp);
+
+ READ_CHECK (read_codec_blueprint_string (in, size, &tmp));
+ codec_blueprint->rtp_caps = gst_caps_from_string (tmp);
+ g_free (tmp);
+
+ READ_CHECK (read_codec_blueprint_int (in, size, &tmp_size));
+ for(i = 0; i < tmp_size; i++) {
+ GstElementFactory *fact = NULL;
+ READ_CHECK (read_codec_blueprint_string (in, size, &(tmp)));
+ fact = gst_element_factory_find (tmp);
+ g_free (tmp);
+ codec_blueprint->send_pipeline_factory =
+ g_list_append (codec_blueprint->send_pipeline_factory, fact);
+ }
+
+ READ_CHECK (read_codec_blueprint_int (in, size, &tmp_size));
+ for(i = 0; i < tmp_size; i++) {
+ GstElementFactory *fact = NULL;
+ READ_CHECK (read_codec_blueprint_string (in, size, &(tmp)));
+ fact = gst_element_factory_find (tmp);
+ g_free (tmp);
+ codec_blueprint->receive_pipeline_factory =
+ g_list_append (codec_blueprint->receive_pipeline_factory, fact);
+ }
+
+ READ_CHECK (read_codec_blueprint_int
+ (in, size, &(codec_blueprint->has_sink)));
+ READ_CHECK (read_codec_blueprint_int
+ (in, size, &(codec_blueprint->has_src)));
+ READ_CHECK (read_codec_blueprint_int
+ (in, size, &(codec_blueprint->send_has_unique)));
+ READ_CHECK (read_codec_blueprint_int
+ (in, size, &(codec_blueprint->receive_has_unique)));
+
+ /* 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_blueprint->codec->encoding_name, codec_blueprint->codec->id,
+ codec_blueprint->send_pipeline_factory,
+ codec_blueprint->receive_pipeline_factory);
+
+ return TRUE;
+}
+
+
+/**
+ * load_codecs_cache
+ * @media_type: a #FsMediaType
+ *
+ * Will load the codecs blueprints from the cache.
+ *
+ * Returns : TRUE if successful, FALSE if error, or cache outdated
+ *
+ */
+gboolean
+load_codecs_cache (FsMediaType media_type)
+{
+ GMappedFile *mapped = NULL;
+ gchar *contents = NULL;
+ gchar *in = NULL;
+ gsize size;
+ GError *err = NULL;
+ gboolean ret = FALSE;
+
+ gchar magic[8] = {0};
+ gchar magic_media = '?';
+ gint num_blueprints;
+ gchar *cache_path;
+ int i;
+
+
+ if (media_type == FS_MEDIA_TYPE_AUDIO) {
+ magic_media = 'A';
+ } else if(media_type == FS_MEDIA_TYPE_VIDEO) {
+ magic_media = 'V';
+ } else {
+ g_warning ("Invalid media type %d", media_type);
+ return FALSE;
+ }
+
+ cache_path = get_codecs_cache_path(media_type);
+
+ if (!codecs_cache_valid(cache_path)) {
+ g_debug ("Codecs cache %s is outdated or does not exist", cache_path);
+ g_free(cache_path);
+ return FALSE;
+ }
+
+ g_debug ("Loading codecs cache %s", cache_path);
+
+ mapped = g_mapped_file_new (cache_path, FALSE, &err);
+ if (err != NULL) {
+ g_debug ("Unable to mmap file %s : %s", cache_path, err->message);
+ g_error_free (err);
+ err = NULL;
+
+ g_file_get_contents (cache_path, &contents, &size, &err);
+ if (err != NULL) {
+ g_warning ("Unable to read file %s : %s", cache_path, err->message);
+ g_error_free (err);
+ goto error;
+ }
+ } else {
+ if ((contents = g_mapped_file_get_contents (mapped)) == NULL) {
+ g_warning ("Can't load file %s : %s", cache_path, g_strerror (errno));
+ goto error;
+ }
+ /* check length for header */
+ size = g_mapped_file_get_length (mapped);
+ }
+
+ /* in is a cursor pointer on the file contents */
+
+ in = contents;
+
+ if (size < sizeof (magic)) {
+ g_warning ("Cache file corrupt");
+ goto error;
+ }
+
+ memcpy (magic, in, sizeof(magic));
+ in += sizeof(magic);
+ size -= sizeof(magic);
+
+ if (magic[0] != 'F' ||
+ magic[1] != 'S' ||
+ magic[2] != magic_media ||
+ magic[3] != 'C' ||
+ magic[4] != '1' ||
+ magic[5] != '0') {
+ g_warning ("Cache file has incorrect magic header. File corrupted");
+ goto error;
+ }
+
+ if (size < sizeof(gint)) {
+ g_warning ("Cache file corrupt");
+ goto error;
+ }
+
+ num_blueprints = *((gint *) in);
+ in += sizeof(gint);
+ size -= sizeof(gint);
+
+ for (i = 0; i < num_blueprints; i++) {
+ if (!load_codec_blueprint (media_type, &in, &size)) {
+ g_warning ("Problem reading codecs cache. File corrupt");
+ goto error;
+ }
+ }
+
+ ret = TRUE;
+ error:
+ if (mapped) {
+ g_mapped_file_free (mapped);
+ } else {
+ g_free (contents);
+ }
+ g_free (cache_path);
+ return ret;
+
+}
+
+#define WRITE_CHECK(x) if(!x) return FALSE;
+
+static gboolean
+write_codec_blueprint_int (int fd, gint val) {
+ return write (fd, &val, sizeof(gint)) == sizeof(gint);
+}
+
+static gboolean
+write_codec_blueprint_string (int fd, const gchar *str) {
+ gint size;
+
+ size = strlen (str);
+ WRITE_CHECK (write_codec_blueprint_int (fd, size));
+ return write (fd, str, size) == size;
+}
+
+static gboolean
+save_codec_blueprint (int fd, CodecBlueprint *codec_blueprint) {
+ gchar *caps;
+ const gchar *factory_name;
+ GList *walk;
+ gint size;
+
+ WRITE_CHECK (write_codec_blueprint_int
+ (fd, codec_blueprint->codec->id));
+ WRITE_CHECK (write_codec_blueprint_string
+ (fd, codec_blueprint->codec->encoding_name));
+ WRITE_CHECK (write_codec_blueprint_int
+ (fd, codec_blueprint->codec->clock_rate));
+ WRITE_CHECK (write_codec_blueprint_int
+ (fd, codec_blueprint->codec->channels));
+
+ size = g_list_length (codec_blueprint->codec->optional_params);
+ WRITE_CHECK (write_codec_blueprint_int (fd, size));
+ for (walk = codec_blueprint->codec->optional_params; walk;
+ walk = g_list_next (walk)) {
+ FsCodecParameter *param = walk->data;
+ WRITE_CHECK (write_codec_blueprint_string (fd, param->name));
+ WRITE_CHECK (write_codec_blueprint_string (fd, param->value));
+ }
+
+ caps = gst_caps_to_string (codec_blueprint->media_caps);
+ WRITE_CHECK (write_codec_blueprint_string (fd, caps));
+ g_free (caps);
+
+ caps = gst_caps_to_string (codec_blueprint->rtp_caps);
+ WRITE_CHECK (write_codec_blueprint_string (fd, caps));
+ g_free (caps);
+
+ walk = codec_blueprint->send_pipeline_factory;
+ size = g_list_length (walk);
+ if (write (fd, &size, sizeof(gint)) != sizeof(gint))
+ return FALSE;
+
+ for (; walk; walk = g_list_next (walk)) {
+ GstElementFactory *fact = walk->data;
+ factory_name = gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (fact));
+ WRITE_CHECK (write_codec_blueprint_string (fd, factory_name));
+ }
+
+ walk = codec_blueprint->receive_pipeline_factory;
+ size = g_list_length (walk);
+ if (write (fd, &size, sizeof(gint)) != sizeof(gint))
+ return FALSE;
+
+ for (; walk; walk = g_list_next (walk)) {
+ GstElementFactory *fact = walk->data;
+ factory_name = gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (fact));
+ WRITE_CHECK (write_codec_blueprint_string (fd, factory_name));
+ }
+
+
+ WRITE_CHECK (write_codec_blueprint_int
+ (fd, (gint) codec_blueprint->has_sink));
+ WRITE_CHECK (write_codec_blueprint_int
+ (fd, (gint) codec_blueprint->has_src));
+ WRITE_CHECK (write_codec_blueprint_int
+ (fd, codec_blueprint->send_has_unique));
+ WRITE_CHECK (write_codec_blueprint_int
+ (fd, codec_blueprint->receive_has_unique));
+
+ return TRUE;
+}
+
+
+static gboolean
+save_codecs_cache(FsMediaType media_type)
+{
+ gchar *cache_path;
+ GList *item;
+ gchar *tmp_path;
+ int fd;
+ int size;
+ gchar magic[8] = {0};
+
+ cache_path = get_codecs_cache_path(media_type);
+ if (!cache_path)
+ return FALSE;
+
+
+ g_debug ("Saving codecs cache to %s", cache_path);
+
+ tmp_path = g_strconcat (cache_path, ".tmpXXXXXX", NULL);
+ fd = g_mkstemp (tmp_path);
+ if (fd == -1) {
+ gchar *dir;
+
+ /* oops, I bet the directory doesn't exist */
+ dir = g_path_get_dirname (cache_path);
+ g_mkdir_with_parents (dir, 0777);
+ g_free (dir);
+
+ /* the previous g_mkstemp call overwrote the XXXXXX placeholder ... */
+ g_free (tmp_path);
+ tmp_path = g_strconcat (cache_path, ".tmpXXXXXX", NULL);
+ fd = g_mkstemp (tmp_path);
+
+ if (fd == -1) {
+ g_debug ("Unable to save codecs cache. g_mkstemp() failed: %s",
+ g_strerror (errno));
+ g_free (tmp_path);
+ g_free (cache_path);
+ return FALSE;
+ }
+ }
+
+ magic[0] = 'F';
+ magic[1] = 'S';
+ magic[2] = '?';
+ magic[3] = 'C';
+
+ if (media_type == FS_MEDIA_TYPE_AUDIO) {
+ magic[2] = 'A';
+ } else if(media_type == FS_MEDIA_TYPE_VIDEO) {
+ magic[2] = 'V';
+ }
+
+ /* version of the binary format */
+ magic[4] = '1';
+ magic[5] = '0';
+
+ if (write (fd, magic, 8) != 8)
+ return FALSE;
+
+
+ size = g_list_length (list_codec_blueprints[media_type]);
+ if (write (fd, &size, sizeof(gint)) != sizeof(gint))
+ return FALSE;
+
+
+ for (item = list_codec_blueprints[media_type]; item;
+ item = g_list_next (item)) {
+ CodecBlueprint *codec_blueprint = item->data;
+ if (!save_codec_blueprint (fd, codec_blueprint)) {
+ g_warning ("Unable to save codec cache");
+ close (fd);
+ g_free (tmp_path);
+ g_free (cache_path);
+ return FALSE;
+ }
+ }
+
+
+ if (close (fd) < 0) {
+ g_debug ("Can't close codecs cache file : %s", g_strerror (errno));
+ g_free (tmp_path);
+ g_free (cache_path);
+ return FALSE;
+ }
+
+ if (g_file_test (tmp_path, G_FILE_TEST_EXISTS)) {
+#ifdef WIN32
+ remove (cache_path);
+#endif
+ rename (tmp_path, cache_path);
+ }
+
+ g_free (tmp_path);
+ g_debug ("Wrote binary codecs cache");
+ return TRUE;
+}
diff --git a/gst/fsrtpconference/fs-rtp-codec-cache.h b/gst/fsrtpconference/fs-rtp-codec-cache.h
new file mode 100644
index 0000000..2dd501c
--- /dev/null
+++ b/gst/fsrtpconference/fs-rtp-codec-cache.h
@@ -0,0 +1,40 @@
+/*
+ * Farsight2 - Farsight RTP Discovered Codecs cache
+ *
+ * Copyright 2007 Collabora Ltd.
+ * @author: Olivier Crete <olivier.crete at collabora.co.uk>
+ * Copyright 2007 Nokia Corp.
+ *
+ * fs-rtp-codec-cache.c - A Farsight RTP Codec Caching gobject
+ *
+ * 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_CODEC_CACHE_H__
+#define __FS_RTP_CODEC_CACHE_H__
+
+#include "fs-rtp-discover-codecs.h"
+
+G_BEGIN_DECLS
+
+gboolean load_codecs_cache(FsMediaType media_type);
+void save_codecs_cache(FsMediaType media_type);
+
+
+G_END_DECLS
+
+#endif /* __FS_RTP_CODEC_CACHE_H__ */
diff --git a/gst/fsrtpconference/fs-rtp-discover-codecs.c b/gst/fsrtpconference/fs-rtp-discover-codecs.c
new file mode 100644
index 0000000..1dfe096
--- /dev/null
+++ b/gst/fsrtpconference/fs-rtp-discover-codecs.c
@@ -0,0 +1,1475 @@
+/*
+ * Farsight2 - Farsight RTP Discover Codecs
+ *
+ * Copyright 2007 Collabora Ltd.
+ * @author: Olivier Crete <olivier.crete at collabora.co.uk>
+ * Copyright 2007 Nokia Corp.
+ *
+ * fs-discover-codecs.c - A Farsight RTP Codec Discovery gobject
+ *
+ * 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
+ */
+
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "fs-rtp-discover-codecs.h"
+
+#include "fs-rtp-codec-cache.h"
+
+#include <string.h>
+
+
+/*
+ * Local TYPES
+ */
+
+typedef 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 */
+} CodecCap;
+
+
+typedef gboolean (FilterFunc) (GstElementFactory *factory);
+
+/* Static Functions */
+
+static gboolean create_codec_lists (FsMediaType media_type,
+ GList *recv_list, GList *send_list);
+static GList *remove_dynamic_duplicates (GList *list);
+static void parse_codec_cap_list (GList *list, FsMediaType media_type);
+static GList *detect_send_codecs (GstCaps *caps);
+static GList *detect_recv_codecs (GstCaps *caps);
+static GList *codec_cap_list_intersect (GList *list1, GList *list2);
+static GList *get_plugins_filtered_from_caps (FilterFunc filter,
+ GstCaps *caps, GstPadDirection direction);
+static gboolean extract_field_data (GQuark field_id,
+ const GValue *value,
+ gpointer user_data);
+
+static gboolean check_for_sink (GList *pipeline);
+static gboolean check_for_src (GList *pipeline);
+
+
+/* GLOBAL variables */
+
+static GList *list_codec_blueprints[FS_MEDIA_TYPE_LAST+1] = { NULL };
+static gint codecs_lists_ref[FS_MEDIA_TYPE_LAST+1] = { 0 };
+
+
+
+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
+
+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);
+}
+
+/**
+ * load_codecs:
+ * @media_type: a #FsMediaType
+ *
+ * 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 (FsMediaType media_type)
+{
+ GstCaps *caps;
+ GList *recv_list = NULL;
+ GList *send_list = NULL;
+ gboolean ret;
+
+
+ codecs_lists_ref[media_type]++;
+
+ /* if already computed just return list */
+ if (codecs_lists_ref[media_type] > 1)
+ return TRUE;
+
+
+#if 0
+ /* let's not reload if already loaded */
+ if (!elem_config)
+ {
+ elem_config = load_config_file();
+ }
+#endif
+
+ if (load_codecs_cache(media_type)) {
+ g_debug("Loaded codec blueprints from cache file");
+ return TRUE;
+ }
+
+ /* caps used to find the payloaders and depayloaders based on media type */
+ if (media_type == FS_MEDIA_TYPE_AUDIO)
+ {
+ caps = gst_caps_new_simple ("application/x-rtp",
+ "media", G_TYPE_STRING, "audio", NULL);
+ }
+ else if (media_type == FS_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",
+ fs_media_type_to_string (media_type));
+
+ ret = FALSE;
+ goto out;
+ }
+
+ ret = create_codec_lists (media_type, recv_list, send_list);
+
+ out:
+ if (recv_list)
+ codec_cap_list_free (recv_list);
+ if (send_list)
+ codec_cap_list_free (send_list);
+
+ /* Save the codecs blueprint cache */
+ save_codecs_cache(media_type);
+
+ return ret;
+}
+
+static gboolean
+create_codec_lists (FsMediaType 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(!duplex_list) {
+ g_warning ("There are no send/recv codecs");
+ return FALSE;
+ }
+
+#if ENABLE_DEBUG_CAPS
+ g_debug ("*******Intersection of send_list and recv_list");
+ debug_codec_cap_list(duplex_list);
+#endif
+
+ duplex_list = remove_dynamic_duplicates (duplex_list);
+
+ if (!duplex_list) {
+ g_warning ("Dynamic duplicate removal left us with nothing");
+ return FALSE;
+ }
+
+ parse_codec_cap_list (duplex_list, media_type);
+
+ codec_cap_list_free (duplex_list);
+
+ return TRUE;
+}
+
+/* Check if any of the element factories in the pipeline that
+ has the unique property set */
+static gint
+pipeline_has_unique (GList *list)
+{
+#if 0
+ 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;
+ }
+ }
+ }
+#endif
+ 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 (strcmp (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( !strcmp (h263version, "h263"))
+ {
+ /* baseline H263 can only be encoding name H263 or H263-1998 */
+ if (strcmp (encoding_name, "H263") &&
+ strcmp (encoding_name, "H263-1998"))
+ {
+ return FALSE;
+ }
+ }
+ else if (!strcmp (h263version, "h263p"))
+ {
+ /* has to be H263-1998 */
+ if (strcmp (encoding_name, "H263-1998"))
+ {
+ return FALSE;
+ }
+ }
+ else if (!strcmp (h263version, "h263pp"))
+ {
+ /* has to be H263-2000 */
+ if (strcmp (encoding_name, "H263-2000"))
+ {
+ return FALSE;
+ }
+ }
+
+ /* if no h263version specified, we assume it's all h263 versions */
+
+ return TRUE;
+}
+
+
+static gboolean
+validate_amr_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);
+ GstStructure *rtp_struct;
+ const gchar *encoding_name;
+
+ rtp_struct = gst_caps_get_structure (codec_cap->rtp_caps, 0);
+ encoding_name = gst_structure_get_string (rtp_struct, "encoding-name");
+
+
+ /* let's check if it's AMRWB */
+ if (!strcmp (name, "audio/AMR-WB"))
+ {
+ if (!strcmp (encoding_name, "AMR-WB"))
+ return TRUE;
+ else
+ return FALSE;
+ }
+ else if (!strcmp (name, "audio/AMR"))
+ {
+ if (!strcmp (encoding_name, "AMR"))
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ /* Everything else is invalid */
+
+ return TRUE;
+}
+
+/* Removes all dynamic pts that already have a static pt in the list */
+static GList *
+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);
+ }
+ }
+ }
+ }
+
+ return list;
+}
+
+/* insert given codec_cap list into list_codecs and list_codec_blueprints */
+static void
+parse_codec_cap_list (GList *list, FsMediaType media_type)
+{
+ GList *walk;
+ CodecCap *codec_cap;
+ FsCodec *codec;
+ CodecBlueprint *codec_blueprint;
+ gint i;
+ GList *w;
+
+ /* go thru all common caps */
+ for (walk = list; walk; walk = g_list_next (walk))
+ {
+ codec_cap = (CodecCap *)(walk->data);
+
+ codec = g_new0 (FsCodec, 1);
+ codec->id = FS_CODEC_ID_ANY;
+ 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 == FS_MEDIA_TYPE_AUDIO ? "audio" : "video",
+ encoding_name ? encoding_name : "unknown", codec->id,
+ codec->clock_rate);
+
+ encoding_name = NULL;
+ fs_codec_destroy (codec);
+ continue;
+ }
+
+ switch (codec->media_type) {
+ case FS_MEDIA_TYPE_VIDEO:
+ if (!validate_h263_codecs (codec_cap)) {
+ fs_codec_destroy (codec);
+ continue;
+ }
+ break;
+ case FS_MEDIA_TYPE_AUDIO:
+ if (!validate_amr_codecs (codec_cap)) {
+ fs_codec_destroy (codec);
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+
+ 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 = fs_codec_copy (codec);
+ g_free (codec->encoding_name);
+ codec->encoding_name = g_strdup ("H263-N800");
+ goto another;
+ }
+ }
+}
+
+
+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, "Sink"));
+}
+
+
+/* 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;
+}
+
+
+static void
+codec_blueprint_destroy (gpointer data)
+{
+ CodecBlueprint *codec_blueprint = data;
+ GList *walk;
+
+ if (codec_blueprint->codec)
+ {
+ fs_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);
+}
+
+void
+unload_codecs (FsMediaType 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;
+ }
+ }
+}
+
+
+/* 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;
+}
+
+
+/* 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;
+ }
+}
+
+
+/* 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);
+ /*
+ 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 0
+ 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
+#endif
+ {
+ 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 0
+ 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);
+ }
+#endif
+ }
+ }
+ }
+ done:
+ if (caps != NULL) {
+ gst_caps_unref (caps);
+ }
+
+ }
+
+ return list;
+}
+
+
+/* function used to sort element features */
+/* Copy-pasted from decodebin */
+static gint
+compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
+{
+ gint diff;
+ const gchar *rname1, *rname2;
+
+ diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
+ if (diff != 0)
+ return diff;
+
+ rname1 = gst_plugin_feature_get_name (f1);
+ rname2 = gst_plugin_feature_get_name (f2);
+
+ diff = strcmp (rname2, rname1);
+
+ return diff;
+}
+
+
+/* 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);
+
+ result = g_list_sort (result, (GCompareFunc) compare_ranks);
+
+ 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);
+ 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;
+}
+
+/**
+ * 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 */
+
+ FsCodec *codec = (FsCodec *) 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 = FS_MEDIA_TYPE_AUDIO;
+ }
+ else if (strcmp (tmp, "video") == 0)
+ {
+ codec->media_type = FS_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)
+ {
+ FsCodecParameter *optional_param = g_new0 (FsCodecParameter, 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;
+}
+
+
+/* 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;
+}
diff --git a/gst/fsrtpconference/fs-rtp-discover-codecs.h b/gst/fsrtpconference/fs-rtp-discover-codecs.h
new file mode 100644
index 0000000..a6a3b5c
--- /dev/null
+++ b/gst/fsrtpconference/fs-rtp-discover-codecs.h
@@ -0,0 +1,57 @@
+/*
+ * Farsight2 - Farsight RTP Discover Codecs
+ *
+ * Copyright 2007 Collabora Ltd.
+ * @author: Olivier Crete <olivier.crete at collabora.co.uk>
+ * Copyright 2007 Nokia Corp.
+ *
+ * fs-discover-codecs.h - A Farsight RTP Codec Discovery gobject
+ *
+ * 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_DISCOVER_CODECS_H__
+#define __FS_RTP_DISCOVER_CODECS_H__
+
+#include <gst/gst.h>
+
+#include <gst/farsight/fs-codec.h>
+
+G_BEGIN_DECLS
+
+typedef struct _CodecBlueprint
+{
+ FsCodec *codec;
+ GstCaps *media_caps;
+ GstCaps *rtp_caps;
+ GList *send_pipeline_factory;
+ GList *receive_pipeline_factory;
+ gboolean has_sink;
+ gboolean has_src;
+
+ gint send_has_unique;
+ gint receive_has_unique;
+ GstElement *send_unique_bin;
+ GstElement *receive_unique_bin;
+} CodecBlueprint;
+
+gboolean load_codecs (FsMediaType media_type);
+void unload_codecs (FsMediaType media_type);
+
+
+
+G_END_DECLS
+
+#endif /* __FS_RTP_DISCOVER_CODECS_H__ */
--
1.5.6.5
More information about the farsight-commits
mailing list