dd0ef7987e7297b271c5aaa7aed969847e358968
[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   if (factory->auth)
131     g_object_unref (factory->auth);
132
133   G_OBJECT_CLASS (gst_rtsp_media_factory_parent_class)->finalize (obj);
134 }
135
136 static void
137 gst_rtsp_media_factory_get_property (GObject * object, guint propid,
138     GValue * value, GParamSpec * pspec)
139 {
140   GstRTSPMediaFactory *factory = GST_RTSP_MEDIA_FACTORY (object);
141
142   switch (propid) {
143     case PROP_LAUNCH:
144       g_value_take_string (value, gst_rtsp_media_factory_get_launch (factory));
145       break;
146     case PROP_SHARED:
147       g_value_set_boolean (value, gst_rtsp_media_factory_is_shared (factory));
148       break;
149     case PROP_EOS_SHUTDOWN:
150       g_value_set_boolean (value,
151           gst_rtsp_media_factory_is_eos_shutdown (factory));
152       break;
153     default:
154       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
155   }
156 }
157
158 static void
159 gst_rtsp_media_factory_set_property (GObject * object, guint propid,
160     const GValue * value, GParamSpec * pspec)
161 {
162   GstRTSPMediaFactory *factory = GST_RTSP_MEDIA_FACTORY (object);
163
164   switch (propid) {
165     case PROP_LAUNCH:
166       gst_rtsp_media_factory_set_launch (factory, g_value_get_string (value));
167       break;
168     case PROP_SHARED:
169       gst_rtsp_media_factory_set_shared (factory, g_value_get_boolean (value));
170       break;
171     case PROP_EOS_SHUTDOWN:
172       gst_rtsp_media_factory_set_eos_shutdown (factory,
173           g_value_get_boolean (value));
174       break;
175     default:
176       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
177   }
178 }
179
180 /**
181  * gst_rtsp_media_factory_new:
182  *
183  * Create a new #GstRTSPMediaFactory instance.
184  *
185  * Returns: a new #GstRTSPMediaFactory object.
186  */
187 GstRTSPMediaFactory *
188 gst_rtsp_media_factory_new (void)
189 {
190   GstRTSPMediaFactory *result;
191
192   result = g_object_new (GST_TYPE_RTSP_MEDIA_FACTORY, NULL);
193
194   return result;
195 }
196
197 /**
198  * gst_rtsp_media_factory_set_launch:
199  * @factory: a #GstRTSPMediaFactory
200  * @launch: the launch description
201  *
202  *
203  * The gst_parse_launch() line to use for constructing the pipeline in the
204  * default prepare vmethod.
205  *
206  * The pipeline description should return a GstBin as the toplevel element
207  * which can be accomplished by enclosing the dscription with brackets '('
208  * ')'.
209  *
210  * The description should return a pipeline with payloaders named pay0, pay1,
211  * etc.. Each of the payloaders will result in a stream.
212  */
213 void
214 gst_rtsp_media_factory_set_launch (GstRTSPMediaFactory * factory,
215     const gchar * launch)
216 {
217   g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
218   g_return_if_fail (launch != NULL);
219
220   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
221   g_free (factory->launch);
222   factory->launch = g_strdup (launch);
223   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
224 }
225
226 /**
227  * gst_rtsp_media_factory_get_launch:
228  * @factory: a #GstRTSPMediaFactory
229  *
230  * Get the gst_parse_launch() pipeline description that will be used in the
231  * default prepare vmethod.
232  *
233  * Returns: the configured launch description. g_free() after usage.
234  */
235 gchar *
236 gst_rtsp_media_factory_get_launch (GstRTSPMediaFactory * factory)
237 {
238   gchar *result;
239
240   g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), NULL);
241
242   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
243   result = g_strdup (factory->launch);
244   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
245
246   return result;
247 }
248
249 /**
250  * gst_rtsp_media_factory_set_shared:
251  * @factory: a #GstRTSPMediaFactory
252  * @shared: the new value
253  *
254  * Configure if media created from this factory can be shared between clients.
255  */
256 void
257 gst_rtsp_media_factory_set_shared (GstRTSPMediaFactory * factory,
258     gboolean shared)
259 {
260   g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
261
262   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
263   factory->shared = shared;
264   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
265 }
266
267 /**
268  * gst_rtsp_media_factory_is_shared:
269  * @factory: a #GstRTSPMediaFactory
270  *
271  * Get if media created from this factory can be shared between clients.
272  *
273  * Returns: %TRUE if the media will be shared between clients.
274  */
275 gboolean
276 gst_rtsp_media_factory_is_shared (GstRTSPMediaFactory * factory)
277 {
278   gboolean result;
279
280   g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), FALSE);
281
282   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
283   result = factory->shared;
284   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
285
286   return result;
287 }
288
289 /**
290  * gst_rtsp_media_factory_set_eos_shutdown:
291  * @factory: a #GstRTSPMediaFactory
292  * @eos_shutdown: the new value
293  *
294  * Configure if media created from this factory will have an EOS sent to the
295  * pipeline before shutdown.
296  */
297 void
298 gst_rtsp_media_factory_set_eos_shutdown (GstRTSPMediaFactory * factory,
299     gboolean eos_shutdown)
300 {
301   g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
302
303   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
304   factory->eos_shutdown = eos_shutdown;
305   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
306 }
307
308 /**
309  * gst_rtsp_media_factory_is_eos_shutdown:
310  * @factory: a #GstRTSPMediaFactory
311  *
312  * Get if media created from this factory will have an EOS event sent to the
313  * pipeline before shutdown.
314  *
315  * Returns: %TRUE if the media will receive EOS before shutdown.
316  */
317 gboolean
318 gst_rtsp_media_factory_is_eos_shutdown (GstRTSPMediaFactory * factory)
319 {
320   gboolean result;
321
322   g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), FALSE);
323
324   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
325   result = factory->eos_shutdown;
326   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
327
328   return result;
329 }
330
331 /**
332  * gst_rtsp_media_factory_set_auth:
333  * @factory: a #GstRTSPMediaFactory
334  * @auth: a #GstRTSPAuth
335  *
336  * configure @auth to be used as the authentication manager of @factory.
337  */
338 void
339 gst_rtsp_media_factory_set_auth (GstRTSPMediaFactory * factory,
340     GstRTSPAuth * auth)
341 {
342   GstRTSPAuth *old;
343
344   g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
345
346   old = factory->auth;
347
348   if (old != auth) {
349     if (auth)
350       g_object_ref (auth);
351     factory->auth = auth;
352     if (old)
353       g_object_unref (old);
354   }
355 }
356
357
358 /**
359  * gst_rtsp_media_factory_get_auth:
360  * @factory: a #GstRTSPMediaFactory
361  *
362  * Get the #GstRTSPAuth used as the authentication manager of @factory.
363  *
364  * Returns: the #GstRTSPAuth of @factory. g_object_unref() after
365  * usage.
366  */
367 GstRTSPAuth *
368 gst_rtsp_factory_get_auth (GstRTSPMediaFactory * factory)
369 {
370   GstRTSPAuth *result;
371
372   g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), NULL);
373
374   if ((result = factory->auth))
375     g_object_ref (result);
376
377   return result;
378 }
379
380
381 static gboolean
382 compare_media (gpointer key, GstRTSPMedia * media1, GstRTSPMedia * media2)
383 {
384   return (media1 == media2);
385 }
386
387 static void
388 media_unprepared (GstRTSPMedia * media, GstRTSPMediaFactory * factory)
389 {
390   g_mutex_lock (factory->medias_lock);
391   g_hash_table_foreach_remove (factory->medias, (GHRFunc) compare_media, media);
392   g_mutex_unlock (factory->medias_lock);
393 }
394
395 /**
396  * gst_rtsp_media_factory_construct:
397  * @factory: a #GstRTSPMediaFactory
398  * @url: the url used
399  *
400  * Prepare the media object and create its streams. Implementations
401  * should create the needed gstreamer elements and add them to the result
402  * object. No state changes should be performed on them yet.
403  *
404  * One or more GstRTSPMediaStream objects should be added to the result with
405  * the srcpad member set to a source pad that produces buffer of type 
406  * application/x-rtp.
407  *
408  * Returns: a new #GstRTSPMedia if the media could be prepared.
409  */
410 GstRTSPMedia *
411 gst_rtsp_media_factory_construct (GstRTSPMediaFactory * factory,
412     const GstRTSPUrl * url)
413 {
414   gchar *key;
415   GstRTSPMedia *media;
416   GstRTSPMediaFactoryClass *klass;
417
418   klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
419
420   /* convert the url to a key for the hashtable. NULL return or a NULL function
421    * will not cache anything for this factory. */
422   if (klass->gen_key)
423     key = klass->gen_key (factory, url);
424   else
425     key = NULL;
426
427   g_mutex_lock (factory->medias_lock);
428   if (key) {
429     /* we have a key, see if we find a cached media */
430     media = g_hash_table_lookup (factory->medias, key);
431     if (media)
432       g_object_ref (media);
433   } else
434     media = NULL;
435
436   if (media == NULL) {
437     /* nothing cached found, try to create one */
438     if (klass->construct)
439       media = klass->construct (factory, url);
440     else
441       media = NULL;
442
443     if (media) {
444       /* configure the media */
445       if (klass->configure)
446         klass->configure (factory, media);
447
448       /* check if we can cache this media */
449       if (gst_rtsp_media_is_shared (media)) {
450         /* insert in the hashtable, takes ownership of the key */
451         g_object_ref (media);
452         g_hash_table_insert (factory->medias, key, media);
453         key = NULL;
454       }
455       if (!gst_rtsp_media_is_reusable (media)) {
456         /* when not reusable, connect to the unprepare signal to remove the item
457          * from our cache when it gets unprepared */
458         g_signal_connect (media, "unprepared", (GCallback) media_unprepared,
459             factory);
460       }
461     }
462   }
463   g_mutex_unlock (factory->medias_lock);
464
465   if (key)
466     g_free (key);
467
468   GST_INFO ("constructed media %p for url %s", media, url->abspath);
469
470   return media;
471 }
472
473 static gchar *
474 default_gen_key (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
475 {
476   gchar *result;
477   const gchar *pre_query;
478   const gchar *query;
479
480   pre_query = url->query ? "?" : "";
481   query = url->query ? url->query : "";
482
483   result =
484       g_strdup_printf ("%u%s%s%s", url->port, url->abspath, pre_query, query);
485
486   return result;
487 }
488
489 static GstElement *
490 default_get_element (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
491 {
492   GstElement *element;
493   GError *error = NULL;
494
495   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
496   /* we need a parse syntax */
497   if (factory->launch == NULL)
498     goto no_launch;
499
500   /* parse the user provided launch line */
501   element = gst_parse_launch (factory->launch, &error);
502   if (element == NULL)
503     goto parse_error;
504
505   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
506
507   if (error != NULL) {
508     /* a recoverable error was encountered */
509     GST_WARNING ("recoverable parsing error: %s", error->message);
510     g_error_free (error);
511   }
512   return element;
513
514   /* ERRORS */
515 no_launch:
516   {
517     GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
518     g_critical ("no launch line specified");
519     return NULL;
520   }
521 parse_error:
522   {
523     GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
524     g_critical ("could not parse launch syntax (%s): %s", factory->launch,
525         (error ? error->message : "unknown reason"));
526     if (error)
527       g_error_free (error);
528     return NULL;
529   }
530 }
531
532 /* try to find all the payloader elements, they should be named 'pay%d'. for
533  * each of the payloaders we will create a stream and collect the source pad. */
534 void
535 gst_rtsp_media_factory_collect_streams (GstRTSPMediaFactory * factory,
536     const GstRTSPUrl * url, GstRTSPMedia * media)
537 {
538   GstElement *element, *elem;
539   GstPad *pad;
540   gint i;
541   GstRTSPMediaStream *stream;
542   gboolean have_elem;
543
544   element = media->element;
545
546   have_elem = TRUE;
547   for (i = 0; have_elem; i++) {
548     gchar *name;
549
550     have_elem = FALSE;
551
552     name = g_strdup_printf ("pay%d", i);
553     if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
554       /* create the stream */
555       stream = g_new0 (GstRTSPMediaStream, 1);
556       stream->payloader = elem;
557
558       GST_INFO ("found stream %d with payloader %p", i, elem);
559
560       pad = gst_element_get_static_pad (elem, "src");
561
562       /* ghost the pad of the payloader to the element */
563       stream->srcpad = gst_ghost_pad_new (name, pad);
564       gst_pad_set_active (stream->srcpad, TRUE);
565       gst_element_add_pad (media->element, stream->srcpad);
566       gst_object_unref (elem);
567
568       /* add stream now */
569       g_array_append_val (media->streams, stream);
570       have_elem = TRUE;
571     }
572     g_free (name);
573
574     name = g_strdup_printf ("dynpay%d", i);
575     if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
576       /* a stream that will dynamically create pads to provide RTP packets */
577
578       GST_INFO ("found dynamic element %d, %p", i, elem);
579
580       media->dynamic = g_list_prepend (media->dynamic, elem);
581
582       have_elem = TRUE;
583     }
584     g_free (name);
585   }
586 }
587
588 static GstRTSPMedia *
589 default_construct (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
590 {
591   GstRTSPMedia *media;
592   GstElement *element;
593   GstRTSPMediaFactoryClass *klass;
594
595   klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
596
597   if (!klass->create_pipeline)
598     goto no_create;
599
600   if (klass->get_element)
601     element = klass->get_element (factory, url);
602   else
603     element = NULL;
604   if (element == NULL)
605     goto no_element;
606
607   /* create a new empty media */
608   media = gst_rtsp_media_new ();
609   media->element = element;
610
611   media->pipeline = klass->create_pipeline (factory, media);
612   if (media->pipeline == NULL)
613     goto no_pipeline;
614
615   gst_rtsp_media_factory_collect_streams (factory, url, media);
616
617   return media;
618
619   /* ERRORS */
620 no_create:
621   {
622     g_critical ("no create_pipeline function");
623     return NULL;
624   }
625 no_element:
626   {
627     g_critical ("could not create element");
628     return NULL;
629   }
630 no_pipeline:
631   {
632     g_critical ("can't create pipeline");
633     g_object_unref (media);
634     return NULL;
635   }
636 }
637
638 static GstElement *
639 default_create_pipeline (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
640 {
641   GstElement *pipeline;
642
643   if (media->element == NULL)
644     goto no_element;
645
646   pipeline = gst_pipeline_new ("media-pipeline");
647   gst_bin_add (GST_BIN_CAST (pipeline), media->element);
648
649   return pipeline;
650
651   /* ERRORS */
652 no_element:
653   {
654     g_critical ("no element");
655     return NULL;
656   }
657 }
658
659 static void
660 default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
661 {
662   gboolean shared, eos_shutdown;
663
664   /* configure the sharedness */
665   GST_RTSP_MEDIA_FACTORY_LOCK (factory);
666   shared = factory->shared;
667   eos_shutdown = factory->eos_shutdown;
668   GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
669
670   gst_rtsp_media_set_shared (media, shared);
671   gst_rtsp_media_set_eos_shutdown (media, eos_shutdown);
672 }