/*
* Farstream - Farstream RTP DTMF Event Source
*
* Copyright 2007 Collabora Ltd.
* @author: Olivier Crete <olivier.crete@collabora.co.uk>
* Copyright 2007 Nokia Corp.
*
* fs-rtp-dtmf-event-source.c - A Farstream RTP Event Source gobject
*
* 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-dtmf-event-source.h"
#include <farstream/fs-conference.h>
#include "fs-rtp-conference.h"
#include "fs-rtp-discover-codecs.h"
#include "fs-rtp-codec-negotiation.h"
#define GST_CAT_DEFAULT fsrtpconference_debug
/*
* SECTION:fs-rtp-dtmf-event-source
* @short_description: Class to create the source of DTMF events
*
* This class is manages the DTMF Event source and related matters
*
*/
/* all privates variables are protected by the mutex */
struct _FsRtpDtmfEventSourcePrivate {
gboolean disposed;
};
G_DEFINE_TYPE (FsRtpDtmfEventSource, fs_rtp_dtmf_event_source,
FS_TYPE_RTP_SPECIAL_SOURCE);
#define FS_RTP_DTMF_EVENT_SOURCE_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), FS_TYPE_RTP_DTMF_EVENT_SOURCE, \
FsRtpDtmfEventSourcePrivate))
static GstElement *
fs_rtp_dtmf_event_source_build (FsRtpSpecialSource *source,
GList *negotiated_codec_associations,
FsCodec *selected_codec);
static GList *fs_rtp_dtmf_event_source_class_add_blueprint (
FsRtpSpecialSourceClass *klass,
GList *blueprints);
static GList *fs_rtp_dtmf_event_source_negotiation_filter (
FsRtpSpecialSourceClass *klass,
GList *codec_associations);
static FsCodec *fs_rtp_dtmf_event_source_get_codec (
FsRtpSpecialSourceClass *klass,
GList *negotiated_codec_associations,
FsCodec *codec);
static void
fs_rtp_dtmf_event_source_class_init (FsRtpDtmfEventSourceClass *klass)
{
FsRtpSpecialSourceClass *spsource_class = FS_RTP_SPECIAL_SOURCE_CLASS (klass);
spsource_class->build = fs_rtp_dtmf_event_source_build;
spsource_class->add_blueprint = fs_rtp_dtmf_event_source_class_add_blueprint;
spsource_class->negotiation_filter =
fs_rtp_dtmf_event_source_negotiation_filter;
spsource_class->get_codec = fs_rtp_dtmf_event_source_get_codec;
g_type_class_add_private (klass, sizeof (FsRtpDtmfEventSourcePrivate));
}
static void
fs_rtp_dtmf_event_source_init (FsRtpDtmfEventSource *self)
{
self->priv = FS_RTP_DTMF_EVENT_SOURCE_GET_PRIVATE (self);
}
/**
* fs_rtp_dtmf_event_source_class_add_blueprint:
*
* Add one blueprint for telephone-event for each different clock-rate that
* exists in the request
*/
static GList*
fs_rtp_dtmf_event_source_class_add_blueprint (FsRtpSpecialSourceClass *klass,
GList *blueprints)
{
GList *item;
GList *already_done = NULL;
GstElementFactory *fact = NULL;
GList *new_blueprints = NULL;
fact = gst_element_factory_find ("rtpdtmfsrc");
if (fact)
{
gst_object_unref (fact);
}
else
{
GST_CAT_WARNING (fsrtpconference_disco,
"Could not find rtpdtmfsrc, will not offer DTMF events");
return blueprints;
}
fact = gst_element_factory_find ("rtpdtmfdepay");
if (!fact)
GST_CAT_WARNING (fsrtpconference_disco,
"Could not find rtpdtmfdepay, will not be able to receive DTMF events");
for (item = g_list_first (blueprints);
item;
item = g_list_next (item))
{
CodecBlueprint *bp = item->data;
GList *done_item = NULL;
gboolean skip = FALSE;
CodecBlueprint *new_bp = NULL;
if (bp->codec->media_type != FS_MEDIA_TYPE_AUDIO)
continue;
if (!g_ascii_strcasecmp (bp->codec->encoding_name, "telephone-event"))
continue;
if (bp->codec->clock_rate == 0)
continue;
for (done_item = g_list_first (already_done);
done_item;
done_item = g_list_next (done_item))
{
if (GPOINTER_TO_UINT (done_item->data) == bp->codec->clock_rate)
{
skip = TRUE;
break;
}
}
if (skip)
continue;
new_bp = g_slice_new0 (CodecBlueprint);
new_bp->codec = fs_codec_new (FS_CODEC_ID_ANY, "telephone-event",
FS_MEDIA_TYPE_AUDIO, bp->codec->clock_rate);
fs_codec_add_optional_parameter (new_bp->codec, "events", "0-15");
new_bp->rtp_caps = fs_codec_to_gst_caps (new_bp->codec);
new_bp->media_caps = gst_caps_new_any ();
if (fact)
new_bp->receive_pipeline_factory = g_list_prepend (NULL,
g_list_prepend (NULL, gst_object_ref (fact)));
new_blueprints = g_list_append (new_blueprints, new_bp);
already_done = g_list_prepend (already_done,
GUINT_TO_POINTER (bp->codec->clock_rate));
}
if (fact)
gst_object_unref (fact);
g_list_free (already_done);
blueprints = g_list_concat (blueprints, new_blueprints);
return blueprints;
}
static gboolean
_is_telephony_codec (CodecAssociation *ca, gpointer user_data)
{
guint clock_rate = GPOINTER_TO_UINT (user_data);
if (codec_association_is_valid_for_sending (ca, FALSE) &&
ca->codec->media_type == FS_MEDIA_TYPE_AUDIO &&
!g_ascii_strcasecmp (ca->codec->encoding_name, "telephone-event") &&
ca->codec->clock_rate == clock_rate)
return TRUE;
else
return FALSE;
}
/**
* fs_rtp_dtmf_event_source_get_codec:
* @negotiated_codec_associations: a #GList of currently negotiated
* #CodecAssociation
* @selected_codec: The current #FsCodec
*
* Find the telephone-event codec with the proper clock rate in the list
*
* Returns: The #FsCodec of type "telephone-event" with the requested clock-rate
* from the list, or %NULL
*/
static FsCodec *
fs_rtp_dtmf_event_source_get_codec (FsRtpSpecialSourceClass *klass,
GList *negotiated_codec_associations, FsCodec *selected_codec)
{
CodecAssociation *ca = NULL;
if (selected_codec->media_type != FS_MEDIA_TYPE_AUDIO)
return NULL;
ca = lookup_codec_association_custom (negotiated_codec_associations,
_is_telephony_codec, GUINT_TO_POINTER (selected_codec->clock_rate));
if (ca)
return ca->send_codec;
else
return NULL;
}
static GstElement *
fs_rtp_dtmf_event_source_build (FsRtpSpecialSource *source,
GList *negotiated_codec_associations,
FsCodec *selected_codec)
{
FsCodec *telephony_codec = NULL;
GstCaps *caps = NULL;
GstPad *pad = NULL;
GstElement *dtmfsrc = NULL;
GstElement *capsfilter = NULL;
GstPad *ghostpad = NULL;
GstElement *bin = NULL;
telephony_codec = fs_rtp_dtmf_event_source_get_codec (
FS_RTP_SPECIAL_SOURCE_GET_CLASS(source), negotiated_codec_associations,
selected_codec);
g_return_val_if_fail (telephony_codec, NULL);
source->codec = fs_codec_copy (telephony_codec);
bin = gst_bin_new (NULL);
GST_DEBUG ("Creating telephone-event source for " FS_CODEC_FORMAT,
FS_CODEC_ARGS (telephony_codec));
dtmfsrc = gst_element_factory_make ("rtpdtmfsrc", NULL);
if (!dtmfsrc)
{
GST_ERROR ("Could not make rtpdtmfsrc");
goto error;
}
if (!gst_bin_add (GST_BIN (bin), dtmfsrc))
{
GST_ERROR ("Could not add rtpdtmfsrc to bin");
gst_object_unref (dtmfsrc);
goto error;
}
capsfilter = gst_element_factory_make ("capsfilter", NULL);
if (!capsfilter)
{
GST_ERROR ("Could not make capsfilter");
goto error;
}
if (!gst_bin_add (GST_BIN (bin), capsfilter))
{
GST_ERROR ("Could not add capsfilter to bin");
gst_object_unref (capsfilter);
goto error;
}
caps = fs_codec_to_gst_caps (telephony_codec);
g_object_set (capsfilter, "caps", caps, NULL);
{
gchar *str = gst_caps_to_string (caps);
GST_DEBUG ("Using caps %s for dtmf", str);
g_free (str);
}
gst_caps_unref (caps);
if (!gst_element_link_pads (dtmfsrc, "src", capsfilter, "sink"))
{
GST_ERROR ("Could not link the rtpdtmfsrc and its capsfilter");
goto error;
}
pad = gst_element_get_static_pad (capsfilter, "src");
if (!pad)
{
GST_ERROR ("Could not get \"src\" pad from capsfilter");
goto error;
}
ghostpad = gst_ghost_pad_new ("src", pad);
if (!ghostpad)
{
GST_ERROR ("Could not create a ghostpad for capsfilter src pad for"
" rtpdtmfsrc");
goto error;
}
if (!gst_element_add_pad (bin, ghostpad))
{
GST_ERROR ("Could not get \"src\" ghostpad to dtmf source bin");
gst_object_unref (pad);
goto error;
}
gst_object_unref (pad);
return bin;
error:
gst_object_unref (bin);
return NULL;
}
/*
* This looks if there is a non-disabled codec with the requested clock rate
* other than telephone-event.
*/
static gboolean
has_rate (CodecAssociation *ca, gpointer user_data)
{
guint clock_rate = GPOINTER_TO_UINT (user_data);
if (ca->codec->clock_rate == clock_rate &&
!ca->recv_only &&
g_ascii_strcasecmp (ca->codec->encoding_name, "telephone-event"))
return TRUE;
else
return FALSE;
}
static GList *
fs_rtp_dtmf_event_source_negotiation_filter (FsRtpSpecialSourceClass *klass,
GList *codec_associations)
{
GList *tmp;
for (tmp = codec_associations; tmp; tmp = g_list_next (tmp))
{
CodecAssociation *ca = tmp->data;
/* Ignore disabled or non telephone-event codecs*/
if (ca->disable || ca->reserved || ca->recv_only ||
g_ascii_strcasecmp (ca->codec->encoding_name, "telephone-event"))
continue;
/* Lets disable telephone-event codecs where we don't find */
if (!lookup_codec_association_custom (codec_associations, has_rate,
GUINT_TO_POINTER (ca->codec->clock_rate)))
ca->disable = TRUE;
}
return codec_associations;
}