/*
* Farstream - Farstream RTP DTMF Sound Source
*
* Copyright 2007-2009 Collabora Ltd.
* @author: Olivier Crete <olivier.crete@collabora.co.uk>
* Copyright 2007-2009 Nokia Corp.
*
* fs-rtp-dtmf-sound-source.c - A Farstream RTP Sound 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-sound-source.h"
#include <farstream/fs-conference.h>
#include "fs-rtp-conference.h"
#include "fs-rtp-discover-codecs.h"
#include "fs-rtp-codec-negotiation.h"
#include "fs-rtp-codec-specific.h"
#define GST_CAT_DEFAULT fsrtpconference_debug
/*
* SECTION:fs-rtp-dtmf-sound-source
* @short_description: Class to create the source of DTMF sounds
*
* This class is manages the DTMF Sound source and related matters
*
*/
/* all privates variables are protected by the mutex */
struct _FsRtpDtmfSoundSourcePrivate {
gboolean disposed;
};
G_DEFINE_TYPE(FsRtpDtmfSoundSource, fs_rtp_dtmf_sound_source,
FS_TYPE_RTP_SPECIAL_SOURCE);
#define FS_RTP_DTMF_SOUND_SOURCE_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), FS_TYPE_RTP_DTMF_SOUND_SOURCE, \
FsRtpDtmfSoundSourcePrivate))
static GstElement *
fs_rtp_dtmf_sound_source_build (FsRtpSpecialSource *source,
GList *negotiated_codec_associations,
FsCodec *selected_codec);
static FsCodec *fs_rtp_dtmf_sound_source_get_codec (
FsRtpSpecialSourceClass *klass,
GList *negotiated_codec_associations,
FsCodec *selected_codec);
static void
fs_rtp_dtmf_sound_source_class_init (FsRtpDtmfSoundSourceClass *klass)
{
FsRtpSpecialSourceClass *spsource_class = FS_RTP_SPECIAL_SOURCE_CLASS (klass);
spsource_class->build = fs_rtp_dtmf_sound_source_build;
spsource_class->get_codec = fs_rtp_dtmf_sound_source_get_codec;
g_type_class_add_private (klass, sizeof (FsRtpDtmfSoundSourcePrivate));
}
static void
fs_rtp_dtmf_sound_source_init (FsRtpDtmfSoundSource *self)
{
self->priv = FS_RTP_DTMF_SOUND_SOURCE_GET_PRIVATE (self);
}
static gboolean
_is_law_codec (CodecAssociation *ca, gpointer user_data)
{
if (codec_association_is_valid_for_sending (ca, FALSE) &&
(ca->codec->id == 0 || ca->codec->id == 8))
return TRUE;
else
return FALSE;
}
/**
* get_telephone_sound_codec:
* @codecs: a #GList of #FsCodec
*
* Find the first occurence of PCMA or PCMU codecs
*
* Returns: The #FsCodec of type PCMA/U from the list or %NULL
*/
static FsCodec *
get_pcm_law_sound_codec (GList *codecs,
gchar **encoder_name,
gchar **payloader_name,
CodecAssociation **out_ca)
{
CodecAssociation *ca = NULL;
ca = lookup_codec_association_custom (codecs, _is_law_codec, NULL);
if (!ca)
return NULL;
if (ca->codec->id == 0)
{
if (encoder_name)
*encoder_name = "mulawenc";
if (payloader_name)
*payloader_name = "rtppcmupay";
}
else if (ca->codec->id == 8)
{
if (encoder_name)
*encoder_name = "alawenc";
if (payloader_name)
*payloader_name = "rtppcmapay";
}
if (out_ca)
*out_ca = ca;
return ca->send_codec;
}
static gboolean
_check_element_factory (gchar *name)
{
GstElementFactory *fact = NULL;
g_return_val_if_fail (name, FALSE);
fact = gst_element_factory_find (name);
if (fact)
gst_object_unref (fact);
return (fact != NULL);
}
static CodecAssociation *
_get_main_codec_association (GList *codec_associations, FsCodec *codec)
{
CodecAssociation *ca = lookup_codec_association_by_codec_for_sending (
codec_associations, codec);
if (ca && codec_association_is_valid_for_sending (ca, TRUE) &&
codec_blueprint_has_factory (ca->blueprint, FS_DIRECTION_SEND))
return ca;
else
return NULL;
}
static FsCodec *
fs_rtp_dtmf_sound_source_get_codec (FsRtpSpecialSourceClass *klass,
GList *negotiated_codec_associations,
FsCodec *selected_codec)
{
FsCodec *codec = NULL;
gchar *encoder_name = NULL;
gchar *payloader_name = NULL;
CodecAssociation *ca;
if (selected_codec->media_type != FS_MEDIA_TYPE_AUDIO)
return NULL;
if (!_check_element_factory ("dtmfsrc"))
return NULL;
if (selected_codec->clock_rate == 8000)
{
codec = get_pcm_law_sound_codec (negotiated_codec_associations,
&encoder_name, &payloader_name, NULL);
if (codec) {
if (!_check_element_factory (encoder_name))
return NULL;
if (!_check_element_factory (payloader_name))
return NULL;
return codec;
}
}
ca = _get_main_codec_association (negotiated_codec_associations,
selected_codec);
if (ca)
return ca->send_codec;
else
return NULL;
}
static GstElement *
fs_rtp_dtmf_sound_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;
GstElement *encoder = NULL;
GstElement *payloader = NULL;
gchar *encoder_name = NULL;
gchar *payloader_name = NULL;
CodecAssociation *ca = NULL;
if (selected_codec->clock_rate == 8000)
telephony_codec = get_pcm_law_sound_codec (negotiated_codec_associations,
&encoder_name, &payloader_name, &ca);
if (!telephony_codec)
{
ca = _get_main_codec_association (negotiated_codec_associations,
selected_codec);
if (ca)
telephony_codec = ca->send_codec;
}
g_return_val_if_fail (telephony_codec, NULL);
source->codec = fs_codec_copy (telephony_codec);
GST_DEBUG ("Creating dtmf sound source for " FS_CODEC_FORMAT,
FS_CODEC_ARGS (telephony_codec));
bin = gst_bin_new (NULL);
dtmfsrc = gst_element_factory_make ("dtmfsrc", 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);
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 dtmfsrc");
goto error;
}
if (!gst_element_add_pad (bin, ghostpad))
{
GST_ERROR ("Could not get \"src\" ghostpad to dtmf sound source bin");
gst_object_unref (pad);
goto error;
}
gst_object_unref (pad);
if (ca)
{
gchar *codec_bin_name = g_strdup_printf ("dtmf_send_codecbin_%d",
telephony_codec->id);
GError *error = NULL;
GstElement *codecbin = create_codec_bin_from_blueprint (
telephony_codec, ca->blueprint, codec_bin_name, FS_DIRECTION_SEND,
&error);
if (!codecbin)
{
GST_ERROR ("Could not make %s: %s", codec_bin_name,
error ? error->message : "No error message!");
g_clear_error (&error);
g_free (codec_bin_name);
goto error;
}
if (!gst_bin_add (GST_BIN (bin), codecbin))
{
GST_ERROR ("Could not add %s to bin", codec_bin_name);
gst_object_unref (codecbin);
g_free (codec_bin_name);
goto error;
}
if (!gst_element_link_pads (dtmfsrc, "src", codecbin, "sink"))
{
GST_ERROR ("Could not link the rtpdtmfsrc and %s", codec_bin_name);
g_free (codec_bin_name);
goto error;
}
if (!gst_element_link_pads (codecbin, "src", capsfilter, "sink"))
{
GST_ERROR ("Could not link the %s and its capsfilter", codec_bin_name);
g_free (codec_bin_name);
goto error;
}
g_free (codec_bin_name);
}
else
{
encoder = gst_element_factory_make (encoder_name, NULL);
if (!encoder)
{
GST_ERROR ("Could not make %s", encoder_name);
goto error;
}
if (!gst_bin_add (GST_BIN (bin), encoder))
{
GST_ERROR ("Could not add %s to bin", encoder_name);
gst_object_unref (encoder);
goto error;
}
if (!gst_element_link_pads (dtmfsrc, "src", encoder, "sink"))
{
GST_ERROR ("Could not link the rtpdtmfsrc and %s", encoder_name);
goto error;
}
payloader = gst_element_factory_make (payloader_name, NULL);
if (!payloader)
{
GST_ERROR ("Could not make %s", payloader_name);
goto error;
}
if (!gst_bin_add (GST_BIN (bin), payloader))
{
GST_ERROR ("Could not add %s to bin", payloader_name);
gst_object_unref (payloader);
goto error;
}
if (!gst_element_link_pads (encoder, "src", payloader, "sink"))
{
GST_ERROR ("Could not link the %s and %s", encoder_name, payloader_name);
goto error;
}
if (!gst_element_link_pads (payloader, "src", capsfilter, "sink"))
{
GST_ERROR ("Could not link the %s and its capsfilter", payloader_name);
goto error;
}
}
return bin;
error:
gst_object_unref (bin);
return NULL;
}