87ad17cf29bed665c9a95323a3b6e895a9f55fe2
[platform/upstream/gstreamer.git] / gst / rtpmanager / gstrtpdtmfmux.c
1 /* RTP DTMF muxer element for GStreamer
2  *
3  * gstrtpdtmfmux.c:
4  *
5  * Copyright (C) <2007-2010> Nokia Corporation.
6  *   Contact: Zeeshan Ali <zeeshan.ali@nokia.com>
7  * Copyright (C) <2007-2010> Collabora Ltd
8  *   Contact: Olivier Crete <olivier.crete@collabora.co.uk>
9  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
10  *               2000,2005 Wim Taymans <wim@fluendo.com>
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with this library; if not, write to the
24  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25  * Boston, MA 02111-1307, USA.
26  */
27
28 /**
29  * SECTION:element-rtpdtmfmux
30  * @see_also: rtpdtmfsrc, dtmfsrc
31  *
32  * The RTPDTMFMuxer mixes/muxes RTP DTMF stream(s) into other RTP
33  * streams. It does exactly what it's parent (rtpmux) does, except
34  * that it allows upstream peer elements to request exclusive access
35  * to the stream, which is required by the RTP DTMF standards (see RFC
36  * 2833, section 3.2, para 1 for details). The peer upstream element
37  * requests the acquisition and release of a stream lock beginning
38  * using custom downstream gstreamer events. To request the acquisition
39  * of the lock, the peer element must send an event of type
40  * GST_EVENT_CUSTOM_DOWNSTREAM_OOB, having a
41  * structure of name "stream-lock" with only one boolean field:
42  * "lock". If this field is set to TRUE, the request is for the
43  * acquisition of the lock, otherwise it is for release of the lock.
44  *
45  * For example, the following code in an upstream peer element
46  * requests the acquisition of the stream lock:
47  *
48  * <programlisting>
49  * GstEvent *event;
50  * GstStructure *structure;
51  * GstPad *srcpad;
52  *
53  * ... /\* srcpad initialization goes here \*\/
54  *
55  * structure = gst_structure_new ("stream-lock",
56  *                    "lock", G_TYPE_BOOLEAN, TRUE, NULL);
57  *
58  * event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB, structure);
59  * gst_pad_push_event (dtmfsrc->srcpad, event);
60  * </programlisting>
61  *
62  */
63
64 #ifdef HAVE_CONFIG_H
65 #include "config.h"
66 #endif
67
68 #include <gst/gst.h>
69 #include <string.h>
70
71 #include "gstrtpdtmfmux.h"
72
73 GST_DEBUG_CATEGORY_STATIC (gst_rtp_dtmf_mux_debug);
74 #define GST_CAT_DEFAULT gst_rtp_dtmf_mux_debug
75
76 enum
77 {
78   SIGNAL_LOCKING_STREAM,
79   SIGNAL_UNLOCKED_STREAM,
80   LAST_SIGNAL
81 };
82
83 static guint gst_rtpdtmfmux_signals[LAST_SIGNAL] = { 0 };
84
85 static void gst_rtp_dtmf_mux_dispose (GObject * object);
86
87 static void gst_rtp_dtmf_mux_release_pad (GstElement * element, GstPad * pad);
88
89 static gboolean gst_rtp_dtmf_mux_sink_event (GstPad * pad, GstEvent * event);
90 static GstFlowReturn gst_rtp_dtmf_mux_chain (GstPad * pad, GstBuffer * buffer);
91
92 GST_BOILERPLATE (GstRTPDTMFMux, gst_rtp_dtmf_mux, GstRTPMux, GST_TYPE_RTP_MUX);
93
94 static void
95 gst_rtp_dtmf_mux_init (GstRTPDTMFMux * object, GstRTPDTMFMuxClass * g_class)
96 {
97 }
98
99 static void
100 gst_rtp_dtmf_mux_base_init (gpointer g_class)
101 {
102   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
103
104   gst_element_class_set_details_simple (element_class, "RTP muxer",
105       "Codec/Muxer",
106       "mixes RTP DTMF streams into other RTP streams",
107       "Zeeshan Ali <first.last@nokia.com>");
108 }
109
110 static void
111 gst_rtp_dtmf_mux_class_init (GstRTPDTMFMuxClass * klass)
112 {
113   GObjectClass *gobject_class;
114   GstElementClass *gstelement_class;
115   GstRTPMuxClass *gstrtpmux_class;
116
117   gobject_class = (GObjectClass *) klass;
118   gstelement_class = (GstElementClass *) klass;
119   gstrtpmux_class = (GstRTPMuxClass *) klass;
120
121   gst_rtpdtmfmux_signals[SIGNAL_LOCKING_STREAM] =
122       g_signal_new ("locking", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
123       G_STRUCT_OFFSET (GstRTPDTMFMuxClass, locking), NULL, NULL,
124       gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
125
126   gst_rtpdtmfmux_signals[SIGNAL_UNLOCKED_STREAM] =
127       g_signal_new ("unlocked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
128       G_STRUCT_OFFSET (GstRTPDTMFMuxClass, unlocked), NULL, NULL,
129       gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
130
131   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_rtp_dtmf_mux_dispose);
132   gstelement_class->release_pad =
133       GST_DEBUG_FUNCPTR (gst_rtp_dtmf_mux_release_pad);
134   gstrtpmux_class->chain_func = GST_DEBUG_FUNCPTR (gst_rtp_dtmf_mux_chain);
135   gstrtpmux_class->sink_event_func =
136       GST_DEBUG_FUNCPTR (gst_rtp_dtmf_mux_sink_event);
137 }
138
139 static void
140 gst_rtp_dtmf_mux_dispose (GObject * object)
141 {
142   GstRTPDTMFMux *mux;
143
144   mux = GST_RTP_DTMF_MUX (object);
145
146   GST_OBJECT_LOCK (mux);
147   if (mux->special_pad != NULL) {
148     gst_object_unref (mux->special_pad);
149     mux->special_pad = NULL;
150   }
151   GST_OBJECT_UNLOCK (mux);
152
153   G_OBJECT_CLASS (parent_class)->dispose (object);
154 }
155
156 static GstFlowReturn
157 gst_rtp_dtmf_mux_chain (GstPad * pad, GstBuffer * buffer)
158 {
159   GstRTPDTMFMux *mux;
160   GstFlowReturn ret;
161
162   mux = GST_RTP_DTMF_MUX (gst_pad_get_parent (pad));
163
164   GST_OBJECT_LOCK (mux);
165   if (mux->special_pad != NULL && mux->special_pad != pad) {
166     /* Drop the buffer */
167     gst_buffer_unref (buffer);
168     ret = GST_FLOW_OK;
169     GST_OBJECT_UNLOCK (mux);
170   }
171
172   else {
173     GST_OBJECT_UNLOCK (mux);
174     if (parent_class->chain_func)
175       ret = parent_class->chain_func (pad, buffer);
176     else {
177       gst_buffer_unref (buffer);
178       ret = GST_FLOW_ERROR;
179     }
180   }
181
182   gst_object_unref (mux);
183   return ret;
184 }
185
186 static void
187 gst_rtp_dtmf_mux_lock_stream (GstRTPDTMFMux * mux, GstPad * pad)
188 {
189   if (mux->special_pad != NULL)
190     GST_WARNING_OBJECT (mux,
191         "Stream lock already acquired by pad %s",
192         GST_ELEMENT_NAME (mux->special_pad));
193
194   else {
195     GST_DEBUG_OBJECT (mux,
196         "Stream lock acquired by pad %s", GST_ELEMENT_NAME (pad));
197     mux->special_pad = gst_object_ref (pad);
198   }
199 }
200
201 static void
202 gst_rtp_dtmf_mux_unlock_stream (GstRTPDTMFMux * mux, GstPad * pad)
203 {
204   if (mux->special_pad == NULL)
205     GST_WARNING_OBJECT (mux, "Stream lock not acquired, can't release it");
206
207   else if (pad != mux->special_pad)
208     GST_WARNING_OBJECT (mux,
209         "pad %s attempted to release Stream lock"
210         " which was acquired by pad %s", GST_ELEMENT_NAME (pad),
211         GST_ELEMENT_NAME (mux->special_pad));
212   else {
213     GST_DEBUG_OBJECT (mux,
214         "Stream lock released by pad %s", GST_ELEMENT_NAME (mux->special_pad));
215     gst_object_unref (mux->special_pad);
216     mux->special_pad = NULL;
217   }
218 }
219
220 static gboolean
221 gst_rtp_dtmf_mux_handle_stream_lock_event (GstRTPDTMFMux * mux, GstPad * pad,
222     const GstStructure * event_structure)
223 {
224   gboolean lock;
225
226   if (!gst_structure_get_boolean (event_structure, "lock", &lock))
227     return FALSE;
228
229   if (lock)
230     g_signal_emit (G_OBJECT (mux),
231         gst_rtpdtmfmux_signals[SIGNAL_LOCKING_STREAM], 0, pad);
232
233   GST_OBJECT_LOCK (mux);
234   if (lock)
235     gst_rtp_dtmf_mux_lock_stream (mux, pad);
236   else
237     gst_rtp_dtmf_mux_unlock_stream (mux, pad);
238   GST_OBJECT_UNLOCK (mux);
239
240   if (!lock)
241     g_signal_emit (G_OBJECT (mux),
242         gst_rtpdtmfmux_signals[SIGNAL_UNLOCKED_STREAM], 0, pad);
243
244   return TRUE;
245 }
246
247 static gboolean
248 gst_rtp_dtmf_mux_handle_downstream_event (GstRTPDTMFMux * mux,
249     GstPad * pad, GstEvent * event)
250 {
251   const GstStructure *structure;
252   gboolean ret = FALSE;
253
254   structure = gst_event_get_structure (event);
255   /* FIXME: is this event generic enough to be given a generic name? */
256   if (structure && gst_structure_has_name (structure, "stream-lock"))
257     ret = gst_rtp_dtmf_mux_handle_stream_lock_event (mux, pad, structure);
258
259   return ret;
260 }
261
262 static gboolean
263 gst_rtp_dtmf_mux_ignore_event (GstPad * pad, GstEvent * event)
264 {
265   gboolean ret;
266
267   if (parent_class->sink_event_func)
268     /* Give the parent a chance to handle the event first */
269     ret = parent_class->sink_event_func (pad, event);
270
271   else
272     ret = gst_pad_event_default (pad, event);
273
274   return ret;
275 }
276
277 static gboolean
278 gst_rtp_dtmf_mux_sink_event (GstPad * pad, GstEvent * event)
279 {
280   GstRTPDTMFMux *mux;
281   GstEventType type;
282   gboolean ret = FALSE;
283
284   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
285
286   mux = (GstRTPDTMFMux *) gst_pad_get_parent (pad);
287
288   switch (type) {
289     case GST_EVENT_CUSTOM_DOWNSTREAM_OOB:
290       ret = gst_rtp_dtmf_mux_handle_downstream_event (mux, pad, event);
291       gst_event_unref (event);
292       break;
293     default:
294       ret = gst_rtp_dtmf_mux_ignore_event (pad, event);
295       break;
296   }
297
298   gst_object_unref (mux);
299   return ret;
300 }
301
302 static void
303 gst_rtp_dtmf_mux_release_pad (GstElement * element, GstPad * pad)
304 {
305   GstRTPDTMFMux *mux = GST_RTP_DTMF_MUX (element);
306
307   GST_OBJECT_LOCK (mux);
308   if (mux->special_pad == pad) {
309     gst_object_unref (mux->special_pad);
310     mux->special_pad = NULL;
311   }
312   GST_OBJECT_UNLOCK (mux);
313
314   GST_CALL_PARENT (GST_ELEMENT_CLASS, release_pad, (element, pad));
315 }
316
317 gboolean
318 gst_rtp_dtmf_mux_plugin_init (GstPlugin * plugin)
319 {
320   GST_DEBUG_CATEGORY_INIT (gst_rtp_dtmf_mux_debug, "rtpdtmfmux", 0,
321       "rtp dtmf muxer");
322
323   return gst_element_register (plugin, "rtpdtmfmux", GST_RANK_NONE,
324       GST_TYPE_RTP_DTMF_MUX);
325 }