From: Youness Alaoui Date: Mon, 20 Aug 2007 18:48:00 +0000 (+0000) Subject: [MOVED FROM GST-P-FARSIGHT] Moved rtpdtmf to dtmf directory X-Git-Tag: 1.1.1~302^2~137 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9d1a8605ddd4fe7f6fa17322a057770d84a67de4;p=platform%2Fupstream%2Fgst-plugins-good.git [MOVED FROM GST-P-FARSIGHT] Moved rtpdtmf to dtmf directory 20070820184800-4f0f6-fa33ea974510161de8c9951c39087af3613b65a4.gz --- 9d1a8605ddd4fe7f6fa17322a057770d84a67de4 diff --git a/gst/dtmf/.git-darcs-dir b/gst/dtmf/.git-darcs-dir new file mode 100644 index 0000000..e69de29 diff --git a/gst/dtmf/Makefile.am b/gst/dtmf/Makefile.am new file mode 100644 index 0000000..603b32c --- /dev/null +++ b/gst/dtmf/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/dtmf/gstrtpdtmfsrc.c b/gst/dtmf/gstrtpdtmfsrc.c new file mode 100644 index 0000000..8e4f31b --- /dev/null +++ b/gst/dtmf/gstrtpdtmfsrc.c @@ -0,0 +1,872 @@ +/* GStreamer RTP DTMF source + * + * gstrtpdtmfsrc.c: + * + * Copyright (C) <2007> Nokia Corporation. + * Contact: Zeeshan Ali + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000,2005 Wim Taymans + * + * 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-rtpdtmfsrc + * @short_description: Generates RTP DTMF packets + * + * + * + * + * The RTPDTMFSrc element generates RTP DTMF (RFC 2833) event 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: + * + * + * + * + * + * + * + * + * + * + * + * + * Name + * GType + * Possible values + * Purpose + * + * + * + * + * + * type + * G_TYPE_INT + * 0-1 + * 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 named events. + * + * + * + * number + * G_TYPE_INT + * 0-16 + * The event number. + * + * + * volume + * G_TYPE_INT + * 0-36 + * 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. + * + * + * + * start + * G_TYPE_BOOLEAN + * True or False + * Whether the event is starting or ending. + * + * + * method + * G_TYPE_INT + * 1 + * The method used for sending event, this element will react if this field + * is absent or 1. + * + * + * + * + * + * + * + * For example, the following code informs the pipeline (and in turn, the + * RTPDTMFSrc element inside the pipeline) about the start of an RTP DTMF named + * event '1' of volume -25 dBm0: + * + * + * + * + * structure = gst_structure_new ("dtmf-event", + * "type", G_TYPE_INT, 1, + * "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); + * + * + * + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "gstrtpdtmfsrc.h" + +#define GST_RTP_DTMF_TYPE_EVENT 1 +#define DEFAULT_PACKET_INTERVAL 50 /* ms */ +#define MIN_PACKET_INTERVAL 10 /* ms */ +#define MAX_PACKET_INTERVAL 50 /* ms */ +#define DEFAULT_SSRC -1 +#define DEFAULT_PT 96 +#define DEFAULT_TIMESTAMP_OFFSET -1 +#define DEFAULT_SEQNUM_OFFSET -1 +#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 +#define MIN_EVENT_DURATION 50 + +#define DEFAULT_PACKET_REDUNDANCY 1 +#define MIN_PACKET_REDUNDANCY 1 +#define MAX_PACKET_REDUNDANCY 5 + +/* 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_TIMESTAMP_OFFSET, + PROP_SEQNUM_OFFSET, + PROP_PT, + PROP_CLOCK_RATE, + PROP_TIMESTAMP, + PROP_SEQNUM, + PROP_INTERVAL, + PROP_REDUNDANCY +}; + +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 ], " + "events = (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_TIMESTAMP, + g_param_spec_uint ("timestamp", "Timestamp", + "The RTP timestamp of the last processed packet", + 0, G_MAXUINT, 0, G_PARAM_READABLE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEQNUM, + g_param_spec_uint ("seqnum", "Sequence number", + "The RTP sequence number of the last processed packet", + 0, G_MAXUINT, 0, G_PARAM_READABLE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_TIMESTAMP_OFFSET, g_param_spec_int ("timestamp-offset", + "Timestamp Offset", + "Offset to add to all outgoing timestamps (-1 = random)", -1, + G_MAXINT, DEFAULT_TIMESTAMP_OFFSET, G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEQNUM_OFFSET, + g_param_spec_int ("seqnum-offset", "Sequence number Offset", + "Offset to add to all outgoing seqnum (-1 = random)", -1, G_MAXINT, + DEFAULT_SEQNUM_OFFSET, G_PARAM_READWRITE)); + 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)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_INTERVAL, + g_param_spec_int ("interval", "Interval between rtp packets", + "Interval in ms between two rtp packets", MIN_PACKET_INTERVAL, + MAX_PACKET_INTERVAL, DEFAULT_PACKET_INTERVAL, G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_REDUNDANCY, + g_param_spec_int ("packet-redundancy", "Packet Redundancy", + "Number of packets to send to indicate start and stop dtmf events", + MIN_PACKET_REDUNDANCY, MAX_PACKET_REDUNDANCY, + DEFAULT_PACKET_REDUNDANCY, 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->seqnum_offset = DEFAULT_SEQNUM_OFFSET; + dtmfsrc->ts_offset = DEFAULT_TIMESTAMP_OFFSET; + dtmfsrc->pt = DEFAULT_PT; + dtmfsrc->clock_rate = DEFAULT_CLOCK_RATE; + dtmfsrc->payload = NULL; + dtmfsrc->interval = DEFAULT_PACKET_INTERVAL; + dtmfsrc->packet_redundancy = DEFAULT_PACKET_REDUNDANCY; + + 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_dtmf_event (GstRTPDTMFSrc *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) || + event_type != GST_RTP_DTMF_TYPE_EVENT) + goto failure; + + if (gst_structure_get_int (event_structure, "method", &method)) { + if (method != 1) { + 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_rtp_dtmf_src_start (dtmfsrc, event_number, event_volume); + } + + else { + GST_DEBUG_OBJECT (dtmfsrc, "Received stop event"); + gst_rtp_dtmf_src_stop (dtmfsrc); + } + + return TRUE; +failure: + return FALSE; +} + +static gboolean +gst_rtp_dtmf_src_handle_custom_upstream (GstRTPDTMFSrc *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_rtp_dtmf_src_handle_dtmf_event (dtmfsrc, structure); + +ret: + return result; +} + +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: + { + result = gst_rtp_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_rtp_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_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_TIMESTAMP_OFFSET: + dtmfsrc->ts_offset = g_value_get_int (value); + break; + case PROP_SEQNUM_OFFSET: + dtmfsrc->seqnum_offset = g_value_get_int (value); + break; + 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; + case PROP_INTERVAL: + dtmfsrc->interval = g_value_get_int (value); + break; + case PROP_REDUNDANCY: + dtmfsrc->packet_redundancy = g_value_get_int (value); + 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_TIMESTAMP_OFFSET: + g_value_set_int (value, dtmfsrc->ts_offset); + break; + case PROP_SEQNUM_OFFSET: + g_value_set_int (value, dtmfsrc->seqnum_offset); + break; + 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; + case PROP_TIMESTAMP: + g_value_set_uint (value, dtmfsrc->rtp_timestamp); + break; + case PROP_SEQNUM: + g_value_set_uint (value, dtmfsrc->seqnum); + break; + case PROP_INTERVAL: + g_value_set_uint (value, dtmfsrc->interval); + break; + case PROP_REDUNDANCY: + g_value_set_uint (value, dtmfsrc->packet_redundancy); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_dtmf_src_set_stream_lock (GstRTPDTMFSrc *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_rtp_dtmf_prepare_timestamps (GstRTPDTMFSrc *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; + } + + dtmfsrc->rtp_timestamp = dtmfsrc->ts_base + + gst_util_uint64_scale_int ( + gst_segment_to_running_time (&dtmfsrc->segment, GST_FORMAT_TIME, + dtmfsrc->timestamp), + dtmfsrc->clock_rate, GST_SECOND); +} + +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->first_packet = TRUE; + dtmfsrc->last_packet = FALSE; + + gst_rtp_dtmf_prepare_timestamps (dtmfsrc); + gst_rtp_dtmf_src_set_caps (dtmfsrc); + + /* Don't forget to get exclusive access to the stream */ + gst_rtp_dtmf_src_set_stream_lock (dtmfsrc, TRUE); + + 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); + + /* Push the last packet with e-bit set */ + /* Next packet sent will be the last */ + dtmfsrc->last_packet = TRUE; + +} + +static void +gst_rtp_dtmf_src_wait_for_buffer_ts (GstRTPDTMFSrc *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 void +gst_rtp_dtmf_prepare_rtp_headers (GstRTPDTMFSrc *dtmfsrc, GstBuffer *buf) +{ + 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; + } else if (dtmfsrc->last_packet) { + dtmfsrc->payload->e = 1; + dtmfsrc->last_packet = FALSE; + } + + dtmfsrc->seqnum++; + gst_rtp_buffer_set_seq (buf, dtmfsrc->seqnum); + + /* timestamp of RTP header */ + gst_rtp_buffer_set_timestamp (buf, dtmfsrc->rtp_timestamp); +} + +static void +gst_rtp_dtmf_prepare_buffer_data (GstRTPDTMFSrc *dtmfsrc, GstBuffer *buf) +{ + GstRTPDTMFPayload *payload; + + gst_rtp_dtmf_prepare_rtp_headers (dtmfsrc, buf); + + /* duration of DTMF payload */ + dtmfsrc->payload->duration += + dtmfsrc->interval * dtmfsrc->clock_rate / 1000; + + /* 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); + + payload = (GstRTPDTMFPayload *) gst_rtp_buffer_get_payload (buf); + + /* copy payload and convert to network-byte order */ + g_memmove (payload, dtmfsrc->payload, sizeof (GstRTPDTMFPayload)); + /* Force the packet duration to a certain minumum + * if its the end of the event + */ + if (payload->e && + payload->duration < MIN_EVENT_DURATION * dtmfsrc->clock_rate / 1000) + payload->duration = MIN_EVENT_DURATION * dtmfsrc->clock_rate / 1000; + + payload->duration = g_htons (payload->duration); +} + +static GstBuffer * +gst_rtp_dtmf_src_create_next_rtp_packet (GstRTPDTMFSrc *dtmfsrc) +{ + GstBuffer *buf = NULL; + + /* create buffer to hold the payload */ + buf = gst_rtp_buffer_new_allocate (sizeof (GstRTPDTMFPayload), 0, 0); + + gst_rtp_dtmf_prepare_buffer_data (dtmfsrc, buf); + + /* FIXME: Should we sync to clock ourselves or leave it to sink */ + gst_rtp_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_rtp_dtmf_src_push_next_rtp_packet (GstRTPDTMFSrc *dtmfsrc) +{ + GstBuffer *buf = NULL; + GstFlowReturn ret; + gint redundancy_count = 1; + + if (dtmfsrc->first_packet == TRUE || dtmfsrc->last_packet == TRUE) { + redundancy_count = dtmfsrc->packet_redundancy; + + if(dtmfsrc->first_packet == TRUE) { + GST_DEBUG_OBJECT (dtmfsrc, + "redundancy count set to %d due to dtmf start", + redundancy_count); + } else if(dtmfsrc->last_packet == TRUE) { + GST_DEBUG_OBJECT (dtmfsrc, + "redundancy count set to %d due to dtmf stop", + redundancy_count); + } + + } + + /* create buffer to hold the payload */ + buf = gst_rtp_dtmf_src_create_next_rtp_packet (dtmfsrc); + + while ( redundancy_count-- ) { + gst_buffer_ref(buf); + + GST_DEBUG_OBJECT (dtmfsrc, + "pushing buffer on src pad of size %d with redundancy count %d", + GST_BUFFER_SIZE (buf), redundancy_count); + 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)); + + /* Make sure only the first packet sent has the marker set */ + gst_rtp_buffer_set_marker (buf, FALSE); + } + + gst_buffer_unref(buf); + GST_DEBUG_OBJECT (dtmfsrc, + "pushed DTMF event '%d' on src pad", dtmfsrc->payload->event); + + if (dtmfsrc->payload->e) { + /* Don't forget to release the stream lock */ + gst_rtp_dtmf_src_set_stream_lock (dtmfsrc, FALSE); + + g_free (dtmfsrc->payload); + dtmfsrc->payload = NULL; + + if (!gst_pad_pause_task (dtmfsrc->srcpad)) { + GST_ERROR_OBJECT (dtmfsrc, "Failed to pause task on src pad"); + return; + } + + } + +} + +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, + "clock-base", G_TYPE_UINT, dtmfsrc->ts_base, + "seqnum-base", G_TYPE_UINT, dtmfsrc->seqnum_base, NULL); + + 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); + + gst_caps_unref (caps); +} + +static void +gst_rtp_dtmf_src_ready_to_paused (GstRTPDTMFSrc *dtmfsrc) +{ + gst_segment_init (&dtmfsrc->segment, GST_FORMAT_UNDEFINED); + + if (dtmfsrc->ssrc == -1) + dtmfsrc->current_ssrc = g_random_int (); + else + dtmfsrc->current_ssrc = dtmfsrc->ssrc; + + if (dtmfsrc->seqnum_offset == -1) + dtmfsrc->seqnum_base = g_random_int_range (0, G_MAXUINT16); + else + dtmfsrc->seqnum_base = dtmfsrc->seqnum_offset; + dtmfsrc->seqnum = dtmfsrc->seqnum_base; + + if (dtmfsrc->ts_offset == -1) + dtmfsrc->ts_base = g_random_int (); + else + dtmfsrc->ts_base = dtmfsrc->ts_offset; +} + +static GstStateChangeReturn +gst_rtp_dtmf_src_change_state (GstElement * element, GstStateChange transition) +{ + GstRTPDTMFSrc *dtmfsrc; + GstStateChangeReturn result; + gboolean no_preroll = FALSE; + + dtmfsrc = GST_RTP_DTMF_SRC (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_dtmf_src_ready_to_paused (dtmfsrc); + /* Indicate that we don't do PRE_ROLL */ + no_preroll = TRUE; + 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; + 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; +} + +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 new file mode 100644 index 0000000..797526e --- /dev/null +++ b/gst/dtmf/gstrtpdtmfsrc.h @@ -0,0 +1,101 @@ +/* GStreamer RTP DTMF source + * + * gstrtpdtmfsrc.h: + * + * Copyright (C) <2007> Nokia Corporation. + * Contact: Zeeshan Ali + * Copyright (C) <2005> Wim Taymans + * + * 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; + + guint32 ts_base; + guint16 seqnum_base; + + gint16 seqnum_offset; + guint16 seqnum; + gint32 ts_offset; + guint32 rtp_timestamp; + guint32 clock_rate; + guint pt; + guint ssrc; + guint current_ssrc; + gboolean first_packet; + gboolean last_packet; + + GstClockTime timestamp; + GstSegment segment; + + guint16 interval; + guint16 packet_redundancy; +}; + +struct _GstRTPDTMFSrcClass { + GstElementClass parent_class; +}; + +GType gst_rtp_dtmf_src_get_type (void); + +G_END_DECLS + +#endif /* __GST_RTP_DTMF_SRC_H__ */