• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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