Make the server handle arbitrary pipelines
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-media-factory.c
1 /* GStreamer
2  * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "rtsp-media-factory.h"
21
22 #define DEFAULT_LAUNCH         NULL
23
24 enum
25 {
26   PROP_0,
27   PROP_LAUNCH,
28   PROP_LAST
29 };
30
31 static void gst_rtsp_media_factory_get_property (GObject *object, guint propid,
32     GValue *value, GParamSpec *pspec);
33 static void gst_rtsp_media_factory_set_property (GObject *object, guint propid,
34     const GValue *value, GParamSpec *pspec);
35 static void gst_rtsp_media_factory_finalize (GObject * obj);
36
37 static GstRTSPMediaBin * default_construct (GstRTSPMediaFactory *factory, const gchar *location);
38
39 G_DEFINE_TYPE (GstRTSPMediaFactory, gst_rtsp_media_factory, G_TYPE_OBJECT);
40
41 static void
42 gst_rtsp_media_factory_class_init (GstRTSPMediaFactoryClass * klass)
43 {
44   GObjectClass *gobject_class;
45
46   gobject_class = G_OBJECT_CLASS (klass);
47
48   gobject_class->get_property = gst_rtsp_media_factory_get_property;
49   gobject_class->set_property = gst_rtsp_media_factory_set_property;
50   gobject_class->finalize = gst_rtsp_media_factory_finalize;
51
52   /**
53    * GstRTSPMediaFactory::launch
54    *
55    * The gst_parse_launch() line to use for constructing the pipeline in the
56    * default prepare vmethod.
57    *
58    * The pipeline description should return a GstBin as the toplevel element
59    * which can be accomplished by enclosing the dscription with brackets '('
60    * ')'.
61    *
62    * The description should return a pipeline with payloaders named pay0, pay1,
63    * etc.. Each of the payloaders will result in a stream.
64    */
65   g_object_class_install_property (gobject_class, PROP_LAUNCH,
66       g_param_spec_string ("launch", "Launch", "A launch description of the pipeline",
67           DEFAULT_LAUNCH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
68
69   klass->construct = default_construct;
70 }
71
72 static void
73 gst_rtsp_media_factory_init (GstRTSPMediaFactory * factory)
74 {
75 }
76
77 static void
78 gst_rtsp_media_factory_finalize (GObject * obj)
79 {
80   GstRTSPMediaFactory *factory = GST_RTSP_MEDIA_FACTORY (obj);
81
82   g_free (factory->launch);
83
84   G_OBJECT_CLASS (gst_rtsp_media_factory_parent_class)->finalize (obj);
85 }
86
87 static void
88 gst_rtsp_media_factory_get_property (GObject *object, guint propid,
89     GValue *value, GParamSpec *pspec)
90 {
91   GstRTSPMediaFactory *factory = GST_RTSP_MEDIA_FACTORY (object);
92
93   switch (propid) {
94     case PROP_LAUNCH:
95       g_value_take_string (value, gst_rtsp_media_factory_get_launch (factory));
96       break;
97     default:
98       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
99   }
100 }
101
102 static void
103 gst_rtsp_media_factory_set_property (GObject *object, guint propid,
104     const GValue *value, GParamSpec *pspec)
105 {
106   GstRTSPMediaFactory *factory = GST_RTSP_MEDIA_FACTORY (object);
107
108   switch (propid) {
109     case PROP_LAUNCH:
110       gst_rtsp_media_factory_set_launch (factory, g_value_get_string (value));
111       break;
112     default:
113       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
114   }
115 }
116
117 /**
118  * gst_rtsp_media_factory_new:
119  *
120  * Create a new #GstRTSPMediaFactory instance.
121  *
122  * Returns: a new #GstRTSPMediaFactory object or %NULL when location did not contain a
123  * valid or understood URL.
124  */
125 GstRTSPMediaFactory *
126 gst_rtsp_media_factory_new (void)
127 {
128   GstRTSPMediaFactory *result;
129
130   result = g_object_new (GST_TYPE_RTSP_MEDIA_FACTORY, NULL);
131
132   return result;
133 }
134
135 /**
136  * gst_rtsp_media_factory_set_launch:
137  * @factory: a #GstRTSPMediaFactory
138  * @launch: the launch description
139  *
140  *
141  * The gst_parse_launch() line to use for constructing the pipeline in the
142  * default prepare vmethod.
143  *
144  * The pipeline description should return a GstBin as the toplevel element
145  * which can be accomplished by enclosing the dscription with brackets '('
146  * ')'.
147  *
148  * The description should return a pipeline with payloaders named pay0, pay1,
149  * etc.. Each of the payloaders will result in a stream.
150  */
151 void
152 gst_rtsp_media_factory_set_launch (GstRTSPMediaFactory *factory, const gchar *launch)
153 {
154   g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
155   g_return_if_fail (launch != NULL);
156
157   factory->launch = g_strdup (launch);
158 }
159
160 /**
161  * gst_rtsp_media_factory_get_launch:
162  * @factory: a #GstRTSPMediaFactory
163  *
164  * Get the gst_parse_launch() pipeline description that will be used in the
165  * default prepare vmethod.
166  *
167  * Returns: the configured launch description. g_free() after usage.
168  */
169 gchar *
170 gst_rtsp_media_factory_get_launch (GstRTSPMediaFactory *factory)
171 {
172   gchar *result;
173
174   result = g_strdup (factory->launch);
175
176   return result;
177 }
178
179 /**
180  * gst_rtsp_media_factory_construct:
181  * @factory: a #GstRTSPMediaFactory
182  * @location: the url used
183  *
184  * Prepare the media bin object and create its streams. Implementations
185  * should create the needed gstreamer elements and add them to the result
186  * object. No state changes should be performed on them yet.
187  *
188  * One or more GstRTSPMediaStream objects should be added to the result with
189  * the srcpad member set to a source pad that produces buffer of type 
190  * application/x-rtp.
191  *
192  * Returns: a new #GstRTSPMediaBin if the media could be prepared.
193  */
194 GstRTSPMediaBin *
195 gst_rtsp_media_factory_construct (GstRTSPMediaFactory *factory, const gchar *location)
196 {
197   GstRTSPMediaBin *res;
198   GstRTSPMediaFactoryClass *klass;
199
200   klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
201
202   if (klass->construct)
203     res = klass->construct (factory, location);
204   else
205     res = NULL;
206
207   g_message ("constructed mediabin %p for location %s", res, location);
208
209   return res;
210 }
211
212 static void
213 caps_notify (GstPad * pad, GParamSpec * unused, GstRTSPMediaStream * stream)
214 {
215   if (stream->caps)
216     gst_caps_unref (stream->caps);
217   if ((stream->caps = GST_PAD_CAPS (pad)))
218     gst_caps_ref (stream->caps);
219 }
220
221 static GstRTSPMediaBin *
222 default_construct (GstRTSPMediaFactory *factory, const gchar *location)
223 {
224   GstRTSPMediaBin *bin;
225   GstRTSPMediaStream *stream;
226   GstElement *pay, *element;
227   GstPad * pad;
228   gint i;
229   GError *error = NULL;
230
231   /* we need a parse syntax */
232   if (factory->launch == NULL)
233     goto no_launch;
234
235   /* parse the user provided launch line */
236   element = gst_parse_launch (factory->launch, &error);
237   if (element == NULL)
238     goto parse_error;
239
240   if (error != NULL) {
241     /* a recoverable error was encountered */
242     g_warning ("recoverable parsing error: %s", error->message);
243     g_error_free (error);
244   }
245
246   bin = g_object_new (GST_TYPE_RTSP_MEDIA_BIN, NULL);
247   bin->element = element;
248
249   /* try to find all the payloader elements, they should be named 'pay%d'. for
250    * each of the payloaders we will create a stream, collect the source pad and
251    * add a notify::caps on the pad. */
252   for (i = 0; ; i++) {
253     gchar *name;
254
255     name = g_strdup_printf ("pay%d", i);
256
257     if (!(pay = gst_bin_get_by_name (GST_BIN (element), name))) {
258       /* no more payloaders found, we have found all the streams and we can
259        * end the loop */
260       g_free (name);
261       break;
262     }
263     
264     /* create the stream */
265     stream = g_new0 (GstRTSPMediaStream, 1);
266     stream->mediabin = bin;
267     stream->element = element;
268     stream->payloader = pay;
269     stream->idx = bin->streams->len;
270
271     pad = gst_element_get_static_pad (pay, "src");
272
273     /* ghost the pad of the payloader to the element */
274     stream->srcpad = gst_ghost_pad_new (name, pad);
275     gst_element_add_pad (stream->element, stream->srcpad);
276
277     stream->caps_sig = g_signal_connect (pad, "notify::caps", (GCallback) caps_notify, stream);
278     gst_object_unref (pad);
279
280     /* add stream now */
281     g_array_append_val (bin->streams, stream);
282     gst_object_unref (pay);
283
284     g_free (name);
285   }
286
287   return bin;
288
289   /* ERRORS */
290 no_launch:
291   {
292     g_critical ("no launch line specified");
293     return NULL;
294   }
295 parse_error:
296   {
297     g_critical ("could not parse launch syntax (%s): %s", factory->launch, 
298          (error ? error->message : "unknown reason"));
299     if (error)
300       g_error_free (error);
301     return NULL;
302   }
303 }