factory: connect to the unprepare signal
[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 #define DEFAULT_SHARED         FALSE
24
25 enum
26 {
27   PROP_0,
28   PROP_LAUNCH,
29   PROP_SHARED,
30   PROP_LAST
31 };
32
33 static void gst_rtsp_media_factory_get_property (GObject *object, guint propid,
34     GValue *value, GParamSpec *pspec);
35 static void gst_rtsp_media_factory_set_property (GObject *object, guint propid,
36     const GValue *value, GParamSpec *pspec);
37 static void gst_rtsp_media_factory_finalize (GObject * obj);
38
39 static gchar * default_gen_key (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
40 static GstElement * default_get_element (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
41 static GstRTSPMedia * default_construct (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
42 static void default_configure (GstRTSPMediaFactory *factory, GstRTSPMedia *media);
43
44 G_DEFINE_TYPE (GstRTSPMediaFactory, gst_rtsp_media_factory, G_TYPE_OBJECT);
45
46 static void
47 gst_rtsp_media_factory_class_init (GstRTSPMediaFactoryClass * klass)
48 {
49   GObjectClass *gobject_class;
50
51   gobject_class = G_OBJECT_CLASS (klass);
52
53   gobject_class->get_property = gst_rtsp_media_factory_get_property;
54   gobject_class->set_property = gst_rtsp_media_factory_set_property;
55   gobject_class->finalize = gst_rtsp_media_factory_finalize;
56
57   /**
58    * GstRTSPMediaFactory::launch
59    *
60    * The gst_parse_launch() line to use for constructing the pipeline in the
61    * default prepare vmethod.
62    *
63    * The pipeline description should return a GstBin as the toplevel element
64    * which can be accomplished by enclosing the dscription with brackets '('
65    * ')'.
66    *
67    * The description should return a pipeline with payloaders named pay0, pay1,
68    * etc.. Each of the payloaders will result in a stream.
69    */
70   g_object_class_install_property (gobject_class, PROP_LAUNCH,
71       g_param_spec_string ("launch", "Launch", "A launch description of the pipeline",
72           DEFAULT_LAUNCH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
73
74   g_object_class_install_property (gobject_class, PROP_SHARED,
75       g_param_spec_boolean ("shared", "Shared", "If media from this factory is shared",
76           DEFAULT_SHARED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
77
78   klass->gen_key = default_gen_key;
79   klass->get_element = default_get_element;
80   klass->construct = default_construct;
81   klass->configure = default_configure;
82 }
83
84 static void
85 gst_rtsp_media_factory_init (GstRTSPMediaFactory * factory)
86 {
87   factory->launch = g_strdup (DEFAULT_LAUNCH);
88   factory->shared = DEFAULT_SHARED;
89
90   factory->lock = g_mutex_new ();
91   factory->medias_lock = g_mutex_new ();
92   factory->medias = g_hash_table_new_full (g_str_hash, g_str_equal,
93                   g_free, g_object_unref);
94 }
95
96 static void
97 gst_rtsp_media_factory_finalize (GObject * obj)
98 {
99   GstRTSPMediaFactory *factory = GST_RTSP_MEDIA_FACTORY (obj);
100
101   g_hash_table_unref (factory->medias);
102   g_mutex_free (factory->medias_lock);
103   g_free (factory->launch);
104   g_mutex_free (factory->lock);
105
106   G_OBJECT_CLASS (gst_rtsp_media_factory_parent_class)->finalize (obj);
107 }
108
109 static void
110 gst_rtsp_media_factory_get_property (GObject *object, guint propid,
111     GValue *value, GParamSpec *pspec)
112 {
113   GstRTSPMediaFactory *factory = GST_RTSP_MEDIA_FACTORY (object);
114
115   switch (propid) {
116     case PROP_LAUNCH:
117       g_value_take_string (value, gst_rtsp_media_factory_get_launch (factory));
118       break;
119     case PROP_SHARED:
120       g_value_set_boolean (value, gst_rtsp_media_factory_is_shared (factory));
121       break;
122     default:
123       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
124   }
125 }
126
127 static void
128 gst_rtsp_media_factory_set_property (GObject *object, guint propid,
129     const GValue *value, GParamSpec *pspec)
130 {
131   GstRTSPMediaFactory *factory = GST_RTSP_MEDIA_FACTORY (object);
132
133   switch (propid) {
134     case PROP_LAUNCH:
135       gst_rtsp_media_factory_set_launch (factory, g_value_get_string (value));
136       break;
137     case PROP_SHARED:
138       gst_rtsp_media_factory_set_shared (factory, g_value_get_boolean (value));
139       break;
140     default:
141       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
142   }
143 }
144
145 /**
146  * gst_rtsp_media_factory_new:
147  *
148  * Create a new #GstRTSPMediaFactory instance.
149  *
150  * Returns: a new #GstRTSPMediaFactory object.
151  */
152 GstRTSPMediaFactory *
153 gst_rtsp_media_factory_new (void)
154 {
155   GstRTSPMediaFactory *result;
156
157   result = g_object_new (GST_TYPE_RTSP_MEDIA_FACTORY, NULL);
158
159   return result;
160 }
161
162 /**
163  * gst_rtsp_media_factory_set_launch:
164  * @factory: a #GstRTSPMediaFactory
165  * @launch: the launch description
166  *
167  *
168  * The gst_parse_launch() line to use for constructing the pipeline in the
169  * default prepare vmethod.
170  *
171  * The pipeline description should return a GstBin as the toplevel element
172  * which can be accomplished by enclosing the dscription with brackets '('
173  * ')'.
174  *
175  * The description should return a pipeline with payloaders named pay0, pay1,
176  * etc.. Each of the payloaders will result in a stream.
177  */
178 void
179 gst_rtsp_media_factory_set_launch (GstRTSPMediaFactory *factory, const gchar *launch)
180 {
181   g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
182   g_return_if_fail (launch != NULL);
183
184   g_mutex_lock (factory->lock);
185   g_free (factory->launch);
186   factory->launch = g_strdup (launch);
187   g_mutex_unlock (factory->lock);
188 }
189
190 /**
191  * gst_rtsp_media_factory_get_launch:
192  * @factory: a #GstRTSPMediaFactory
193  *
194  * Get the gst_parse_launch() pipeline description that will be used in the
195  * default prepare vmethod.
196  *
197  * Returns: the configured launch description. g_free() after usage.
198  */
199 gchar *
200 gst_rtsp_media_factory_get_launch (GstRTSPMediaFactory *factory)
201 {
202   gchar *result;
203
204   g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), NULL);
205
206   g_mutex_lock (factory->lock);
207   result = g_strdup (factory->launch);
208   g_mutex_unlock (factory->lock);
209
210   return result;
211 }
212
213 /**
214  * gst_rtsp_media_factory_set_shared:
215  * @factory: a #GstRTSPMediaFactory
216  * @shared: the new value
217  *
218  * Configure if media created from this factory can be shared between clients.
219  */
220 void
221 gst_rtsp_media_factory_set_shared (GstRTSPMediaFactory *factory,
222     gboolean shared)
223 {
224   g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
225
226   g_mutex_lock (factory->lock);
227   factory->shared = shared;
228   g_mutex_unlock (factory->lock);
229 }
230
231 /**
232  * gst_rtsp_media_factory_is_shared:
233  * @factory: a #GstRTSPMediaFactory
234  *
235  * Get if media created from this factory can be shared between clients.
236  *
237  * Returns: %TRUE if the media will be shared between clients.
238  */
239 gboolean
240 gst_rtsp_media_factory_is_shared (GstRTSPMediaFactory *factory)
241 {
242   gboolean result;
243
244   g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), FALSE);
245
246   g_mutex_lock (factory->lock);
247   result = factory->shared;
248   g_mutex_unlock (factory->lock);
249
250   return result;
251 }
252
253 static gboolean
254 compare_media (gpointer key, GstRTSPMedia *media1, GstRTSPMedia *media2)
255 {
256   return (media1 == media2);
257 }
258
259 static void
260 media_unprepared (GstRTSPMedia *media, GstRTSPMediaFactory *factory)
261 {
262   g_mutex_lock (factory->medias_lock);
263   g_hash_table_foreach_remove (factory->medias, (GHRFunc) compare_media,
264        media);
265   g_mutex_unlock (factory->medias_lock);
266 }
267
268 /**
269  * gst_rtsp_media_factory_construct:
270  * @factory: a #GstRTSPMediaFactory
271  * @url: the url used
272  *
273  * Prepare the media object and create its streams. Implementations
274  * should create the needed gstreamer elements and add them to the result
275  * object. No state changes should be performed on them yet.
276  *
277  * One or more GstRTSPMediaStream objects should be added to the result with
278  * the srcpad member set to a source pad that produces buffer of type 
279  * application/x-rtp.
280  *
281  * Returns: a new #GstRTSPMedia if the media could be prepared.
282  */
283 GstRTSPMedia *
284 gst_rtsp_media_factory_construct (GstRTSPMediaFactory *factory, const GstRTSPUrl *url)
285 {
286   gchar *key;
287   GstRTSPMedia *media;
288   GstRTSPMediaFactoryClass *klass;
289
290   klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
291
292   /* convert the url to a key for the hashtable. NULL return or a NULL function
293    * will not cache anything for this factory. */
294   if (klass->gen_key)
295     key = klass->gen_key (factory, url);
296   else
297     key = NULL;
298
299   g_mutex_lock (factory->medias_lock);
300   if (key) {
301     /* we have a key, see if we find a cached media */
302     media = g_hash_table_lookup (factory->medias, key);
303     if (media)
304       g_object_ref (media);
305   }
306   else
307     media = NULL;
308
309   if (media == NULL) {
310     /* nothing cached found, try to create one */
311     if (klass->construct)
312       media = klass->construct (factory, url);
313     else
314       media = NULL;
315
316     if (media) {
317       /* configure the media */
318       if (klass->configure)
319         klass->configure (factory, media);
320
321       /* check if we can cache this media */
322       if (gst_rtsp_media_is_shared (media)) {
323         /* insert in the hashtable, takes ownership of the key */
324         g_object_ref (media);
325         g_hash_table_insert (factory->medias, key, media);
326         key = NULL;
327       }
328       if (!gst_rtsp_media_is_reusable (media)) {
329         /* when not reusable, connect to the unprepare signal to remove the item
330          * from our cache when it gets unprepared */
331         g_signal_connect (media, "unprepared", (GCallback) media_unprepared,
332                         factory);
333       }
334     }
335   }
336   g_mutex_unlock (factory->medias_lock);
337
338   if (key)
339     g_free (key);
340
341   g_message ("constructed media %p for url %s", media, url->abspath);
342
343   return media;
344 }
345
346 static gchar *
347 default_gen_key (GstRTSPMediaFactory *factory, const GstRTSPUrl *url)
348 {
349   gchar *result;
350
351   result = gst_rtsp_url_get_request_uri ((GstRTSPUrl *)url);
352
353   return result;
354 }
355
356 static GstElement *
357 default_get_element (GstRTSPMediaFactory *factory, const GstRTSPUrl *url)
358 {
359   GstElement *element;
360   GError *error = NULL;
361
362   g_mutex_lock (factory->lock);
363   /* we need a parse syntax */
364   if (factory->launch == NULL)
365     goto no_launch;
366
367   /* parse the user provided launch line */
368   element = gst_parse_launch (factory->launch, &error);
369   if (element == NULL)
370     goto parse_error;
371
372   g_mutex_unlock (factory->lock);
373
374   if (error != NULL) {
375     /* a recoverable error was encountered */
376     g_warning ("recoverable parsing error: %s", error->message);
377     g_error_free (error);
378   }
379   return element;
380
381   /* ERRORS */
382 no_launch:
383   {
384     g_mutex_unlock (factory->lock);
385     g_critical ("no launch line specified");
386     return NULL;
387   }
388 parse_error:
389   {
390     g_mutex_unlock (factory->lock);
391     g_critical ("could not parse launch syntax (%s): %s", factory->launch, 
392          (error ? error->message : "unknown reason"));
393     if (error)
394       g_error_free (error);
395     return NULL;
396   }
397 }
398
399
400 static GstRTSPMedia *
401 default_construct (GstRTSPMediaFactory *factory, const GstRTSPUrl *url)
402 {
403   GstRTSPMedia *media;
404   GstRTSPMediaStream *stream;
405   GstElement *pay, *element;
406   GstPad * pad;
407   gint i;
408   GstRTSPMediaFactoryClass *klass;
409
410   klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
411
412   if (klass->get_element)
413     element = klass->get_element (factory, url);
414   else
415     element = NULL;
416   if (element == NULL)
417     goto no_element;
418
419   /* create a new empty media */
420   media = gst_rtsp_media_new ();
421   media->element = element;
422
423   /* try to find all the payloader elements, they should be named 'pay%d'. for
424    * each of the payloaders we will create a stream and collect the source pad.
425    */
426   for (i = 0; ; i++) {
427     gchar *name;
428
429     name = g_strdup_printf ("pay%d", i);
430
431     if (!(pay = gst_bin_get_by_name (GST_BIN (element), name))) {
432       /* no more payloaders found, we have found all the streams and we can
433        * end the loop */
434       g_free (name);
435       break;
436     }
437     
438     /* create the stream */
439     stream = g_new0 (GstRTSPMediaStream, 1);
440     stream->payloader = pay;
441
442     pad = gst_element_get_static_pad (pay, "src");
443
444     /* ghost the pad of the payloader to the element */
445     stream->srcpad = gst_ghost_pad_new (name, pad);
446     gst_element_add_pad (media->element, stream->srcpad);
447     gst_object_unref (pay);
448     g_free (name);
449
450     /* add stream now */
451     g_array_append_val (media->streams, stream);
452   }
453
454   return media;
455
456   /* ERRORS */
457 no_element:
458   {
459     g_critical ("could not create element");
460     return NULL;
461   }
462 }
463
464 static void
465 default_configure (GstRTSPMediaFactory *factory, GstRTSPMedia *media)
466 {
467   gboolean shared;
468
469   /* configure the sharedness */
470   g_mutex_lock (factory->lock);
471   shared = factory->shared;
472   g_mutex_unlock (factory->lock);
473
474   gst_rtsp_media_set_shared (media, shared);
475 }