gst/rtpmanager/: Some more ghostpad magic.
[platform/upstream/gst-plugins-good.git] / gst / rtpmanager / gstrtpbin.c
1 /* GStreamer
2  * Copyright (C) <2007> Wim Taymans <wim@fluendo.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-rtpbin
22  * @short_description: handle media from one RTP bin
23  * @see_also: rtpjitterbuffer, rtpclient, rtpsession
24  *
25  * <refsect2>
26  * <para>
27  * </para>
28  * <title>Example pipelines</title>
29  * <para>
30  * <programlisting>
31  * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink
32  * </programlisting>
33  * </para>
34  * </refsect2>
35  *
36  * Last reviewed on 2007-04-02 (0.10.6)
37  */
38
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42 #include <string.h>
43
44 #include "gstrtpbin.h"
45
46 /* elementfactory information */
47 static const GstElementDetails rtpbin_details = GST_ELEMENT_DETAILS ("RTP Bin",
48     "Filter/Editor/Video",
49     "Implement an RTP bin",
50     "Wim Taymans <wim@fluendo.com>");
51
52 /* sink pads */
53 static GstStaticPadTemplate rtpbin_recv_rtp_sink_template =
54 GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink_%d",
55     GST_PAD_SINK,
56     GST_PAD_REQUEST,
57     GST_STATIC_CAPS ("application/x-rtp")
58     );
59
60 static GstStaticPadTemplate rtpbin_recv_rtcp_sink_template =
61 GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink_%d",
62     GST_PAD_SINK,
63     GST_PAD_REQUEST,
64     GST_STATIC_CAPS ("application/x-rtcp")
65     );
66
67 static GstStaticPadTemplate rtpbin_send_rtp_sink_template =
68 GST_STATIC_PAD_TEMPLATE ("send_rtp_sink_%d",
69     GST_PAD_SINK,
70     GST_PAD_REQUEST,
71     GST_STATIC_CAPS ("application/x-rtp")
72     );
73
74 /* src pads */
75 static GstStaticPadTemplate rtpbin_recv_rtp_src_template =
76 GST_STATIC_PAD_TEMPLATE ("recv_rtp_src_%d_%d_%d",
77     GST_PAD_SRC,
78     GST_PAD_SOMETIMES,
79     GST_STATIC_CAPS ("application/x-rtp")
80     );
81
82 static GstStaticPadTemplate rtpbin_rtcp_src_template =
83 GST_STATIC_PAD_TEMPLATE ("rtcp_src_%d",
84     GST_PAD_SRC,
85     GST_PAD_REQUEST,
86     GST_STATIC_CAPS ("application/x-rtcp")
87     );
88
89 static GstStaticPadTemplate rtpbin_send_rtp_src_template =
90 GST_STATIC_PAD_TEMPLATE ("send_rtp_src_%d",
91     GST_PAD_SRC,
92     GST_PAD_SOMETIMES,
93     GST_STATIC_CAPS ("application/x-rtp")
94     );
95
96 #define GST_RTP_BIN_GET_PRIVATE(obj)  \
97    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_BIN, GstRTPBinPrivate))
98
99 struct _GstRTPBinPrivate
100 {
101 };
102
103 /* signals and args */
104 enum
105 {
106   /* FILL ME */
107   LAST_SIGNAL
108 };
109
110 enum
111 {
112   PROP_0
113 };
114
115 /* helper objects */
116 typedef struct
117 {
118   /* session id */
119   gint id;
120   /* the session element */
121   GstElement *session;
122   /* the SSRC demuxer */
123   GstElement *ssrcdemux;
124
125   /* the pads of the session */
126   GstPad *recv_rtp_sink;
127   GstPad *recv_rtcp_sink;
128   GstPad *send_rtp_sink;
129   GstPad *rtcp_src;
130
131 } GstRTPBinSession;
132
133 /* find a session with the given id */
134 static GstRTPBinSession *
135 find_session_by_id (GstRTPBin * rtpbin, gint id)
136 {
137   GList *walk;
138
139   for (walk = rtpbin->sessions; walk; walk = g_list_next (walk)) {
140     GstRTPBinSession *sess = (GstRTPBinSession *) walk->data;
141
142     if (sess->id == id)
143       return sess;
144   }
145   return NULL;
146 }
147
148 /* create a session with the given id */
149 static GstRTPBinSession *
150 create_session (GstRTPBin * rtpbin, gint id)
151 {
152   GstRTPBinSession *sess;
153   GstElement *elem;
154
155   if (!(elem = gst_element_factory_make ("rtpsession", NULL)))
156     goto no_session;
157
158   sess = g_new0 (GstRTPBinSession, 1);
159   sess->id = id;
160   sess->session = elem;
161
162   return sess;
163
164   /* ERRORS */
165 no_session:
166   {
167     g_warning ("rtpbin: could not create rtpsession element");
168     return NULL;
169   }
170 }
171
172 /* GObject vmethods */
173 static void gst_rtp_bin_finalize (GObject * object);
174 static void gst_rtp_bin_set_property (GObject * object, guint prop_id,
175     const GValue * value, GParamSpec * pspec);
176 static void gst_rtp_bin_get_property (GObject * object, guint prop_id,
177     GValue * value, GParamSpec * pspec);
178
179 /* GstElement vmethods */
180 static GstStateChangeReturn gst_rtp_bin_change_state (GstElement * element,
181     GstStateChange transition);
182 static GstPad *gst_rtp_bin_request_new_pad (GstElement * element,
183     GstPadTemplate * templ, const gchar * name);
184 static void gst_rtp_bin_release_pad (GstElement * element, GstPad * pad);
185
186 /*static guint gst_rtp_bin_signals[LAST_SIGNAL] = { 0 }; */
187
188 GST_BOILERPLATE (GstRTPBin, gst_rtp_bin, GstBin, GST_TYPE_BIN);
189
190 static void
191 gst_rtp_bin_base_init (gpointer klass)
192 {
193   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
194
195   /* sink pads */
196   gst_element_class_add_pad_template (element_class,
197       gst_static_pad_template_get (&rtpbin_recv_rtp_sink_template));
198   gst_element_class_add_pad_template (element_class,
199       gst_static_pad_template_get (&rtpbin_recv_rtcp_sink_template));
200   gst_element_class_add_pad_template (element_class,
201       gst_static_pad_template_get (&rtpbin_send_rtp_sink_template));
202
203   /* src pads */
204   gst_element_class_add_pad_template (element_class,
205       gst_static_pad_template_get (&rtpbin_recv_rtp_src_template));
206   gst_element_class_add_pad_template (element_class,
207       gst_static_pad_template_get (&rtpbin_rtcp_src_template));
208   gst_element_class_add_pad_template (element_class,
209       gst_static_pad_template_get (&rtpbin_send_rtp_src_template));
210
211   gst_element_class_set_details (element_class, &rtpbin_details);
212 }
213
214 static void
215 gst_rtp_bin_class_init (GstRTPBinClass * klass)
216 {
217   GObjectClass *gobject_class;
218   GstElementClass *gstelement_class;
219
220   gobject_class = (GObjectClass *) klass;
221   gstelement_class = (GstElementClass *) klass;
222
223   g_type_class_add_private (klass, sizeof (GstRTPBinPrivate));
224
225   gobject_class->finalize = gst_rtp_bin_finalize;
226   gobject_class->set_property = gst_rtp_bin_set_property;
227   gobject_class->get_property = gst_rtp_bin_get_property;
228
229   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_bin_change_state);
230   gstelement_class->request_new_pad =
231       GST_DEBUG_FUNCPTR (gst_rtp_bin_request_new_pad);
232   gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_rtp_bin_release_pad);
233 }
234
235 static void
236 gst_rtp_bin_init (GstRTPBin * rtpbin, GstRTPBinClass * klass)
237 {
238   rtpbin->priv = GST_RTP_BIN_GET_PRIVATE (rtpbin);
239 }
240
241 static void
242 gst_rtp_bin_finalize (GObject * object)
243 {
244   GstRTPBin *rtpbin;
245
246   rtpbin = GST_RTP_BIN (object);
247
248   G_OBJECT_CLASS (parent_class)->finalize (object);
249 }
250
251 static void
252 gst_rtp_bin_set_property (GObject * object, guint prop_id,
253     const GValue * value, GParamSpec * pspec)
254 {
255   GstRTPBin *rtpbin;
256
257   rtpbin = GST_RTP_BIN (object);
258
259   switch (prop_id) {
260     default:
261       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
262       break;
263   }
264 }
265
266 static void
267 gst_rtp_bin_get_property (GObject * object, guint prop_id,
268     GValue * value, GParamSpec * pspec)
269 {
270   GstRTPBin *rtpbin;
271
272   rtpbin = GST_RTP_BIN (object);
273
274   switch (prop_id) {
275     default:
276       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
277       break;
278   }
279 }
280
281 static GstStateChangeReturn
282 gst_rtp_bin_change_state (GstElement * element, GstStateChange transition)
283 {
284   GstStateChangeReturn res;
285   GstRTPBin *rtpbin;
286
287   rtpbin = GST_RTP_BIN (element);
288
289   switch (transition) {
290     case GST_STATE_CHANGE_NULL_TO_READY:
291       break;
292     case GST_STATE_CHANGE_READY_TO_PAUSED:
293       break;
294     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
295       break;
296     default:
297       break;
298   }
299
300   res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
301
302   switch (transition) {
303     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
304       break;
305     case GST_STATE_CHANGE_PAUSED_TO_READY:
306       break;
307     case GST_STATE_CHANGE_READY_TO_NULL:
308       break;
309     default:
310       break;
311   }
312   return res;
313 }
314
315 /* Create a pad for receiving RTP for the session in @name
316  */
317 static GstPad *
318 create_recv_rtp (GstRTPBin * rtpbin, GstPadTemplate * templ, const gchar * name)
319 {
320   GstPad *result;
321   guint sessid;
322   GstRTPBinSession *session;
323
324   /* first get the session number */
325   if (name == NULL || sscanf (name, "recv_rtp_sink_%d", &sessid) != 1)
326     goto no_name;
327
328   /* get or create session */
329   session = find_session_by_id (rtpbin, sessid);
330   if (!session) {
331     /* create session now */
332     session = create_session (rtpbin, sessid);
333     if (session == NULL)
334       goto create_error;
335   }
336   /* check if pad was requested */
337   if (session->recv_rtp_sink != NULL)
338     goto existed;
339
340   /* get recv_rtp pad and store */
341   session->recv_rtp_sink =
342       gst_element_get_request_pad (session->session, "recv_rtp_sink");
343   if (session->recv_rtp_sink == NULL)
344     goto pad_failed;
345
346   result =
347       gst_ghost_pad_new_from_template (name, session->recv_rtp_sink, templ);
348   gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result);
349
350   /* FIXME, get srcpad, link to SSRCDemux */
351
352   return result;
353
354   /* ERRORS */
355 no_name:
356   {
357     g_warning ("rtpbin: invalid name given");
358     return NULL;
359   }
360 create_error:
361   {
362     /* create_session already warned */
363     return NULL;
364   }
365 existed:
366   {
367     g_warning ("rtpbin: recv_rtp pad already requested for session %d", sessid);
368     return NULL;
369   }
370 pad_failed:
371   {
372     g_warning ("rtpbin: failed to get session pad");
373     return NULL;
374   }
375 }
376
377 /* Create a pad for receiving RTCP for the session in @name
378  */
379 static GstPad *
380 create_recv_rtcp (GstRTPBin * rtpbin, GstPadTemplate * templ,
381     const gchar * name)
382 {
383   GstPad *result;
384   guint sessid;
385   GstRTPBinSession *session;
386
387   /* first get the session number */
388   if (name == NULL || sscanf (name, "recv_rtcp_sink_%d", &sessid) != 1)
389     goto no_name;
390
391   /* get the session, it must exist or we error */
392   session = find_session_by_id (rtpbin, sessid);
393   if (!session)
394     goto no_session;
395
396   /* check if pad was requested */
397   if (session->recv_rtcp_sink != NULL)
398     goto existed;
399
400   /* get recv_rtp pad and store */
401   session->recv_rtcp_sink =
402       gst_element_get_request_pad (session->session, "recv_rtcp_sink");
403   if (session->recv_rtcp_sink == NULL)
404     goto pad_failed;
405
406   result =
407       gst_ghost_pad_new_from_template (name, session->recv_rtcp_sink, templ);
408   gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result);
409
410   /* FIXME, get srcpad, link to SSRCDemux */
411
412   return result;
413
414   /* ERRORS */
415 no_name:
416   {
417     g_warning ("rtpbin: invalid name given");
418     return NULL;
419   }
420 no_session:
421   {
422     g_warning ("rtpbin: no session with id %d", sessid);
423     return NULL;
424   }
425 existed:
426   {
427     g_warning ("rtpbin: recv_rtcp pad already requested for session %d",
428         sessid);
429     return NULL;
430   }
431 pad_failed:
432   {
433     g_warning ("rtpbin: failed to get session pad");
434     return NULL;
435   }
436 }
437
438 /* Create a pad for sending RTP for the session in @name
439  */
440 static GstPad *
441 create_send_rtp (GstRTPBin * rtpbin, GstPadTemplate * templ, const gchar * name)
442 {
443   GstPad *result, *srcpad, *srcghost;
444   gchar *gname;
445   guint sessid;
446   GstRTPBinSession *session;
447   GstElementClass *klass;
448
449   /* first get the session number */
450   if (name == NULL || sscanf (name, "send_rtp_sink_%d", &sessid) != 1)
451     goto no_name;
452
453   /* get or create session */
454   session = find_session_by_id (rtpbin, sessid);
455   if (!session) {
456     /* create session now */
457     session = create_session (rtpbin, sessid);
458     if (session == NULL)
459       goto create_error;
460   }
461
462   /* check if pad was requested */
463   if (session->send_rtp_sink != NULL)
464     goto existed;
465
466   /* get recv_rtp pad and store */
467   session->send_rtp_sink =
468       gst_element_get_request_pad (session->session, "send_rtp_sink");
469   if (session->send_rtp_sink == NULL)
470     goto pad_failed;
471
472   result =
473       gst_ghost_pad_new_from_template (name, session->send_rtp_sink, templ);
474   gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result);
475
476   /* get srcpad */
477   srcpad = gst_element_get_pad (session->session, "send_rtp_src");
478   if (srcpad == NULL)
479     goto no_srcpad;
480
481   /* ghost the new source pad */
482   klass = GST_ELEMENT_GET_CLASS (rtpbin);
483   gname = g_strdup_printf ("send_rtp_src_%d", sessid);
484   templ = gst_element_class_get_pad_template (klass, "send_rtp_src_%d");
485   srcghost =
486       gst_ghost_pad_new_from_template (gname, session->send_rtp_sink, templ);
487   gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), srcghost);
488   g_free (gname);
489
490   return result;
491
492   /* ERRORS */
493 no_name:
494   {
495     g_warning ("rtpbin: invalid name given");
496     return NULL;
497   }
498 create_error:
499   {
500     /* create_session already warned */
501     return NULL;
502   }
503 existed:
504   {
505     g_warning ("rtpbin: send_rtp pad already requested for session %d", sessid);
506     return NULL;
507   }
508 pad_failed:
509   {
510     g_warning ("rtpbin: failed to get session pad for session %d", sessid);
511     return NULL;
512   }
513 no_srcpad:
514   {
515     g_warning ("rtpbin: failed to get rtp source pad for session %d", sessid);
516     return NULL;
517   }
518 }
519
520 /* Create a pad for sending RTCP for the session in @name
521  */
522 static GstPad *
523 create_rtcp (GstRTPBin * rtpbin, GstPadTemplate * templ, const gchar * name)
524 {
525   GstPad *result;
526   guint sessid;
527   GstRTPBinSession *session;
528
529   /* first get the session number */
530   if (name == NULL || sscanf (name, "send_rtp_sink_%d", &sessid) != 1)
531     goto no_name;
532
533   /* get or create session */
534   session = find_session_by_id (rtpbin, sessid);
535   if (!session)
536     goto no_session;
537
538   /* check if pad was requested */
539   if (session->rtcp_src != NULL)
540     goto existed;
541
542   /* get rtcp_src pad and store */
543   session->rtcp_src =
544       gst_element_get_request_pad (session->session, "rtcp_src");
545   if (session->rtcp_src == NULL)
546     goto pad_failed;
547
548   result = gst_ghost_pad_new_from_template (name, session->rtcp_src, templ);
549   gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result);
550
551   return result;
552
553   /* ERRORS */
554 no_name:
555   {
556     g_warning ("rtpbin: invalid name given");
557     return NULL;
558   }
559 no_session:
560   {
561     g_warning ("rtpbin: session with id %d does not exist", sessid);
562     return NULL;
563   }
564 existed:
565   {
566     g_warning ("rtpbin: rtcp_src pad already requested for session %d", sessid);
567     return NULL;
568   }
569 pad_failed:
570   {
571     g_warning ("rtpbin: failed to get rtcp pad for session %d", sessid);
572     return NULL;
573   }
574 }
575
576 /* 
577  */
578 static GstPad *
579 gst_rtp_bin_request_new_pad (GstElement * element,
580     GstPadTemplate * templ, const gchar * name)
581 {
582   GstRTPBin *rtpbin;
583   GstElementClass *klass;
584   GstPad *result;
585
586   g_return_val_if_fail (templ != NULL, NULL);
587   g_return_val_if_fail (GST_IS_RTP_BIN (element), NULL);
588
589   rtpbin = GST_RTP_BIN (element);
590   klass = GST_ELEMENT_GET_CLASS (element);
591
592   /* figure out the template */
593   if (templ == gst_element_class_get_pad_template (klass, "recv_rtp_sink_%d")) {
594     result = create_recv_rtp (rtpbin, templ, name);
595   } else if (templ == gst_element_class_get_pad_template (klass,
596           "recv_rtcp_sink_%d")) {
597     result = create_recv_rtcp (rtpbin, templ, name);
598   } else if (templ == gst_element_class_get_pad_template (klass,
599           "send_rtp_sink_%d")) {
600     result = create_send_rtp (rtpbin, templ, name);
601   } else if (templ == gst_element_class_get_pad_template (klass, "rtcp_src_%d")) {
602     result = create_rtcp (rtpbin, templ, name);
603   } else
604     goto wrong_template;
605
606   return result;
607
608   /* ERRORS */
609 wrong_template:
610   {
611     g_warning ("rtpbin: this is not our template");
612     return NULL;
613   }
614 }
615
616 static void
617 gst_rtp_bin_release_pad (GstElement * element, GstPad * pad)
618 {
619 }