diff options
author | Youness Alaoui <youness.alaoui@collabora.co.uk> | 2007-08-20 18:48:52 +0000 |
---|---|---|
committer | Edward Hervey <bilboed@bilboed.com> | 2009-02-21 17:47:59 +0100 |
commit | 2a45c1b9d4e899b311b78fd4679cd8286676354c (patch) | |
tree | ee31c3fca678b844376707cf3b3aacfeeaf339e9 | |
parent | 0127d7b6e59b8e605f7aead5cfa07ae7b1f87c34 (diff) | |
download | gst-plugins-bad-2a45c1b9d4e899b311b78fd4679cd8286676354c.tar.gz gst-plugins-bad-2a45c1b9d4e899b311b78fd4679cd8286676354c.tar.bz2 gst-plugins-bad-2a45c1b9d4e899b311b78fd4679cd8286676354c.zip |
[MOVED FROM GST-P-FARSIGHT] Added dtmfsrc, a DTMF Tone Generator, and made it part of the 'dtmf' plugin.
20070820184852-4f0f6-a0d85e67708290aebafa89ab79d3cedd5815b620.gz
-rw-r--r-- | gst/dtmf/Makefile.am | 16 | ||||
-rw-r--r-- | gst/dtmf/gstdtmf.c | 27 | ||||
-rw-r--r-- | gst/dtmf/gstdtmfsrc.c | 729 | ||||
-rw-r--r-- | gst/dtmf/gstdtmfsrc.h | 74 | ||||
-rw-r--r-- | gst/dtmf/gstrtpdtmfsrc.c | 19 | ||||
-rw-r--r-- | gst/dtmf/gstrtpdtmfsrc.h | 3 |
6 files changed, 845 insertions, 23 deletions
diff --git a/gst/dtmf/Makefile.am b/gst/dtmf/Makefile.am index 603b32c2..210dcade 100644 --- a/gst/dtmf/Makefile.am +++ b/gst/dtmf/Makefile.am @@ -1,9 +1,13 @@ -plugin_LTLIBRARIES = libgstrtpdtmf.la +plugin_LTLIBRARIES = libgstdtmf.la -libgstrtpdtmf_la_SOURCES = gstrtpdtmfsrc.c +libgstdtmf_la_SOURCES = gstdtmfsrc.c \ + gstrtpdtmfsrc.c \ + gstdtmf.c -libgstrtpdtmf_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(ERROR_CFLAGS) -DEXTERN_BUF -DRTP_SUPPORT -libgstrtpdtmf_la_LIBADD = $(GST_LIBS_LIBS) -libgstrtpdtmf_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstrtp-@GST_MAJORMINOR@ +noinst_HEADERS = gstdtmfsrc.h \ + gstrtpdtmfsrc.h + +libgstdtmf_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(ERROR_CFLAGS) -DEXTERN_BUF -DRTP_SUPPORT +libgstdtmf_la_LIBADD = $(GST_LIBS_LIBS) +libgstdtmf_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstrtp-@GST_MAJORMINOR@ -noinst_HEADERS = gstrtpdtmfsrc.h diff --git a/gst/dtmf/gstdtmf.c b/gst/dtmf/gstdtmf.c new file mode 100644 index 00000000..30868857 --- /dev/null +++ b/gst/dtmf/gstdtmf.c @@ -0,0 +1,27 @@ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstdtmfsrc.h" +#include "gstrtpdtmfsrc.h" + + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_dtmf_src_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_dtmf_src_plugin_init (plugin)) + return FALSE; + + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "dtmf", + "DTMF plugins", + plugin_init, "0.1" , "LGPL", "DTMF", ""); diff --git a/gst/dtmf/gstdtmfsrc.c b/gst/dtmf/gstdtmfsrc.c new file mode 100644 index 00000000..523419db --- /dev/null +++ b/gst/dtmf/gstdtmfsrc.c @@ -0,0 +1,729 @@ +/* GStreamer DTMF source + * + * gstdtmfsrc.c: + * + * Copyright (C) <2007> Collabora. + * Contact: Youness Alaoui <youness.alaoui@collabora.co.uk> + * Copyright (C) <2007> Nokia Corporation. + * Contact: Zeeshan Ali <zeeshan.ali@nokia.com> + * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> + * 2000,2005 Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-dtmfsrc + * @short_description: Generates DTMF packets + * + * <refsect2> + * + * <para> + * The DTMFSrc element generates DTMF (ITU-T Q.23 Specification) tone packets on request + * from application. The application communicates the beginning and end of a + * DTMF event using custom upstream gstreamer events. To report a DTMF event, an + * application must send an event of type GST_EVENT_CUSTOM_UPSTREAM, having a + * structure of name "dtmf-event" with fields set according to the following + * table: + * </para> + * + * <para> + * <informaltable> + * <tgroup cols='4'> + * <colspec colname='Name' /> + * <colspec colname='Type' /> + * <colspec colname='Possible values' /> + * <colspec colname='Purpose' /> + * + * <thead> + * <row> + * <entry>Name</entry> + * <entry>GType</entry> + * <entry>Possible values</entry> + * <entry>Purpose</entry> + * </row> + * </thead> + * + * <tbody> + * <row> + * <entry>type</entry> + * <entry>G_TYPE_INT</entry> + * <entry>0-1</entry> + * <entry>The application uses this field to specify which of the two methods + * specified in RFC 2833 to use. The value should be 0 for tones and 1 for + * named events. This element is only capable of generating tones. + * </entry> + * </row> + * <row> + * <entry>number</entry> + * <entry>G_TYPE_INT</entry> + * <entry>0-16</entry> + * <entry>The event number.</entry> + * </row> + * <row> + * <entry>volume</entry> + * <entry>G_TYPE_INT</entry> + * <entry>0-36</entry> + * <entry>This field describes the power level of the tone, expressed in dBm0 + * after dropping the sign. Power levels range from 0 to -63 dBm0. The range of + * valid DTMF is from 0 to -36 dBm0. Can be omitted if start is set to FALSE. + * </entry> + * </row> + * <row> + * <entry>start</entry> + * <entry>G_TYPE_BOOLEAN</entry> + * <entry>True or False</entry> + * <entry>Whether the event is starting or ending.</entry> + * </row> + * <row> + * <entry>method</entry> + * <entry>G_TYPE_INT</entry> + * <entry>1</entry> + * <entry>The method used for sending event, this element will react if this field + * is absent or 2. + * </entry> + * </row> + * </tbody> + * </tgroup> + * </informaltable> + * </para> + * + * <para>For example, the following code informs the pipeline (and in turn, the + * DTMFSrc element inside the pipeline) about the start of a DTMF named + * event '1' of volume -25 dBm0: + * </para> + * + * <para> + * <programlisting> + * structure = gst_structure_new ("dtmf-event", + * "type", G_TYPE_INT, 0, + * "number", G_TYPE_INT, 1, + * "volume", G_TYPE_INT, 25, + * "start", G_TYPE_BOOLEAN, TRUE, NULL); + * + * event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, structure); + * gst_element_send_event (pipeline, event); + * </programlisting> + * </para> + * + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#ifndef M_PI +# define M_PI 3.14159265358979323846 /* pi */ +#endif + + +#include "gstdtmfsrc.h" + +#define GST_TONE_DTMF_TYPE_EVENT 0 +#define DEFAULT_PACKET_INTERVAL 50 /* ms */ +#define MIN_PACKET_INTERVAL 10 /* ms */ +#define MAX_PACKET_INTERVAL 50 /* ms */ +#define SAMPLE_RATE 8000 +#define SAMPLE_SIZE 16 +#define CHANNELS 1 +#define MIN_EVENT 0 +#define MAX_EVENT 16 +#define MIN_VOLUME 0 +#define MAX_VOLUME 36 + + + +typedef struct st_dtmf_key { + char *event_name; + int event_encoding; + float low_frequency; + float high_frequency; +} DTMF_KEY; + +static const DTMF_KEY DTMF_KEYS[] = { + {"DTMF_KEY_EVENT_0", 0, 941, 1336}, + {"DTMF_KEY_EVENT_1", 1, 697, 1209}, + {"DTMF_KEY_EVENT_2", 2, 697, 1336}, + {"DTMF_KEY_EVENT_3", 3, 697, 1477}, + {"DTMF_KEY_EVENT_4", 4, 770, 1209}, + {"DTMF_KEY_EVENT_5", 5, 770, 1336}, + {"DTMF_KEY_EVENT_6", 6, 770, 1477}, + {"DTMF_KEY_EVENT_7", 7, 852, 1209}, + {"DTMF_KEY_EVENT_8", 8, 852, 1336}, + {"DTMF_KEY_EVENT_9", 9, 852, 1477}, + {"DTMF_KEY_EVENT_S", 10, 941, 1209}, + {"DTMF_KEY_EVENT_P", 11, 941, 1477}, + {"DTMF_KEY_EVENT_A", 12, 697, 1633}, + {"DTMF_KEY_EVENT_B", 13, 770, 1633}, + {"DTMF_KEY_EVENT_C", 14, 852, 1633}, + {"DTMF_KEY_EVENT_D", 15, 941, 1633}, +}; + +#define MAX_DTMF_EVENTS 16 + +enum { +DTMF_KEY_EVENT_1 = 1, +DTMF_KEY_EVENT_2 = 2, +DTMF_KEY_EVENT_3 = 3, +DTMF_KEY_EVENT_4 = 4, +DTMF_KEY_EVENT_5 = 5, +DTMF_KEY_EVENT_6 = 6, +DTMF_KEY_EVENT_7 = 7, +DTMF_KEY_EVENT_8 = 8, +DTMF_KEY_EVENT_9 = 9, +DTMF_KEY_EVENT_0 = 0, +DTMF_KEY_EVENT_STAR = 10, +DTMF_KEY_EVENT_POUND = 11, +DTMF_KEY_EVENT_A = 12, +DTMF_KEY_EVENT_B = 13, +DTMF_KEY_EVENT_C = 14, +DTMF_KEY_EVENT_D = 15, +}; + +/* elementfactory information */ +static const GstElementDetails gst_dtmf_src_details = +GST_ELEMENT_DETAILS ("DTMF tone generator", + "Source/Audio", + "Generates DTMF tones", + "Youness Alaoui <youness.alaoui@collabora.co.uk>"); + +GST_DEBUG_CATEGORY_STATIC (gst_dtmf_src_debug); +#define GST_CAT_DEFAULT gst_dtmf_src_debug + +enum +{ + PROP_0, + PROP_INTERVAL, +}; + +static GstStaticPadTemplate gst_dtmf_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "width = (int) 16, " + "depth = (int) 16, " + "endianness = (int) 1234, " + "signed = (bool) true, " + "rate = (int) 8000, " + "channels = (int) 1") + ); + +static GstElementClass *parent_class = NULL; + +static void gst_dtmf_src_base_init (gpointer g_class); +static void gst_dtmf_src_class_init (GstDTMFSrcClass * klass); +static void gst_dtmf_src_init (GstDTMFSrc * dtmfsrc, gpointer g_class); +static void gst_dtmf_src_finalize (GObject * object); + +GType +gst_dtmf_src_get_type (void) +{ + static GType base_src_type = 0; + + if (G_UNLIKELY (base_src_type == 0)) { + static const GTypeInfo base_src_info = { + sizeof (GstDTMFSrcClass), + (GBaseInitFunc) gst_dtmf_src_base_init, + NULL, + (GClassInitFunc) gst_dtmf_src_class_init, + NULL, + NULL, + sizeof (GstDTMFSrc), + 0, + (GInstanceInitFunc) gst_dtmf_src_init, + }; + + base_src_type = g_type_register_static (GST_TYPE_ELEMENT, + "GstDTMFSrc", &base_src_info, 0); + } + return base_src_type; +} + +static void gst_dtmf_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_dtmf_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static gboolean gst_dtmf_src_handle_event (GstPad * pad, GstEvent * event); +static GstStateChangeReturn gst_dtmf_src_change_state (GstElement * element, + GstStateChange transition); +static void gst_dtmf_src_generate_tone(GstDTMFSrc *dtmfsrc, DTMF_KEY key, float duration, + GstBuffer * buffer); +static void gst_dtmf_src_push_next_tone_packet (GstDTMFSrc *dtmfsrc); +static void gst_dtmf_src_start (GstDTMFSrc *dtmfsrc, gint event_number, + gint event_volume); +static void gst_dtmf_src_stop (GstDTMFSrc *dtmfsrc); + +static void +gst_dtmf_src_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + GST_DEBUG_CATEGORY_INIT (gst_dtmf_src_debug, + "dtmfsrc", 0, "dtmfsrc element"); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_dtmf_src_template)); + + gst_element_class_set_details (element_class, &gst_dtmf_src_details); +} + +static void +gst_dtmf_src_class_init (GstDTMFSrcClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = G_OBJECT_CLASS (klass); + gstelement_class = GST_ELEMENT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_dtmf_src_finalize); + gobject_class->set_property = + GST_DEBUG_FUNCPTR (gst_dtmf_src_set_property); + gobject_class->get_property = + GST_DEBUG_FUNCPTR (gst_dtmf_src_get_property); + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_INTERVAL, + g_param_spec_int ("interval", "Interval between tone packets", + "Interval in ms between two tone packets", MIN_PACKET_INTERVAL, + MAX_PACKET_INTERVAL, DEFAULT_PACKET_INTERVAL, G_PARAM_READWRITE)); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_dtmf_src_change_state); +} + +static void +gst_dtmf_src_init (GstDTMFSrc * dtmfsrc, gpointer g_class) +{ + dtmfsrc->srcpad = + gst_pad_new_from_static_template (&gst_dtmf_src_template, "src"); + GST_DEBUG_OBJECT (dtmfsrc, "adding src pad"); + gst_element_add_pad (GST_ELEMENT (dtmfsrc), dtmfsrc->srcpad); + + gst_pad_set_event_function (dtmfsrc->srcpad, gst_dtmf_src_handle_event); + + dtmfsrc->interval = DEFAULT_PACKET_INTERVAL; + + dtmfsrc->sample = 0; + + GST_DEBUG_OBJECT (dtmfsrc, "init done"); +} + +static void +gst_dtmf_src_finalize (GObject * object) +{ + GstDTMFSrc *dtmfsrc; + + dtmfsrc = GST_DTMF_SRC (object); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_dtmf_src_handle_dtmf_event (GstDTMFSrc *dtmfsrc, + const GstStructure * event_structure) +{ + gint event_type; + gboolean start; + gint method; + + if (!gst_structure_get_int (event_structure, "type", &event_type) || + !gst_structure_get_boolean (event_structure, "start", &start) || + (start == TRUE && event_type != GST_TONE_DTMF_TYPE_EVENT)) + goto failure; + + if (gst_structure_get_int (event_structure, "method", &method)) { + if (method != 2) { + goto failure; + } + } + + if (start) { + gint event_number; + gint event_volume; + + if (!gst_structure_get_int (event_structure, "number", &event_number) || + !gst_structure_get_int (event_structure, "volume", &event_volume)) + goto failure; + + GST_DEBUG_OBJECT (dtmfsrc, "Received start event %d with volume %d", + event_number, event_volume); + gst_dtmf_src_start (dtmfsrc, event_number, event_volume); + } + + else { + GST_DEBUG_OBJECT (dtmfsrc, "Received stop event"); + gst_dtmf_src_stop (dtmfsrc); + } + + return TRUE; +failure: + return FALSE; +} + +static gboolean +gst_dtmf_src_handle_custom_upstream (GstDTMFSrc *dtmfsrc, + GstEvent * event) +{ + gboolean result = FALSE; + const GstStructure *structure; + + if (GST_STATE (dtmfsrc) != GST_STATE_PLAYING) { + GST_DEBUG_OBJECT (dtmfsrc, "Received event while not in PLAYING state"); + goto ret; + } + + GST_DEBUG_OBJECT (dtmfsrc, "Received event is of our interest"); + structure = gst_event_get_structure (event); + if (structure && gst_structure_has_name (structure, "dtmf-event")) + result = gst_dtmf_src_handle_dtmf_event (dtmfsrc, structure); + +ret: + return result; +} + +static gboolean +gst_dtmf_src_handle_event (GstPad * pad, GstEvent * event) +{ + GstDTMFSrc *dtmfsrc; + gboolean result = FALSE; + + dtmfsrc = GST_DTMF_SRC (GST_PAD_PARENT (pad)); + + GST_DEBUG_OBJECT (dtmfsrc, "Received an event on the src pad"); + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CUSTOM_UPSTREAM: + { + result = gst_dtmf_src_handle_custom_upstream (dtmfsrc, event); + break; + } + /* Ideally this element should not be flushed but let's handle the event + * just in case it is */ + case GST_EVENT_FLUSH_START: + gst_dtmf_src_stop (dtmfsrc); + result = TRUE; + break; + case GST_EVENT_FLUSH_STOP: + gst_segment_init (&dtmfsrc->segment, GST_FORMAT_UNDEFINED); + break; + case GST_EVENT_NEWSEGMENT: + { + gboolean update; + gdouble rate; + GstFormat fmt; + gint64 start, stop, position; + + gst_event_parse_new_segment (event, &update, &rate, &fmt, &start, + &stop, &position); + gst_segment_set_newsegment (&dtmfsrc->segment, update, rate, fmt, + start, stop, position); + } + /* fallthrough */ + default: + result = gst_pad_event_default (pad, event); + break; + } + + gst_event_unref (event); + return result; +} + +static void +gst_dtmf_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstDTMFSrc *dtmfsrc; + + dtmfsrc = GST_DTMF_SRC (object); + + switch (prop_id) { + case PROP_INTERVAL: + dtmfsrc->interval = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_dtmf_src_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstDTMFSrc *dtmfsrc; + + dtmfsrc = GST_DTMF_SRC (object); + + switch (prop_id) { + case PROP_INTERVAL: + g_value_set_uint (value, dtmfsrc->interval); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_dtmf_src_set_stream_lock (GstDTMFSrc *dtmfsrc, gboolean lock) +{ + GstEvent *event; + GstStructure *structure; + + structure = gst_structure_new ("stream-lock", + "lock", G_TYPE_BOOLEAN, lock, NULL); + + event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB, structure); + gst_pad_push_event (dtmfsrc->srcpad, event); +} + +static void +gst_dtmf_prepare_timestamps (GstDTMFSrc *dtmfsrc) +{ + GstClock *clock; + + clock = GST_ELEMENT_CLOCK (dtmfsrc); + if (clock != NULL) + dtmfsrc->timestamp = gst_clock_get_time (GST_ELEMENT_CLOCK (dtmfsrc)); + + else { + GST_ERROR_OBJECT (dtmfsrc, "No clock set for element %s", + GST_ELEMENT_NAME (dtmfsrc)); + dtmfsrc->timestamp = GST_CLOCK_TIME_NONE; + } +} + +static void +gst_dtmf_src_start (GstDTMFSrc *dtmfsrc, + gint event_number, gint event_volume) +{ + GstCaps * caps = gst_pad_get_pad_template_caps (dtmfsrc->srcpad); + + dtmfsrc->event = CLAMP (event_number, MIN_EVENT, MAX_EVENT); + dtmfsrc->volume = CLAMP (event_volume, MIN_VOLUME, MAX_VOLUME); + + gst_dtmf_prepare_timestamps (dtmfsrc); + + /* Don't forget to get exclusive access to the stream */ + gst_dtmf_src_set_stream_lock (dtmfsrc, TRUE); + + if (!gst_pad_set_caps (dtmfsrc->srcpad, caps)) + GST_ERROR_OBJECT (dtmfsrc, + "Failed to set caps %" GST_PTR_FORMAT " on src pad", caps); + else + GST_DEBUG_OBJECT (dtmfsrc, + "caps %" GST_PTR_FORMAT " set on src pad", caps); + + + if (!gst_pad_start_task (dtmfsrc->srcpad, + (GstTaskFunction) gst_dtmf_src_push_next_tone_packet, dtmfsrc)) { + GST_ERROR_OBJECT (dtmfsrc, "Failed to start task on src pad"); + } +} + +static void +gst_dtmf_src_stop (GstDTMFSrc *dtmfsrc) +{ + /* Don't forget to release the stream lock */ + gst_dtmf_src_set_stream_lock (dtmfsrc, FALSE); + + if (!gst_pad_pause_task (dtmfsrc->srcpad)) { + GST_ERROR_OBJECT (dtmfsrc, "Failed to pause task on src pad"); + return; + } + +} + +static void +gst_dtmf_src_generate_tone(GstDTMFSrc *dtmfsrc, DTMF_KEY key, float duration, GstBuffer * buffer) +{ + gint16 *p; + gint tone_size; + double i = 0; + double amplitude, f1, f2; + + /* Create a buffer for the tone */ + tone_size = ((duration/1000)*SAMPLE_RATE*SAMPLE_SIZE*CHANNELS)/8; + GST_BUFFER_SIZE (buffer) = tone_size; + GST_BUFFER_MALLOCDATA (buffer) = g_malloc(tone_size); + GST_BUFFER_DATA (buffer) = GST_BUFFER_MALLOCDATA (buffer); + + p = (gint16 *) GST_BUFFER_MALLOCDATA (buffer); + + /* + * For each sample point we calculate 'x' as the + * the amplitude value. + */ + for (i = 0; i < (tone_size / (SAMPLE_SIZE/8)); i++) { + /* + * We add the fundamental frequencies together. + */ + f1 = sin(2 * M_PI * key.low_frequency * (dtmfsrc->sample / SAMPLE_RATE)); + f2 = sin(2 * M_PI * key.high_frequency * (dtmfsrc->sample / SAMPLE_RATE)); + + amplitude = (f1 + f2) / 2; + + /* Make the [-1:1] interval into a [-32767:32767] interval */ + amplitude *= 32767; + + /* Store it in the data buffer */ + *(p++) = (gint16) amplitude; + + (dtmfsrc->sample)++; + } +} + +static void +gst_dtmf_src_wait_for_buffer_ts (GstDTMFSrc *dtmfsrc, GstBuffer * buf) +{ + GstClock *clock; + + clock = GST_ELEMENT_CLOCK (dtmfsrc); + if (clock != NULL) { + GstClockID clock_id; + GstClockReturn clock_ret; + + clock_id = gst_clock_new_single_shot_id (clock, GST_BUFFER_TIMESTAMP (buf)); + clock_ret = gst_clock_id_wait (clock_id, NULL); + if (clock_ret != GST_CLOCK_OK && clock_ret != GST_CLOCK_EARLY) { + GST_ERROR_OBJECT (dtmfsrc, "Failed to wait on clock %s", + GST_ELEMENT_NAME (clock)); + } + gst_clock_id_unref (clock_id); + } + + else { + GST_ERROR_OBJECT (dtmfsrc, "No clock set for element %s", + GST_ELEMENT_NAME (dtmfsrc)); + } +} + + +static GstBuffer * +gst_dtmf_src_create_next_tone_packet (GstDTMFSrc *dtmfsrc) +{ + GstBuffer *buf = NULL; + + + GST_DEBUG_OBJECT (dtmfsrc, + "Creating buffer for tone"); + + /* create buffer to hold the tone */ + buf = gst_buffer_new (); + + /* Generate the tone */ + gst_dtmf_src_generate_tone(dtmfsrc, DTMF_KEYS[dtmfsrc->event], dtmfsrc->interval, buf); + + + /* timestamp and duration of GstBuffer */ + GST_BUFFER_DURATION (buf) = dtmfsrc->interval * GST_MSECOND; + GST_BUFFER_TIMESTAMP (buf) = dtmfsrc->timestamp; + dtmfsrc->timestamp += GST_BUFFER_DURATION (buf) /2; + + /* FIXME: Should we sync to clock ourselves or leave it to sink */ + gst_dtmf_src_wait_for_buffer_ts (dtmfsrc, buf); + + /* Set caps on the buffer before pushing it */ + gst_buffer_set_caps (buf, GST_PAD_CAPS (dtmfsrc->srcpad)); + + return buf; +} + +static void +gst_dtmf_src_push_next_tone_packet (GstDTMFSrc *dtmfsrc) +{ + GstBuffer *buf = NULL; + GstFlowReturn ret; + + buf = gst_dtmf_src_create_next_tone_packet (dtmfsrc); + + gst_buffer_ref(buf); + + GST_DEBUG_OBJECT (dtmfsrc, + "pushing buffer on src pad of size %d", GST_BUFFER_SIZE (buf)); + ret = gst_pad_push (dtmfsrc->srcpad, buf); + if (ret != GST_FLOW_OK) { + GST_ERROR_OBJECT (dtmfsrc, "Failed to push buffer on src pad", GST_BUFFER_SIZE (buf)); + } + + gst_buffer_unref(buf); + GST_DEBUG_OBJECT (dtmfsrc, "pushed DTMF tone on src pad"); + +} + +static GstStateChangeReturn +gst_dtmf_src_change_state (GstElement * element, GstStateChange transition) +{ + GstDTMFSrc *dtmfsrc; + GstStateChangeReturn result; + gboolean no_preroll = FALSE; + + dtmfsrc = GST_DTMF_SRC (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_segment_init (&dtmfsrc->segment, GST_FORMAT_UNDEFINED); + /* Indicate that we don't do PRE_ROLL */ + no_preroll = TRUE; + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + /* gst_dtmf_src_start (dtmfsrc, 6, 30); */ + break; + default: + break; + } + + if ((result = + GST_ELEMENT_CLASS (parent_class)->change_state (element, + transition)) == GST_STATE_CHANGE_FAILURE) + goto failure; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + /* Indicate that we don't do PRE_ROLL */ + /* gst_dtmf_src_stop (dtmfsrc); */ + no_preroll = TRUE; + break; + default: + break; + } + + if (no_preroll && result == GST_STATE_CHANGE_SUCCESS) + result = GST_STATE_CHANGE_NO_PREROLL; + + return result; + + /* ERRORS */ +failure: + { + GST_ERROR_OBJECT (dtmfsrc, "parent failed state change"); + return result; + } +} + +gboolean +gst_dtmf_src_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "dtmfsrc", + GST_RANK_NONE, GST_TYPE_DTMF_SRC); +} diff --git a/gst/dtmf/gstdtmfsrc.h b/gst/dtmf/gstdtmfsrc.h new file mode 100644 index 00000000..234120e9 --- /dev/null +++ b/gst/dtmf/gstdtmfsrc.h @@ -0,0 +1,74 @@ +/* GStreamer DTMF source + * + * gstdtmfsrc.h: + * + * Copyright (C) <2007> Collabora. + * Contact: Youness Alaoui <youness.alaoui@collabora.co.uk> + * Copyright (C) <2007> Nokia Corporation. + * Contact: Zeeshan Ali <zeeshan.ali@nokia.com> + * Copyright (C) <2005> Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_DTMF_SRC_H__ +#define __GST_DTMF_SRC_H__ + +#include <gst/gst.h> +#include <gst/gstbuffer.h> + +G_BEGIN_DECLS + +#define GST_TYPE_DTMF_SRC (gst_dtmf_src_get_type()) +#define GST_DTMF_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DTMF_SRC,GstDTMFSrc)) +#define GST_DTMF_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DTMF_SRC,GstDTMFSrcClass)) +#define GST_DTMF_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DTMF_SRC, GstDTMFSrcClass)) +#define GST_IS_DTMF_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DTMF_SRC)) +#define GST_IS_DTMF_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DTMF_SRC)) +#define GST_DTMF_SRC_CAST(obj) ((GstDTMFSrc *)(obj)) + +typedef struct _GstDTMFSrc GstDTMFSrc; +typedef struct _GstDTMFSrcClass GstDTMFSrcClass; + +/** + * GstDTMFSrc: + * @element: the parent element. + * + * The opaque #GstDTMFSrc data structure. + */ +struct _GstDTMFSrc { + GstElement element; + GstPad *srcpad; + GstClockTime timestamp; + GstSegment segment; + double sample; + + guint16 event; + guint16 volume; + guint16 interval; +}; + +struct _GstDTMFSrcClass { + GstElementClass parent_class; +}; + +GType gst_dtmf_src_get_type (void); + +gboolean gst_dtmf_src_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_DTMF_SRC_H__ */ diff --git a/gst/dtmf/gstrtpdtmfsrc.c b/gst/dtmf/gstrtpdtmfsrc.c index 8e4f31b5..11bff786 100644 --- a/gst/dtmf/gstrtpdtmfsrc.c +++ b/gst/dtmf/gstrtpdtmfsrc.c @@ -244,7 +244,7 @@ gst_rtp_dtmf_src_base_init (gpointer g_class) GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); GST_DEBUG_CATEGORY_INIT (gst_rtp_dtmf_src_debug, - "dtmfsrc", 0, "dtmfsrc element"); + "rtpdtmfsrc", 0, "rtpdtmfsrc element"); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_rtp_dtmf_src_template)); @@ -849,24 +849,9 @@ failure: } } -static gboolean +gboolean gst_rtp_dtmf_src_plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "rtpdtmfsrc", GST_RANK_NONE, GST_TYPE_RTP_DTMF_SRC); } - -static gboolean -plugin_init (GstPlugin * plugin) -{ - if (!gst_rtp_dtmf_src_plugin_init (plugin)) - return FALSE; - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "dtmf", - "DTMF plugins", - plugin_init, "0.1" , "LGPL", "DTMF", ""); diff --git a/gst/dtmf/gstrtpdtmfsrc.h b/gst/dtmf/gstrtpdtmfsrc.h index 797526ea..8a763cce 100644 --- a/gst/dtmf/gstrtpdtmfsrc.h +++ b/gst/dtmf/gstrtpdtmfsrc.h @@ -96,6 +96,9 @@ struct _GstRTPDTMFSrcClass { GType gst_rtp_dtmf_src_get_type (void); +gboolean gst_rtp_dtmf_src_plugin_init (GstPlugin * plugin); + + G_END_DECLS #endif /* __GST_RTP_DTMF_SRC_H__ */ |