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