webrtcbin: an element that handles the transport aspects of webrtc connections
[platform/upstream/gst-plugins-bad.git] / ext / webrtc / transportsendbin.c
1 /* GStreamer
2  * Copyright (C) 2017 Matthew Waters <matthew@centricular.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 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include "transportsendbin.h"
25 #include "utils.h"
26
27 /*
28  *           ,------------------------transport_send_%u-------------------------,
29  *           ;                          ,-----dtlssrtpenc---,                   ;
30  *  rtp_sink o--------------------------o rtp_sink_0        ;  ,---nicesink---, ;
31  *           ;                          ;               src o--o sink         ; ;
32  *           ;   ,--outputselector--, ,-o rtcp_sink_0       ;  '--------------' ;
33  *           ;   ;            src_0 o-' '-------------------'                   ;
34  * rtcp_sink ;---o sink             ;   ,----dtlssrtpenc----,  ,---nicesink---, ;
35  *           ;   ;            src_1 o---o rtcp_sink_0   src o--o sink         ; ;
36  *           ;   '------------------'   '-------------------'  '--------------' ;
37  *           '------------------------------------------------------------------'
38  *
39  * outputselecter is used to switch between rtcp-mux and no rtcp-mux
40  *
41  * FIXME: Do we need a valve drop=TRUE for the no RTCP case?
42  */
43
44 #define GST_CAT_DEFAULT gst_webrtc_transport_send_bin_debug
45 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
46
47 #define transport_send_bin_parent_class parent_class
48 G_DEFINE_TYPE_WITH_CODE (TransportSendBin, transport_send_bin, GST_TYPE_BIN,
49     GST_DEBUG_CATEGORY_INIT (gst_webrtc_transport_send_bin_debug,
50         "webrtctransportsendbin", 0, "webrtctransportsendbin"););
51
52 static GstStaticPadTemplate rtp_sink_template =
53 GST_STATIC_PAD_TEMPLATE ("rtp_sink",
54     GST_PAD_SINK,
55     GST_PAD_ALWAYS,
56     GST_STATIC_CAPS ("application/x-rtp"));
57
58 static GstStaticPadTemplate rtcp_sink_template =
59 GST_STATIC_PAD_TEMPLATE ("rtcp_sink",
60     GST_PAD_SINK,
61     GST_PAD_ALWAYS,
62     GST_STATIC_CAPS ("application/x-rtp"));
63
64 enum
65 {
66   PROP_0,
67   PROP_STREAM,
68   PROP_RTCP_MUX,
69 };
70
71 static void
72 _set_rtcp_mux (TransportSendBin * send, gboolean rtcp_mux)
73 {
74   GstPad *active_pad;
75
76   if (rtcp_mux)
77     active_pad = gst_element_get_static_pad (send->outputselector, "src_0");
78   else
79     active_pad = gst_element_get_static_pad (send->outputselector, "src_1");
80   send->rtcp_mux = rtcp_mux;
81   GST_OBJECT_UNLOCK (send);
82
83   g_object_set (send->outputselector, "active-pad", active_pad, NULL);
84
85   gst_object_unref (active_pad);
86   GST_OBJECT_LOCK (send);
87 }
88
89 static void
90 transport_send_bin_set_property (GObject * object, guint prop_id,
91     const GValue * value, GParamSpec * pspec)
92 {
93   TransportSendBin *send = TRANSPORT_SEND_BIN (object);
94
95   GST_OBJECT_LOCK (send);
96   switch (prop_id) {
97     case PROP_STREAM:
98       /* XXX: weak-ref this? */
99       send->stream = TRANSPORT_STREAM (g_value_get_object (value));
100       break;
101     case PROP_RTCP_MUX:
102       _set_rtcp_mux (send, g_value_get_boolean (value));
103       break;
104     default:
105       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
106       break;
107   }
108   GST_OBJECT_UNLOCK (send);
109 }
110
111 static void
112 transport_send_bin_get_property (GObject * object, guint prop_id,
113     GValue * value, GParamSpec * pspec)
114 {
115   TransportSendBin *send = TRANSPORT_SEND_BIN (object);
116
117   GST_OBJECT_LOCK (send);
118   switch (prop_id) {
119     case PROP_STREAM:
120       g_value_set_object (value, send->stream);
121       break;
122     case PROP_RTCP_MUX:
123       g_value_set_boolean (value, send->rtcp_mux);
124       break;
125     default:
126       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
127       break;
128   }
129   GST_OBJECT_UNLOCK (send);
130 }
131
132 static GstPadProbeReturn
133 pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
134 {
135   GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
136
137   return GST_PAD_PROBE_OK;
138 }
139
140 static GstStateChangeReturn
141 transport_send_bin_change_state (GstElement * element,
142     GstStateChange transition)
143 {
144   TransportSendBin *send = TRANSPORT_SEND_BIN (element);
145   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
146
147   GST_DEBUG_OBJECT (element, "changing state: %s => %s",
148       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
149       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
150
151   switch (transition) {
152     case GST_STATE_CHANGE_NULL_TO_READY:{
153       /* XXX: don't change state until the client-ness has been chosen
154        * arguably the element should be able to deal with this itself or
155        * we should only add it once/if we get the encoding keys */
156
157       gst_element_set_locked_state (send->stream->transport->dtlssrtpenc, TRUE);
158       gst_element_set_locked_state (send->stream->rtcp_transport->dtlssrtpenc,
159           TRUE);
160       break;
161     }
162     case GST_STATE_CHANGE_READY_TO_PAUSED:{
163       GstElement *elem;
164       GstPad *pad;
165
166       /* unblock the encoder once the key is set, this should also be automatic */
167       elem = send->stream->transport->dtlssrtpenc;
168       pad = gst_element_get_static_pad (elem, "rtp_sink_0");
169       send->rtp_block = _create_pad_block (elem, pad, 0, NULL, NULL);
170       send->rtp_block->block_id =
171           gst_pad_add_probe (pad,
172           GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER |
173           GST_PAD_PROBE_TYPE_BUFFER_LIST, (GstPadProbeCallback) pad_block, NULL,
174           NULL);
175       gst_object_unref (pad);
176
177       /* unblock the encoder once the key is set, this should also be automatic */
178       pad = gst_element_get_static_pad (elem, "rtcp_sink_0");
179       send->rtcp_mux_block = _create_pad_block (elem, pad, 0, NULL, NULL);
180       send->rtcp_mux_block->block_id =
181           gst_pad_add_probe (pad,
182           GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER |
183           GST_PAD_PROBE_TYPE_BUFFER_LIST, (GstPadProbeCallback) pad_block, NULL,
184           NULL);
185       gst_object_unref (pad);
186
187
188       elem = send->stream->rtcp_transport->dtlssrtpenc;
189       /* unblock the encoder once the key is set, this should also be automatic */
190       pad = gst_element_get_static_pad (elem, "rtcp_sink_0");
191       send->rtcp_block = _create_pad_block (elem, pad, 0, NULL, NULL);
192       send->rtcp_block->block_id =
193           gst_pad_add_probe (pad,
194           GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER |
195           GST_PAD_PROBE_TYPE_BUFFER_LIST, (GstPadProbeCallback) pad_block, NULL,
196           NULL);
197       gst_object_unref (pad);
198
199       /* unblock ice sink once a connection is made, this should also be automatic */
200       elem = send->stream->transport->transport->sink;
201       pad = gst_element_get_static_pad (elem, "sink");
202       send->rtp_nice_block = _create_pad_block (elem, pad, 0, NULL, NULL);
203       send->rtp_nice_block->block_id =
204           gst_pad_add_probe (pad,
205           GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER |
206           GST_PAD_PROBE_TYPE_BUFFER_LIST, (GstPadProbeCallback) pad_block, NULL,
207           NULL);
208       gst_object_unref (pad);
209
210       /* unblock ice sink once a connection is made, this should also be automatic */
211       elem = send->stream->rtcp_transport->transport->sink;
212       pad = gst_element_get_static_pad (elem, "sink");
213       send->rtcp_nice_block = _create_pad_block (elem, pad, 0, NULL, NULL);
214       send->rtcp_nice_block->block_id =
215           gst_pad_add_probe (pad,
216           GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER |
217           GST_PAD_PROBE_TYPE_BUFFER_LIST, (GstPadProbeCallback) pad_block, NULL,
218           NULL);
219       gst_object_unref (pad);
220       break;
221     }
222     case GST_STATE_CHANGE_PAUSED_TO_READY:
223     {
224       /* Release pad blocks */
225       if (send->rtp_block && send->rtp_block->block_id) {
226         gst_pad_set_active (send->rtp_block->pad, FALSE);
227         gst_pad_remove_probe (send->rtp_block->pad, send->rtp_block->block_id);
228         send->rtp_block->block_id = 0;
229       }
230       if (send->rtcp_mux_block && send->rtcp_mux_block->block_id) {
231         gst_pad_set_active (send->rtcp_mux_block->pad, FALSE);
232         gst_pad_remove_probe (send->rtcp_mux_block->pad,
233             send->rtcp_mux_block->block_id);
234         send->rtcp_mux_block->block_id = 0;
235       }
236       if (send->rtcp_block && send->rtcp_block->block_id) {
237         gst_pad_set_active (send->rtcp_block->pad, FALSE);
238         gst_pad_remove_probe (send->rtcp_block->pad,
239             send->rtcp_block->block_id);
240         send->rtcp_block->block_id = 0;
241       }
242       if (send->rtp_nice_block && send->rtp_nice_block->block_id) {
243         gst_pad_set_active (send->rtp_nice_block->pad, FALSE);
244         gst_pad_remove_probe (send->rtp_nice_block->pad,
245             send->rtp_nice_block->block_id);
246         send->rtp_nice_block->block_id = 0;
247       }
248       if (send->rtcp_nice_block && send->rtcp_nice_block->block_id) {
249         gst_pad_set_active (send->rtcp_nice_block->pad, FALSE);
250         gst_pad_remove_probe (send->rtcp_nice_block->pad,
251             send->rtcp_nice_block->block_id);
252         send->rtcp_nice_block->block_id = 0;
253       }
254       break;
255     }
256     case GST_STATE_CHANGE_READY_TO_NULL:{
257       GstElement *elem;
258
259       if (send->rtp_block)
260         _free_pad_block (send->rtp_block);
261       send->rtp_block = NULL;
262       if (send->rtcp_mux_block)
263         _free_pad_block (send->rtcp_mux_block);
264       send->rtcp_mux_block = NULL;
265       elem = send->stream->transport->dtlssrtpenc;
266       gst_element_set_locked_state (elem, FALSE);
267
268       if (send->rtcp_block)
269         _free_pad_block (send->rtcp_block);
270       send->rtcp_block = NULL;
271       elem = send->stream->rtcp_transport->dtlssrtpenc;
272       gst_element_set_locked_state (elem, FALSE);
273
274       if (send->rtp_nice_block)
275         _free_pad_block (send->rtp_nice_block);
276       send->rtp_nice_block = NULL;
277       if (send->rtcp_nice_block)
278         _free_pad_block (send->rtcp_nice_block);
279       send->rtcp_nice_block = NULL;
280       break;
281     }
282     default:
283       break;
284   }
285
286   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
287   return ret;
288 }
289
290 static void
291 _on_dtls_enc_key_set (GstElement * element, TransportSendBin * send)
292 {
293   if (element == send->stream->transport->dtlssrtpenc) {
294     GST_LOG_OBJECT (send, "Unblocking pad %" GST_PTR_FORMAT,
295         send->rtp_block->pad);
296     _free_pad_block (send->rtp_block);
297     send->rtp_block = NULL;
298     GST_LOG_OBJECT (send, "Unblocking pad %" GST_PTR_FORMAT,
299         send->rtcp_mux_block->pad);
300     _free_pad_block (send->rtcp_mux_block);
301     send->rtcp_mux_block = NULL;
302   } else if (element == send->stream->rtcp_transport->dtlssrtpenc) {
303     GST_LOG_OBJECT (send, "Unblocking pad %" GST_PTR_FORMAT,
304         send->rtcp_block->pad);
305     _free_pad_block (send->rtcp_block);
306     send->rtcp_block = NULL;
307   }
308 }
309
310 static void
311 _on_notify_ice_connection_state (GstWebRTCICETransport * transport,
312     GParamSpec * pspec, TransportSendBin * send)
313 {
314   GstWebRTCICEConnectionState state;
315
316   g_object_get (transport, "state", &state, NULL);
317
318   if (state == GST_WEBRTC_ICE_CONNECTION_STATE_CONNECTED ||
319       state == GST_WEBRTC_ICE_CONNECTION_STATE_COMPLETED) {
320     GST_OBJECT_LOCK (send);
321     if (transport == send->stream->transport->transport) {
322       if (send->rtp_nice_block) {
323         GST_LOG_OBJECT (send, "Unblocking pad %" GST_PTR_FORMAT,
324             send->rtp_nice_block->pad);
325         _free_pad_block (send->rtp_nice_block);
326       }
327       send->rtp_nice_block = NULL;
328     } else if (transport == send->stream->rtcp_transport->transport) {
329       if (send->rtcp_nice_block) {
330         GST_LOG_OBJECT (send, "Unblocking pad %" GST_PTR_FORMAT,
331             send->rtcp_nice_block->pad);
332         _free_pad_block (send->rtcp_nice_block);
333       }
334       send->rtcp_nice_block = NULL;
335     }
336     GST_OBJECT_UNLOCK (send);
337   }
338 }
339
340 static void
341 transport_send_bin_constructed (GObject * object)
342 {
343   TransportSendBin *send = TRANSPORT_SEND_BIN (object);
344   GstWebRTCDTLSTransport *transport;
345   GstPadTemplate *templ;
346   GstPad *ghost, *pad;
347
348   g_return_if_fail (send->stream);
349
350   g_object_bind_property (send, "rtcp-mux", send->stream, "rtcp-mux",
351       G_BINDING_BIDIRECTIONAL);
352
353   transport = send->stream->transport;
354
355   templ = _find_pad_template (transport->dtlssrtpenc,
356       GST_PAD_SINK, GST_PAD_REQUEST, "rtp_sink_%d");
357   pad = gst_element_request_pad (transport->dtlssrtpenc, templ, "rtp_sink_0",
358       NULL);
359
360   /* unblock the encoder once the key is set */
361   g_signal_connect (transport->dtlssrtpenc, "on-key-set",
362       G_CALLBACK (_on_dtls_enc_key_set), send);
363   gst_bin_add (GST_BIN (send), GST_ELEMENT (transport->dtlssrtpenc));
364
365   /* unblock ice sink once it signals a connection */
366   g_signal_connect (transport->transport, "notify::state",
367       G_CALLBACK (_on_notify_ice_connection_state), send);
368   gst_bin_add (GST_BIN (send), GST_ELEMENT (transport->transport->sink));
369
370   if (!gst_element_link_pads (GST_ELEMENT (transport->dtlssrtpenc), "src",
371           GST_ELEMENT (transport->transport->sink), "sink"))
372     g_warn_if_reached ();
373
374   send->outputselector = gst_element_factory_make ("output-selector", NULL);
375   gst_bin_add (GST_BIN (send), send->outputselector);
376
377   if (!gst_element_link_pads (GST_ELEMENT (send->outputselector), "src_0",
378           GST_ELEMENT (transport->dtlssrtpenc), "rtcp_sink_0"))
379     g_warn_if_reached ();
380
381   ghost = gst_ghost_pad_new ("rtp_sink", pad);
382   gst_element_add_pad (GST_ELEMENT (send), ghost);
383   gst_object_unref (pad);
384
385   transport = send->stream->rtcp_transport;
386
387   templ = _find_pad_template (transport->dtlssrtpenc,
388       GST_PAD_SINK, GST_PAD_REQUEST, "rtcp_sink_%d");
389
390   /* unblock the encoder once the key is set */
391   g_signal_connect (transport->dtlssrtpenc, "on-key-set",
392       G_CALLBACK (_on_dtls_enc_key_set), send);
393   gst_bin_add (GST_BIN (send), GST_ELEMENT (transport->dtlssrtpenc));
394
395   /* unblock ice sink once it signals a connection */
396   g_signal_connect (transport->transport, "notify::state",
397       G_CALLBACK (_on_notify_ice_connection_state), send);
398   gst_bin_add (GST_BIN (send), GST_ELEMENT (transport->transport->sink));
399
400   if (!gst_element_link_pads (GST_ELEMENT (transport->dtlssrtpenc), "src",
401           GST_ELEMENT (transport->transport->sink), "sink"))
402     g_warn_if_reached ();
403
404   if (!gst_element_link_pads (GST_ELEMENT (send->outputselector), "src_1",
405           GST_ELEMENT (transport->dtlssrtpenc), "rtcp_sink_0"))
406     g_warn_if_reached ();
407
408   pad = gst_element_get_static_pad (send->outputselector, "sink");
409
410   ghost = gst_ghost_pad_new ("rtcp_sink", pad);
411   gst_element_add_pad (GST_ELEMENT (send), ghost);
412   gst_object_unref (pad);
413
414   G_OBJECT_CLASS (parent_class)->constructed (object);
415 }
416
417 static void
418 transport_send_bin_dispose (GObject * object)
419 {
420   TransportSendBin *send = TRANSPORT_SEND_BIN (object);
421
422   if (send->stream) {
423     g_signal_handlers_disconnect_by_data (send->stream->transport->transport,
424         send);
425     g_signal_handlers_disconnect_by_data (send->stream->
426         rtcp_transport->transport, send);
427   }
428   send->stream = NULL;
429
430   G_OBJECT_CLASS (parent_class)->dispose (object);
431 }
432
433 static void
434 transport_send_bin_class_init (TransportSendBinClass * klass)
435 {
436   GObjectClass *gobject_class = (GObjectClass *) klass;
437   GstElementClass *element_class = (GstElementClass *) klass;
438
439   element_class->change_state = transport_send_bin_change_state;
440
441   gst_element_class_add_static_pad_template (element_class, &rtp_sink_template);
442   gst_element_class_add_static_pad_template (element_class,
443       &rtcp_sink_template);
444
445   gst_element_class_set_metadata (element_class, "WebRTC Transport Send Bin",
446       "Filter/Network/WebRTC", "A bin for webrtc connections",
447       "Matthew Waters <matthew@centricular.com>");
448
449   gobject_class->constructed = transport_send_bin_constructed;
450   gobject_class->dispose = transport_send_bin_dispose;
451   gobject_class->get_property = transport_send_bin_get_property;
452   gobject_class->set_property = transport_send_bin_set_property;
453
454   g_object_class_install_property (gobject_class,
455       PROP_STREAM,
456       g_param_spec_object ("stream", "Stream",
457           "The TransportStream for this sending bin",
458           transport_stream_get_type (),
459           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
460
461   g_object_class_install_property (gobject_class,
462       PROP_RTCP_MUX,
463       g_param_spec_boolean ("rtcp-mux", "RTCP Mux",
464           "Whether RTCP packets are muxed with RTP packets",
465           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
466 }
467
468 static void
469 transport_send_bin_init (TransportSendBin * send)
470 {
471 }