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