1 /*
2 * Copyright (C) 2016 Sebastian Dröge <sebastian@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 "gstsdpsrc.h"
25 #include <gst/app/app.h>
26 #include <string.h>
27
28 GST_DEBUG_CATEGORY_STATIC (sdp_src_debug);
29 #define GST_CAT_DEFAULT sdp_src_debug
30
31 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("stream_%u",
32 GST_PAD_SRC,
33 GST_PAD_SOMETIMES,
34 GST_STATIC_CAPS ("application/x-rtp"));
35
36 enum
37 {
38 PROP_0,
39 PROP_LOCATION,
40 PROP_SDP
41 };
42
43 static void gst_sdp_src_handler_init (gpointer g_iface, gpointer iface_data);
44
45 #define gst_sdp_src_parent_class parent_class
46 G_DEFINE_TYPE_WITH_CODE (GstSdpSrc, gst_sdp_src, GST_TYPE_BIN,
47 G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_sdp_src_handler_init));
48 GST_ELEMENT_REGISTER_DEFINE (sdpsrc, "sdpsrc", GST_RANK_NONE, GST_TYPE_SDP_SRC);
49
50 static void
gst_sdp_src_finalize(GObject * object)51 gst_sdp_src_finalize (GObject * object)
52 {
53 GstSdpSrc *self = GST_SDP_SRC_CAST (object);
54
55 if (self->sdp_buffer)
56 gst_buffer_unref (self->sdp_buffer);
57 g_free (self->location);
58 g_free (self->sdp);
59
60 G_OBJECT_CLASS (parent_class)->finalize (object);
61 }
62
63 static void
gst_sdp_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)64 gst_sdp_src_get_property (GObject * object, guint prop_id,
65 GValue * value, GParamSpec * pspec)
66 {
67 GstSdpSrc *self = GST_SDP_SRC_CAST (object);
68
69 switch (prop_id) {
70 case PROP_LOCATION:
71 g_value_set_string (value, self->location);
72 break;
73 case PROP_SDP:
74 g_value_set_string (value, self->sdp);
75 break;
76 default:
77 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
78 break;
79 }
80 }
81
82 static void
gst_sdp_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)83 gst_sdp_src_set_property (GObject * object, guint prop_id,
84 const GValue * value, GParamSpec * pspec)
85 {
86 GstSdpSrc *self = GST_SDP_SRC_CAST (object);
87
88 switch (prop_id) {
89 case PROP_LOCATION:
90 g_free (self->location);
91 self->location = g_value_dup_string (value);
92 break;
93 case PROP_SDP:
94 g_free (self->sdp);
95 self->sdp = g_value_dup_string (value);
96 break;
97 default:
98 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
99 break;
100 }
101 }
102
103 static void
pad_added_cb(GstElement * element,GstPad * pad,gpointer user_data)104 pad_added_cb (GstElement * element, GstPad * pad, gpointer user_data)
105 {
106 GstSdpSrc *self = GST_SDP_SRC_CAST (user_data);
107 GstPad *ghost;
108
109 ghost =
110 gst_ghost_pad_new_from_template (GST_PAD_NAME (pad), pad,
111 gst_static_pad_template_get (&src_template));
112 gst_pad_set_active (ghost, TRUE);
113 gst_element_add_pad (GST_ELEMENT_CAST (self), ghost);
114 g_object_set_data (G_OBJECT (pad), "GstSdpSrc.ghostpad", ghost);
115 }
116
117 static void
pad_removed_cb(GstElement * element,GstPad * pad,gpointer user_data)118 pad_removed_cb (GstElement * element, GstPad * pad, gpointer user_data)
119 {
120 GstSdpSrc *self = GST_SDP_SRC_CAST (user_data);
121 GstPad *ghost;
122
123 ghost = g_object_get_data (G_OBJECT (pad), "GstSdpSrc.ghostpad");
124 if (ghost) {
125 g_object_set_data (G_OBJECT (pad), "GstSdpSrc.ghostpad", NULL);
126
127 gst_pad_set_active (ghost, FALSE);
128 gst_element_remove_pad (GST_ELEMENT_CAST (self), ghost);
129 }
130 }
131
132 static void
no_more_pads_cb(GstElement * element,gpointer user_data)133 no_more_pads_cb (GstElement * element, gpointer user_data)
134 {
135 gst_element_no_more_pads (GST_ELEMENT_CAST (user_data));
136 }
137
138 static void
remove_pad(const GValue * item,gpointer user_data)139 remove_pad (const GValue * item, gpointer user_data)
140 {
141 GstElement *self = user_data;
142 GstPad *pad = g_value_get_object (item);
143
144 gst_element_remove_pad (self, pad);
145 }
146
147 static GstStateChangeReturn
gst_sdp_src_change_state(GstElement * element,GstStateChange transition)148 gst_sdp_src_change_state (GstElement * element, GstStateChange transition)
149 {
150 GstSdpSrc *self = GST_SDP_SRC_CAST (element);
151 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
152
153 switch (transition) {
154 case GST_STATE_CHANGE_NULL_TO_READY:
155 GST_OBJECT_LOCK (self);
156 if (self->sdp_buffer)
157 gst_buffer_unref (self->sdp_buffer);
158 self->sdp_buffer = NULL;
159
160 if (self->location && strcmp (self->location, "sdp://") != 0) {
161 /* Do nothing */
162 } else if (self->sdp) {
163 guint sdp_len = strlen (self->sdp);
164
165 self->sdp_buffer =
166 gst_buffer_new_wrapped (g_strndup (self->sdp, sdp_len),
167 sdp_len + 1);
168 } else {
169 ret = GST_STATE_CHANGE_FAILURE;
170 }
171 GST_OBJECT_UNLOCK (self);
172
173 if (ret != GST_STATE_CHANGE_FAILURE) {
174 if (self->sdp_buffer) {
175 GstCaps *caps = gst_caps_new_empty_simple ("application/sdp");
176
177 self->src = gst_element_factory_make ("appsrc", NULL);
178 g_object_set (self->src, "caps", caps, "emit-signals", FALSE, NULL);
179 gst_caps_unref (caps);
180 } else {
181 self->src = gst_element_factory_make ("filesrc", NULL);
182 g_object_set (self->src, "location", self->location + 6, NULL);
183 }
184
185 self->demux = gst_element_factory_make ("sdpdemux", NULL);
186 g_signal_connect (self->demux, "pad-added", G_CALLBACK (pad_added_cb),
187 self);
188 g_signal_connect (self->demux, "pad-removed",
189 G_CALLBACK (pad_removed_cb), self);
190 g_signal_connect (self->demux, "no-more-pads",
191 G_CALLBACK (no_more_pads_cb), self);
192 gst_bin_add_many (GST_BIN_CAST (self), self->src, self->demux, NULL);
193 gst_element_link_pads (self->src, "src", self->demux, "sink");
194 }
195 break;
196 default:
197 break;
198 }
199
200 if (ret == GST_STATE_CHANGE_FAILURE)
201 return ret;
202 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
203 if (ret == GST_STATE_CHANGE_FAILURE)
204 return ret;
205
206 switch (transition) {
207 case GST_STATE_CHANGE_READY_TO_NULL:{
208 GstIterator *it;
209
210 it = gst_element_iterate_src_pads (GST_ELEMENT_CAST (self));
211 while (gst_iterator_foreach (it, remove_pad, self) == GST_ITERATOR_RESYNC)
212 gst_iterator_resync (it);
213 gst_iterator_free (it);
214
215 if (self->src) {
216 gst_bin_remove (GST_BIN_CAST (self), self->src);
217 self->src = NULL;
218 }
219 if (self->demux) {
220 gst_bin_remove (GST_BIN_CAST (self), self->demux);
221 self->demux = NULL;
222 }
223 break;
224 }
225 case GST_STATE_CHANGE_READY_TO_PAUSED:
226 if (ret != GST_STATE_CHANGE_FAILURE)
227 ret = GST_STATE_CHANGE_NO_PREROLL;
228 if (self->sdp_buffer) {
229 if (gst_app_src_push_buffer (GST_APP_SRC_CAST (self->src),
230 gst_buffer_ref (self->sdp_buffer)) != GST_FLOW_OK)
231 ret = GST_STATE_CHANGE_FAILURE;
232 else
233 gst_app_src_end_of_stream (GST_APP_SRC_CAST (self->src));
234 }
235 break;
236 default:
237 break;
238 }
239
240 return ret;
241 }
242
243 static void
gst_sdp_src_class_init(GstSdpSrcClass * klass)244 gst_sdp_src_class_init (GstSdpSrcClass * klass)
245 {
246 GObjectClass *gobject_class = (GObjectClass *) klass;
247 GstElementClass *element_class = (GstElementClass *) klass;
248
249 GST_DEBUG_CATEGORY_INIT (sdp_src_debug, "sdpsrc", 0, "SDP Source");
250
251 gobject_class->finalize = gst_sdp_src_finalize;
252 gobject_class->set_property = gst_sdp_src_set_property;
253 gobject_class->get_property = gst_sdp_src_get_property;
254
255 g_object_class_install_property (gobject_class, PROP_LOCATION,
256 g_param_spec_string ("location",
257 "Location",
258 "URI to SDP file (sdp:///path/to/file)", NULL,
259 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
260
261 g_object_class_install_property (gobject_class, PROP_SDP,
262 g_param_spec_string ("sdp",
263 "SDP",
264 "SDP description used instead of location", NULL,
265 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
266
267 gst_element_class_add_pad_template (element_class,
268 gst_static_pad_template_get (&src_template));
269
270 gst_element_class_set_static_metadata (element_class, "SDP Source",
271 "Source/Network/RTP",
272 "Stream RTP based on an SDP",
273 "Sebastian Dröge <sebastian@centricular.com>");
274
275 element_class->change_state = GST_DEBUG_FUNCPTR (gst_sdp_src_change_state);
276 }
277
278 static void
gst_sdp_src_init(GstSdpSrc * self)279 gst_sdp_src_init (GstSdpSrc * self)
280 {
281 }
282
283 static GstURIType
gst_sdp_src_get_uri_type(GType type)284 gst_sdp_src_get_uri_type (GType type)
285 {
286 return GST_URI_SRC;
287 }
288
289 static const gchar *const *
gst_sdp_src_get_protocols(GType type)290 gst_sdp_src_get_protocols (GType type)
291 {
292 static const gchar *protocols[] = { "sdp", 0 };
293
294 return protocols;
295 }
296
297 static gchar *
gst_sdp_src_get_uri(GstURIHandler * handler)298 gst_sdp_src_get_uri (GstURIHandler * handler)
299 {
300 gchar *uri = NULL;
301
302 g_object_get (handler, "location", &uri, NULL);
303
304 return uri;
305 }
306
307 static gboolean
gst_sdp_src_set_uri(GstURIHandler * handler,const gchar * uri,GError ** error)308 gst_sdp_src_set_uri (GstURIHandler * handler, const gchar * uri,
309 GError ** error)
310 {
311 if (uri && !g_str_has_prefix (uri, "sdp://")) {
312 g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
313 "Invalid SDP URI");
314 return FALSE;
315 }
316
317 g_object_set (handler, "location", uri, NULL);
318
319 return TRUE;
320 }
321
322 static void
gst_sdp_src_handler_init(gpointer g_iface,gpointer iface_data)323 gst_sdp_src_handler_init (gpointer g_iface, gpointer iface_data)
324 {
325 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
326
327 iface->get_type = gst_sdp_src_get_uri_type;
328 iface->get_protocols = gst_sdp_src_get_protocols;
329 iface->get_uri = gst_sdp_src_get_uri;
330 iface->set_uri = gst_sdp_src_set_uri;
331 }
332