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