• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "gstwebrtcice.h"
28 #include "gstwebrtcbin.h"
29 #include "utils.h"
30 #include "gst/webrtc/webrtc-priv.h"
31 
32 #define transport_stream_parent_class parent_class
33 G_DEFINE_TYPE (TransportStream, transport_stream, GST_TYPE_OBJECT);
34 
35 enum
36 {
37   PROP_0,
38   PROP_WEBRTC,
39   PROP_SESSION_ID,
40   PROP_DTLS_CLIENT,
41 };
42 
43 GstCaps *
transport_stream_get_caps_for_pt(TransportStream * stream,guint pt)44 transport_stream_get_caps_for_pt (TransportStream * stream, guint pt)
45 {
46   guint i, len;
47 
48   len = stream->ptmap->len;
49   for (i = 0; i < len; i++) {
50     PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
51     if (item->pt == pt)
52       return item->caps;
53   }
54   return NULL;
55 }
56 
57 int
transport_stream_get_pt(TransportStream * stream,const gchar * encoding_name,guint media_idx)58 transport_stream_get_pt (TransportStream * stream, const gchar * encoding_name,
59     guint media_idx)
60 {
61   guint i;
62   gint ret = 0;
63 
64   for (i = 0; i < stream->ptmap->len; i++) {
65     PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
66 
67     if (media_idx != -1 && media_idx != item->media_idx)
68       continue;
69 
70     if (!gst_caps_is_empty (item->caps)) {
71       GstStructure *s = gst_caps_get_structure (item->caps, 0);
72       if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"),
73               encoding_name)) {
74         ret = item->pt;
75         break;
76       }
77     }
78   }
79 
80   return ret;
81 }
82 
83 int *
transport_stream_get_all_pt(TransportStream * stream,const gchar * encoding_name,gsize * pt_len)84 transport_stream_get_all_pt (TransportStream * stream,
85     const gchar * encoding_name, gsize * pt_len)
86 {
87   guint i;
88   gsize ret_i = 0;
89   gsize ret_size = 8;
90   int *ret = NULL;
91 
92   for (i = 0; i < stream->ptmap->len; i++) {
93     PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
94     if (!gst_caps_is_empty (item->caps)) {
95       GstStructure *s = gst_caps_get_structure (item->caps, 0);
96       if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"),
97               encoding_name)) {
98         if (!ret)
99           ret = g_new0 (int, ret_size);
100         if (ret_i >= ret_size) {
101           ret_size *= 2;
102           ret = g_realloc_n (ret, ret_size, sizeof (int));
103         }
104         ret[ret_i++] = item->pt;
105       }
106     }
107   }
108 
109   *pt_len = ret_i;
110   return ret;
111 }
112 
113 static void
transport_stream_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)114 transport_stream_set_property (GObject * object, guint prop_id,
115     const GValue * value, GParamSpec * pspec)
116 {
117   TransportStream *stream = TRANSPORT_STREAM (object);
118 
119   switch (prop_id) {
120     case PROP_WEBRTC:
121       gst_object_set_parent (GST_OBJECT (stream), g_value_get_object (value));
122       break;
123   }
124 
125   GST_OBJECT_LOCK (stream);
126   switch (prop_id) {
127     case PROP_WEBRTC:
128       break;
129     case PROP_SESSION_ID:
130       stream->session_id = g_value_get_uint (value);
131       break;
132     case PROP_DTLS_CLIENT:
133       stream->dtls_client = g_value_get_boolean (value);
134       break;
135     default:
136       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
137       break;
138   }
139   GST_OBJECT_UNLOCK (stream);
140 }
141 
142 static void
transport_stream_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)143 transport_stream_get_property (GObject * object, guint prop_id,
144     GValue * value, GParamSpec * pspec)
145 {
146   TransportStream *stream = TRANSPORT_STREAM (object);
147 
148   GST_OBJECT_LOCK (stream);
149   switch (prop_id) {
150     case PROP_SESSION_ID:
151       g_value_set_uint (value, stream->session_id);
152       break;
153     case PROP_DTLS_CLIENT:
154       g_value_set_boolean (value, stream->dtls_client);
155       break;
156     default:
157       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
158       break;
159   }
160   GST_OBJECT_UNLOCK (stream);
161 }
162 
163 static void
transport_stream_dispose(GObject * object)164 transport_stream_dispose (GObject * object)
165 {
166   TransportStream *stream = TRANSPORT_STREAM (object);
167 
168   if (stream->send_bin)
169     gst_object_unref (stream->send_bin);
170   stream->send_bin = NULL;
171 
172   if (stream->receive_bin)
173     gst_object_unref (stream->receive_bin);
174   stream->receive_bin = NULL;
175 
176   if (stream->transport)
177     gst_object_unref (stream->transport);
178   stream->transport = NULL;
179 
180   if (stream->rtxsend)
181     gst_object_unref (stream->rtxsend);
182   stream->rtxsend = NULL;
183 
184   if (stream->rtxreceive)
185     gst_object_unref (stream->rtxreceive);
186   stream->rtxreceive = NULL;
187 
188   GST_OBJECT_PARENT (object) = NULL;
189 
190   G_OBJECT_CLASS (parent_class)->dispose (object);
191 }
192 
193 static void
transport_stream_finalize(GObject * object)194 transport_stream_finalize (GObject * object)
195 {
196   TransportStream *stream = TRANSPORT_STREAM (object);
197 
198   g_array_free (stream->ptmap, TRUE);
199   g_ptr_array_free (stream->remote_ssrcmap, TRUE);
200 
201   G_OBJECT_CLASS (parent_class)->finalize (object);
202 }
203 
204 static void
transport_stream_constructed(GObject * object)205 transport_stream_constructed (GObject * object)
206 {
207   TransportStream *stream = TRANSPORT_STREAM (object);
208   GstWebRTCBin *webrtc;
209   GstWebRTCICETransport *ice_trans;
210 
211   stream->transport = gst_webrtc_dtls_transport_new (stream->session_id);
212 
213   webrtc = GST_WEBRTC_BIN (gst_object_get_parent (GST_OBJECT (object)));
214 
215   g_object_bind_property (stream->transport, "client", stream, "dtls-client",
216       G_BINDING_BIDIRECTIONAL);
217 
218   /* Need to go full Java and have a transport manager?
219    * Or make the caller set the ICE transport up? */
220 
221   stream->stream = _find_ice_stream_for_session (webrtc, stream->session_id);
222   if (stream->stream == NULL) {
223     stream->stream = gst_webrtc_ice_add_stream (webrtc->priv->ice,
224         stream->session_id);
225     _add_ice_stream_item (webrtc, stream->session_id, stream->stream);
226   }
227   ice_trans =
228       gst_webrtc_ice_find_transport (webrtc->priv->ice, stream->stream,
229       GST_WEBRTC_ICE_COMPONENT_RTP);
230   gst_webrtc_dtls_transport_set_transport (stream->transport, ice_trans);
231   gst_object_unref (ice_trans);
232 
233   stream->send_bin = g_object_new (transport_send_bin_get_type (), "stream",
234       stream, NULL);
235   gst_object_ref_sink (stream->send_bin);
236   stream->receive_bin = g_object_new (transport_receive_bin_get_type (),
237       "stream", stream, NULL);
238   gst_object_ref_sink (stream->receive_bin);
239 
240   gst_object_unref (webrtc);
241 
242   G_OBJECT_CLASS (parent_class)->constructed (object);
243 }
244 
245 static void
transport_stream_class_init(TransportStreamClass * klass)246 transport_stream_class_init (TransportStreamClass * klass)
247 {
248   GObjectClass *gobject_class = (GObjectClass *) klass;
249 
250   gobject_class->constructed = transport_stream_constructed;
251   gobject_class->get_property = transport_stream_get_property;
252   gobject_class->set_property = transport_stream_set_property;
253   gobject_class->dispose = transport_stream_dispose;
254   gobject_class->finalize = transport_stream_finalize;
255 
256   /* some acrobatics are required to set the parent before _constructed()
257    * has been called */
258   g_object_class_install_property (gobject_class,
259       PROP_WEBRTC,
260       g_param_spec_object ("webrtc", "Parent webrtcbin",
261           "Parent webrtcbin",
262           GST_TYPE_WEBRTC_BIN,
263           G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
264 
265   g_object_class_install_property (gobject_class,
266       PROP_SESSION_ID,
267       g_param_spec_uint ("session-id", "Session ID",
268           "Session ID used for this transport",
269           0, G_MAXUINT, 0,
270           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
271 
272   g_object_class_install_property (gobject_class,
273       PROP_DTLS_CLIENT,
274       g_param_spec_boolean ("dtls-client", "DTLS client",
275           "Whether we take the client role in DTLS negotiation",
276           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
277 }
278 
279 static void
clear_ptmap_item(PtMapItem * item)280 clear_ptmap_item (PtMapItem * item)
281 {
282   if (item->caps)
283     gst_caps_unref (item->caps);
284 }
285 
286 SsrcMapItem *
ssrcmap_item_new(guint32 ssrc,guint media_idx)287 ssrcmap_item_new (guint32 ssrc, guint media_idx)
288 {
289   SsrcMapItem *ssrc_item = g_slice_new (SsrcMapItem);
290 
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
ssrcmap_item_free(SsrcMapItem * item)299 ssrcmap_item_free (SsrcMapItem * item)
300 {
301   g_weak_ref_clear (&item->rtpjitterbuffer);
302   g_slice_free (SsrcMapItem, item);
303 }
304 
305 static void
transport_stream_init(TransportStream * stream)306 transport_stream_init (TransportStream * stream)
307 {
308   stream->ptmap = g_array_new (FALSE, TRUE, sizeof (PtMapItem));
309   g_array_set_clear_func (stream->ptmap, (GDestroyNotify) clear_ptmap_item);
310   stream->remote_ssrcmap = g_ptr_array_new_with_free_func (
311       (GDestroyNotify) ssrcmap_item_free);
312 }
313 
314 TransportStream *
transport_stream_new(GstWebRTCBin * webrtc,guint session_id)315 transport_stream_new (GstWebRTCBin * webrtc, guint session_id)
316 {
317   TransportStream *stream;
318 
319   stream = g_object_new (transport_stream_get_type (), "webrtc", webrtc,
320       "session-id", session_id, NULL);
321 
322   return stream;
323 }
324