webrtcbin: Don't duplicate enum string values
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / ext / webrtc / transportstream.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 "transportstream.h"
25 #include "transportsendbin.h"
26 #include "transportreceivebin.h"
27 #include "gstwebrtcbin.h"
28 #include "utils.h"
29 #include "gst/webrtc/webrtc-priv.h"
30
31 #define GST_CAT_DEFAULT transport_stream_debug
32 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
33
34 #define transport_stream_parent_class parent_class
35 G_DEFINE_TYPE_WITH_CODE (TransportStream, transport_stream, GST_TYPE_OBJECT,
36     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "webrtctransportstream", 0,
37         "webrtctransportstream"););
38
39 enum
40 {
41   PROP_0,
42   PROP_WEBRTC,
43   PROP_SESSION_ID,
44   PROP_DTLS_CLIENT,
45 };
46
47 GstCaps *
48 transport_stream_get_caps_for_pt (TransportStream * stream, guint pt)
49 {
50   guint i, len;
51
52   len = stream->ptmap->len;
53   for (i = 0; i < len; i++) {
54     PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
55     if (item->pt == pt)
56       return item->caps;
57   }
58   return NULL;
59 }
60
61 int
62 transport_stream_get_pt (TransportStream * stream, const gchar * encoding_name,
63     guint media_idx)
64 {
65   guint i;
66   gint ret = -1;
67
68   for (i = 0; i < stream->ptmap->len; i++) {
69     PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
70
71     if (media_idx != -1 && media_idx != item->media_idx)
72       continue;
73
74     if (!gst_caps_is_empty (item->caps)) {
75       GstStructure *s = gst_caps_get_structure (item->caps, 0);
76       if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"),
77               encoding_name)) {
78         ret = item->pt;
79         break;
80       }
81     }
82   }
83
84   return ret;
85 }
86
87 int *
88 transport_stream_get_all_pt (TransportStream * stream,
89     const gchar * encoding_name, gsize * pt_len)
90 {
91   guint i;
92   gsize ret_i = 0;
93   gsize ret_size = 8;
94   int *ret = NULL;
95
96   for (i = 0; i < stream->ptmap->len; i++) {
97     PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
98     if (!gst_caps_is_empty (item->caps)) {
99       GstStructure *s = gst_caps_get_structure (item->caps, 0);
100       if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"),
101               encoding_name)) {
102         if (!ret)
103           ret = g_new0 (int, ret_size);
104         if (ret_i >= ret_size) {
105           ret_size *= 2;
106           ret = g_realloc_n (ret, ret_size, sizeof (int));
107         }
108         ret[ret_i++] = item->pt;
109       }
110     }
111   }
112
113   *pt_len = ret_i;
114   return ret;
115 }
116
117 static void
118 transport_stream_set_property (GObject * object, guint prop_id,
119     const GValue * value, GParamSpec * pspec)
120 {
121   TransportStream *stream = TRANSPORT_STREAM (object);
122
123   switch (prop_id) {
124     case PROP_WEBRTC:
125       gst_object_set_parent (GST_OBJECT (stream), g_value_get_object (value));
126       break;
127   }
128
129   GST_OBJECT_LOCK (stream);
130   switch (prop_id) {
131     case PROP_WEBRTC:
132       break;
133     case PROP_SESSION_ID:
134       stream->session_id = g_value_get_uint (value);
135       break;
136     case PROP_DTLS_CLIENT:
137       stream->dtls_client = g_value_get_boolean (value);
138       break;
139     default:
140       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
141       break;
142   }
143   GST_OBJECT_UNLOCK (stream);
144 }
145
146 static void
147 transport_stream_get_property (GObject * object, guint prop_id,
148     GValue * value, GParamSpec * pspec)
149 {
150   TransportStream *stream = TRANSPORT_STREAM (object);
151
152   GST_OBJECT_LOCK (stream);
153   switch (prop_id) {
154     case PROP_SESSION_ID:
155       g_value_set_uint (value, stream->session_id);
156       break;
157     case PROP_DTLS_CLIENT:
158       g_value_set_boolean (value, stream->dtls_client);
159       break;
160     default:
161       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
162       break;
163   }
164   GST_OBJECT_UNLOCK (stream);
165 }
166
167 static void
168 transport_stream_dispose (GObject * object)
169 {
170   TransportStream *stream = TRANSPORT_STREAM (object);
171
172   gst_clear_object (&stream->send_bin);
173   gst_clear_object (&stream->receive_bin);
174   gst_clear_object (&stream->transport);
175   gst_clear_object (&stream->rtxsend);
176   gst_clear_object (&stream->rtxreceive);
177   gst_clear_object (&stream->reddec);
178   g_list_free_full (stream->fecdecs, (GDestroyNotify) gst_object_unref);
179   stream->fecdecs = NULL;
180
181   GST_OBJECT_PARENT (object) = NULL;
182
183   G_OBJECT_CLASS (parent_class)->dispose (object);
184 }
185
186 static void
187 transport_stream_finalize (GObject * object)
188 {
189   TransportStream *stream = TRANSPORT_STREAM (object);
190
191   g_array_free (stream->ptmap, TRUE);
192   g_ptr_array_free (stream->ssrcmap, TRUE);
193
194   gst_clear_object (&stream->rtxsend_stream_id);
195   gst_clear_object (&stream->rtxsend_repaired_stream_id);
196   gst_clear_object (&stream->rtxreceive_stream_id);
197   gst_clear_object (&stream->rtxreceive_repaired_stream_id);
198
199   G_OBJECT_CLASS (parent_class)->finalize (object);
200 }
201
202 static void
203 transport_stream_constructed (GObject * object)
204 {
205   TransportStream *stream = TRANSPORT_STREAM (object);
206   GstWebRTCBin *webrtc;
207   GstWebRTCICETransport *ice_trans;
208
209   stream->transport = gst_webrtc_dtls_transport_new (stream->session_id);
210
211   webrtc = GST_WEBRTC_BIN (gst_object_get_parent (GST_OBJECT (object)));
212
213   g_object_bind_property (stream->transport, "client", stream, "dtls-client",
214       G_BINDING_BIDIRECTIONAL);
215
216   /* Need to go full Java and have a transport manager?
217    * Or make the caller set the ICE transport up? */
218
219   stream->stream = _find_ice_stream_for_session (webrtc, stream->session_id);
220   if (stream->stream == NULL) {
221     stream->stream = gst_webrtc_ice_add_stream (webrtc->priv->ice,
222         stream->session_id);
223     _add_ice_stream_item (webrtc, stream->session_id, stream->stream);
224   }
225   ice_trans =
226       gst_webrtc_ice_find_transport (webrtc->priv->ice, stream->stream,
227       GST_WEBRTC_ICE_COMPONENT_RTP);
228   gst_webrtc_dtls_transport_set_transport (stream->transport, ice_trans);
229   gst_object_unref (ice_trans);
230
231   stream->send_bin = g_object_new (transport_send_bin_get_type (), "stream",
232       stream, NULL);
233   gst_object_ref_sink (stream->send_bin);
234   stream->receive_bin = g_object_new (transport_receive_bin_get_type (),
235       "stream", stream, NULL);
236   gst_object_ref_sink (stream->receive_bin);
237
238   gst_object_unref (webrtc);
239
240   G_OBJECT_CLASS (parent_class)->constructed (object);
241 }
242
243 static void
244 transport_stream_class_init (TransportStreamClass * klass)
245 {
246   GObjectClass *gobject_class = (GObjectClass *) klass;
247
248   gobject_class->constructed = transport_stream_constructed;
249   gobject_class->get_property = transport_stream_get_property;
250   gobject_class->set_property = transport_stream_set_property;
251   gobject_class->dispose = transport_stream_dispose;
252   gobject_class->finalize = transport_stream_finalize;
253
254   /* some acrobatics are required to set the parent before _constructed()
255    * has been called */
256   g_object_class_install_property (gobject_class,
257       PROP_WEBRTC,
258       g_param_spec_object ("webrtc", "Parent webrtcbin",
259           "Parent webrtcbin",
260           GST_TYPE_WEBRTC_BIN,
261           G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
262
263   g_object_class_install_property (gobject_class,
264       PROP_SESSION_ID,
265       g_param_spec_uint ("session-id", "Session ID",
266           "Session ID used for this transport",
267           0, G_MAXUINT, 0,
268           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
269
270   g_object_class_install_property (gobject_class,
271       PROP_DTLS_CLIENT,
272       g_param_spec_boolean ("dtls-client", "DTLS client",
273           "Whether we take the client role in DTLS negotiation",
274           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
275 }
276
277 static void
278 clear_ptmap_item (PtMapItem * item)
279 {
280   if (item->caps)
281     gst_caps_unref (item->caps);
282 }
283
284 static SsrcMapItem *
285 ssrcmap_item_new (GstWebRTCRTPTransceiverDirection direction, guint32 ssrc,
286     guint media_idx)
287 {
288   SsrcMapItem *ssrc_item = g_new0 (SsrcMapItem, 1);
289
290   ssrc_item->direction = direction;
291   ssrc_item->media_idx = media_idx;
292   ssrc_item->ssrc = ssrc;
293   g_weak_ref_init (&ssrc_item->rtpjitterbuffer, NULL);
294
295   return ssrc_item;
296 }
297
298 static void
299 ssrcmap_item_free (SsrcMapItem * item)
300 {
301   g_weak_ref_clear (&item->rtpjitterbuffer);
302   g_clear_pointer (&item->mid, g_free);
303   g_clear_pointer (&item->rid, g_free);
304   g_free (item);
305 }
306
307 SsrcMapItem *
308 transport_stream_find_ssrc_map_item (TransportStream * stream,
309     gconstpointer data, FindSsrcMapFunc func)
310 {
311   int i;
312
313   for (i = 0; i < stream->ssrcmap->len; i++) {
314     SsrcMapItem *item = g_ptr_array_index (stream->ssrcmap, i);
315
316     if (func (item, data))
317       return item;
318   }
319
320   return NULL;
321 }
322
323 void
324 transport_stream_filter_ssrc_map_item (TransportStream * stream,
325     gconstpointer data, FindSsrcMapFunc func)
326 {
327   int i;
328
329   for (i = 0; i < stream->ssrcmap->len;) {
330     SsrcMapItem *item = g_ptr_array_index (stream->ssrcmap, i);
331
332     if (!func (item, data)) {
333       GST_TRACE_OBJECT (stream, "removing ssrc %u", item->ssrc);
334       g_ptr_array_remove_index_fast (stream->ssrcmap, i);
335     } else {
336       i++;
337     }
338   }
339 }
340
341 SsrcMapItem *
342 transport_stream_add_ssrc_map_item (TransportStream * stream,
343     GstWebRTCRTPTransceiverDirection direction, guint32 ssrc, guint media_idx)
344 {
345   SsrcMapItem *ret = NULL;
346
347   g_return_val_if_fail (direction ==
348       GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY
349       || direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, NULL);
350   g_return_val_if_fail (ssrc != 0, NULL);
351
352   GST_INFO_OBJECT (stream, "Adding mapping for rtp session %u media_idx %u "
353       "direction %s ssrc %u", stream->session_id, media_idx,
354       gst_webrtc_rtp_transceiver_direction_to_string (direction), ssrc);
355
356   /* XXX: duplicates? */
357   ret = ssrcmap_item_new (direction, ssrc, media_idx);
358
359   g_ptr_array_add (stream->ssrcmap, ret);
360
361   return ret;
362 }
363
364 static void
365 transport_stream_init (TransportStream * stream)
366 {
367   stream->ptmap = g_array_new (FALSE, TRUE, sizeof (PtMapItem));
368   g_array_set_clear_func (stream->ptmap, (GDestroyNotify) clear_ptmap_item);
369   stream->ssrcmap = g_ptr_array_new_with_free_func (
370       (GDestroyNotify) ssrcmap_item_free);
371
372   stream->rtphdrext_id_stream_id = -1;
373   stream->rtphdrext_id_repaired_stream_id = -1;
374 }
375
376 TransportStream *
377 transport_stream_new (GstWebRTCBin * webrtc, guint session_id)
378 {
379   TransportStream *stream;
380
381   stream = g_object_new (transport_stream_get_type (), "webrtc", webrtc,
382       "session-id", session_id, NULL);
383
384   return stream;
385 }