media-factory: make lock macro
[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 #define DEFAULT_EOS_SHUTDOWN   FALSE
25
26 enum
27 {
28   PROP_0,
29   PROP_LAUNCH,
30   PROP_SHARED,
31   PROP_EOS_SHUTDOWN,
32   PROP_LAST
33 };
34
35 GST_DEBUG_CATEGORY (rtsp_media_debug);
36 #define GST_CAT_DEFAULT rtsp_media_debug
37
38 static void gst_rtsp_media_factory_get_property (GObject * object, guint propid,
39     GValue * value, GParamSpec * pspec);
40 static void gst_rtsp_media_factory_set_property (GObject * object, guint propid,
41     const GValue * value, GParamSpec * pspec);
42 static void gst_rtsp_media_factory_finalize (GObject * obj);
43
44 static gchar *default_gen_key (GstRTSPMediaFactory * factory,
45     const GstRTSPUrl * url);
46 static GstElement *default_get_element (GstRTSPMediaFactory * factory,
47     const GstRTSPUrl * url);
48 static GstRTSPMedia *default_construct (GstRTSPMediaFactory * factory,
49     const GstRTSPUrl * url);
50 static void default_configure (GstRTSPMediaFactory * factory,
51     GstRTSPMedia * media);
52 static GstElement *default_create_pipeline (GstRTSPMediaFactory * factory,
53     GstRTSPMedia * media);
54
55 G_DEFINE_TYPE (GstRTSPMediaFactory, gst_rtsp_media_factory, G_TYPE_OBJECT);
56
57 static void
58 gst_rtsp_media_factory_class_init (GstRTSPMediaFactoryClass * klass)
59 {
60   GObjectClass *gobject_class;
61
62   gobject_class = G_OBJECT_CLASS (klass);
63
64   gobject_class->get_property = gst_rtsp_media_factory_get_property;
65   gobject_class->set_property = gst_rtsp_media_factory_set_property;
66   gobject_class->finalize = gst_rtsp_media_factory_finalize;
67
68   /**
69    * GstRTSPMediaFactory::launch
70    *
71    * The gst_parse_launch() line to use for constructing the pipeline in the
72    * default prepare vmethod.
73    *
74    * The pipeline description should return a GstBin as the toplevel element
75    * which can be accomplished by enclosing the dscription with brackets '('
76    * ')'.
77    *
78    * The description should return a pipeline with payloaders named pay0, pay1,
79    * etc.. Each of the payloaders will result in a stream.
80    *
81    * Support for dynamic payloaders can be accomplished by adding payloaders
82    * named dynpay0, dynpay1, etc..
83    */
84   g_object_class_install_property (gobject_class, PROP_LAUNCH,
85       g_param_spec_string ("launch", "Launch",
86           "A launch description of the pipeline", DEFAULT_LAUNCH,
87           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
88
89   g_object_class_install_property (gobject_class, PROP_SHARED,
90       g_param_spec_boolean ("shared", "Shared",
91           "If media from this factory is shared", DEFAULT_SHARED,
92           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
93
94   g_object_class_install_property (gobject_class, PROP_EOS_SHUTDOWN,
95       g_param_spec_boolean ("eos-shutdown", "EOS Shutdown",
96           "Send EOS down the pipeline before shutting down",
97           DEFAULT_EOS_SHUTDOWN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
98
99   klass->gen_key = default_gen_key;
100   klass->get_element = default_get_element;
101   klass->construct = default_construct;
102   klass->configure = default_configure;
103   klass->create_pipeline = default_create_pipeline;
104
105   GST_DEBUG_CATEGORY_INIT (rtsp_media_debug, "rtspmedia", 0, "GstRTSPMedia");
106 }
107
108 static void
109 gst_rtsp_media_factory_init (GstRTSPMediaFactory * factory)
110 {
111   factory->launch = g_strdup (DEFAULT_LAUNCH);
112   factory->shared = DEFAULT_SHARED;
113   factory->eos_shutdown = DEFAULT_EOS_SHUTDOWN;
114
115   factory->lock = g_mutex_new ();
116   factory->medias_lock = g_mutex_new ();
117   factory->medias = g_hash_table_new_full (g_str_hash, g_str_equal,
118       g_free, g_object_unref);
119 }
120
121 static void
122 gst_rtsp_media_factory_finalize (GObject * obj)
123 {
124   GstRTSPMediaFactory *factory = GST_RTSP_MEDIA_FACTORY (obj);
125
126   g_hash_table_unref (factory->medias);
127   g_mutex_free (factory->medias_lock);
128   g_free (factory->launch);
129   g_mutex_free (factory->lock);
130
131   G_OBJECT_CLASS (gst_rtsp_media_factory_parent_class)->finalize (obj);
132 }
133
134 static void
135 gst_rtsp_media_factory_get_property (GObject * object, guint propid,
136     GValue * value, GParamSpec * pspec)
137 {
138   GstRTSPMediaFactory *factory = GST_RTSP_MEDIA_FACTORY (object);
139
140   switch (propid) {
141     case PROP_LAUNCH:
142       g_value_take_string (value, gst_rtsp_media_factory_get_launch (factory));
143       break;
144     case PROP_SHARED:
145       g_value_set_boolean (value, gst_rtsp_media_factory_is_shared (factory));
146       break;
147     case PROP_EOS_SHUTDOWN:
148       g_value_set_boolean (value,
149           gst_rtsp_media_factory_is_eos_shutdown (factory));
150       break;
151     default:
152       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
153   }
154 }
155
156 static void
157 gst_rtsp_media_factory_set_property (GObject * object, guint propid,
158     const GValue * value, GParamSpec * pspec)
159 {
160   GstRTSPMediaFactory *factory = GST_RTSP_MEDIA_FACTORY (object);
161
162   switch (propid) {
163     case PROP_LAUNCH:
164       gst_rtsp_media_factory_set_launch (factory, g_value_get_string (value));
165       break;
166     case PROP_SHARED:
167       gst_rtsp_media_factory_set_shared (factory, g_value_get_boolean (value));
168       break;
169     case PROP_EOS_SHUTDOWN:
170       gst_rtsp_media_factory_set_eos_shutdown (factory,
171           g_value_get_boolean (value));
172       break;
173     default:
174       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
175   }
176 }
177
178 /**
179  * gst_rtsp_media_factory_new:
180  *
181  * Create a new #GstRTSPMediaFactory instance.
182  *
183  * Returns: a new #GstRTSPMediaFactory object.
184  */
185 GstRTSPMediaFactory *
186 gst_rtsp_media_factory_new (void)
187 {
188   GstRTSPMediaFactory *result;
189
190   result = g_object_new (GST_TYPE_RTSP_MEDIA_FACTORY, NULL);
191
192   return result;
193 }
194
195 /**
196  * gst_rtsp_media_factory_set_launch:
197  * @factory: a #GstRTSPMediaFactory
198  * @launch: the launch description
199  *
200  *
201  * The gst_parse_launch() line to use for constructing the pipeline in the
202  * default prepare vmethod.
203  *
204  * The pipeline description should return a GstBin as the toplevel element
205  * which can be accomplished by enclosing the dscription with brackets '('
206  * ')'.
207  *
208  * The description should return a pipeline with payloaders named pay0, pay1,
209  * etc.. Each of the payloaders will result in a stream.
210  */
211 void
212 gst_rtsp_media_factory_set_launch (GstRTSPMediaFactory * factory,
213     const gchar * launch)
214 {
215   g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
216   g_return_if_fail (launch != NULL);
217
218   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
219   g_free (factory->launch);
220   factory->launch = g_strdup (launch);
221   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
222 }
223
224 /**
225  * gst_rtsp_media_factory_get_launch:
226  * @factory: a #GstRTSPMediaFactory
227  *
228  * Get the gst_parse_launch() pipeline description that will be used in the
229  * default prepare vmethod.
230  *
231  * Returns: the configured launch description. g_free() after usage.
232  */
233 gchar *
234 gst_rtsp_media_factory_get_launch (GstRTSPMediaFactory * factory)
235 {
236   gchar *result;
237
238   g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), NULL);
239
240   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
241   result = g_strdup (factory->launch);
242   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
243
244   return result;
245 }
246
247 /**
248  * gst_rtsp_media_factory_set_shared:
249  * @factory: a #GstRTSPMediaFactory
250  * @shared: the new value
251  *
252  * Configure if media created from this factory can be shared between clients.
253  */
254 void
255 gst_rtsp_media_factory_set_shared (GstRTSPMediaFactory * factory,
256     gboolean shared)
257 {
258   g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
259
260   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
261   factory->shared = shared;
262   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
263 }
264
265 /**
266  * gst_rtsp_media_factory_is_shared:
267  * @factory: a #GstRTSPMediaFactory
268  *
269  * Get if media created from this factory can be shared between clients.
270  *
271  * Returns: %TRUE if the media will be shared between clients.
272  */
273 gboolean
274 gst_rtsp_media_factory_is_shared (GstRTSPMediaFactory * factory)
275 {
276   gboolean result;
277
278   g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), FALSE);
279
280   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
281   result = factory->shared;
282   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
283
284   return result;
285 }
286
287 /**
288  * gst_rtsp_media_factory_set_eos_shutdown:
289  * @factory: a #GstRTSPMediaFactory
290  * @eos_shutdown: the new value
291  *
292  * Configure if media created from this factory will have an EOS sent to the
293  * pipeline before shutdown.
294  */
295 void
296 gst_rtsp_media_factory_set_eos_shutdown (GstRTSPMediaFactory * factory,
297     gboolean eos_shutdown)
298 {
299   g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
300
301   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
302   factory->eos_shutdown = eos_shutdown;
303   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
304 }
305
306 /**
307  * gst_rtsp_media_factory_is_eos_shutdown:
308  * @factory: a #GstRTSPMediaFactory
309  *
310  * Get if media created from this factory will have an EOS event sent to the
311  * pipeline before shutdown.
312  *
313  * Returns: %TRUE if the media will receive EOS before shutdown.
314  */
315 gboolean
316 gst_rtsp_media_factory_is_eos_shutdown (GstRTSPMediaFactory * factory)
317 {
318   gboolean result;
319
320   g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), FALSE);
321
322   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
323   result = factory->eos_shutdown;
324   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
325
326   return result;
327 }
328
329 static gboolean
330 compare_media (gpointer key, GstRTSPMedia * media1, GstRTSPMedia * media2)
331 {
332   return (media1 == media2);
333 }
334
335 static void
336 media_unprepared (GstRTSPMedia * media, GstRTSPMediaFactory * factory)
337 {
338   g_mutex_lock (factory->medias_lock);
339   g_hash_table_foreach_remove (factory->medias, (GHRFunc) compare_media, media);
340   g_mutex_unlock (factory->medias_lock);
341 }
342
343 /**
344  * gst_rtsp_media_factory_construct:
345  * @factory: a #GstRTSPMediaFactory
346  * @url: the url used
347  *
348  * Prepare the media object and create its streams. Implementations
349  * should create the needed gstreamer elements and add them to the result
350  * object. No state changes should be performed on them yet.
351  *
352  * One or more GstRTSPMediaStream objects should be added to the result with
353  * the srcpad member set to a source pad that produces buffer of type 
354  * application/x-rtp.
355  *
356  * Returns: a new #GstRTSPMedia if the media could be prepared.
357  */
358 GstRTSPMedia *
359 gst_rtsp_media_factory_construct (GstRTSPMediaFactory * factory,
360     const GstRTSPUrl * url)
361 {
362   gchar *key;
363   GstRTSPMedia *media;
364   GstRTSPMediaFactoryClass *klass;
365
366   klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
367
368   /* convert the url to a key for the hashtable. NULL return or a NULL function
369    * will not cache anything for this factory. */
370   if (klass->gen_key)
371     key = klass->gen_key (factory, url);
372   else
373     key = NULL;
374
375   g_mutex_lock (factory->medias_lock);
376   if (key) {
377     /* we have a key, see if we find a cached media */
378     media = g_hash_table_lookup (factory->medias, key);
379     if (media)
380       g_object_ref (media);
381   } else
382     media = NULL;
383
384   if (media == NULL) {
385     /* nothing cached found, try to create one */
386     if (klass->construct)
387       media = klass->construct (factory, url);
388     else
389       media = NULL;
390
391     if (media) {
392       /* configure the media */
393       if (klass->configure)
394         klass->configure (factory, media);
395
396       /* check if we can cache this media */
397       if (gst_rtsp_media_is_shared (media)) {
398         /* insert in the hashtable, takes ownership of the key */
399         g_object_ref (media);
400         g_hash_table_insert (factory->medias, key, media);
401         key = NULL;
402       }
403       if (!gst_rtsp_media_is_reusable (media)) {
404         /* when not reusable, connect to the unprepare signal to remove the item
405          * from our cache when it gets unprepared */
406         g_signal_connect (media, "unprepared", (GCallback) media_unprepared,
407             factory);
408       }
409     }
410   }
411   g_mutex_unlock (factory->medias_lock);
412
413   if (key)
414     g_free (key);
415
416   GST_INFO ("constructed media %p for url %s", media, url->abspath);
417
418   return media;
419 }
420
421 static gchar *
422 default_gen_key (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
423 {
424   gchar *result;
425   const gchar *pre_query;
426   const gchar *query;
427
428   pre_query = url->query ? "?" : "";
429   query = url->query ? url->query : "";
430
431   result =
432       g_strdup_printf ("%u%s%s%s", url->port, url->abspath, pre_query, query);
433
434   return result;
435 }
436
437 static GstElement *
438 default_get_element (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
439 {
440   GstElement *element;
441   GError *error = NULL;
442
443   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
444   /* we need a parse syntax */
445   if (factory->launch == NULL)
446     goto no_launch;
447
448   /* parse the user provided launch line */
449   element = gst_parse_launch (factory->launch, &error);
450   if (element == NULL)
451     goto parse_error;
452
453   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
454
455   if (error != NULL) {
456     /* a recoverable error was encountered */
457     GST_WARNING ("recoverable parsing error: %s", error->message);
458     g_error_free (error);
459   }
460   return element;
461
462   /* ERRORS */
463 no_launch:
464   {
465     GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
466     g_critical ("no launch line specified");
467     return NULL;
468   }
469 parse_error:
470   {
471     GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
472     g_critical ("could not parse launch syntax (%s): %s", factory->launch,
473         (error ? error->message : "unknown reason"));
474     if (error)
475       g_error_free (error);
476     return NULL;
477   }
478 }
479
480 /* try to find all the payloader elements, they should be named 'pay%d'. for
481  * each of the payloaders we will create a stream and collect the source pad. */
482 void
483 gst_rtsp_media_factory_collect_streams (GstRTSPMediaFactory * factory,
484     const GstRTSPUrl * url, GstRTSPMedia * media)
485 {
486   GstElement *element, *elem;
487   GstPad *pad;
488   gint i;
489   GstRTSPMediaStream *stream;
490   gboolean have_elem;
491
492   element = media->element;
493
494   have_elem = TRUE;
495   for (i = 0; have_elem; i++) {
496     gchar *name;
497
498     have_elem = FALSE;
499
500     name = g_strdup_printf ("pay%d", i);
501     if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
502       /* create the stream */
503       stream = g_new0 (GstRTSPMediaStream, 1);
504       stream->payloader = elem;
505
506       GST_INFO ("found stream %d with payloader %p", i, elem);
507
508       pad = gst_element_get_static_pad (elem, "src");
509
510       /* ghost the pad of the payloader to the element */
511       stream->srcpad = gst_ghost_pad_new (name, pad);
512       gst_pad_set_active (stream->srcpad, TRUE);
513       gst_element_add_pad (media->element, stream->srcpad);
514       gst_object_unref (elem);
515
516       /* add stream now */
517       g_array_append_val (media->streams, stream);
518       have_elem = TRUE;
519     }
520     g_free (name);
521
522     name = g_strdup_printf ("dynpay%d", i);
523     if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
524       /* a stream that will dynamically create pads to provide RTP packets */
525
526       GST_INFO ("found dynamic element %d, %p", i, elem);
527
528       media->dynamic = g_list_prepend (media->dynamic, elem);
529
530       have_elem = TRUE;
531     }
532     g_free (name);
533   }
534 }
535
536 static GstRTSPMedia *
537 default_construct (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
538 {
539   GstRTSPMedia *media;
540   GstElement *element;
541   GstRTSPMediaFactoryClass *klass;
542
543   klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
544
545   if (!klass->create_pipeline)
546     goto no_create;
547
548   if (klass->get_element)
549     element = klass->get_element (factory, url);
550   else
551     element = NULL;
552   if (element == NULL)
553     goto no_element;
554
555   /* create a new empty media */
556   media = gst_rtsp_media_new ();
557   media->element = element;
558
559   media->pipeline = klass->create_pipeline (factory, media);
560   if (media->pipeline == NULL)
561     goto no_pipeline;
562
563   gst_rtsp_media_factory_collect_streams (factory, url, media);
564
565   return media;
566
567   /* ERRORS */
568 no_create:
569   {
570     g_critical ("no create_pipeline function");
571     return NULL;
572   }
573 no_element:
574   {
575     g_critical ("could not create element");
576     return NULL;
577   }
578 no_pipeline:
579   {
580     g_critical ("can't create pipeline");
581     g_object_unref (media);
582     return NULL;
583   }
584 }
585
586 static GstElement *
587 default_create_pipeline (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
588 {
589   GstElement *pipeline;
590
591   if (media->element == NULL)
592     goto no_element;
593
594   pipeline = gst_pipeline_new ("media-pipeline");
595   gst_bin_add (GST_BIN_CAST (pipeline), media->element);
596
597   return pipeline;
598
599   /* ERRORS */
600 no_element:
601   {
602     g_critical ("no element");
603     return NULL;
604   }
605 }
606
607 static void
608 default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
609 {
610   gboolean shared, eos_shutdown;
611
612   /* configure the sharedness */
613   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
614   shared = factory->shared;
615   eos_shutdown = factory->eos_shutdown;
616   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
617
618   gst_rtsp_media_set_shared (media, shared);
619   gst_rtsp_media_set_eos_shutdown (media, eos_shutdown);
620 }