/*
* Farstream - Farstream RTP Discover Codecs
*
* Copyright 2007 Collabora Ltd.
* @author: Olivier Crete <olivier.crete@collabora.co.uk>
* @author Rob Taylor <rob.taylor@collabora.co.uk>
* @author Philippe Kalaf <philippe.kalaf@collabora.co.uk>
* Copyright 2007 Nokia Corp.
* Copyright 2005 INdT
* @author Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>
*
* fs-discover-codecs.c - A Farstream RTP Codec Discovery
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "fs-rtp-discover-codecs.h"
#include <string.h>
#include <farstream/fs-conference.h>
#include "fs-rtp-bin-error-downgrade.h"
#include "fs-rtp-conference.h"
#include "fs-rtp-codec-cache.h"
#include "fs-rtp-special-source.h"
#define GST_CAT_DEFAULT fsrtpconference_disco
/*
* 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 GList *remove_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,
gboolean one_is_enough);
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 void codec_blueprints_add_caps (FsMediaType media_type);
/* GLOBAL variables */
static GList *list_codec_blueprints[FS_MEDIA_TYPE_LAST+1] = { NULL };
static gint codecs_lists_ref[FS_MEDIA_TYPE_LAST+1] = { 0 };
G_LOCK_DEFINE_STATIC (codecs_lists);
static void
debug_pipeline (GstDebugLevel level, const gchar *prefix, GList *pipeline)
{
GList *walk;
GString *str;
gboolean first = TRUE;
if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) < level)
return;
str = g_string_new (prefix);
for (walk = pipeline; walk; walk = g_list_next (walk))
{
GList *walk2;
gboolean first_alt = TRUE;
if (!first)
g_string_append (str, " ->");
first = FALSE;
for (walk2 = g_list_first (walk->data); walk2; walk2 = g_list_next (walk2))
{
if (first_alt)
g_string_append_printf (str, " %s",
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (walk2->data)));
else
g_string_append_printf (str, " | %s",
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (walk2->data)));
first_alt = FALSE;
}
}
GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, level, NULL, "%s", str->str);
g_string_free (str, TRUE);
}
static void
debug_codec_cap (CodecCap *codec_cap)
{
if (codec_cap->caps)
GST_LOG ("%p:%d:media_caps %" GST_PTR_FORMAT, codec_cap->caps,
GST_CAPS_REFCOUNT_VALUE (codec_cap->caps), codec_cap->caps);
if (codec_cap->rtp_caps) {
GST_LOG ("%p:%d:rtp_caps %" GST_PTR_FORMAT, codec_cap->rtp_caps,
GST_CAPS_REFCOUNT_VALUE (codec_cap->rtp_caps), codec_cap->rtp_caps);
g_assert (gst_caps_get_size (codec_cap->rtp_caps) == 1);
}
debug_pipeline (GST_LEVEL_LOG, "element_list1: ", codec_cap->element_list1);
debug_pipeline (GST_LEVEL_LOG, "element_list2: ", codec_cap->element_list2);
}
static void
debug_codec_cap_list (GList *codec_cap_list)
{
GList *walk;
GST_LOG ("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
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)
{
g_list_foreach (walk->data, (GFunc) gst_object_unref, NULL);
g_list_free (walk->data);
}
}
for (walk = codec_cap->element_list2; walk; walk = g_list_next (walk))
{
if (walk->data)
{
g_list_foreach (walk->data, (GFunc) gst_object_unref, NULL);
g_list_free (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_slice_free (CodecCap, 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);
}
/**
* fs_rtp_blueprints_get
* @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: a #GList of #CodecBlueprint or NULL on error
*/
GList *
fs_rtp_blueprints_get (FsMediaType media_type, GError **error)
{
GstCaps *caps;
GList *recv_list = NULL;
GList *send_list = NULL;
GList *ret = NULL;
if (media_type > FS_MEDIA_TYPE_LAST)
{
g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS,
"Invalid media type given");
return NULL;
}
G_LOCK (codecs_lists);
codecs_lists_ref[media_type]++;
/* if already computed just return list */
if (codecs_lists_ref[media_type] > 1)
{
if (!list_codec_blueprints[media_type])
g_set_error (error, FS_ERROR, FS_ERROR_NO_CODECS,
"No codecs for media type %s detected",
fs_media_type_to_string (media_type));
ret = list_codec_blueprints[media_type];
goto out;
}
list_codec_blueprints[media_type] = load_codecs_cache (media_type);
if (list_codec_blueprints[media_type]) {
GST_DEBUG ("Loaded codec blueprints from cache file");
ret = list_codec_blueprints[media_type];
goto out;
}
/* 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 if (media_type == FS_MEDIA_TYPE_APPLICATION)
{
caps = gst_caps_new_simple ("application/x-rtp",
"media", G_TYPE_STRING, "application", NULL);
}
else
{
g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS,
"Invalid media type given to load_codecs");
codecs_lists_ref[media_type]--;
goto out;
}
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_set_error (error, FS_ERROR, FS_ERROR_NO_CODECS,
"No codecs for media type %s detected",
fs_media_type_to_string (media_type));
list_codec_blueprints[media_type] = NULL;
goto out;
}
create_codec_lists (media_type, recv_list, send_list);
/* Save the codecs blueprint cache */
save_codecs_cache (media_type, list_codec_blueprints[media_type]);
ret = list_codec_blueprints[media_type];
out:
G_UNLOCK (codecs_lists);
if (recv_list)
codec_cap_list_free (recv_list);
if (send_list)
codec_cap_list_free (send_list);
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, FALSE);
if (!duplex_list) {
GST_WARNING ("There are no send/recv codecs");
return FALSE;
}
GST_LOG ("*******Intersection of send_list and recv_list");
debug_codec_cap_list (duplex_list);
duplex_list = remove_dynamic_duplicates (duplex_list);
duplex_list = remove_duplicates (duplex_list);
if (!duplex_list) {
GST_WARNING ("Dynamic duplicate removal left us with nothing");
return FALSE;
}
parse_codec_cap_list (duplex_list, media_type);
codec_cap_list_free (duplex_list);
list_codec_blueprints[media_type] =
fs_rtp_special_sources_add_blueprints (list_codec_blueprints[media_type]);
codec_blueprints_add_caps (media_type);
return (list_codec_blueprints[media_type] != NULL);
}
static gboolean
struct_field_has_line (GstStructure *s, const gchar *field, const gchar *value)
{
const gchar *tmp = gst_structure_get_string (s, field);
const GValue *v = NULL;
gint i;
if (tmp)
return !strcmp (value, tmp);
if (!gst_structure_has_field_typed (s, field, GST_TYPE_LIST))
return FALSE;
v = gst_structure_get_value (s, field);
for (i=0; i < gst_value_list_get_size (v); i++)
{
const GValue *listval = gst_value_list_get_value (v, i);
if (G_VALUE_HOLDS_STRING(listval) &&
!strcmp (value, g_value_get_string (listval)))
return TRUE;
}
return FALSE;
}
/*
* This function returns TRUE if the codec_cap should be accepted,
* FALSE otherwise
*/
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);
GstStructure *rtp_struct;
const gchar *encoding_name;
if (!name)
return FALSE;
/* let's check if it's h263 */
if (strcmp (name, "video/x-h263"))
return TRUE;
/* If we don't have a h263version field, accept everything */
if (!gst_structure_has_field (media_struct, "h263version"))
return TRUE;
rtp_struct = gst_caps_get_structure (codec_cap->rtp_caps, 0);
if (!rtp_struct)
return FALSE;
encoding_name = gst_structure_get_string (rtp_struct, "encoding-name");
/* If there is no encoding name, we have a problem, lets refuse it */
if (!encoding_name)
return FALSE;
if (struct_field_has_line (media_struct, "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 (struct_field_has_line (media_struct, "h263version", "h263p"))
{
/* has to be H263-1998 */
if (strcmp (encoding_name, "H263-1998"))
return FALSE;
}
else if (struct_field_has_line (media_struct, "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;
GList *remove_us = NULL;
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");
if (!value)
continue;
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)
remove_us = g_list_prepend (remove_us, cur_codec_cap);
}
}
}
for (walk1 = remove_us; walk1; walk1 = g_list_next (walk1))
{
list = g_list_remove_all (list, walk1->data);
codec_cap_free (walk1->data);
}
g_list_free (remove_us);
return list;
}
/* Removes duplicate codecs that have the same RTP expression */
static GList *
remove_duplicates (GList *list)
{
GList *walk1, *walk2;
for (walk1 = list; walk1; walk1 = g_list_next (walk1))
{
CodecCap *codec_cap1 = walk1->data;
again:
for (walk2 = walk1->next; walk2; walk2 = g_list_next (walk2))
{
CodecCap *codec_cap2 = walk2->data;
if (gst_caps_is_equal (codec_cap1->rtp_caps, codec_cap2->rtp_caps))
{
codec_cap_free (codec_cap2);
walk1 = g_list_delete_link (walk1, walk2);
goto again;
}
}
}
return list;
}
static GList *
copy_element_list (GList *inlist)
{
GQueue outqueue = G_QUEUE_INIT;
GList *tmp1;
for (tmp1 = g_list_first (inlist); tmp1; tmp1 = g_list_next (tmp1)) {
g_queue_push_tail (&outqueue, g_list_copy (tmp1->data));
g_list_foreach (tmp1->data, (GFunc) gst_object_ref, NULL);
}
return outqueue.head;
}
/* 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;
GstElementFactory *tmpfact;
/* go thru all common caps */
for (walk = list; walk; walk = g_list_next (walk))
{
codec_cap = (CodecCap *)(walk->data);
codec = fs_codec_new (FS_CODEC_ID_ANY, NULL, media_type, 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");
GST_DEBUG ("skipping codec %s/%s, no encoding name specified"
" (pt: %d clock_rate:%u",
fs_media_type_to_string (media_type),
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;
}
codec_blueprint = g_slice_new0 (CodecBlueprint);
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 =
copy_element_list (codec_cap->element_list2);
codec_blueprint->receive_pipeline_factory =
copy_element_list (codec_cap->element_list1);
/* Lets add the converters at the beginning of the encoding pipelines */
if (media_type == FS_MEDIA_TYPE_VIDEO)
{
tmpfact = gst_element_factory_find ("fsvideoanyrate");
if (tmpfact)
{
codec_blueprint->send_pipeline_factory = g_list_append (
codec_blueprint->send_pipeline_factory,
g_list_append (NULL, tmpfact));
}
tmpfact = gst_element_factory_find ("videoconvert");
if (tmpfact)
{
codec_blueprint->send_pipeline_factory = g_list_append (
codec_blueprint->send_pipeline_factory,
g_list_append (NULL, tmpfact));
}
tmpfact = gst_element_factory_find ("videoscale");
if (tmpfact)
{
codec_blueprint->send_pipeline_factory = g_list_append (
codec_blueprint->send_pipeline_factory,
g_list_append (NULL, tmpfact));
}
}
else if (media_type == FS_MEDIA_TYPE_AUDIO)
{
tmpfact = gst_element_factory_find ("audioconvert");
if (tmpfact)
{
codec_blueprint->send_pipeline_factory = g_list_append (
codec_blueprint->send_pipeline_factory,
g_list_append (NULL, tmpfact));
}
tmpfact = gst_element_factory_find ("audioresample");
if (tmpfact)
{
codec_blueprint->send_pipeline_factory = g_list_append (
codec_blueprint->send_pipeline_factory,
g_list_append (NULL, tmpfact));
}
tmpfact = gst_element_factory_find ("audioconvert");
if (tmpfact)
{
codec_blueprint->send_pipeline_factory = g_list_append (
codec_blueprint->send_pipeline_factory,
g_list_append (NULL, tmpfact));
}
tmpfact = gst_element_factory_find ("spanplc");
if (tmpfact)
{
GstElementFactory *tmpfact2;
tmpfact2 = gst_element_factory_find ("audioconvert");
if (tmpfact2)
{
codec_blueprint->receive_pipeline_factory = g_list_append (
codec_blueprint->receive_pipeline_factory,
g_list_append (NULL, tmpfact2));
codec_blueprint->receive_pipeline_factory = g_list_append (
codec_blueprint->receive_pipeline_factory,
g_list_append (NULL, tmpfact));
}
}
}
/* insert new information into tables */
list_codec_blueprints[media_type] = g_list_append (
list_codec_blueprints[media_type], codec_blueprint);
GST_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);
GST_DEBUG ("media_caps: %" GST_PTR_FORMAT, codec_blueprint->media_caps);
GST_DEBUG ("rtp_caps: %" GST_PTR_FORMAT, codec_blueprint->rtp_caps);
debug_pipeline (GST_LEVEL_DEBUG, "send pipeline: ",
codec_blueprint->send_pipeline_factory);
debug_pipeline (GST_LEVEL_DEBUG, "receive pipeline: ",
codec_blueprint->receive_pipeline_factory);
}
}
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"));
}
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"));
}
/* 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)
{
GST_WARNING ("No RTP Payloaders found");
return NULL;
}
else {
GST_LOG ("**Payloaders");
debug_codec_cap_list (payloaders);
}
/* 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);
GST_WARNING ("No encoders found");
return NULL;
}
else {
GST_LOG ("**Encoders");
debug_codec_cap_list (encoders);
}
/* create intersection list of codecs common
* to encoders and payloaders lists */
send_list = codec_cap_list_intersect (payloaders, encoders, TRUE);
if (!send_list)
{
GST_WARNING ("No compatible encoder/payloader pairs found");
}
else {
GST_LOG ("**intersection of payloaders and encoders");
debug_codec_cap_list (send_list);
}
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)
{
GST_WARNING ("No RTP Depayloaders found");
return NULL;
}
else {
GST_LOG ("**Depayloaders");
debug_codec_cap_list (depayloaders);
}
/* 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);
GST_WARNING ("No decoders found");
return NULL;
}
else {
GST_LOG ("**Decoders");
debug_codec_cap_list (decoders);
}
/* create intersection list of codecs common
* to decoders and depayloaders lists */
recv_list = codec_cap_list_intersect (depayloaders, decoders, TRUE);
if (!recv_list)
{
GST_WARNING ("No compatible decoder/depayloader pairs found");
}
else {
GST_LOG ("**intersection of depayloaders and decoders");
debug_codec_cap_list (recv_list);
}
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, gboolean one_is_enough)
{
GList *walk1, *walk2;
CodecCap *codec_cap1, *codec_cap2;
GstCaps *caps1, *caps2;
GstCaps *rtp_caps1, *rtp_caps2;
GList *intersection_list = NULL;
for (walk1 = g_list_first (list1); walk1; walk1 = g_list_next (walk1))
{
CodecCap *item = NULL;
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 = NULL;
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)))
{
if (item) {
GList *tmplist;
item->caps = gst_caps_merge (item->caps, intersection);
for (tmplist = g_list_first (codec_cap2->element_list1->data);
tmplist;
tmplist = g_list_next (tmplist)) {
if (g_list_index (item->element_list2->data, tmplist->data) < 0) {
item->element_list2->data = g_list_concat (
item->element_list2->data,
g_list_copy (codec_cap2->element_list1->data));
g_list_foreach (codec_cap2->element_list1->data,
(GFunc) gst_object_ref, NULL);
}
}
} else {
item = g_slice_new0 (CodecCap);
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 */
item->element_list1 = g_list_concat (
copy_element_list (codec_cap1->element_list1),
copy_element_list (codec_cap1->element_list2));
item->element_list2 = g_list_concat (
copy_element_list (codec_cap2->element_list1),
copy_element_list (codec_cap2->element_list2));
intersection_list = g_list_append (intersection_list, item);
if (rtp_intersection) {
break;
}
}
} else {
if (rtp_intersection)
gst_caps_unref (rtp_intersection);
gst_caps_unref (intersection);
}
}
if (!item && one_is_enough) {
item = g_slice_new0 (CodecCap);
item->caps = gst_caps_ref (codec_cap1->caps);
item->rtp_caps = gst_caps_ref (codec_cap1->rtp_caps);
item->element_list1 = copy_element_list (codec_cap1->element_list1);
item->element_list2 = copy_element_list (codec_cap1->element_list2);
intersection_list = g_list_append (intersection_list, item);
}
}
return intersection_list;
}
void
codec_blueprint_destroy (CodecBlueprint *codec_blueprint)
{
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);
}
if (codec_blueprint->input_caps)
{
gst_caps_unref (codec_blueprint->input_caps);
}
if (codec_blueprint->output_caps)
{
gst_caps_unref (codec_blueprint->output_caps);
}
for (walk = codec_blueprint->send_pipeline_factory;
walk; walk = g_list_next (walk))
{
if (walk->data)
{
g_list_foreach (walk->data, (GFunc) gst_object_unref, NULL);
g_list_free (walk->data);
}
}
for (walk = codec_blueprint->receive_pipeline_factory;
walk; walk = g_list_next (walk))
{
if (walk->data)
{
g_list_foreach (walk->data, (GFunc) gst_object_unref, NULL);
g_list_free (walk->data);
}
}
g_list_free (codec_blueprint->send_pipeline_factory);
g_list_free (codec_blueprint->receive_pipeline_factory);
g_slice_free (CodecBlueprint, codec_blueprint);
}
void
fs_rtp_blueprints_unref (FsMediaType media_type)
{
G_LOCK (codecs_lists);
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;
}
}
G_UNLOCK (codecs_lists);
}
/* 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 (!gst_element_factory_get_num_pad_templates (factory))
{
return FALSE;
}
pads = gst_element_factory_get_static_pad_templates (factory);
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;
return !gst_caps_can_intersect (element->caps, c_caps);
}
static gint
compare_rtp_caps (CodecCap *element, GstCaps *c_caps)
{
return !gst_caps_can_intersect (element->rtp_caps, c_caps);
}
/* 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 = gst_element_factory_get_static_pad_templates (factory);
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 (padtemplate->presence != GST_PAD_ALWAYS) {
continue;
}
caps = gst_static_pad_template_get_caps (padtemplate);
/*
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 = 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)
{
GST_DEBUG ("skipping %s : %s",
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)), name);
continue;
}
cur_caps = gst_caps_new_full (gst_structure_copy (structure), NULL);
/* 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_slice_new0 (CodecCap);
entry->caps = cur_caps;
if (rtp_caps)
{
entry->rtp_caps = rtp_caps;
gst_caps_ref (rtp_caps);
}
list = g_list_append (list, entry);
entry->element_list1 = g_list_prepend (NULL,
g_list_prepend (NULL, factory));
gst_object_ref (factory);
}
else
{
entry->element_list1->data =
g_list_append (entry->element_list1->data, factory);
gst_object_ref (factory);
if (rtp_caps) {
if (entry->rtp_caps) {
GstCaps *tmp = gst_caps_intersect (rtp_caps, entry->rtp_caps);
gst_caps_unref (entry->rtp_caps);
entry->rtp_caps = tmp;
} else {
entry->rtp_caps = gst_caps_ref (rtp_caps);
/* This shouldn't happen, its we're looking at rtp elements
* or we're not */
g_assert_not_reached ();
}
}
entry->caps = gst_caps_merge (cur_caps, entry->caps);
}
}
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;
GList *list = NULL;
GstCaps *matched_caps = NULL;
result = gst_registry_get_feature_list (gst_registry_get (),
GST_TYPE_ELEMENT_FACTORY);
result = g_list_sort (result, (GCompareFunc) compare_ranks);
for (walk = result; walk; walk = walk->next)
{
GstElementFactory *factory = GST_ELEMENT_FACTORY (walk->data);
/* Ignore unranked plugins */
if (gst_plugin_feature_get_rank (GST_PLUGIN_FEATURE (factory)) ==
GST_RANK_NONE)
continue;
if (!filter (factory))
continue;
if (caps && !check_caps_compatibility (factory, caps, &matched_caps))
continue;
if (!matched_caps)
{
list = create_codec_cap_list (factory, direction, list, NULL);
}
else
{
gint i;
GPtrArray *capslist = g_ptr_array_new_with_free_func (
(GDestroyNotify) gst_caps_unref);
while (gst_caps_get_size (matched_caps) > 0)
{
GstCaps *stolencaps = gst_caps_new_full (
gst_caps_steal_structure (matched_caps, 0), NULL);
gboolean got_match = FALSE;
for (i = 0; i < capslist->len; i++)
{
GstCaps *intersect = gst_caps_intersect (stolencaps,
g_ptr_array_index (capslist, i));
if (gst_caps_is_empty (intersect))
{
gst_caps_unref (intersect);
}
else
{
got_match = TRUE;
gst_caps_unref (g_ptr_array_index (capslist, i));
g_ptr_array_index (capslist, i) = intersect;
}
}
if (got_match)
gst_caps_unref (stolencaps);
else
g_ptr_array_add (capslist, stolencaps);
}
gst_caps_unref (matched_caps);
for (i = 0; i < capslist->len; i++)
list = create_codec_cap_list (factory, direction, list,
g_ptr_array_index (capslist, i));
g_ptr_array_unref (capslist);
}
}
gst_plugin_feature_list_free (result);
return list;
}
/*
* fill FarstreamCodec 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 (strcmp (tmp, "application") == 0)
{
codec->media_type = FS_MEDIA_TYPE_APPLICATION;
}
}
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 Farstream */
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 == GST_TYPE_LIST)
{
value = gst_value_list_get_value (value, 0);
type = G_VALUE_TYPE (value);
}
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)
fs_codec_add_optional_parameter (codec, field_name,
g_value_get_string (value));
}
return TRUE;
}
gboolean
codec_blueprint_has_factory (CodecBlueprint *blueprint,
FsStreamDirection direction)
{
if (direction == FS_DIRECTION_SEND)
return (blueprint->send_pipeline_factory != NULL);
else if (direction == FS_DIRECTION_RECV)
return (blueprint->receive_pipeline_factory != NULL);
else
g_assert_not_reached ();
}
static gboolean
_g_object_has_property (GObject *object, const gchar *property)
{
GObjectClass *klass;
klass = G_OBJECT_GET_CLASS (object);
return NULL != g_object_class_find_property (klass, property);
}
static gboolean
_create_ghost_pad (GstElement *current_element, const gchar *padname, GstElement
*codec_bin, GError **error)
{
GstPad *ghostpad;
GstPad *pad = gst_element_get_static_pad (current_element, padname);
gboolean ret = FALSE;
if (!pad)
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not find the %s pad on the element", padname);
return FALSE;
}
ghostpad = gst_ghost_pad_new (padname, pad);
if (!ghostpad)
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not create a ghost pad for pad %s", padname);
goto done;
}
if (!gst_pad_set_active (ghostpad, TRUE))
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not active ghostpad %s", padname);
gst_object_unref (ghostpad);
goto done;
}
if (!gst_element_add_pad (codec_bin, ghostpad))
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not add ghostpad %s to the codec bin", padname);
ret = TRUE;
done:
gst_object_unref (pad);
return ret;
}
/*
* Builds a codec bin in the specified direction for the specified codec
* using the specified blueprint
*/
GstElement *
create_codec_bin_from_blueprint (const FsCodec *codec,
CodecBlueprint *blueprint, const gchar *name, FsStreamDirection direction,
GError **error)
{
GstElement *codec_bin = NULL;
const gchar *direction_str;
GList *walk = NULL;
GstElement *current_element = NULL;
GstElement *previous_element = NULL;
GList *pipeline_factory = NULL;
if (direction == FS_DIRECTION_SEND)
{
direction_str = "send";
pipeline_factory = blueprint->send_pipeline_factory;
}
else if (direction == FS_DIRECTION_RECV)
{
direction_str = "receive";
pipeline_factory = blueprint->receive_pipeline_factory;
}
else
{
g_assert_not_reached ();
}
if (!pipeline_factory)
{
g_set_error (error, FS_ERROR, FS_ERROR_UNKNOWN_CODEC,
"The %s codec %s does not have a pipeline,"
" its probably a special codec",
fs_media_type_to_string (codec->media_type),
codec->encoding_name);
return NULL;
}
GST_DEBUG ("creating %s codec bin for id %d, pipeline_factory %p",
direction_str, codec->id, pipeline_factory);
if (direction == FS_DIRECTION_SEND)
codec_bin = gst_bin_new (name);
else if (direction == FS_DIRECTION_RECV)
codec_bin = fs_rtp_bin_error_downgrade_new (name);
else
g_assert_not_reached ();
for (walk = g_list_first (pipeline_factory); walk; walk = g_list_next (walk))
{
if (g_list_next (g_list_first (walk->data)))
{
/* We have to check some kind of configuration to see if we have a
favorite */
current_element = gst_element_factory_make ("autoconvert", NULL);
if (!current_element)
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not create autoconvert element");
goto error;
}
g_object_set (current_element, "factories", walk->data, NULL);
} else {
current_element =
gst_element_factory_create (
GST_ELEMENT_FACTORY (g_list_first (walk->data)->data), NULL);
if (!current_element)
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not create element for pt %d", codec->id);
goto error;
}
}
if (!gst_bin_add (GST_BIN (codec_bin), current_element))
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not add new element to %s codec_bin for pt %d",
direction_str, codec->id);
goto error;
}
if (_g_object_has_property (G_OBJECT (current_element), "pt"))
g_object_set (current_element, "pt", codec->id,
NULL);
/* Lets create the ghost pads on the codec bin */
if (g_list_previous (walk) == NULL)
/* if its the first element of the codec bin */
if (!_create_ghost_pad (current_element,
(direction == FS_DIRECTION_SEND) ? "src" : "sink", codec_bin,
error))
goto error;
if (g_list_next (walk) == NULL)
/* if its the last element of the codec bin */
if (!_create_ghost_pad (current_element,
(direction == FS_DIRECTION_SEND) ? "sink" : "src" , codec_bin,
error))
goto error;
/* 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)
{
GstPad *sinkpad;
GstPad *srcpad;
GstPadLinkReturn ret;
if (direction == FS_DIRECTION_SEND)
sinkpad = gst_element_get_static_pad (previous_element, "sink");
else if (direction == FS_DIRECTION_RECV)
sinkpad = gst_element_get_static_pad (current_element, "sink");
else
g_assert_not_reached ();
if (!sinkpad)
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not get the sink pad one of the elements in the %s codec bin"
" for pt %d", direction_str, codec->id);
goto error;
}
if (direction == FS_DIRECTION_SEND)
srcpad = gst_element_get_static_pad (current_element, "src");
else if (direction == FS_DIRECTION_RECV)
srcpad = gst_element_get_static_pad (previous_element, "src");
else
g_assert_not_reached ();
if (!srcpad)
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not get the src pad one of the elements in the %s codec bin"
" for pt %d", direction_str, codec->id);
gst_object_unref (sinkpad);
goto error;
}
ret = gst_pad_link (srcpad, sinkpad);
gst_object_unref (srcpad);
gst_object_unref (sinkpad);
if (GST_PAD_LINK_FAILED (ret))
{
g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION,
"Could not link element inside the %s codec bin for pt %d",
direction_str, codec->id);
goto error;
}
}
previous_element = current_element;
}
return codec_bin;
error:
gst_object_unref (codec_bin);
return NULL;
}
GstCaps *
codec_get_in_out_caps (FsCodec *codec, GstCaps *rtp_caps,
FsStreamDirection direction, GstElement *codecbin)
{
GstElement *capsfilter = NULL;
GstPad *pad = NULL;
gboolean r;
const gchar *padname = (direction == FS_DIRECTION_SEND) ? "sink" : "src";
GstCaps *caps = NULL;
capsfilter = gst_element_factory_make ("capsfilter", NULL);
g_object_set (capsfilter, "caps", rtp_caps, NULL);
if (direction == FS_DIRECTION_SEND)
r = gst_element_link (codecbin, capsfilter);
else if (direction == FS_DIRECTION_RECV)
r = gst_element_link (capsfilter, codecbin);
else
g_assert_not_reached ();
if (!r)
{
GST_WARNING ("Could not link capsfilter to codecbin for " FS_CODEC_FORMAT,
FS_CODEC_ARGS (codec));
goto done;
}
pad = gst_element_get_static_pad (codecbin, padname);
if (!pad)
{
GST_WARNING ("Could not get %s pad on codecbin for " FS_CODEC_FORMAT,
padname, FS_CODEC_ARGS (codec));
goto done;
}
caps = gst_pad_query_caps (pad, NULL);
if (!caps)
{
GST_WARNING ("Query for caps on codecbin failed for "
FS_CODEC_FORMAT, FS_CODEC_ARGS (codec));
goto done;
}
done:
g_clear_object (&pad);
g_clear_object (&capsfilter);
return caps;
}
static void
codec_blueprints_add_caps (FsMediaType media_type)
{
GList *item;
for (item = list_codec_blueprints[media_type]; item;)
{
GList *next = item->next;
CodecBlueprint *blueprint = item->data;
gboolean success = FALSE;
GError *error = NULL;
FsCodec *codec_copy = NULL;
/* If there are no pipelines, it's all ok */
if (!blueprint->send_pipeline_factory &&
!blueprint->receive_pipeline_factory)
{
success = TRUE;
goto next;
}
codec_copy = fs_codec_copy (blueprint->codec);
if (codec_copy->id == FS_CODEC_ID_ANY)
codec_copy->id = 96;
if (blueprint->send_pipeline_factory)
{
GstElement *codecbin;
codecbin = create_codec_bin_from_blueprint (codec_copy, blueprint,
"gather_send_codecbin", FS_DIRECTION_SEND, &error);
if (!codecbin)
{
GST_WARNING ("Could not create send codec bin from blueprint for "
FS_CODEC_FORMAT": %s", FS_CODEC_ARGS (blueprint->codec),
error->message);
goto next;
}
blueprint->input_caps = codec_get_in_out_caps (blueprint->codec,
blueprint->rtp_caps, FS_DIRECTION_SEND, codecbin);
gst_object_unref (codecbin);
if (blueprint->input_caps == NULL)
goto next;
}
if (blueprint->receive_pipeline_factory)
{
GstElement *codecbin;
codecbin = create_codec_bin_from_blueprint (codec_copy, blueprint,
"gather_recv_codecbin", FS_DIRECTION_RECV, &error);
if (!codecbin)
{
GST_WARNING ("Could not create receive codec bin from blueprint for "
FS_CODEC_FORMAT": %s", FS_CODEC_ARGS (blueprint->codec),
error->message);
goto next;
}
blueprint->output_caps = codec_get_in_out_caps (blueprint->codec,
blueprint->rtp_caps, FS_DIRECTION_RECV, codecbin);
gst_object_unref (codecbin);
if (!blueprint->output_caps)
goto next;
}
if (blueprint->input_caps == NULL)
blueprint->input_caps = gst_caps_new_any ();
if (blueprint->output_caps == NULL)
blueprint->output_caps = gst_caps_new_any ();
success = TRUE;
next:
if (codec_copy)
fs_codec_destroy (codec_copy);
g_clear_error (&error);
if (!success)
{
codec_blueprint_destroy (blueprint);
list_codec_blueprints[media_type] = g_list_delete_link (
list_codec_blueprints[media_type], item);
}
item = next;
}
}