1 /* GStreamer
2  * Copyright (C) 2018 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 <stdio.h>
25 
26 #include "sctptransport.h"
27 #include "gstwebrtcbin.h"
28 
29 #define GST_CAT_DEFAULT gst_webrtc_sctp_transport_debug
30 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
31 
32 enum
33 {
34   SIGNAL_0,
35   ON_RESET_STREAM_SIGNAL,
36   LAST_SIGNAL,
37 };
38 
39 enum
40 {
41   PROP_0,
42   PROP_TRANSPORT,
43   PROP_STATE,
44   PROP_MAX_MESSAGE_SIZE,
45   PROP_MAX_CHANNELS,
46 };
47 
48 static guint gst_webrtc_sctp_transport_signals[LAST_SIGNAL] = { 0 };
49 
50 #define gst_webrtc_sctp_transport_parent_class parent_class
51 G_DEFINE_TYPE_WITH_CODE (GstWebRTCSCTPTransport, gst_webrtc_sctp_transport,
52     GST_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (gst_webrtc_sctp_transport_debug,
53         "webrtcsctptransport", 0, "webrtcsctptransport"););
54 
55 typedef void (*SCTPTask) (GstWebRTCSCTPTransport * sctp, gpointer user_data);
56 
57 struct task
58 {
59   GstWebRTCSCTPTransport *sctp;
60   SCTPTask func;
61   gpointer user_data;
62   GDestroyNotify notify;
63 };
64 
65 static void
_execute_task(GstWebRTCBin * webrtc,struct task * task)66 _execute_task (GstWebRTCBin * webrtc, struct task *task)
67 {
68   if (task->func)
69     task->func (task->sctp, task->user_data);
70 }
71 
72 static void
_free_task(struct task * task)73 _free_task (struct task *task)
74 {
75   gst_object_unref (task->sctp);
76 
77   if (task->notify)
78     task->notify (task->user_data);
79   g_free (task);
80 }
81 
82 static void
_sctp_enqueue_task(GstWebRTCSCTPTransport * sctp,SCTPTask func,gpointer user_data,GDestroyNotify notify)83 _sctp_enqueue_task (GstWebRTCSCTPTransport * sctp, SCTPTask func,
84     gpointer user_data, GDestroyNotify notify)
85 {
86   struct task *task = g_new0 (struct task, 1);
87 
88   task->sctp = gst_object_ref (sctp);
89   task->func = func;
90   task->user_data = user_data;
91   task->notify = notify;
92 
93   gst_webrtc_bin_enqueue_task (sctp->webrtcbin,
94       (GstWebRTCBinFunc) _execute_task, task, (GDestroyNotify) _free_task);
95 }
96 
97 static void
_emit_stream_reset(GstWebRTCSCTPTransport * sctp,gpointer user_data)98 _emit_stream_reset (GstWebRTCSCTPTransport * sctp, gpointer user_data)
99 {
100   guint stream_id = GPOINTER_TO_UINT (user_data);
101 
102   g_signal_emit (sctp,
103       gst_webrtc_sctp_transport_signals[ON_RESET_STREAM_SIGNAL], 0, stream_id);
104 }
105 
106 static void
_on_sctp_dec_pad_removed(GstElement * sctpdec,GstPad * pad,GstWebRTCSCTPTransport * sctp)107 _on_sctp_dec_pad_removed (GstElement * sctpdec, GstPad * pad,
108     GstWebRTCSCTPTransport * sctp)
109 {
110   guint stream_id;
111 
112   if (sscanf (GST_PAD_NAME (pad), "src_%u", &stream_id) != 1)
113     return;
114 
115   _sctp_enqueue_task (sctp, (SCTPTask) _emit_stream_reset,
116       GUINT_TO_POINTER (stream_id), NULL);
117 }
118 
119 static void
_on_sctp_association_established(GstElement * sctpenc,gboolean established,GstWebRTCSCTPTransport * sctp)120 _on_sctp_association_established (GstElement * sctpenc, gboolean established,
121     GstWebRTCSCTPTransport * sctp)
122 {
123   GST_OBJECT_LOCK (sctp);
124   if (established)
125     sctp->state = GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED;
126   else
127     sctp->state = GST_WEBRTC_SCTP_TRANSPORT_STATE_CLOSED;
128   sctp->association_established = established;
129   GST_OBJECT_UNLOCK (sctp);
130 
131   g_object_notify (G_OBJECT (sctp), "state");
132 }
133 
134 static void
gst_webrtc_sctp_transport_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)135 gst_webrtc_sctp_transport_set_property (GObject * object, guint prop_id,
136     const GValue * value, GParamSpec * pspec)
137 {
138 //  GstWebRTCSCTPTransport *sctp = GST_WEBRTC_SCTP_TRANSPORT (object);
139 
140   switch (prop_id) {
141     default:
142       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
143       break;
144   }
145 }
146 
147 static void
gst_webrtc_sctp_transport_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)148 gst_webrtc_sctp_transport_get_property (GObject * object, guint prop_id,
149     GValue * value, GParamSpec * pspec)
150 {
151   GstWebRTCSCTPTransport *sctp = GST_WEBRTC_SCTP_TRANSPORT (object);
152 
153   switch (prop_id) {
154     case PROP_TRANSPORT:
155       g_value_set_object (value, sctp->transport);
156       break;
157     case PROP_STATE:
158       g_value_set_enum (value, sctp->state);
159       break;
160     case PROP_MAX_MESSAGE_SIZE:
161       g_value_set_uint64 (value, sctp->max_message_size);
162       break;
163     case PROP_MAX_CHANNELS:
164       g_value_set_uint (value, sctp->max_channels);
165       break;
166     default:
167       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
168       break;
169   }
170 }
171 
172 static void
gst_webrtc_sctp_transport_finalize(GObject * object)173 gst_webrtc_sctp_transport_finalize (GObject * object)
174 {
175   GstWebRTCSCTPTransport *sctp = GST_WEBRTC_SCTP_TRANSPORT (object);
176 
177   g_signal_handlers_disconnect_by_data (sctp->sctpdec, sctp);
178   g_signal_handlers_disconnect_by_data (sctp->sctpenc, sctp);
179 
180   gst_object_unref (sctp->sctpdec);
181   gst_object_unref (sctp->sctpenc);
182 
183   g_clear_object (&sctp->transport);
184 
185   G_OBJECT_CLASS (parent_class)->finalize (object);
186 }
187 
188 static void
gst_webrtc_sctp_transport_constructed(GObject * object)189 gst_webrtc_sctp_transport_constructed (GObject * object)
190 {
191   GstWebRTCSCTPTransport *sctp = GST_WEBRTC_SCTP_TRANSPORT (object);
192   guint association_id;
193 
194   association_id = g_random_int_range (0, G_MAXUINT16);
195 
196   sctp->sctpdec =
197       g_object_ref_sink (gst_element_factory_make ("sctpdec", NULL));
198   g_object_set (sctp->sctpdec, "sctp-association-id", association_id, NULL);
199   sctp->sctpenc =
200       g_object_ref_sink (gst_element_factory_make ("sctpenc", NULL));
201   g_object_set (sctp->sctpenc, "sctp-association-id", association_id, NULL);
202 
203   g_signal_connect (sctp->sctpdec, "pad-removed",
204       G_CALLBACK (_on_sctp_dec_pad_removed), sctp);
205   g_signal_connect (sctp->sctpenc, "sctp-association-established",
206       G_CALLBACK (_on_sctp_association_established), sctp);
207 
208   G_OBJECT_CLASS (parent_class)->constructed (object);
209 }
210 
211 static void
gst_webrtc_sctp_transport_class_init(GstWebRTCSCTPTransportClass * klass)212 gst_webrtc_sctp_transport_class_init (GstWebRTCSCTPTransportClass * klass)
213 {
214   GObjectClass *gobject_class = (GObjectClass *) klass;
215 
216   gobject_class->constructed = gst_webrtc_sctp_transport_constructed;
217   gobject_class->get_property = gst_webrtc_sctp_transport_get_property;
218   gobject_class->set_property = gst_webrtc_sctp_transport_set_property;
219   gobject_class->finalize = gst_webrtc_sctp_transport_finalize;
220 
221   g_object_class_install_property (gobject_class,
222       PROP_TRANSPORT,
223       g_param_spec_object ("transport",
224           "WebRTC DTLS Transport",
225           "DTLS transport used for this SCTP transport",
226           GST_TYPE_WEBRTC_DTLS_TRANSPORT,
227           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
228 
229   g_object_class_install_property (gobject_class,
230       PROP_STATE,
231       g_param_spec_enum ("state",
232           "WebRTC SCTP Transport state", "WebRTC SCTP Transport state",
233           GST_TYPE_WEBRTC_SCTP_TRANSPORT_STATE,
234           GST_WEBRTC_SCTP_TRANSPORT_STATE_NEW,
235           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
236 
237   g_object_class_install_property (gobject_class,
238       PROP_MAX_MESSAGE_SIZE,
239       g_param_spec_uint64 ("max-message-size",
240           "Maximum message size",
241           "Maximum message size as reported by the transport", 0, G_MAXUINT64,
242           0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
243 
244   g_object_class_install_property (gobject_class,
245       PROP_MAX_CHANNELS,
246       g_param_spec_uint ("max-channels",
247           "Maximum number of channels", "Maximum number of channels",
248           0, G_MAXUINT16, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
249 
250   /**
251    * GstWebRTCSCTPTransport::reset-stream:
252    * @object: the #GstWebRTCSCTPTransport
253    * @stream_id: the SCTP stream that was reset
254    */
255   gst_webrtc_sctp_transport_signals[ON_RESET_STREAM_SIGNAL] =
256       g_signal_new ("stream-reset", G_TYPE_FROM_CLASS (klass),
257       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
258       G_TYPE_NONE, 1, G_TYPE_UINT);
259 }
260 
261 static void
gst_webrtc_sctp_transport_init(GstWebRTCSCTPTransport * nice)262 gst_webrtc_sctp_transport_init (GstWebRTCSCTPTransport * nice)
263 {
264 }
265 
266 GstWebRTCSCTPTransport *
gst_webrtc_sctp_transport_new(void)267 gst_webrtc_sctp_transport_new (void)
268 {
269   return g_object_new (GST_TYPE_WEBRTC_SCTP_TRANSPORT, NULL);
270 }
271