1 /*
2 * Copyright (c) 2014, Ericsson AB. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this
11 * list of conditions and the following disclaimer in the documentation and/or other
12 * materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
23 * OF SUCH DAMAGE.
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "gstdtlselements.h"
31 #include "gstdtlssrtpenc.h"
32 #include "gstdtlsconnection.h"
33
34 #include <stdio.h>
35
36 static GstStaticPadTemplate rtp_sink_template =
37 GST_STATIC_PAD_TEMPLATE ("rtp_sink_%d",
38 GST_PAD_SINK,
39 GST_PAD_REQUEST,
40 GST_STATIC_CAPS ("application/x-rtp;application/x-rtcp")
41 );
42
43 static GstStaticPadTemplate rtcp_sink_template =
44 GST_STATIC_PAD_TEMPLATE ("rtcp_sink_%d",
45 GST_PAD_SINK,
46 GST_PAD_REQUEST,
47 GST_STATIC_CAPS ("application/x-rtp;application/x-rtcp")
48 );
49
50 static GstStaticPadTemplate data_sink_template =
51 GST_STATIC_PAD_TEMPLATE ("data_sink",
52 GST_PAD_SINK,
53 GST_PAD_REQUEST,
54 GST_STATIC_CAPS_ANY);
55
56 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
57 GST_PAD_SRC,
58 GST_PAD_ALWAYS,
59 GST_STATIC_CAPS_ANY);
60
61 GST_DEBUG_CATEGORY_STATIC (gst_dtls_srtp_enc_debug);
62 #define GST_CAT_DEFAULT gst_dtls_srtp_enc_debug
63
64 #define gst_dtls_srtp_enc_parent_class parent_class
65 G_DEFINE_TYPE_WITH_CODE (GstDtlsSrtpEnc, gst_dtls_srtp_enc,
66 GST_TYPE_DTLS_SRTP_BIN, GST_DEBUG_CATEGORY_INIT (gst_dtls_srtp_enc_debug,
67 "dtlssrtpenc", 0, "DTLS Decoder"));
68 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (dtlssrtpenc, "dtlssrtpenc",
69 GST_RANK_NONE, GST_TYPE_DTLS_SRTP_ENC, dtls_element_init (plugin));
70
71 enum
72 {
73 SIGNAL_ON_KEY_SET,
74 NUM_SIGNALS
75 };
76
77 static guint signals[NUM_SIGNALS];
78
79 enum
80 {
81 PROP_0,
82 PROP_IS_CLIENT,
83 PROP_CONNECTION_STATE,
84 PROP_RTP_SYNC,
85 NUM_PROPERTIES
86 };
87
88 static GParamSpec *properties[NUM_PROPERTIES];
89
90 #define DEFAULT_IS_CLIENT FALSE
91 #define DEFAULT_RTP_SYNC FALSE
92
93 static gboolean transform_enum (GBinding *, const GValue * source_value,
94 GValue * target_value, GEnumClass *);
95
96 static void gst_dtls_srtp_enc_set_property (GObject *, guint prop_id,
97 const GValue *, GParamSpec *);
98 static void gst_dtls_srtp_enc_get_property (GObject *, guint prop_id,
99 GValue *, GParamSpec *);
100
101 static GstPad *add_ghost_pad (GstElement *, const gchar * name, GstPad *,
102 GstPadTemplate *);
103 static GstPad *gst_dtls_srtp_enc_request_new_pad (GstElement *,
104 GstPadTemplate *, const gchar * name, const GstCaps *);
105
106 static void on_key_received (GObject * encoder, GstDtlsSrtpEnc *);
107
108 static void gst_dtls_srtp_enc_remove_dtls_element (GstDtlsSrtpBin *);
109 static GstPadProbeReturn remove_dtls_encoder_probe_callback (GstPad *,
110 GstPadProbeInfo *, GstElement *);
111
112 static void
gst_dtls_srtp_enc_class_init(GstDtlsSrtpEncClass * klass)113 gst_dtls_srtp_enc_class_init (GstDtlsSrtpEncClass * klass)
114 {
115 GObjectClass *gobject_class;
116 GstElementClass *element_class;
117 GstDtlsSrtpBinClass *dtls_srtp_bin_class;
118
119 gobject_class = (GObjectClass *) klass;
120 element_class = (GstElementClass *) klass;
121 dtls_srtp_bin_class = (GstDtlsSrtpBinClass *) klass;
122
123 gobject_class->set_property =
124 GST_DEBUG_FUNCPTR (gst_dtls_srtp_enc_set_property);
125 gobject_class->get_property =
126 GST_DEBUG_FUNCPTR (gst_dtls_srtp_enc_get_property);
127
128 element_class->request_new_pad =
129 GST_DEBUG_FUNCPTR (gst_dtls_srtp_enc_request_new_pad);
130
131 dtls_srtp_bin_class->remove_dtls_element =
132 GST_DEBUG_FUNCPTR (gst_dtls_srtp_enc_remove_dtls_element);
133
134 signals[SIGNAL_ON_KEY_SET] =
135 g_signal_new ("on-key-set", G_TYPE_FROM_CLASS (klass),
136 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
137
138 properties[PROP_IS_CLIENT] =
139 g_param_spec_boolean ("is-client",
140 "Is client",
141 "Set to true if the decoder should act as "
142 "client and initiate the handshake",
143 DEFAULT_IS_CLIENT,
144 GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
145
146 properties[PROP_CONNECTION_STATE] =
147 g_param_spec_enum ("connection-state",
148 "Connection State",
149 "Current connection state",
150 GST_DTLS_TYPE_CONNECTION_STATE,
151 GST_DTLS_CONNECTION_STATE_NEW, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
152
153
154 properties[PROP_RTP_SYNC] =
155 g_param_spec_boolean ("rtp-sync", "Synchronize RTP",
156 "Synchronize RTP to the pipeline clock before merging with RTCP",
157 DEFAULT_RTP_SYNC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
158
159 g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
160
161 gst_element_class_add_static_pad_template (element_class, &rtp_sink_template);
162 gst_element_class_add_static_pad_template (element_class,
163 &rtcp_sink_template);
164 gst_element_class_add_static_pad_template (element_class,
165 &data_sink_template);
166 gst_element_class_add_static_pad_template (element_class, &src_template);
167
168 gst_element_class_set_static_metadata (element_class,
169 "DTLS-SRTP Encoder",
170 "Encoder/Network/DTLS/SRTP",
171 "Encodes SRTP packets with a key received from DTLS",
172 "Patrik Oldsberg patrik.oldsberg@ericsson.com");
173 }
174
175 static void
on_connection_state_changed(GObject * object,GParamSpec * pspec,gpointer user_data)176 on_connection_state_changed (GObject * object, GParamSpec * pspec,
177 gpointer user_data)
178 {
179 GstDtlsSrtpEnc *self = GST_DTLS_SRTP_ENC (user_data);
180
181 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CONNECTION_STATE]);
182 }
183
184 static void
gst_dtls_srtp_enc_init(GstDtlsSrtpEnc * self)185 gst_dtls_srtp_enc_init (GstDtlsSrtpEnc * self)
186 {
187 GstElementClass *klass = GST_ELEMENT_GET_CLASS (GST_ELEMENT (self));
188 static GEnumClass *cipher_enum_class, *auth_enum_class;
189 gboolean ret;
190
191 /*
192 +--------------------+ +--------------+ +-----------------+
193 rtp_sink-R-o|rtp_sink rtp_src|o-R-o clocksync |o-R-o| |
194 | srtpenc | +--------------+ | |
195 | | | |
196 rtcp_sink-R-o|srtcp_sink rtcp_src|o-----------R-----------o| |
197 +--------------------+ | funnel |o---src
198 | |
199 +--------------------+ | |
200 data_sink-R-o| dtlsenc |o-----------------------o| |
201 +--------------------+ +-----------------+
202
203 The clocksync element is tied to the sync property. If sync=true, RTP output will be
204 synchronised to the clock, so it doesn't slow down RTCP traffic by being synched later
205 in the pipeline
206 */
207
208 self->srtp_enc = gst_element_factory_make ("srtpenc", NULL);
209 if (!self->srtp_enc) {
210 GST_ERROR_OBJECT (self,
211 "failed to create srtp encoder, is the srtp plugin registered?");
212 return;
213 }
214 g_return_if_fail (self->srtp_enc);
215 self->bin.dtls_element = gst_element_factory_make ("dtlsenc", NULL);
216 if (!self->bin.dtls_element) {
217 GST_ERROR_OBJECT (self, "failed to create dtls encoder");
218 return;
219 }
220 self->funnel = gst_element_factory_make ("funnel", NULL);
221 if (!self->funnel) {
222 GST_ERROR_OBJECT (self, "failed to create funnel");
223 return;
224 }
225
226 gst_bin_add_many (GST_BIN (self), self->bin.dtls_element, self->srtp_enc,
227 self->funnel, NULL);
228
229 ret = gst_element_link (self->bin.dtls_element, self->funnel);
230 g_return_if_fail (ret);
231
232 add_ghost_pad (GST_ELEMENT (self), "src",
233 gst_element_get_static_pad (self->funnel, "src"),
234 gst_element_class_get_pad_template (klass, "src"));
235
236 g_signal_connect (self->bin.dtls_element, "on-key-received",
237 G_CALLBACK (on_key_received), self);
238
239 if (g_once_init_enter (&cipher_enum_class)) {
240 GType type = g_type_from_name ("GstSrtpCipherType");
241 g_assert (type);
242 g_once_init_leave (&cipher_enum_class, g_type_class_peek (type));
243 }
244 if (g_once_init_enter (&auth_enum_class)) {
245 GType type = g_type_from_name ("GstSrtpAuthType");
246 g_assert (type);
247 g_once_init_leave (&auth_enum_class, g_type_class_peek (type));
248 }
249
250 g_object_set (self->srtp_enc, "random-key", TRUE, NULL);
251
252 g_signal_connect (self->bin.dtls_element, "notify::connection-state",
253 G_CALLBACK (on_connection_state_changed), self);
254
255 g_object_bind_property (G_OBJECT (self), "key", self->srtp_enc, "key",
256 G_BINDING_DEFAULT);
257 g_object_bind_property_full (G_OBJECT (self), "srtp-cipher", self->srtp_enc,
258 "rtp-cipher", G_BINDING_DEFAULT, (GBindingTransformFunc) transform_enum,
259 NULL, cipher_enum_class, NULL);
260 g_object_bind_property_full (G_OBJECT (self), "srtcp-cipher", self->srtp_enc,
261 "rtcp-cipher", G_BINDING_DEFAULT, (GBindingTransformFunc) transform_enum,
262 NULL, cipher_enum_class, NULL);
263 g_object_bind_property_full (G_OBJECT (self), "srtp-auth", self->srtp_enc,
264 "rtp-auth", G_BINDING_DEFAULT, (GBindingTransformFunc) transform_enum,
265 NULL, auth_enum_class, NULL);
266 g_object_bind_property_full (G_OBJECT (self), "srtcp-auth", self->srtp_enc,
267 "rtcp-auth", G_BINDING_DEFAULT, (GBindingTransformFunc) transform_enum,
268 NULL, auth_enum_class, NULL);
269 }
270
271 #if GLIB_CHECK_VERSION(2,68,0)
272 #define binding_get_source(b) g_binding_dup_source(b)
273 #define unref_source(s) G_STMT_START { if(s) g_object_unref(s); } G_STMT_END
274 #else
275 #define binding_get_source(b) g_binding_get_source(b)
276 #define unref_source(s) /* no op */
277 #endif
278
279 static gboolean
transform_enum(GBinding * binding,const GValue * source_value,GValue * target_value,GEnumClass * enum_class)280 transform_enum (GBinding * binding, const GValue * source_value,
281 GValue * target_value, GEnumClass * enum_class)
282 {
283 GEnumValue *enum_value;
284 const gchar *nick;
285 GObject *bind_src;
286
287 nick = g_value_get_string (source_value);
288 g_return_val_if_fail (nick, FALSE);
289
290 enum_value = g_enum_get_value_by_nick (enum_class, nick);
291 g_return_val_if_fail (enum_value, FALSE);
292
293 bind_src = binding_get_source (binding);
294
295 GST_DEBUG_OBJECT (bind_src,
296 "transforming enum from %s to %d", nick, enum_value->value);
297
298 unref_source (bind_src);
299
300 g_value_set_enum (target_value, enum_value->value);
301
302 return TRUE;
303 }
304
305 static void
gst_dtls_srtp_enc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)306 gst_dtls_srtp_enc_set_property (GObject * object,
307 guint prop_id, const GValue * value, GParamSpec * pspec)
308 {
309 GstDtlsSrtpEnc *self = GST_DTLS_SRTP_ENC (object);
310
311 switch (prop_id) {
312 case PROP_IS_CLIENT:
313 if (self->bin.dtls_element) {
314 g_object_set_property (G_OBJECT (self->bin.dtls_element), "is-client",
315 value);
316 } else {
317 GST_WARNING_OBJECT (self,
318 "tried to set is-client after disabling DTLS");
319 }
320 break;
321 case PROP_RTP_SYNC:
322 self->rtp_sync = g_value_get_boolean (value);
323 break;
324 default:
325 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
326 }
327 }
328
329 static void
gst_dtls_srtp_enc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)330 gst_dtls_srtp_enc_get_property (GObject * object,
331 guint prop_id, GValue * value, GParamSpec * pspec)
332 {
333 GstDtlsSrtpEnc *self = GST_DTLS_SRTP_ENC (object);
334
335 switch (prop_id) {
336 case PROP_IS_CLIENT:
337 if (self->bin.dtls_element) {
338 g_object_get_property (G_OBJECT (self->bin.dtls_element), "is-client",
339 value);
340 } else {
341 GST_WARNING_OBJECT (self,
342 "tried to get is-client after disabling DTLS");
343 }
344 break;
345 case PROP_CONNECTION_STATE:
346 if (self->bin.dtls_element) {
347 g_object_get_property (G_OBJECT (self->bin.dtls_element),
348 "connection-state", value);
349 } else {
350 GST_WARNING_OBJECT (self,
351 "tried to get connection-state after disabling DTLS");
352 }
353 break;
354 case PROP_RTP_SYNC:
355 g_value_set_boolean (value, self->rtp_sync);
356 break;
357 default:
358 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
359 }
360 }
361
362 static GstPad *
add_ghost_pad(GstElement * element,const gchar * name,GstPad * target,GstPadTemplate * templ)363 add_ghost_pad (GstElement * element,
364 const gchar * name, GstPad * target, GstPadTemplate * templ)
365 {
366 GstPad *pad;
367 gboolean ret;
368
369 pad = gst_ghost_pad_new_from_template (name, target, templ);
370 gst_object_unref (target);
371 target = NULL;
372
373 ret = gst_pad_set_active (pad, TRUE);
374 g_warn_if_fail (ret);
375
376 ret = gst_element_add_pad (element, pad);
377 g_warn_if_fail (ret);
378
379 return pad;
380 }
381
382 static GstPad *
gst_dtls_srtp_enc_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * name,const GstCaps * caps)383 gst_dtls_srtp_enc_request_new_pad (GstElement * element,
384 GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
385 {
386 GstDtlsSrtpEnc *self = GST_DTLS_SRTP_ENC (element);
387 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
388 GstPad *target_pad;
389 GstPad *ghost_pad = NULL;
390 guint pad_n;
391 gchar *srtp_src_name;
392
393 GST_DEBUG_OBJECT (element, "pad requested");
394
395 g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
396 g_return_val_if_fail (self->srtp_enc, NULL);
397
398 if (templ == gst_element_class_get_pad_template (klass, "rtp_sink_%d")) {
399 gchar *clocksync_name;
400 GstElement *clocksync;
401
402 sscanf (name, "rtp_sink_%d", &pad_n);
403
404 clocksync_name = g_strdup_printf ("clocksync_%d", pad_n);
405 clocksync = gst_element_factory_make ("clocksync", clocksync_name);
406 g_free (clocksync_name);
407
408 if (clocksync == NULL) {
409 goto fail_create;
410 }
411
412 g_object_bind_property (self, "rtp-sync", clocksync, "sync",
413 G_BINDING_SYNC_CREATE);
414
415 gst_bin_add (GST_BIN (self), clocksync);
416 gst_element_sync_state_with_parent (clocksync);
417
418 target_pad = gst_element_request_pad_simple (self->srtp_enc, name);
419 g_return_val_if_fail (target_pad, NULL);
420
421 srtp_src_name = g_strdup_printf ("rtp_src_%d", pad_n);
422
423 gst_element_link_pads (self->srtp_enc, srtp_src_name, clocksync, NULL);
424 gst_element_link_pads (clocksync, "src", self->funnel, NULL);
425
426 g_free (srtp_src_name);
427
428 ghost_pad = add_ghost_pad (element, name, target_pad, templ);
429
430 GST_LOG_OBJECT (self, "added rtp sink pad");
431 } else if (templ == gst_element_class_get_pad_template (klass,
432 "rtcp_sink_%d")) {
433 target_pad = gst_element_request_pad_simple (self->srtp_enc, name);
434 g_return_val_if_fail (target_pad, NULL);
435
436 sscanf (GST_PAD_NAME (target_pad), "rtcp_sink_%d", &pad_n);
437 srtp_src_name = g_strdup_printf ("rtcp_src_%d", pad_n);
438
439 gst_element_link_pads (self->srtp_enc, srtp_src_name, self->funnel, NULL);
440
441 g_free (srtp_src_name);
442
443 ghost_pad = add_ghost_pad (element, name, target_pad, templ);
444
445 GST_LOG_OBJECT (self, "added rtcp sink pad");
446 } else if (templ == gst_element_class_get_pad_template (klass, "data_sink")) {
447 g_return_val_if_fail (self->bin.dtls_element, NULL);
448 target_pad =
449 gst_element_request_pad_simple (self->bin.dtls_element, "sink");
450
451 ghost_pad = add_ghost_pad (element, name, target_pad, templ);
452
453 GST_LOG_OBJECT (self, "added data sink pad");
454 } else {
455 g_warn_if_reached ();
456 }
457
458 if (caps && ghost_pad) {
459 g_object_set (ghost_pad, "caps", caps, NULL);
460 }
461
462 return ghost_pad;
463
464 fail_create:
465 GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, NULL,
466 ("%s", "Failed to create internal clocksync element"));
467 return NULL;
468 }
469
470 static void
on_key_received(GObject * encoder,GstDtlsSrtpEnc * self)471 on_key_received (GObject * encoder, GstDtlsSrtpEnc * self)
472 {
473 GstDtlsSrtpBin *bin = GST_DTLS_SRTP_BIN (self);
474 GstBuffer *buffer = NULL;
475 guint cipher, auth;
476
477 if (!(bin->key_is_set || bin->srtp_cipher || bin->srtp_auth
478 || bin->srtcp_cipher || bin->srtcp_auth)) {
479 g_object_get (encoder,
480 "encoder-key", &buffer,
481 "srtp-cipher", &cipher, "srtp-auth", &auth, NULL);
482
483 g_object_set (self->srtp_enc,
484 "rtp-cipher", cipher,
485 "rtcp-cipher", cipher,
486 "rtp-auth", auth,
487 "rtcp-auth", auth, "key", buffer, "random-key", FALSE, NULL);
488
489 gst_buffer_unref (buffer);
490
491 g_signal_emit (self, signals[SIGNAL_ON_KEY_SET], 0);
492 } else {
493 GST_DEBUG_OBJECT (self,
494 "ignoring keys received from DTLS handshake, key struct is set");
495 }
496 }
497
498 static void
gst_dtls_srtp_enc_remove_dtls_element(GstDtlsSrtpBin * bin)499 gst_dtls_srtp_enc_remove_dtls_element (GstDtlsSrtpBin * bin)
500 {
501 GstDtlsSrtpEnc *self = GST_DTLS_SRTP_ENC (bin);
502 GstPad *dtls_sink_pad, *peer_pad;
503 gulong id;
504 guint rtp_cipher = 1, rtcp_cipher = 1, rtp_auth = 1, rtcp_auth = 1;
505
506 if (!bin->dtls_element) {
507 return;
508 }
509
510 g_object_get (self->srtp_enc,
511 "rtp-cipher", &rtp_cipher,
512 "rtcp-cipher", &rtcp_cipher,
513 "rtp-auth", &rtp_auth, "rtcp-auth", &rtcp_auth, NULL);
514
515 if (!rtp_cipher && !rtcp_cipher && !rtp_auth && !rtcp_auth) {
516 g_object_set (self->srtp_enc, "random-key", FALSE, NULL);
517 }
518
519 dtls_sink_pad = gst_element_get_static_pad (bin->dtls_element, "sink");
520
521 if (!dtls_sink_pad) {
522 gst_element_set_state (GST_ELEMENT (bin->dtls_element), GST_STATE_NULL);
523 gst_bin_remove (GST_BIN (self), bin->dtls_element);
524 bin->dtls_element = NULL;
525 return;
526 }
527
528 peer_pad = gst_pad_get_peer (dtls_sink_pad);
529 g_return_if_fail (peer_pad);
530 gst_object_unref (dtls_sink_pad);
531 dtls_sink_pad = NULL;
532
533 id = gst_pad_add_probe (peer_pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
534 (GstPadProbeCallback) remove_dtls_encoder_probe_callback,
535 bin->dtls_element, NULL);
536 g_return_if_fail (id);
537 bin->dtls_element = NULL;
538
539 gst_pad_push_event (peer_pad,
540 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
541 gst_structure_new_empty ("dummy")));
542
543 gst_object_unref (peer_pad);
544 }
545
546 static GstPadProbeReturn
remove_dtls_encoder_probe_callback(GstPad * pad,GstPadProbeInfo * info,GstElement * element)547 remove_dtls_encoder_probe_callback (GstPad * pad,
548 GstPadProbeInfo * info, GstElement * element)
549 {
550 gst_pad_remove_probe (pad, GST_PAD_PROBE_INFO_ID (info));
551
552 gst_element_set_state (GST_ELEMENT (element), GST_STATE_NULL);
553 gst_bin_remove (GST_BIN (GST_ELEMENT_PARENT (element)), element);
554
555 return GST_PAD_PROBE_OK;
556 }
557