2 * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
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.
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.
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.
24 #include "transportsendbin.h"
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 * '------------------------------------------------------------------'
39 * outputselecter is used to switch between rtcp-mux and no rtcp-mux
41 * FIXME: Do we need a valve drop=TRUE for the no RTCP case?
44 #define GST_CAT_DEFAULT gst_webrtc_transport_send_bin_debug
45 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
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"););
52 static GstStaticPadTemplate rtp_sink_template =
53 GST_STATIC_PAD_TEMPLATE ("rtp_sink",
56 GST_STATIC_CAPS ("application/x-rtp"));
58 static GstStaticPadTemplate rtcp_sink_template =
59 GST_STATIC_PAD_TEMPLATE ("rtcp_sink",
62 GST_STATIC_CAPS ("application/x-rtp"));
72 _set_rtcp_mux (TransportSendBin * send, gboolean rtcp_mux)
77 active_pad = gst_element_get_static_pad (send->outputselector, "src_0");
79 active_pad = gst_element_get_static_pad (send->outputselector, "src_1");
80 send->rtcp_mux = rtcp_mux;
81 GST_OBJECT_UNLOCK (send);
83 g_object_set (send->outputselector, "active-pad", active_pad, NULL);
85 gst_object_unref (active_pad);
86 GST_OBJECT_LOCK (send);
90 transport_send_bin_set_property (GObject * object, guint prop_id,
91 const GValue * value, GParamSpec * pspec)
93 TransportSendBin *send = TRANSPORT_SEND_BIN (object);
95 GST_OBJECT_LOCK (send);
98 /* XXX: weak-ref this? */
99 send->stream = TRANSPORT_STREAM (g_value_get_object (value));
102 _set_rtcp_mux (send, g_value_get_boolean (value));
105 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
108 GST_OBJECT_UNLOCK (send);
112 transport_send_bin_get_property (GObject * object, guint prop_id,
113 GValue * value, GParamSpec * pspec)
115 TransportSendBin *send = TRANSPORT_SEND_BIN (object);
117 GST_OBJECT_LOCK (send);
120 g_value_set_object (value, send->stream);
123 g_value_set_boolean (value, send->rtcp_mux);
126 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
129 GST_OBJECT_UNLOCK (send);
132 static GstPadProbeReturn
133 pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
135 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
137 return GST_PAD_PROBE_OK;
140 static GstStateChangeReturn
141 transport_send_bin_change_state (GstElement * element,
142 GstStateChange transition)
144 TransportSendBin *send = TRANSPORT_SEND_BIN (element);
145 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
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)));
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 */
157 gst_element_set_locked_state (send->stream->transport->dtlssrtpenc, TRUE);
158 gst_element_set_locked_state (send->stream->rtcp_transport->dtlssrtpenc,
162 case GST_STATE_CHANGE_READY_TO_PAUSED:{
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,
175 gst_object_unref (pad);
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,
185 gst_object_unref (pad);
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,
197 gst_object_unref (pad);
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,
208 gst_object_unref (pad);
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,
219 gst_object_unref (pad);
222 case GST_STATE_CHANGE_PAUSED_TO_READY:
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;
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;
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;
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;
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;
256 case GST_STATE_CHANGE_READY_TO_NULL:{
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);
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);
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;
286 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
291 _on_dtls_enc_key_set (GstElement * element, TransportSendBin * send)
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;
311 _on_notify_ice_connection_state (GstWebRTCICETransport * transport,
312 GParamSpec * pspec, TransportSendBin * send)
314 GstWebRTCICEConnectionState state;
316 g_object_get (transport, "state", &state, NULL);
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);
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);
334 send->rtcp_nice_block = NULL;
336 GST_OBJECT_UNLOCK (send);
341 transport_send_bin_constructed (GObject * object)
343 TransportSendBin *send = TRANSPORT_SEND_BIN (object);
344 GstWebRTCDTLSTransport *transport;
345 GstPadTemplate *templ;
348 g_return_if_fail (send->stream);
350 g_object_bind_property (send, "rtcp-mux", send->stream, "rtcp-mux",
351 G_BINDING_BIDIRECTIONAL);
353 transport = send->stream->transport;
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",
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));
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));
370 if (!gst_element_link_pads (GST_ELEMENT (transport->dtlssrtpenc), "src",
371 GST_ELEMENT (transport->transport->sink), "sink"))
372 g_warn_if_reached ();
374 send->outputselector = gst_element_factory_make ("output-selector", NULL);
375 gst_bin_add (GST_BIN (send), send->outputselector);
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 ();
381 ghost = gst_ghost_pad_new ("rtp_sink", pad);
382 gst_element_add_pad (GST_ELEMENT (send), ghost);
383 gst_object_unref (pad);
385 transport = send->stream->rtcp_transport;
387 templ = _find_pad_template (transport->dtlssrtpenc,
388 GST_PAD_SINK, GST_PAD_REQUEST, "rtcp_sink_%d");
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));
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));
400 if (!gst_element_link_pads (GST_ELEMENT (transport->dtlssrtpenc), "src",
401 GST_ELEMENT (transport->transport->sink), "sink"))
402 g_warn_if_reached ();
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 ();
408 pad = gst_element_get_static_pad (send->outputselector, "sink");
410 ghost = gst_ghost_pad_new ("rtcp_sink", pad);
411 gst_element_add_pad (GST_ELEMENT (send), ghost);
412 gst_object_unref (pad);
414 G_OBJECT_CLASS (parent_class)->constructed (object);
418 transport_send_bin_dispose (GObject * object)
420 TransportSendBin *send = TRANSPORT_SEND_BIN (object);
423 g_signal_handlers_disconnect_by_data (send->stream->transport->transport,
425 g_signal_handlers_disconnect_by_data (send->stream->
426 rtcp_transport->transport, send);
430 G_OBJECT_CLASS (parent_class)->dispose (object);
434 transport_send_bin_class_init (TransportSendBinClass * klass)
436 GObjectClass *gobject_class = (GObjectClass *) klass;
437 GstElementClass *element_class = (GstElementClass *) klass;
439 element_class->change_state = transport_send_bin_change_state;
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);
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>");
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;
454 g_object_class_install_property (gobject_class,
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));
461 g_object_class_install_property (gobject_class,
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));
469 transport_send_bin_init (TransportSendBin * send)