webrtc: nice: WeakRef access fixes
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / gst-libs / gst / webrtc / nice / nicestream.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 "nicestream.h"
25 #include "nicetransport.h"
26
27 #define GST_CAT_DEFAULT gst_webrtc_nice_stream_debug
28 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
29
30 enum
31 {
32   PROP_0,
33   PROP_ICE,
34 };
35
36 struct _GstWebRTCNiceStreamPrivate
37 {
38   gboolean gathered;
39   GList *transports;
40   gboolean gathering_started;
41   gulong candidate_gathering_done_id;
42   GWeakRef ice_weak;
43 };
44
45 #define gst_webrtc_nice_stream_parent_class parent_class
46 G_DEFINE_TYPE_WITH_CODE (GstWebRTCNiceStream, gst_webrtc_nice_stream,
47     GST_TYPE_WEBRTC_ICE_STREAM, G_ADD_PRIVATE (GstWebRTCNiceStream)
48     GST_DEBUG_CATEGORY_INIT (gst_webrtc_nice_stream_debug,
49         "webrtcnicestream", 0, "webrtcnicestream"););
50
51 static void
52 gst_webrtc_nice_stream_set_property (GObject * object, guint prop_id,
53     const GValue * value, GParamSpec * pspec)
54 {
55   GstWebRTCNiceStream *stream = GST_WEBRTC_NICE_STREAM (object);
56
57   switch (prop_id) {
58     case PROP_ICE:
59       g_weak_ref_set (&stream->priv->ice_weak, g_value_get_object (value));
60       break;
61     default:
62       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
63       break;
64   }
65 }
66
67 static void
68 gst_webrtc_nice_stream_get_property (GObject * object, guint prop_id,
69     GValue * value, GParamSpec * pspec)
70 {
71   GstWebRTCNiceStream *stream = GST_WEBRTC_NICE_STREAM (object);
72
73   switch (prop_id) {
74     case PROP_ICE:
75       g_value_take_object (value, g_weak_ref_get (&stream->priv->ice_weak));
76       break;
77     default:
78       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
79       break;
80   }
81 }
82
83 static void
84 gst_webrtc_nice_stream_finalize (GObject * object)
85 {
86   GstWebRTCNiceStream *stream = GST_WEBRTC_NICE_STREAM (object);
87   GstWebRTCNice *ice = g_weak_ref_get (&stream->priv->ice_weak);
88
89   if (ice) {
90     NiceAgent *agent;
91     g_object_get (ice, "agent", &agent, NULL);
92
93     if (stream->priv->candidate_gathering_done_id != 0) {
94       g_signal_handler_disconnect (agent,
95           stream->priv->candidate_gathering_done_id);
96     }
97
98     g_object_unref (agent);
99     gst_object_unref (ice);
100   }
101
102   g_list_free (stream->priv->transports);
103   stream->priv->transports = NULL;
104
105   g_weak_ref_clear (&stream->priv->ice_weak);
106
107   G_OBJECT_CLASS (parent_class)->finalize (object);
108 }
109
110 static void
111 _on_candidate_gathering_done (NiceAgent * agent, guint stream_id,
112     GWeakRef * ice_weak)
113 {
114   GstWebRTCNiceStream *ice = g_weak_ref_get (ice_weak);
115   GList *l;
116
117   if (!ice)
118     return;
119
120   if (stream_id != GST_WEBRTC_ICE_STREAM (ice)->stream_id)
121     goto cleanup;
122
123   GST_DEBUG_OBJECT (ice, "%u gathering done", stream_id);
124
125   ice->priv->gathered = TRUE;
126
127   for (l = ice->priv->transports; l; l = l->next) {
128     GstWebRTCICETransport *ice = l->data;
129
130     gst_webrtc_ice_transport_gathering_state_change (ice,
131         GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE);
132   }
133
134 cleanup:
135   gst_object_unref (ice);
136 }
137
138 static GstWebRTCICETransport *
139 gst_webrtc_nice_stream_find_transport (GstWebRTCICEStream * stream,
140     GstWebRTCICEComponent component)
141 {
142   GstWebRTCICEComponent trans_comp;
143   GstWebRTCICETransport *ret;
144   GList *l;
145   GstWebRTCNiceStream *nice_stream = GST_WEBRTC_NICE_STREAM (stream);
146
147   for (l = nice_stream->priv->transports; l; l = l->next) {
148     GstWebRTCICETransport *trans = l->data;
149     g_object_get (trans, "component", &trans_comp, NULL);
150
151     if (component == trans_comp)
152       return gst_object_ref (trans);
153   }
154
155   ret =
156       GST_WEBRTC_ICE_TRANSPORT (gst_webrtc_nice_transport_new (nice_stream,
157           component));
158   nice_stream->priv->transports =
159       g_list_prepend (nice_stream->priv->transports, ret);
160
161   return ret;
162 }
163
164 static GWeakRef *
165 weak_new (GstWebRTCNiceStream * stream)
166 {
167   GWeakRef *weak = g_new0 (GWeakRef, 1);
168   g_weak_ref_init (weak, stream);
169   return weak;
170 }
171
172 static void
173 weak_free (GWeakRef * weak)
174 {
175   g_weak_ref_clear (weak);
176   g_free (weak);
177 }
178
179 static void
180 gst_webrtc_nice_stream_constructed (GObject * object)
181 {
182   GstWebRTCNiceStream *stream;
183   NiceAgent *agent;
184   GstWebRTCNice *ice;
185
186   G_OBJECT_CLASS (parent_class)->constructed (object);
187
188   stream = GST_WEBRTC_NICE_STREAM (object);
189   ice = g_weak_ref_get (&stream->priv->ice_weak);
190
191
192   g_assert (ice != NULL);
193   g_object_get (ice, "agent", &agent, NULL);
194   stream->priv->candidate_gathering_done_id = g_signal_connect_data (agent,
195       "candidate-gathering-done", G_CALLBACK (_on_candidate_gathering_done),
196       weak_new (stream), (GClosureNotify) weak_free, (GConnectFlags) 0);
197
198   g_object_unref (agent);
199   gst_object_unref (ice);
200 }
201
202 static gboolean
203 gst_webrtc_nice_stream_gather_candidates (GstWebRTCICEStream * stream)
204 {
205   NiceAgent *agent;
206   GList *l;
207   GstWebRTCICE *ice;
208   gboolean ret = TRUE;
209   GstWebRTCNiceStream *nice_stream = GST_WEBRTC_NICE_STREAM (stream);
210
211   GST_DEBUG_OBJECT (nice_stream, "start gathering candidates");
212
213   if (nice_stream->priv->gathered)
214     return TRUE;
215
216   for (l = nice_stream->priv->transports; l; l = l->next) {
217     GstWebRTCICETransport *trans = l->data;
218
219     gst_webrtc_ice_transport_gathering_state_change (trans,
220         GST_WEBRTC_ICE_GATHERING_STATE_GATHERING);
221   }
222
223   ice = GST_WEBRTC_ICE (g_weak_ref_get (&nice_stream->priv->ice_weak));
224   g_assert (ice != NULL);
225
226   g_object_get (ice, "agent", &agent, NULL);
227
228   if (!nice_stream->priv->gathering_started) {
229     if (ice->min_rtp_port != 0 || ice->max_rtp_port != 65535) {
230       if (ice->min_rtp_port > ice->max_rtp_port) {
231         GST_ERROR_OBJECT (ice,
232             "invalid port range: min-rtp-port %d must be <= max-rtp-port %d",
233             ice->min_rtp_port, ice->max_rtp_port);
234         ret = FALSE;
235         goto cleanup;
236       }
237
238       nice_agent_set_port_range (agent, stream->stream_id,
239           NICE_COMPONENT_TYPE_RTP, ice->min_rtp_port, ice->max_rtp_port);
240     }
241     /* mark as gathering started to prevent changing ports again */
242     nice_stream->priv->gathering_started = TRUE;
243   }
244
245   if (!nice_agent_gather_candidates (agent, stream->stream_id)) {
246     ret = FALSE;
247     goto cleanup;
248   }
249
250   for (l = nice_stream->priv->transports; l; l = l->next) {
251     GstWebRTCNiceTransport *trans = l->data;
252
253     gst_webrtc_nice_transport_update_buffer_size (trans);
254   }
255
256 cleanup:
257   if (agent)
258     g_object_unref (agent);
259   if (ice)
260     gst_object_unref (ice);
261
262   return ret;
263 }
264
265 static void
266 gst_webrtc_nice_stream_class_init (GstWebRTCNiceStreamClass * klass)
267 {
268   GObjectClass *gobject_class = (GObjectClass *) klass;
269   GstWebRTCICEStreamClass *gst_webrtc_ice_stream_class =
270       GST_WEBRTC_ICE_STREAM_CLASS (klass);
271
272   gst_webrtc_ice_stream_class->find_transport =
273       gst_webrtc_nice_stream_find_transport;
274   gst_webrtc_ice_stream_class->gather_candidates =
275       gst_webrtc_nice_stream_gather_candidates;
276
277   gobject_class->constructed = gst_webrtc_nice_stream_constructed;
278   gobject_class->get_property = gst_webrtc_nice_stream_get_property;
279   gobject_class->set_property = gst_webrtc_nice_stream_set_property;
280   gobject_class->finalize = gst_webrtc_nice_stream_finalize;
281
282   g_object_class_install_property (gobject_class,
283       PROP_ICE,
284       g_param_spec_object ("ice",
285           "ICE", "ICE agent associated with this stream",
286           GST_TYPE_WEBRTC_ICE,
287           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
288 }
289
290 static void
291 gst_webrtc_nice_stream_init (GstWebRTCNiceStream * stream)
292 {
293   stream->priv = gst_webrtc_nice_stream_get_instance_private (stream);
294
295   g_weak_ref_init (&stream->priv->ice_weak, NULL);
296 }
297
298 GstWebRTCNiceStream *
299 gst_webrtc_nice_stream_new (GstWebRTCICE * ice, guint stream_id)
300 {
301   return g_object_new (GST_TYPE_WEBRTC_NICE_STREAM, "ice", ice,
302       "stream-id", stream_id, NULL);
303 }