From: zeeshan.ali@nokia.com Date: Wed, 14 Feb 2007 13:13:52 +0000 (+0000) Subject: [MOVED FROM GST-P-FARSIGHT] Add RTP DTMF event packet generator element and test... X-Git-Tag: 1.19.3~507^2~19414 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c48a4e1734041795905b6e0918384f58a70cf790;p=platform%2Fupstream%2Fgstreamer.git [MOVED FROM GST-P-FARSIGHT] Add RTP DTMF event packet generator element and test app for it 20070214131352-65035-3c14a1047c3cd6696f3a716a62b21d3f4b9da62b.gz --- diff --git a/gst/rtpdtmf/.git-darcs-dir b/gst/rtpdtmf/.git-darcs-dir new file mode 100644 index 0000000..e69de29 diff --git a/gst/rtpdtmf/Makefile.am b/gst/rtpdtmf/Makefile.am new file mode 100644 index 0000000..603b32c --- /dev/null +++ b/gst/rtpdtmf/Makefile.am @@ -0,0 +1,9 @@ +plugin_LTLIBRARIES = libgstrtpdtmf.la + +libgstrtpdtmf_la_SOURCES = gstrtpdtmfsrc.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 = gstrtpdtmfsrc.h diff --git a/gst/rtpdtmf/gstrtpdtmfsrc.c b/gst/rtpdtmf/gstrtpdtmfsrc.c new file mode 100644 index 0000000..0d036bd --- /dev/null +++ b/gst/rtpdtmf/gstrtpdtmfsrc.c @@ -0,0 +1,511 @@ +/* GStreamer RTP DTMF source + * Copyright (C) 2007 Zeeshan Ali + * + * gstrtpdtmfsrc.c: + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "gstrtpdtmfsrc.h" + +#define GST_RTP_DTMF_TYPE_EVENT 1 +#define DEFAULT_PACKET_INTERVAL ((guint16) 50) /* ms */ +#define DEFAULT_SSRC -1 +#define DEFAULT_PT 96 +#define DEFAULT_CLOCK_RATE 8000 +#define MIN_EVENT 0 +#define MAX_EVENT 16 +#define MIN_EVENT_STRING "0" +#define MAX_EVENT_STRING "16" +#define MIN_VOLUME 0 +#define MAX_VOLUME 36 + +/* elementfactory information */ +static const GstElementDetails gst_rtp_dtmf_src_details = +GST_ELEMENT_DETAILS ("RTP DTMF packet generator", + "Source/Network", + "Generates RTP DTMF packets", + "Zeeshan Ali "); + +GST_DEBUG_CATEGORY_STATIC (gst_rtp_dtmf_src_debug); +#define GST_CAT_DEFAULT gst_rtp_dtmf_src_debug + +/* signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_SSRC, + PROP_PT, + PROP_CLOCK_RATE, +}; + +static GstStaticPadTemplate gst_rtp_dtmf_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) [ 96, 127 ], " + "clock-rate = (int) [ 0, MAX ], " + "ssrc = (int) [ 0, MAX ], " + "event = (int) [ " MIN_EVENT_STRING ", " MAX_EVENT_STRING " ], " + "encoding-name = (string) \"telephone-event\"") + ); + +static GstElementClass *parent_class = NULL; + +static void gst_rtp_dtmf_src_base_init (gpointer g_class); +static void gst_rtp_dtmf_src_class_init (GstRTPDTMFSrcClass * klass); +static void gst_rtp_dtmf_src_init (GstRTPDTMFSrc * dtmfsrc, gpointer g_class); +static void gst_rtp_dtmf_src_finalize (GObject * object); + +GType +gst_rtp_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 (GstRTPDTMFSrcClass), + (GBaseInitFunc) gst_rtp_dtmf_src_base_init, + NULL, + (GClassInitFunc) gst_rtp_dtmf_src_class_init, + NULL, + NULL, + sizeof (GstRTPDTMFSrc), + 0, + (GInstanceInitFunc) gst_rtp_dtmf_src_init, + }; + + base_src_type = g_type_register_static (GST_TYPE_ELEMENT, + "GstRTPDTMFSrc", &base_src_info, 0); + } + return base_src_type; +} + +static void gst_rtp_dtmf_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_dtmf_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static gboolean gst_rtp_dtmf_src_handle_event (GstPad * pad, GstEvent * event); +static GstStateChangeReturn gst_rtp_dtmf_src_change_state (GstElement * element, + GstStateChange transition); +static void gst_rtp_dtmf_src_push_next_rtp_packet (GstRTPDTMFSrc *dtmfsrc); +static void gst_rtp_dtmf_src_start (GstRTPDTMFSrc *dtmfsrc, gint event_number, + gint event_volume); +static void gst_rtp_dtmf_src_stop (GstRTPDTMFSrc *dtmfsrc); +static void gst_rtp_dtmf_src_set_caps (GstRTPDTMFSrc *dtmfsrc); + +static void +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"); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_dtmf_src_template)); + + gst_element_class_set_details (element_class, &gst_rtp_dtmf_src_details); +} + +static void +gst_rtp_dtmf_src_class_init (GstRTPDTMFSrcClass * 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_rtp_dtmf_src_finalize); + gobject_class->set_property = + GST_DEBUG_FUNCPTR (gst_rtp_dtmf_src_set_property); + gobject_class->get_property = + GST_DEBUG_FUNCPTR (gst_rtp_dtmf_src_get_property); + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CLOCK_RATE, + g_param_spec_uint ("clock-rate", "clockrate", + "The clock-rate at which to generate the dtmf packets", + 0, G_MAXUINT, DEFAULT_CLOCK_RATE, G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SSRC, + g_param_spec_uint ("ssrc", "SSRC", + "The SSRC of the packets (-1 == random)", + 0, G_MAXUINT, DEFAULT_SSRC, G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PT, + g_param_spec_uint ("pt", "payload type", + "The payload type of the packets", + 0, 0x80, DEFAULT_PT, G_PARAM_READWRITE)); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_rtp_dtmf_src_change_state); +} + +static void +gst_rtp_dtmf_src_init (GstRTPDTMFSrc * dtmfsrc, gpointer g_class) +{ + dtmfsrc->srcpad = + gst_pad_new_from_static_template (&gst_rtp_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_rtp_dtmf_src_handle_event); + + dtmfsrc->ssrc = DEFAULT_SSRC; + dtmfsrc->pt = DEFAULT_PT; + dtmfsrc->clock_rate = DEFAULT_CLOCK_RATE; + + gst_rtp_dtmf_src_set_caps (dtmfsrc); + + GST_DEBUG_OBJECT (dtmfsrc, "init done"); +} + +static void +gst_rtp_dtmf_src_finalize (GObject * object) +{ + GstRTPDTMFSrc *dtmfsrc; + + dtmfsrc = GST_RTP_DTMF_SRC (object); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_rtp_dtmf_src_handle_event (GstPad * pad, GstEvent * event) +{ + GstRTPDTMFSrc *dtmfsrc; + gboolean result = FALSE; + + dtmfsrc = GST_RTP_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: + { + const GstStructure *structure; + + if (GST_STATE (dtmfsrc) != GST_STATE_PLAYING) { + GST_DEBUG_OBJECT (dtmfsrc, "Received event while not in PLAYING state"); + break; + } + + 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")) { + gint event_type; + gboolean start; + + if (!gst_structure_get_int (structure, "type", &event_type) || + !gst_structure_get_boolean (structure, "start", &start) || + event_type != GST_RTP_DTMF_TYPE_EVENT) + break; + + if (start) { + gint event_number; + gint event_volume; + + if (!gst_structure_get_int (structure, "number", &event_number) || + !gst_structure_get_int (structure, "volume", &event_volume)) + break; + + gst_rtp_dtmf_src_start (dtmfsrc, event_number, event_volume); + } + + else { + gst_rtp_dtmf_src_stop (dtmfsrc); + } + } + break; + } + default: + break; + } + gst_event_unref (event); + return result; +} + +static void +gst_rtp_dtmf_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRTPDTMFSrc *dtmfsrc; + + dtmfsrc = GST_RTP_DTMF_SRC (object); + + switch (prop_id) { + case PROP_CLOCK_RATE: + dtmfsrc->clock_rate = g_value_get_uint (value); + gst_rtp_dtmf_src_set_caps (dtmfsrc); + break; + case PROP_SSRC: + dtmfsrc->ssrc = g_value_get_uint (value); + break; + case PROP_PT: + dtmfsrc->pt = g_value_get_uint (value); + gst_rtp_dtmf_src_set_caps (dtmfsrc); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_dtmf_src_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstRTPDTMFSrc *dtmfsrc; + + dtmfsrc = GST_RTP_DTMF_SRC (object); + + switch (prop_id) { + case PROP_CLOCK_RATE: + g_value_set_uint (value, dtmfsrc->clock_rate); + break; + case PROP_SSRC: + g_value_set_uint (value, dtmfsrc->ssrc); + break; + case PROP_PT: + g_value_set_uint (value, dtmfsrc->pt); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_dtmf_src_start (GstRTPDTMFSrc *dtmfsrc, + gint event_number, gint event_volume) +{ + g_return_if_fail (dtmfsrc->payload == NULL); + + dtmfsrc->payload = g_new0 (GstRTPDTMFPayload, 1); + dtmfsrc->payload->event = CLAMP (event_number, MIN_EVENT, MAX_EVENT); + dtmfsrc->payload->volume = CLAMP (event_volume, MIN_VOLUME, MAX_VOLUME); + + dtmfsrc->next_ts = gst_clock_get_time (GST_ELEMENT_CLOCK (dtmfsrc)); + dtmfsrc->first_packet = TRUE; + + if (dtmfsrc->ssrc == -1) + dtmfsrc->current_ssrc = g_random_int (); + else + dtmfsrc->current_ssrc = dtmfsrc->ssrc; + + if (!gst_pad_start_task (dtmfsrc->srcpad, + (GstTaskFunction) gst_rtp_dtmf_src_push_next_rtp_packet, dtmfsrc)) { + GST_ERROR_OBJECT (dtmfsrc, "Failed to start task on src pad"); + } +} + +static void +gst_rtp_dtmf_src_stop (GstRTPDTMFSrc *dtmfsrc) +{ + g_return_if_fail (dtmfsrc->payload != NULL); + + if (!gst_pad_stop_task (dtmfsrc->srcpad)) { + GST_ERROR_OBJECT (dtmfsrc, "Failed to stop task on src pad"); + return; + } + + /* Push the last packet with e-bit set */ + dtmfsrc->payload->e = 1; + gst_rtp_dtmf_src_push_next_rtp_packet (dtmfsrc); + + g_free (dtmfsrc->payload); + dtmfsrc->payload = NULL; +} + +static void +gst_rtp_dtmf_src_push_next_rtp_packet (GstRTPDTMFSrc *dtmfsrc) +{ + GstBuffer *buf = NULL; + GstFlowReturn ret; + GstRTPDTMFPayload *payload; + GstClock * clock; + guint32 rtp_ts; + + /* create buffer to hold the payload */ + buf = gst_rtp_buffer_new_allocate (sizeof (GstRTPDTMFPayload), 0, 0); + + gst_rtp_buffer_set_ssrc (buf, dtmfsrc->current_ssrc); + gst_rtp_buffer_set_payload_type (buf, dtmfsrc->pt); + if (dtmfsrc->first_packet) { + gst_rtp_buffer_set_marker (buf, TRUE); + dtmfsrc->first_packet = FALSE; + } + + /* timestamp and duration of GstBuffer */ + GST_BUFFER_TIMESTAMP (buf) = dtmfsrc->next_ts; + GST_BUFFER_DURATION (buf) = DEFAULT_PACKET_INTERVAL * GST_MSECOND; + + /* duration of DTMF payload */ + dtmfsrc->payload->duration += + DEFAULT_PACKET_INTERVAL * dtmfsrc->clock_rate / 1000; + + payload = (GstRTPDTMFPayload *) gst_rtp_buffer_get_payload (buf); + /* timestamp of RTP header */ + rtp_ts = dtmfsrc->next_ts * dtmfsrc->clock_rate / GST_SECOND; + gst_rtp_buffer_set_timestamp (buf, rtp_ts); + + /* copy payload and convert to network-byte order */ + g_memmove (payload, dtmfsrc->payload, sizeof (GstRTPDTMFPayload)); + payload->duration = g_htons (payload->duration); + + /* FIXME: Should we sync to clock ourselves or leave it to sink */ + clock = GST_ELEMENT_CLOCK (dtmfsrc); + if (clock != NULL) { + GstClockID clock_id; + GstClockReturn clock_ret; + + clock_id = gst_clock_new_single_shot_id (clock, dtmfsrc->next_ts); + 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)); + } + + dtmfsrc->next_ts += GST_BUFFER_DURATION (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_DEBUG_OBJECT (dtmfsrc, + "pushed DTMF event '%d' on src pad", dtmfsrc->payload->event); +} + +static void +gst_rtp_dtmf_src_set_caps (GstRTPDTMFSrc *dtmfsrc) +{ + GstCaps *caps; + + caps = gst_caps_new_simple ("application/x-rtp", + "media", G_TYPE_STRING, "audio", + "payload", G_TYPE_INT, dtmfsrc->pt, + "clock-rate", G_TYPE_INT, dtmfsrc->clock_rate, + "encoding-name", G_TYPE_STRING, "telephone-event", + "ssrc", G_TYPE_UINT, dtmfsrc->current_ssrc, NULL); + + if (!gst_pad_set_caps (dtmfsrc->srcpad, caps)) { + GST_ERROR_OBJECT (dtmfsrc, "Failed to set caps % on src pad", + GST_PTR_FORMAT, caps); + } + + gst_caps_unref (caps); +} + +static GstStateChangeReturn +gst_rtp_dtmf_src_change_state (GstElement * element, GstStateChange transition) +{ + GstRTPDTMFSrc *dtmfsrc; + GstStateChangeReturn result; + gboolean no_preroll = TRUE; + + dtmfsrc = GST_RTP_DTMF_SRC (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + /* Indicate that we don't do PRE_ROLL */ + no_preroll = TRUE; + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + 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 */ + no_preroll = TRUE; + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_NULL: + 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; + } +} + +static 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; +} + +#define PACKAGE "gstreamer" + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "dtmf", + "DTMF plugins", + plugin_init, "0.1" , "LGPL", "DTMF", ""); diff --git a/gst/rtpdtmf/gstrtpdtmfsrc.h b/gst/rtpdtmf/gstrtpdtmfsrc.h new file mode 100644 index 0000000..399dbca --- /dev/null +++ b/gst/rtpdtmf/gstrtpdtmfsrc.h @@ -0,0 +1,85 @@ +/* GStreamer RTP DTMF source + * Copyright (C) 2007 Zeeshan Ali + * + * gstrtpdtmfsrc.h: + * + * 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_RTP_DTMF_SRC_H__ +#define __GST_RTP_DTMF_SRC_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_DTMF_SRC (gst_rtp_dtmf_src_get_type()) +#define GST_RTP_DTMF_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_DTMF_SRC,GstRTPDTMFSrc)) +#define GST_RTP_DTMF_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_DTMF_SRC,GstRTPDTMFSrcClass)) +#define GST_RTP_DTMF_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTP_DTMF_SRC, GstRTPDTMFSrcClass)) +#define GST_IS_RTP_DTMF_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_DTMF_SRC)) +#define GST_IS_RTP_DTMF_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_DTMF_SRC)) +#define GST_RTP_DTMF_SRC_CAST(obj) ((GstRTPDTMFSrc *)(obj)) + +typedef struct { + unsigned event:8; /* Current DTMF event */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + unsigned volume:6; /* power level of the tone, in dBm0 */ + unsigned r:1; /* Reserved-bit */ + unsigned e:1; /* End-bit */ +#elif G_BYTE_ORDER == G_BIG_ENDIAN + unsigned e:1; /* End-bit */ + unsigned r:1; /* Reserved-bit */ + unsigned volume:6; /* power level of the tone, in dBm0 */ +#else +#error "G_BYTE_ORDER should be big or little endian." +#endif + unsigned duration:16; /* Duration of digit, in timestamp units */ +} GstRTPDTMFPayload; + +typedef struct _GstRTPDTMFSrc GstRTPDTMFSrc; +typedef struct _GstRTPDTMFSrcClass GstRTPDTMFSrcClass; + +/** + * GstRTPDTMFSrc: + * @element: the parent element. + * + * The opaque #GstRTPDTMFSrc data structure. + */ +struct _GstRTPDTMFSrc { + GstElement element; + + GstPad *srcpad; + GstRTPDTMFPayload *payload; + + GstClockTime next_ts; + guint32 clock_rate; + guint pt; + guint ssrc; + guint current_ssrc; + gboolean first_packet; +}; + +struct _GstRTPDTMFSrcClass { + GstElementClass parent_class; +}; + +GType gst_rtp_dtmf_src_get_type (void); + +G_END_DECLS + +#endif /* __GST_RTP_DTMF_SRC_H__ */