rtpmanagerbad: add RTP streaming elements
[platform/upstream/gstreamer.git] / gst / rtp / gstrtpsink.c
1 /* GStreamer
2  * Copyright (C) <2018> Marc Leeman <marc.leeman@gmail.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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * SECTION: gstrtsinkp
22  * @title: GstRtpSink
23  * @short description: element with Uri interface to stream RTP data to
24  * the network.
25  *
26  * RTP (RFC 3550) is a protocol to stream media over the network while
27  * retaining the timing information and providing enough information to
28  * reconstruct the correct timing domain by the receiver.
29  *
30  * The RTP data port should be even, while the RTCP port should be
31  * odd. The URI that is entered defines the data port, the RTCP port will
32  * be allocated to the next port.
33  *
34  * This element hooks up the correct sockets to support both RTP as the
35  * accompanying RTCP layer.
36  *
37  * This Bin handles streaming RTP payloaded data on the network.
38  *
39  * This element also implements the URI scheme `rtp://` allowing to send
40  * data on the network by bins that allow use the URI to determine the sink.
41  * The RTP URI handler also allows setting properties through the URI query.
42  */
43 #ifdef HAVE_CONFIG_H
44 #include <config.h>
45 #endif
46
47 #include <gio/gio.h>
48
49 #include "gstrtpsink.h"
50 #include "gstrtp-utils.h"
51
52 GST_DEBUG_CATEGORY_STATIC (gst_rtp_sink_debug);
53 #define GST_CAT_DEFAULT gst_rtp_sink_debug
54
55 #define DEFAULT_PROP_URI              "rtp://0.0.0.0:5004"
56 #define DEFAULT_PROP_TTL              64
57 #define DEFAULT_PROP_TTL_MC           1
58
59 enum
60 {
61   PROP_0,
62
63   PROP_URI,
64   PROP_TTL,
65   PROP_TTL_MC,
66
67   PROP_LAST
68 };
69
70 static void gst_rtp_sink_uri_handler_init (gpointer g_iface,
71     gpointer iface_data);
72
73 #define gst_rtp_sink_parent_class parent_class
74 G_DEFINE_TYPE_WITH_CODE (GstRtpSink, gst_rtp_sink, GST_TYPE_BIN,
75     G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_rtp_sink_uri_handler_init);
76     GST_DEBUG_CATEGORY_INIT (gst_rtp_sink_debug, "rtpsink", 0, "RTP Sink"));
77
78 #define GST_RTP_SINK_GET_LOCK(obj) (&((GstRtpSink*)(obj))->lock)
79 #define GST_RTP_SINK_LOCK(obj) (g_mutex_lock (GST_RTP_SINK_GET_LOCK(obj)))
80 #define GST_RTP_SINK_UNLOCK(obj) (g_mutex_unlock (GST_RTP_SINK_GET_LOCK(obj)))
81
82 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
83     GST_PAD_SINK,
84     GST_PAD_REQUEST,
85     GST_STATIC_CAPS ("application/x-rtp"));
86
87 static GstStateChangeReturn
88 gst_rtp_sink_change_state (GstElement * element, GstStateChange transition);
89
90 static void
91 gst_rtp_sink_set_property (GObject * object, guint prop_id,
92     const GValue * value, GParamSpec * pspec)
93 {
94   GstRtpSink *self = GST_RTP_SINK (object);
95
96   switch (prop_id) {
97     case PROP_URI:{
98       GstUri *uri = NULL;
99
100       GST_RTP_SINK_LOCK (object);
101       uri = gst_uri_from_string (g_value_get_string (value));
102       if (uri == NULL)
103         break;
104
105       if (self->uri)
106         gst_uri_unref (self->uri);
107       self->uri = uri;
108       /* RTP data ports should be even according to RFC 3550, while the
109        * RTCP is sent on odd ports. Just warn if there is a mismatch. */
110       if (gst_uri_get_port (self->uri) % 2)
111         GST_WARNING_OBJECT (self,
112             "Port %u is not even, this is not standard (see RFC 3550).",
113             gst_uri_get_port (self->uri));
114
115       gst_rtp_utils_set_properties_from_uri_query (G_OBJECT (self), self->uri);
116       GST_RTP_SINK_UNLOCK (object);
117       break;
118     }
119     case PROP_TTL:
120       self->ttl = g_value_get_int (value);
121       break;
122     case PROP_TTL_MC:
123       self->ttl_mc = g_value_get_int (value);
124       break;
125     default:
126       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
127       break;
128   }
129 }
130
131 static void
132 gst_rtp_sink_get_property (GObject * object, guint prop_id,
133     GValue * value, GParamSpec * pspec)
134 {
135   GstRtpSink *self = GST_RTP_SINK (object);
136
137   switch (prop_id) {
138     case PROP_URI:
139       GST_RTP_SINK_LOCK (object);
140       if (self->uri)
141         g_value_take_string (value, gst_uri_to_string (self->uri));
142       else
143         g_value_set_string (value, NULL);
144       GST_RTP_SINK_UNLOCK (object);
145       break;
146     case PROP_TTL:
147       g_value_set_int (value, self->ttl);
148       break;
149     case PROP_TTL_MC:
150       g_value_set_int (value, self->ttl_mc);
151       break;
152     default:
153       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
154       break;
155   }
156 }
157
158 static void
159 gst_rtp_sink_finalize (GObject * gobject)
160 {
161   GstRtpSink *self = GST_RTP_SINK (gobject);
162
163   if (self->uri)
164     gst_uri_unref (self->uri);
165
166   g_mutex_clear (&self->lock);
167   G_OBJECT_CLASS (parent_class)->finalize (gobject);
168 }
169
170 static gboolean
171 gst_rtp_sink_setup_elements (GstRtpSink * self)
172 {
173   /*GstPad *pad; */
174   GSocket *socket;
175   GInetAddress *addr;
176   gchar name[48];
177   GstCaps *caps;
178
179   /* Should not be NULL */
180   g_return_val_if_fail (self->uri != NULL, FALSE);
181
182   /* if not already configured */
183   if (self->funnel_rtp == NULL) {
184     self->funnel_rtp = gst_element_factory_make ("funnel", NULL);
185     if (self->funnel_rtp == NULL) {
186       GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
187           ("%s", "funnel_rtp element is not available"));
188       return FALSE;
189     }
190
191     self->funnel_rtcp = gst_element_factory_make ("funnel", NULL);
192     if (self->funnel_rtcp == NULL) {
193       GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
194           ("%s", "funnel_rtcp element is not available"));
195       return FALSE;
196     }
197
198     self->rtp_sink = gst_element_factory_make ("udpsink", NULL);
199     if (self->rtp_sink == NULL) {
200       GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
201           ("%s", "rtp_sink element is not available"));
202       return FALSE;
203     }
204
205     self->rtcp_src = gst_element_factory_make ("udpsrc", NULL);
206     if (self->rtcp_src == NULL) {
207       GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
208           ("%s", "rtcp_src element is not available"));
209       return FALSE;
210     }
211
212     self->rtcp_sink = gst_element_factory_make ("udpsink", NULL);
213     if (self->rtcp_sink == NULL) {
214       GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
215           ("%s", "rtcp_sink element is not available"));
216       return FALSE;
217     }
218
219     gst_bin_add (GST_BIN (self), self->funnel_rtp);
220     gst_bin_add (GST_BIN (self), self->funnel_rtcp);
221
222     /* Add elements as needed, since udpsrc/udpsink for RTCP share a socket,
223      * not all at the same moment */
224     g_object_set (self->rtp_sink,
225         "host", gst_uri_get_host (self->uri),
226         "port", gst_uri_get_port (self->uri),
227         "ttl", self->ttl, "ttl-mc", self->ttl_mc, NULL);
228
229     gst_bin_add (GST_BIN (self), self->rtp_sink);
230
231     g_object_set (self->rtcp_sink,
232         "host", gst_uri_get_host (self->uri),
233         "port", gst_uri_get_port (self->uri) + 1,
234         "ttl", self->ttl, "ttl-mc", self->ttl_mc,
235         /* Set false since we're reusing a socket */
236         "auto-multicast", FALSE, NULL);
237
238     gst_bin_add (GST_BIN (self), self->rtcp_sink);
239
240     /* no need to set address if unicast */
241     caps = gst_caps_new_empty_simple ("application/x-rtcp");
242     g_object_set (self->rtcp_src,
243         "port", gst_uri_get_port (self->uri) + 1, "caps", caps, NULL);
244     gst_caps_unref (caps);
245
246     addr = g_inet_address_new_from_string (gst_uri_get_host (self->uri));
247     if (g_inet_address_get_is_multicast (addr)) {
248       g_object_set (self->rtcp_src, "address", gst_uri_get_host (self->uri),
249           NULL);
250     }
251     g_object_unref (addr);
252
253     gst_bin_add (GST_BIN (self), self->rtcp_src);
254
255     gst_element_link (self->funnel_rtp, self->rtp_sink);
256     gst_element_link (self->funnel_rtcp, self->rtcp_sink);
257
258     gst_element_sync_state_with_parent (self->funnel_rtp);
259     gst_element_sync_state_with_parent (self->funnel_rtcp);
260     gst_element_sync_state_with_parent (self->rtp_sink);
261     gst_element_sync_state_with_parent (self->rtcp_src);
262
263     g_object_get (G_OBJECT (self->rtcp_src), "used-socket", &socket, NULL);
264     g_object_set (G_OBJECT (self->rtcp_sink), "socket", socket, NULL);
265
266     gst_element_sync_state_with_parent (self->rtcp_sink);
267
268   }
269
270   /* pads are all named */
271   g_snprintf (name, 48, "send_rtp_src_%u", GST_ELEMENT (self)->numpads);
272   gst_element_link_pads (self->rtpbin, name, self->funnel_rtp, "sink_%u");
273
274   g_snprintf (name, 48, "send_rtcp_src_%u", GST_ELEMENT (self)->numpads);
275   gst_element_link_pads (self->rtpbin, name, self->funnel_rtcp, "sink_%u");
276
277   g_snprintf (name, 48, "recv_rtcp_sink_%u", GST_ELEMENT (self)->numpads);
278   gst_element_link_pads (self->rtcp_src, "src", self->rtpbin, name);
279
280   return TRUE;
281 }
282
283 static GstPad *
284 gst_rtp_sink_request_new_pad (GstElement * element,
285     GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
286 {
287   GstRtpSink *self = GST_RTP_SINK (element);
288   GstPad *pad = NULL;
289
290   if (self->rtpbin == NULL) {
291     GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
292         ("%s", "rtpbin element is not available"));
293     return NULL;
294   }
295
296   if (gst_rtp_sink_setup_elements (self) == FALSE)
297     return NULL;
298
299   GST_RTP_SINK_LOCK (self);
300
301   pad = gst_element_get_request_pad (self->rtpbin, "send_rtp_sink_%u");
302   g_return_val_if_fail (pad != NULL, NULL);
303
304   GST_RTP_SINK_UNLOCK (self);
305
306   return pad;
307 }
308
309 static void
310 gst_rtp_sink_release_pad (GstElement * element, GstPad * pad)
311 {
312   GstRtpSink *self = GST_RTP_SINK (element);
313   GstPad *rpad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
314
315   GST_RTP_SINK_LOCK (self);
316   gst_element_release_request_pad (self->rtpbin, rpad);
317   gst_object_unref (rpad);
318
319   gst_pad_set_active (pad, FALSE);
320   gst_element_remove_pad (GST_ELEMENT (self), pad);
321
322   GST_RTP_SINK_UNLOCK (self);
323 }
324
325 static void
326 gst_rtp_sink_class_init (GstRtpSinkClass * klass)
327 {
328   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
329   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
330
331   gobject_class->set_property = gst_rtp_sink_set_property;
332   gobject_class->get_property = gst_rtp_sink_get_property;
333   gobject_class->finalize = gst_rtp_sink_finalize;
334   gstelement_class->change_state = gst_rtp_sink_change_state;
335
336   gstelement_class->request_new_pad =
337       GST_DEBUG_FUNCPTR (gst_rtp_sink_request_new_pad);
338   gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_rtp_sink_release_pad);
339
340   /**
341    * GstRtpSink:uri:
342    *
343    * uri to stream RTP to. All GStreamer parameters can be
344    * encoded in the URI, this URI format is RFC compliant.
345    */
346   g_object_class_install_property (gobject_class, PROP_URI,
347       g_param_spec_string ("uri", "URI",
348           "URI in the form of rtp://host:port?query", DEFAULT_PROP_URI,
349           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
350
351   /**
352    * GstRtpSink:ttl:
353    *
354    * Set the unicast TTL parameter.
355    */
356   g_object_class_install_property (gobject_class, PROP_TTL,
357       g_param_spec_int ("ttl", "Unicast TTL",
358           "Used for setting the unicast TTL parameter",
359           0, 255, DEFAULT_PROP_TTL,
360           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
361
362   /**
363    * GstRtpSink:ttl-mc:
364    *
365    * Set the multicast TTL parameter.
366    */
367   g_object_class_install_property (gobject_class, PROP_TTL_MC,
368       g_param_spec_int ("ttl-mc", "Multicast TTL",
369           "Used for setting the multicast TTL parameter", 0, 255,
370           DEFAULT_PROP_TTL_MC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
371
372   gst_element_class_add_pad_template (gstelement_class,
373       gst_static_pad_template_get (&sink_template));
374
375   gst_element_class_set_static_metadata (gstelement_class,
376       "RTP Sink element",
377       "Generic/Bin/Sink",
378       "Simple RTP sink", "Marc Leeman <marc.leeman@gmail.com>");
379 }
380
381 static void
382 gst_rtp_sink_rtpbin_element_added_cb (GstBin * element,
383     GstElement * new_element, gpointer data)
384 {
385   GstRtpSink *self = GST_RTP_SINK (data);
386   GST_INFO_OBJECT (self,
387       "Element %" GST_PTR_FORMAT " added element %" GST_PTR_FORMAT ".", element,
388       new_element);
389 }
390
391 static void
392 gst_rtp_sink_rtpbin_pad_added_cb (GstElement * element, GstPad * pad,
393     gpointer data)
394 {
395   GstRtpSink *self = GST_RTP_SINK (data);
396   GstCaps *caps = gst_pad_query_caps (pad, NULL);
397   GstPad *upad;
398
399   /* Expose RTP data pad only */
400   GST_INFO_OBJECT (self,
401       "Element %" GST_PTR_FORMAT " added pad %" GST_PTR_FORMAT "with caps %"
402       GST_PTR_FORMAT ".", element, pad, caps);
403
404   /* Sanity checks */
405   if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
406     /* Src pad, do not expose */
407     gst_caps_unref (caps);
408     return;
409   }
410
411   if (G_LIKELY (caps)) {
412     GstCaps *ref_caps = gst_caps_new_empty_simple ("application/x-rtcp");
413
414     if (gst_caps_can_intersect (caps, ref_caps)) {
415       /* SRC RTCP caps, do not expose */
416       gst_caps_unref (ref_caps);
417       gst_caps_unref (caps);
418
419       return;
420     }
421     gst_caps_unref (ref_caps);
422   } else {
423     GST_ERROR_OBJECT (self, "Pad with no caps detected.");
424     gst_caps_unref (caps);
425
426     return;
427   }
428   gst_caps_unref (caps);
429
430   upad = gst_element_get_compatible_pad (self->funnel_rtp, pad, NULL);
431   if (upad == NULL) {
432     GST_ERROR_OBJECT (self, "No compatible pad found to link pad.");
433     gst_caps_unref (caps);
434
435     return;
436   }
437   GST_INFO_OBJECT (self, "Linking with pad %" GST_PTR_FORMAT ".", upad);
438   gst_pad_link (pad, upad);
439   gst_object_unref (upad);
440 }
441
442 static void
443 gst_rtp_sink_rtpbin_pad_removed_cb (GstElement * element, GstPad * pad,
444     gpointer data)
445 {
446   GstRtpSink *self = GST_RTP_SINK (data);
447   GST_INFO_OBJECT (self,
448       "Element %" GST_PTR_FORMAT " removed pad %" GST_PTR_FORMAT ".", element,
449       pad);
450 }
451
452 static gboolean
453 gst_rtp_sink_setup_rtpbin (GstRtpSink * self)
454 {
455   self->rtpbin = gst_element_factory_make ("rtpbin", NULL);
456   if (self->rtpbin == NULL) {
457     GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
458         ("%s", "rtpbin element is not available"));
459     return FALSE;
460   }
461
462   /* Add rtpbin callbacks to monitor the operation of rtpbin */
463   g_signal_connect (self->rtpbin, "element-added",
464       G_CALLBACK (gst_rtp_sink_rtpbin_element_added_cb), self);
465   g_signal_connect (self->rtpbin, "pad-added",
466       G_CALLBACK (gst_rtp_sink_rtpbin_pad_added_cb), self);
467   g_signal_connect (self->rtpbin, "pad-removed",
468       G_CALLBACK (gst_rtp_sink_rtpbin_pad_removed_cb), self);
469
470   gst_bin_add (GST_BIN (self), self->rtpbin);
471
472   gst_element_sync_state_with_parent (self->rtpbin);
473
474   return TRUE;
475 }
476
477 static GstStateChangeReturn
478 gst_rtp_sink_change_state (GstElement * element, GstStateChange transition)
479 {
480   GstRtpSink *self = GST_RTP_SINK (element);
481   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
482
483   GST_DEBUG_OBJECT (self, "changing state: %s => %s",
484       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
485       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
486
487   switch (transition) {
488     case GST_STATE_CHANGE_NULL_TO_READY:
489       break;
490     case GST_STATE_CHANGE_READY_TO_PAUSED:
491       break;
492     default:
493       break;
494   }
495
496   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
497   if (ret == GST_STATE_CHANGE_FAILURE)
498     return ret;
499
500   switch (transition) {
501     case GST_STATE_CHANGE_READY_TO_PAUSED:
502       break;
503     case GST_STATE_CHANGE_PAUSED_TO_READY:
504       break;
505     default:
506       break;
507   }
508
509   return ret;
510 }
511
512
513 static void
514 gst_rtp_sink_init (GstRtpSink * self)
515 {
516   self->rtpbin = NULL;
517   self->funnel_rtp = NULL;
518   self->funnel_rtcp = NULL;
519   self->rtp_sink = NULL;
520   self->rtcp_src = NULL;
521   self->rtcp_sink = NULL;
522
523   self->uri = gst_uri_from_string (DEFAULT_PROP_URI);
524   self->ttl = DEFAULT_PROP_TTL;
525   self->ttl_mc = DEFAULT_PROP_TTL_MC;
526
527   if (gst_rtp_sink_setup_rtpbin (self) == FALSE)
528     return;
529
530   GST_OBJECT_FLAG_SET (GST_OBJECT (self), GST_ELEMENT_FLAG_SINK);
531   gst_bin_set_suppressed_flags (GST_BIN (self),
532       GST_ELEMENT_FLAG_SOURCE | GST_ELEMENT_FLAG_SINK);
533
534   g_mutex_init (&self->lock);
535 }
536
537 static guint
538 gst_rtp_sink_uri_get_type (GType type)
539 {
540   return GST_URI_SINK;
541 }
542
543 static const gchar *const *
544 gst_rtp_sink_uri_get_protocols (GType type)
545 {
546   static const gchar *protocols[] = { (char *) "rtp", NULL };
547
548   return protocols;
549 }
550
551 static gchar *
552 gst_rtp_sink_uri_get_uri (GstURIHandler * handler)
553 {
554   GstRtpSink *self = (GstRtpSink *) handler;
555
556   return gst_uri_to_string (self->uri);
557 }
558
559 static gboolean
560 gst_rtp_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri,
561     GError ** error)
562 {
563   GstRtpSink *self = (GstRtpSink *) handler;
564
565   g_object_set (G_OBJECT (self), "uri", uri, NULL);
566
567   return TRUE;
568 }
569
570 static void
571 gst_rtp_sink_uri_handler_init (gpointer g_iface, gpointer iface_data)
572 {
573   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
574
575   iface->get_type = gst_rtp_sink_uri_get_type;
576   iface->get_protocols = gst_rtp_sink_uri_get_protocols;
577   iface->get_uri = gst_rtp_sink_uri_get_uri;
578   iface->set_uri = gst_rtp_sink_uri_set_uri;
579 }
580
581 /* ex: set tabstop=2 shiftwidth=2 expandtab: */