media: configure address pool in new streams
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-media.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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include <string.h>
21 #include <stdlib.h>
22
23 #include <gst/app/gstappsrc.h>
24 #include <gst/app/gstappsink.h>
25
26 #include "rtsp-media.h"
27
28 #define DEFAULT_SHARED          FALSE
29 #define DEFAULT_REUSABLE        FALSE
30 #define DEFAULT_PROTOCOLS       GST_RTSP_LOWER_TRANS_UDP | GST_RTSP_LOWER_TRANS_TCP
31 //#define DEFAULT_PROTOCOLS      GST_RTSP_LOWER_TRANS_UDP_MCAST
32 #define DEFAULT_EOS_SHUTDOWN    FALSE
33 #define DEFAULT_BUFFER_SIZE     0x80000
34
35 /* define to dump received RTCP packets */
36 #undef DUMP_STATS
37
38 enum
39 {
40   PROP_0,
41   PROP_SHARED,
42   PROP_REUSABLE,
43   PROP_PROTOCOLS,
44   PROP_EOS_SHUTDOWN,
45   PROP_BUFFER_SIZE,
46   PROP_LAST
47 };
48
49 enum
50 {
51   SIGNAL_PREPARED,
52   SIGNAL_UNPREPARED,
53   SIGNAL_NEW_STATE,
54   SIGNAL_LAST
55 };
56
57 GST_DEBUG_CATEGORY_STATIC (rtsp_media_debug);
58 #define GST_CAT_DEFAULT rtsp_media_debug
59
60 static void gst_rtsp_media_get_property (GObject * object, guint propid,
61     GValue * value, GParamSpec * pspec);
62 static void gst_rtsp_media_set_property (GObject * object, guint propid,
63     const GValue * value, GParamSpec * pspec);
64 static void gst_rtsp_media_finalize (GObject * obj);
65
66 static gpointer do_loop (GstRTSPMediaClass * klass);
67 static gboolean default_handle_message (GstRTSPMedia * media,
68     GstMessage * message);
69 static void finish_unprepare (GstRTSPMedia * media);
70 static gboolean default_unprepare (GstRTSPMedia * media);
71
72 static guint gst_rtsp_media_signals[SIGNAL_LAST] = { 0 };
73
74 G_DEFINE_TYPE (GstRTSPMedia, gst_rtsp_media, G_TYPE_OBJECT);
75
76 static void
77 gst_rtsp_media_class_init (GstRTSPMediaClass * klass)
78 {
79   GObjectClass *gobject_class;
80
81   gobject_class = G_OBJECT_CLASS (klass);
82
83   gobject_class->get_property = gst_rtsp_media_get_property;
84   gobject_class->set_property = gst_rtsp_media_set_property;
85   gobject_class->finalize = gst_rtsp_media_finalize;
86
87   g_object_class_install_property (gobject_class, PROP_SHARED,
88       g_param_spec_boolean ("shared", "Shared",
89           "If this media pipeline can be shared", DEFAULT_SHARED,
90           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
91
92   g_object_class_install_property (gobject_class, PROP_REUSABLE,
93       g_param_spec_boolean ("reusable", "Reusable",
94           "If this media pipeline can be reused after an unprepare",
95           DEFAULT_REUSABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
96
97   g_object_class_install_property (gobject_class, PROP_PROTOCOLS,
98       g_param_spec_flags ("protocols", "Protocols",
99           "Allowed lower transport protocols", GST_TYPE_RTSP_LOWER_TRANS,
100           DEFAULT_PROTOCOLS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
101
102   g_object_class_install_property (gobject_class, PROP_EOS_SHUTDOWN,
103       g_param_spec_boolean ("eos-shutdown", "EOS Shutdown",
104           "Send an EOS event to the pipeline before unpreparing",
105           DEFAULT_EOS_SHUTDOWN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
106
107   g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE,
108       g_param_spec_uint ("buffer-size", "Buffer Size",
109           "The kernel UDP buffer size to use", 0, G_MAXUINT,
110           DEFAULT_BUFFER_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
111
112   gst_rtsp_media_signals[SIGNAL_PREPARED] =
113       g_signal_new ("prepared", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
114       G_STRUCT_OFFSET (GstRTSPMediaClass, prepared), NULL, NULL,
115       g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
116
117   gst_rtsp_media_signals[SIGNAL_UNPREPARED] =
118       g_signal_new ("unprepared", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
119       G_STRUCT_OFFSET (GstRTSPMediaClass, unprepared), NULL, NULL,
120       g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
121
122   gst_rtsp_media_signals[SIGNAL_NEW_STATE] =
123       g_signal_new ("new-state", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
124       G_STRUCT_OFFSET (GstRTSPMediaClass, new_state), NULL, NULL,
125       g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 0, G_TYPE_INT);
126
127   klass->context = g_main_context_new ();
128   klass->loop = g_main_loop_new (klass->context, TRUE);
129
130   GST_DEBUG_CATEGORY_INIT (rtsp_media_debug, "rtspmedia", 0, "GstRTSPMedia");
131
132   klass->thread = g_thread_new ("Bus Thread", (GThreadFunc) do_loop, klass);
133
134   klass->handle_message = default_handle_message;
135   klass->unprepare = default_unprepare;
136 }
137
138 static void
139 gst_rtsp_media_init (GstRTSPMedia * media)
140 {
141   media->streams = g_ptr_array_new_with_free_func (g_object_unref);
142   g_mutex_init (&media->lock);
143   g_cond_init (&media->cond);
144   g_rec_mutex_init (&media->state_lock);
145
146   media->shared = DEFAULT_SHARED;
147   media->reusable = DEFAULT_REUSABLE;
148   media->protocols = DEFAULT_PROTOCOLS;
149   media->eos_shutdown = DEFAULT_EOS_SHUTDOWN;
150   media->buffer_size = DEFAULT_BUFFER_SIZE;
151 }
152
153 static void
154 gst_rtsp_media_finalize (GObject * obj)
155 {
156   GstRTSPMedia *media;
157
158   media = GST_RTSP_MEDIA (obj);
159
160   GST_INFO ("finalize media %p", media);
161
162   gst_rtsp_media_unprepare (media);
163
164   g_ptr_array_unref (media->streams);
165
166   g_list_free_full (media->dynamic, gst_object_unref);
167
168   if (media->source) {
169     g_source_destroy (media->source);
170     g_source_unref (media->source);
171   }
172   if (media->auth)
173     g_object_unref (media->auth);
174   if (media->pool)
175     g_object_unref (media->pool);
176   g_mutex_clear (&media->lock);
177   g_cond_clear (&media->cond);
178   g_rec_mutex_clear (&media->state_lock);
179
180   G_OBJECT_CLASS (gst_rtsp_media_parent_class)->finalize (obj);
181 }
182
183 static void
184 gst_rtsp_media_get_property (GObject * object, guint propid,
185     GValue * value, GParamSpec * pspec)
186 {
187   GstRTSPMedia *media = GST_RTSP_MEDIA (object);
188
189   switch (propid) {
190     case PROP_SHARED:
191       g_value_set_boolean (value, gst_rtsp_media_is_shared (media));
192       break;
193     case PROP_REUSABLE:
194       g_value_set_boolean (value, gst_rtsp_media_is_reusable (media));
195       break;
196     case PROP_PROTOCOLS:
197       g_value_set_flags (value, gst_rtsp_media_get_protocols (media));
198       break;
199     case PROP_EOS_SHUTDOWN:
200       g_value_set_boolean (value, gst_rtsp_media_is_eos_shutdown (media));
201       break;
202     case PROP_BUFFER_SIZE:
203       g_value_set_uint (value, gst_rtsp_media_get_buffer_size (media));
204       break;
205     default:
206       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
207   }
208 }
209
210 static void
211 gst_rtsp_media_set_property (GObject * object, guint propid,
212     const GValue * value, GParamSpec * pspec)
213 {
214   GstRTSPMedia *media = GST_RTSP_MEDIA (object);
215
216   switch (propid) {
217     case PROP_SHARED:
218       gst_rtsp_media_set_shared (media, g_value_get_boolean (value));
219       break;
220     case PROP_REUSABLE:
221       gst_rtsp_media_set_reusable (media, g_value_get_boolean (value));
222       break;
223     case PROP_PROTOCOLS:
224       gst_rtsp_media_set_protocols (media, g_value_get_flags (value));
225       break;
226     case PROP_EOS_SHUTDOWN:
227       gst_rtsp_media_set_eos_shutdown (media, g_value_get_boolean (value));
228       break;
229     case PROP_BUFFER_SIZE:
230       gst_rtsp_media_set_buffer_size (media, g_value_get_uint (value));
231       break;
232     default:
233       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
234   }
235 }
236
237 static gpointer
238 do_loop (GstRTSPMediaClass * klass)
239 {
240   GST_INFO ("enter mainloop");
241   g_main_loop_run (klass->loop);
242   GST_INFO ("exit mainloop");
243
244   return NULL;
245 }
246
247 /* must be called with state lock */
248 static void
249 collect_media_stats (GstRTSPMedia * media)
250 {
251   gint64 position, duration;
252
253   media->range.unit = GST_RTSP_RANGE_NPT;
254
255   GST_INFO ("collect media stats");
256
257   if (media->is_live) {
258     media->range.min.type = GST_RTSP_TIME_NOW;
259     media->range.min.seconds = -1;
260     media->range.max.type = GST_RTSP_TIME_END;
261     media->range.max.seconds = -1;
262   } else {
263     /* get the position */
264     if (!gst_element_query_position (media->pipeline, GST_FORMAT_TIME,
265             &position)) {
266       GST_INFO ("position query failed");
267       position = 0;
268     }
269
270     /* get the duration */
271     if (!gst_element_query_duration (media->pipeline, GST_FORMAT_TIME,
272             &duration)) {
273       GST_INFO ("duration query failed");
274       duration = -1;
275     }
276
277     GST_INFO ("stats: position %" GST_TIME_FORMAT ", duration %"
278         GST_TIME_FORMAT, GST_TIME_ARGS (position), GST_TIME_ARGS (duration));
279
280     if (position == -1) {
281       media->range.min.type = GST_RTSP_TIME_NOW;
282       media->range.min.seconds = -1;
283     } else {
284       media->range.min.type = GST_RTSP_TIME_SECONDS;
285       media->range.min.seconds = ((gdouble) position) / GST_SECOND;
286     }
287     if (duration == -1) {
288       media->range.max.type = GST_RTSP_TIME_END;
289       media->range.max.seconds = -1;
290     } else {
291       media->range.max.type = GST_RTSP_TIME_SECONDS;
292       media->range.max.seconds = ((gdouble) duration) / GST_SECOND;
293     }
294   }
295 }
296
297 /**
298  * gst_rtsp_media_new:
299  *
300  * Create a new #GstRTSPMedia instance. The #GstRTSPMedia object contains the
301  * element to produce RTP data for one or more related (audio/video/..)
302  * streams.
303  *
304  * Returns: a new #GstRTSPMedia object.
305  */
306 GstRTSPMedia *
307 gst_rtsp_media_new (void)
308 {
309   GstRTSPMedia *result;
310
311   result = g_object_new (GST_TYPE_RTSP_MEDIA, NULL);
312
313   return result;
314 }
315
316 /**
317  * gst_rtsp_media_set_shared:
318  * @media: a #GstRTSPMedia
319  * @shared: the new value
320  *
321  * Set or unset if the pipeline for @media can be shared will multiple clients.
322  * When @shared is %TRUE, client requests for this media will share the media
323  * pipeline.
324  */
325 void
326 gst_rtsp_media_set_shared (GstRTSPMedia * media, gboolean shared)
327 {
328   g_return_if_fail (GST_IS_RTSP_MEDIA (media));
329
330   g_mutex_lock (&media->lock);
331   media->shared = shared;
332   g_mutex_unlock (&media->lock);
333 }
334
335 /**
336  * gst_rtsp_media_is_shared:
337  * @media: a #GstRTSPMedia
338  *
339  * Check if the pipeline for @media can be shared between multiple clients.
340  *
341  * Returns: %TRUE if the media can be shared between clients.
342  */
343 gboolean
344 gst_rtsp_media_is_shared (GstRTSPMedia * media)
345 {
346   gboolean res;
347
348   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
349
350   g_mutex_lock (&media->lock);
351   res = media->shared;
352   g_mutex_unlock (&media->lock);
353
354   return res;
355 }
356
357 /**
358  * gst_rtsp_media_set_reusable:
359  * @media: a #GstRTSPMedia
360  * @reusable: the new value
361  *
362  * Set or unset if the pipeline for @media can be reused after the pipeline has
363  * been unprepared.
364  */
365 void
366 gst_rtsp_media_set_reusable (GstRTSPMedia * media, gboolean reusable)
367 {
368   g_return_if_fail (GST_IS_RTSP_MEDIA (media));
369
370   g_mutex_lock (&media->lock);
371   media->reusable = reusable;
372   g_mutex_unlock (&media->lock);
373 }
374
375 /**
376  * gst_rtsp_media_is_reusable:
377  * @media: a #GstRTSPMedia
378  *
379  * Check if the pipeline for @media can be reused after an unprepare.
380  *
381  * Returns: %TRUE if the media can be reused
382  */
383 gboolean
384 gst_rtsp_media_is_reusable (GstRTSPMedia * media)
385 {
386   gboolean res;
387
388   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
389
390   g_mutex_lock (&media->lock);
391   res = media->reusable;
392   g_mutex_unlock (&media->lock);
393
394   return res;
395 }
396
397 /**
398  * gst_rtsp_media_set_protocols:
399  * @media: a #GstRTSPMedia
400  * @protocols: the new flags
401  *
402  * Configure the allowed lower transport for @media.
403  */
404 void
405 gst_rtsp_media_set_protocols (GstRTSPMedia * media, GstRTSPLowerTrans protocols)
406 {
407   g_return_if_fail (GST_IS_RTSP_MEDIA (media));
408
409   g_mutex_lock (&media->lock);
410   media->protocols = protocols;
411   g_mutex_unlock (&media->lock);
412 }
413
414 /**
415  * gst_rtsp_media_get_protocols:
416  * @media: a #GstRTSPMedia
417  *
418  * Get the allowed protocols of @media.
419  *
420  * Returns: a #GstRTSPLowerTrans
421  */
422 GstRTSPLowerTrans
423 gst_rtsp_media_get_protocols (GstRTSPMedia * media)
424 {
425   GstRTSPLowerTrans res;
426
427   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media),
428       GST_RTSP_LOWER_TRANS_UNKNOWN);
429
430   g_mutex_lock (&media->lock);
431   res = media->protocols;
432   g_mutex_unlock (&media->lock);
433
434   return res;
435 }
436
437 /**
438  * gst_rtsp_media_set_eos_shutdown:
439  * @media: a #GstRTSPMedia
440  * @eos_shutdown: the new value
441  *
442  * Set or unset if an EOS event will be sent to the pipeline for @media before
443  * it is unprepared.
444  */
445 void
446 gst_rtsp_media_set_eos_shutdown (GstRTSPMedia * media, gboolean eos_shutdown)
447 {
448   g_return_if_fail (GST_IS_RTSP_MEDIA (media));
449
450   g_mutex_lock (&media->lock);
451   media->eos_shutdown = eos_shutdown;
452   g_mutex_unlock (&media->lock);
453 }
454
455 /**
456  * gst_rtsp_media_is_eos_shutdown:
457  * @media: a #GstRTSPMedia
458  *
459  * Check if the pipeline for @media will send an EOS down the pipeline before
460  * unpreparing.
461  *
462  * Returns: %TRUE if the media will send EOS before unpreparing.
463  */
464 gboolean
465 gst_rtsp_media_is_eos_shutdown (GstRTSPMedia * media)
466 {
467   gboolean res;
468
469   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
470
471   g_mutex_lock (&media->lock);
472   res = media->eos_shutdown;
473   g_mutex_unlock (&media->lock);
474
475   return res;
476 }
477
478 /**
479  * gst_rtsp_media_set_buffer_size:
480  * @media: a #GstRTSPMedia
481  * @size: the new value
482  *
483  * Set the kernel UDP buffer size.
484  */
485 void
486 gst_rtsp_media_set_buffer_size (GstRTSPMedia * media, guint size)
487 {
488   g_return_if_fail (GST_IS_RTSP_MEDIA (media));
489
490   g_mutex_lock (&media->lock);
491   media->buffer_size = size;
492   g_mutex_unlock (&media->lock);
493 }
494
495 /**
496  * gst_rtsp_media_get_buffer_size:
497  * @media: a #GstRTSPMedia
498  *
499  * Get the kernel UDP buffer size.
500  *
501  * Returns: the kernel UDP buffer size.
502  */
503 guint
504 gst_rtsp_media_get_buffer_size (GstRTSPMedia * media)
505 {
506   guint res;
507
508   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
509
510   g_mutex_unlock (&media->lock);
511   res = media->buffer_size;
512   g_mutex_unlock (&media->lock);
513
514   return res;
515 }
516
517 /**
518  * gst_rtsp_media_set_auth:
519  * @media: a #GstRTSPMedia
520  * @auth: a #GstRTSPAuth
521  *
522  * configure @auth to be used as the authentication manager of @media.
523  */
524 void
525 gst_rtsp_media_set_auth (GstRTSPMedia * media, GstRTSPAuth * auth)
526 {
527   GstRTSPAuth *old;
528
529   g_return_if_fail (GST_IS_RTSP_MEDIA (media));
530
531   g_mutex_lock (&media->lock);
532   if ((old = media->auth) != auth)
533     media->auth = auth ? g_object_ref (auth) : NULL;
534   else
535     old = NULL;
536   g_mutex_unlock (&media->lock);
537
538   if (old)
539     g_object_unref (old);
540 }
541
542 /**
543  * gst_rtsp_media_get_auth:
544  * @media: a #GstRTSPMedia
545  *
546  * Get the #GstRTSPAuth used as the authentication manager of @media.
547  *
548  * Returns: (transfer full): the #GstRTSPAuth of @media. g_object_unref() after
549  * usage.
550  */
551 GstRTSPAuth *
552 gst_rtsp_media_get_auth (GstRTSPMedia * media)
553 {
554   GstRTSPAuth *result;
555
556   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
557
558   g_mutex_lock (&media->lock);
559   if ((result = media->auth))
560     g_object_ref (result);
561   g_mutex_unlock (&media->lock);
562
563   return result;
564 }
565
566 /**
567  * gst_rtsp_media_set_address_pool:
568  * @media: a #GstRTSPMedia
569  * @pool: a #GstRTSPAddressPool
570  *
571  * configure @pool to be used as the address pool of @media.
572  */
573 void
574 gst_rtsp_media_set_address_pool (GstRTSPMedia * media,
575     GstRTSPAddressPool * pool)
576 {
577   GstRTSPAddressPool *old;
578
579   g_return_if_fail (GST_IS_RTSP_MEDIA (media));
580
581   g_mutex_lock (&media->lock);
582   if ((old = media->pool) != pool)
583     media->pool = pool ? g_object_ref (pool) : NULL;
584   else
585     old = NULL;
586   g_mutex_unlock (&media->lock);
587
588   if (old)
589     g_object_unref (old);
590 }
591
592 /**
593  * gst_rtsp_media_get_address_pool:
594  * @media: a #GstRTSPMedia
595  *
596  * Get the #GstRTSPAddressPool used as the address pool of @media.
597  *
598  * Returns: (transfer full): the #GstRTSPAddressPool of @media. g_object_unref() after
599  * usage.
600  */
601 GstRTSPAddressPool *
602 gst_rtsp_media_get_address_pool (GstRTSPMedia * media)
603 {
604   GstRTSPAddressPool *result;
605
606   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
607
608   g_mutex_lock (&media->lock);
609   if ((result = media->pool))
610     g_object_ref (result);
611   g_mutex_unlock (&media->lock);
612
613   return result;
614 }
615
616 /**
617  * gst_rtsp_media_collect_streams:
618  * @media: a #GstRTSPMedia
619  *
620  * Find all payloader elements, they should be named pay%d in the
621  * element of @media, and create #GstRTSPStreams for them.
622  *
623  * Collect all dynamic elements, named dynpay%d, and add them to
624  * the list of dynamic elements.
625  */
626 void
627 gst_rtsp_media_collect_streams (GstRTSPMedia * media)
628 {
629   GstElement *element, *elem;
630   GstPad *pad;
631   gint i;
632   gboolean have_elem;
633
634   g_return_if_fail (GST_IS_RTSP_MEDIA (media));
635
636   element = media->element;
637
638   have_elem = TRUE;
639   for (i = 0; have_elem; i++) {
640     gchar *name;
641
642     have_elem = FALSE;
643
644     name = g_strdup_printf ("pay%d", i);
645     if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
646       GST_INFO ("found stream %d with payloader %p", i, elem);
647
648       /* take the pad of the payloader */
649       pad = gst_element_get_static_pad (elem, "src");
650       /* create the stream */
651       gst_rtsp_media_create_stream (media, elem, pad);
652       g_object_unref (pad);
653
654       gst_object_unref (elem);
655
656       have_elem = TRUE;
657     }
658     g_free (name);
659
660     name = g_strdup_printf ("dynpay%d", i);
661     if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
662       /* a stream that will dynamically create pads to provide RTP packets */
663
664       GST_INFO ("found dynamic element %d, %p", i, elem);
665
666       g_mutex_lock (&media->lock);
667       media->dynamic = g_list_prepend (media->dynamic, elem);
668       g_mutex_unlock (&media->lock);
669
670       have_elem = TRUE;
671     }
672     g_free (name);
673   }
674 }
675
676 /**
677  * gst_rtsp_media_create_stream:
678  * @media: a #GstRTSPMedia
679  * @payloader: a #GstElement
680  * @srcpad: a source #GstPad
681  *
682  * Create a new stream in @media that provides RTP data on @srcpad.
683  * @srcpad should be a pad of an element inside @media->element.
684  *
685  * Returns: (transfer none): a new #GstRTSPStream that remains valid for as long
686  *          as @media exists.
687  */
688 GstRTSPStream *
689 gst_rtsp_media_create_stream (GstRTSPMedia * media, GstElement * payloader,
690     GstPad * pad)
691 {
692   GstRTSPStream *stream;
693   GstPad *srcpad;
694   gchar *name;
695   gint idx;
696
697   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
698   g_return_val_if_fail (GST_IS_ELEMENT (payloader), NULL);
699   g_return_val_if_fail (GST_IS_PAD (pad), NULL);
700   g_return_val_if_fail (GST_PAD_IS_SRC (pad), NULL);
701
702   g_mutex_lock (&media->lock);
703   idx = media->streams->len;
704
705   name = g_strdup_printf ("src_%u", idx);
706   srcpad = gst_ghost_pad_new (name, pad);
707   gst_pad_set_active (srcpad, TRUE);
708   gst_element_add_pad (media->element, srcpad);
709   g_free (name);
710
711   stream = gst_rtsp_stream_new (idx, payloader, srcpad);
712   if (media->pool)
713     gst_rtsp_stream_set_address_pool (stream, media->pool);
714
715   g_ptr_array_add (media->streams, stream);
716   g_mutex_unlock (&media->lock);
717
718   return stream;
719 }
720
721 /**
722  * gst_rtsp_media_n_streams:
723  * @media: a #GstRTSPMedia
724  *
725  * Get the number of streams in this media.
726  *
727  * Returns: The number of streams.
728  */
729 guint
730 gst_rtsp_media_n_streams (GstRTSPMedia * media)
731 {
732   guint res;
733
734   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), 0);
735
736   g_mutex_lock (&media->lock);
737   res = media->streams->len;
738   g_mutex_unlock (&media->lock);
739
740   return res;
741 }
742
743 /**
744  * gst_rtsp_media_get_stream:
745  * @media: a #GstRTSPMedia
746  * @idx: the stream index
747  *
748  * Retrieve the stream with index @idx from @media.
749  *
750  * Returns: (transfer none): the #GstRTSPStream at index @idx or %NULL when a stream with
751  * that index did not exist.
752  */
753 GstRTSPStream *
754 gst_rtsp_media_get_stream (GstRTSPMedia * media, guint idx)
755 {
756   GstRTSPStream *res;
757
758   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
759
760   g_mutex_lock (&media->lock);
761   if (idx < media->streams->len)
762     res = g_ptr_array_index (media->streams, idx);
763   else
764     res = NULL;
765   g_mutex_unlock (&media->lock);
766
767   return res;
768 }
769
770 /**
771  * gst_rtsp_media_get_range_string:
772  * @media: a #GstRTSPMedia
773  * @play: for the PLAY request
774  *
775  * Get the current range as a string.
776  *
777  * Returns: The range as a string, g_free() after usage.
778  */
779 gchar *
780 gst_rtsp_media_get_range_string (GstRTSPMedia * media, gboolean play)
781 {
782   gchar *result;
783   GstRTSPTimeRange range;
784
785   g_mutex_lock (&media->lock);
786   /* make copy */
787   range = media->range;
788
789   if (!play && media->n_active > 0) {
790     range.min.type = GST_RTSP_TIME_NOW;
791     range.min.seconds = -1;
792   }
793   g_mutex_unlock (&media->lock);
794
795   result = gst_rtsp_range_to_string (&range);
796
797   return result;
798 }
799
800 /**
801  * gst_rtsp_media_seek:
802  * @media: a #GstRTSPMedia
803  * @range: a #GstRTSPTimeRange
804  *
805  * Seek the pipeline to @range.
806  *
807  * Returns: %TRUE on success.
808  */
809 gboolean
810 gst_rtsp_media_seek (GstRTSPMedia * media, GstRTSPTimeRange * range)
811 {
812   GstSeekFlags flags;
813   gboolean res;
814   gint64 start, stop;
815   GstSeekType start_type, stop_type;
816
817   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
818   g_return_val_if_fail (range != NULL, FALSE);
819
820   g_rec_mutex_lock (&media->state_lock);
821   if (!media->seekable)
822     goto not_seekable;
823
824   if (range->unit != GST_RTSP_RANGE_NPT)
825     goto not_supported;
826
827   /* depends on the current playing state of the pipeline. We might need to
828    * queue this until we get EOS. */
829   flags = GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_KEY_UNIT;
830
831   start_type = stop_type = GST_SEEK_TYPE_NONE;
832
833   switch (range->min.type) {
834     case GST_RTSP_TIME_NOW:
835       start = -1;
836       break;
837     case GST_RTSP_TIME_SECONDS:
838       /* only seek when something changed */
839       if (media->range.min.seconds == range->min.seconds) {
840         start = -1;
841       } else {
842         start = range->min.seconds * GST_SECOND;
843         start_type = GST_SEEK_TYPE_SET;
844       }
845       break;
846     case GST_RTSP_TIME_END:
847     default:
848       goto weird_type;
849   }
850   switch (range->max.type) {
851     case GST_RTSP_TIME_SECONDS:
852       /* only seek when something changed */
853       if (media->range.max.seconds == range->max.seconds) {
854         stop = -1;
855       } else {
856         stop = range->max.seconds * GST_SECOND;
857         stop_type = GST_SEEK_TYPE_SET;
858       }
859       break;
860     case GST_RTSP_TIME_END:
861       stop = -1;
862       stop_type = GST_SEEK_TYPE_SET;
863       break;
864     case GST_RTSP_TIME_NOW:
865     default:
866       goto weird_type;
867   }
868
869   if (start != -1 || stop != -1) {
870     GST_INFO ("seeking to %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
871         GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
872
873     res = gst_element_seek (media->pipeline, 1.0, GST_FORMAT_TIME,
874         flags, start_type, start, stop_type, stop);
875
876     /* and block for the seek to complete */
877     GST_INFO ("done seeking %d", res);
878     gst_element_get_state (media->pipeline, NULL, NULL, -1);
879     GST_INFO ("prerolled again");
880
881     collect_media_stats (media);
882   } else {
883     GST_INFO ("no seek needed");
884     res = TRUE;
885   }
886   g_rec_mutex_unlock (&media->state_lock);
887
888   return res;
889
890   /* ERRORS */
891 not_seekable:
892   {
893     g_rec_mutex_unlock (&media->state_lock);
894     GST_INFO ("pipeline is not seekable");
895     return TRUE;
896   }
897 not_supported:
898   {
899     g_rec_mutex_unlock (&media->state_lock);
900     GST_WARNING ("seek unit %d not supported", range->unit);
901     return FALSE;
902   }
903 weird_type:
904   {
905     g_rec_mutex_unlock (&media->state_lock);
906     GST_WARNING ("weird range type %d not supported", range->min.type);
907     return FALSE;
908   }
909 }
910
911 static void
912 gst_rtsp_media_set_status (GstRTSPMedia * media, GstRTSPMediaStatus status)
913 {
914   g_mutex_lock (&media->lock);
915   /* never overwrite the error status */
916   if (media->status != GST_RTSP_MEDIA_STATUS_ERROR)
917     media->status = status;
918   GST_DEBUG ("setting new status to %d", status);
919   g_cond_broadcast (&media->cond);
920   g_mutex_unlock (&media->lock);
921 }
922
923 static GstRTSPMediaStatus
924 gst_rtsp_media_get_status (GstRTSPMedia * media)
925 {
926   GstRTSPMediaStatus result;
927   gint64 end_time;
928
929   g_mutex_lock (&media->lock);
930   end_time = g_get_monotonic_time () + 20 * G_TIME_SPAN_SECOND;
931   /* while we are preparing, wait */
932   while (media->status == GST_RTSP_MEDIA_STATUS_PREPARING) {
933     GST_DEBUG ("waiting for status change");
934     if (!g_cond_wait_until (&media->cond, &media->lock, end_time)) {
935       GST_DEBUG ("timeout, assuming error status");
936       media->status = GST_RTSP_MEDIA_STATUS_ERROR;
937     }
938   }
939   /* could be success or error */
940   result = media->status;
941   GST_DEBUG ("got status %d", result);
942   g_mutex_unlock (&media->lock);
943
944   return result;
945 }
946
947 /* called with state-lock */
948 static gboolean
949 default_handle_message (GstRTSPMedia * media, GstMessage * message)
950 {
951   GstMessageType type;
952
953   type = GST_MESSAGE_TYPE (message);
954
955   switch (type) {
956     case GST_MESSAGE_STATE_CHANGED:
957       break;
958     case GST_MESSAGE_BUFFERING:
959     {
960       gint percent;
961
962       gst_message_parse_buffering (message, &percent);
963
964       /* no state management needed for live pipelines */
965       if (media->is_live)
966         break;
967
968       if (percent == 100) {
969         /* a 100% message means buffering is done */
970         media->buffering = FALSE;
971         /* if the desired state is playing, go back */
972         if (media->target_state == GST_STATE_PLAYING) {
973           GST_INFO ("Buffering done, setting pipeline to PLAYING");
974           gst_element_set_state (media->pipeline, GST_STATE_PLAYING);
975         } else {
976           GST_INFO ("Buffering done");
977         }
978       } else {
979         /* buffering busy */
980         if (media->buffering == FALSE) {
981           if (media->target_state == GST_STATE_PLAYING) {
982             /* we were not buffering but PLAYING, PAUSE  the pipeline. */
983             GST_INFO ("Buffering, setting pipeline to PAUSED ...");
984             gst_element_set_state (media->pipeline, GST_STATE_PAUSED);
985           } else {
986             GST_INFO ("Buffering ...");
987           }
988         }
989         media->buffering = TRUE;
990       }
991       break;
992     }
993     case GST_MESSAGE_LATENCY:
994     {
995       gst_bin_recalculate_latency (GST_BIN_CAST (media->pipeline));
996       break;
997     }
998     case GST_MESSAGE_ERROR:
999     {
1000       GError *gerror;
1001       gchar *debug;
1002
1003       gst_message_parse_error (message, &gerror, &debug);
1004       GST_WARNING ("%p: got error %s (%s)", media, gerror->message, debug);
1005       g_error_free (gerror);
1006       g_free (debug);
1007
1008       gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_ERROR);
1009       break;
1010     }
1011     case GST_MESSAGE_WARNING:
1012     {
1013       GError *gerror;
1014       gchar *debug;
1015
1016       gst_message_parse_warning (message, &gerror, &debug);
1017       GST_WARNING ("%p: got warning %s (%s)", media, gerror->message, debug);
1018       g_error_free (gerror);
1019       g_free (debug);
1020       break;
1021     }
1022     case GST_MESSAGE_ELEMENT:
1023       break;
1024     case GST_MESSAGE_STREAM_STATUS:
1025       break;
1026     case GST_MESSAGE_ASYNC_DONE:
1027       if (!media->adding) {
1028         /* when we are dynamically adding pads, the addition of the udpsrc will
1029          * temporarily produce ASYNC_DONE messages. We have to ignore them and
1030          * wait for the final ASYNC_DONE after everything prerolled */
1031         GST_INFO ("%p: got ASYNC_DONE", media);
1032         collect_media_stats (media);
1033
1034         gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARED);
1035       } else {
1036         GST_INFO ("%p: ignoring ASYNC_DONE", media);
1037       }
1038       break;
1039     case GST_MESSAGE_EOS:
1040       GST_INFO ("%p: got EOS", media);
1041
1042       if (media->status == GST_RTSP_MEDIA_STATUS_UNPREPARING) {
1043         GST_DEBUG ("shutting down after EOS");
1044         finish_unprepare (media);
1045         g_object_unref (media);
1046       }
1047       break;
1048     default:
1049       GST_INFO ("%p: got message type %s", media,
1050           gst_message_type_get_name (type));
1051       break;
1052   }
1053   return TRUE;
1054 }
1055
1056 static gboolean
1057 bus_message (GstBus * bus, GstMessage * message, GstRTSPMedia * media)
1058 {
1059   GstRTSPMediaClass *klass;
1060   gboolean ret;
1061
1062   klass = GST_RTSP_MEDIA_GET_CLASS (media);
1063
1064   g_rec_mutex_lock (&media->state_lock);
1065   if (klass->handle_message)
1066     ret = klass->handle_message (media, message);
1067   else
1068     ret = FALSE;
1069   g_rec_mutex_unlock (&media->state_lock);
1070
1071   return ret;
1072 }
1073
1074 /* called from streaming threads */
1075 static void
1076 pad_added_cb (GstElement * element, GstPad * pad, GstRTSPMedia * media)
1077 {
1078   GstRTSPStream *stream;
1079
1080   /* FIXME, element is likely not a payloader, find the payloader here */
1081   stream = gst_rtsp_media_create_stream (media, element, pad);
1082
1083   GST_INFO ("pad added %s:%s, stream %d", GST_DEBUG_PAD_NAME (pad),
1084       stream->idx);
1085
1086   g_rec_mutex_lock (&media->state_lock);
1087   /* we will be adding elements below that will cause ASYNC_DONE to be
1088    * posted in the bus. We want to ignore those messages until the
1089    * pipeline really prerolled. */
1090   media->adding = TRUE;
1091
1092   /* join the element in the PAUSED state because this callback is
1093    * called from the streaming thread and it is PAUSED */
1094   gst_rtsp_stream_join_bin (stream, GST_BIN (media->pipeline),
1095       media->rtpbin, GST_STATE_PAUSED);
1096
1097   media->adding = FALSE;
1098   g_rec_mutex_unlock (&media->state_lock);
1099 }
1100
1101 static void
1102 no_more_pads_cb (GstElement * element, GstRTSPMedia * media)
1103 {
1104   GstElement *fakesink;
1105
1106   g_mutex_lock (&media->lock);
1107   GST_INFO ("no more pads");
1108   if ((fakesink = media->fakesink)) {
1109     gst_object_ref (fakesink);
1110     media->fakesink = NULL;
1111     g_mutex_unlock (&media->lock);
1112
1113     gst_bin_remove (GST_BIN (media->pipeline), fakesink);
1114     gst_element_set_state (fakesink, GST_STATE_NULL);
1115     gst_object_unref (fakesink);
1116     GST_INFO ("removed fakesink");
1117   }
1118 }
1119
1120 /**
1121  * gst_rtsp_media_prepare:
1122  * @media: a #GstRTSPMedia
1123  *
1124  * Prepare @media for streaming. This function will create the pipeline and
1125  * other objects to manage the streaming.
1126  *
1127  * It will preroll the pipeline and collect vital information about the streams
1128  * such as the duration.
1129  *
1130  * Returns: %TRUE on success.
1131  */
1132 gboolean
1133 gst_rtsp_media_prepare (GstRTSPMedia * media)
1134 {
1135   GstStateChangeReturn ret;
1136   GstRTSPMediaStatus status;
1137   guint i;
1138   GstRTSPMediaClass *klass;
1139   GstBus *bus;
1140   GList *walk;
1141
1142   g_rec_mutex_lock (&media->state_lock);
1143   if (media->status == GST_RTSP_MEDIA_STATUS_PREPARED)
1144     goto was_prepared;
1145
1146   if (media->status == GST_RTSP_MEDIA_STATUS_PREPARING)
1147     goto wait_status;
1148
1149   if (media->status != GST_RTSP_MEDIA_STATUS_UNPREPARED)
1150     goto not_unprepared;
1151
1152   if (!media->reusable && media->reused)
1153     goto is_reused;
1154
1155   media->rtpbin = gst_element_factory_make ("rtpbin", NULL);
1156   if (media->rtpbin == NULL)
1157     goto no_rtpbin;
1158
1159   GST_INFO ("preparing media %p", media);
1160
1161   /* reset some variables */
1162   media->is_live = FALSE;
1163   media->seekable = FALSE;
1164   media->buffering = FALSE;
1165   /* we're preparing now */
1166   media->status = GST_RTSP_MEDIA_STATUS_PREPARING;
1167
1168   bus = gst_pipeline_get_bus (GST_PIPELINE_CAST (media->pipeline));
1169
1170   /* add the pipeline bus to our custom mainloop */
1171   media->source = gst_bus_create_watch (bus);
1172   gst_object_unref (bus);
1173
1174   g_source_set_callback (media->source, (GSourceFunc) bus_message, media, NULL);
1175
1176   klass = GST_RTSP_MEDIA_GET_CLASS (media);
1177   media->id = g_source_attach (media->source, klass->context);
1178
1179   /* add stuff to the bin */
1180   gst_bin_add (GST_BIN (media->pipeline), media->rtpbin);
1181
1182   /* link streams we already have, other streams might appear when we have
1183    * dynamic elements */
1184   for (i = 0; i < media->streams->len; i++) {
1185     GstRTSPStream *stream;
1186
1187     stream = g_ptr_array_index (media->streams, i);
1188
1189     gst_rtsp_stream_join_bin (stream, GST_BIN (media->pipeline),
1190         media->rtpbin, GST_STATE_NULL);
1191   }
1192
1193   for (walk = media->dynamic; walk; walk = g_list_next (walk)) {
1194     GstElement *elem = walk->data;
1195
1196     GST_INFO ("adding callbacks for dynamic element %p", elem);
1197
1198     g_signal_connect (elem, "pad-added", (GCallback) pad_added_cb, media);
1199     g_signal_connect (elem, "no-more-pads", (GCallback) no_more_pads_cb, media);
1200
1201     /* we add a fakesink here in order to make the state change async. We remove
1202      * the fakesink again in the no-more-pads callback. */
1203     media->fakesink = gst_element_factory_make ("fakesink", "fakesink");
1204     gst_bin_add (GST_BIN (media->pipeline), media->fakesink);
1205   }
1206
1207   GST_INFO ("setting pipeline to PAUSED for media %p", media);
1208   /* first go to PAUSED */
1209   ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED);
1210   media->target_state = GST_STATE_PAUSED;
1211
1212   switch (ret) {
1213     case GST_STATE_CHANGE_SUCCESS:
1214       GST_INFO ("SUCCESS state change for media %p", media);
1215       media->seekable = TRUE;
1216       break;
1217     case GST_STATE_CHANGE_ASYNC:
1218       GST_INFO ("ASYNC state change for media %p", media);
1219       media->seekable = TRUE;
1220       break;
1221     case GST_STATE_CHANGE_NO_PREROLL:
1222       /* we need to go to PLAYING */
1223       GST_INFO ("NO_PREROLL state change: live media %p", media);
1224       /* FIXME we disable seeking for live streams for now. We should perform a
1225        * seeking query in preroll instead */
1226       media->seekable = FALSE;
1227       media->is_live = TRUE;
1228       ret = gst_element_set_state (media->pipeline, GST_STATE_PLAYING);
1229       if (ret == GST_STATE_CHANGE_FAILURE)
1230         goto state_failed;
1231       break;
1232     case GST_STATE_CHANGE_FAILURE:
1233       goto state_failed;
1234   }
1235 wait_status:
1236   g_rec_mutex_unlock (&media->state_lock);
1237
1238   /* now wait for all pads to be prerolled, FIXME, we should somehow be
1239    * able to do this async so that we don't block the server thread. */
1240   status = gst_rtsp_media_get_status (media);
1241   if (status == GST_RTSP_MEDIA_STATUS_ERROR)
1242     goto state_failed;
1243
1244   g_signal_emit (media, gst_rtsp_media_signals[SIGNAL_PREPARED], 0, NULL);
1245
1246   GST_INFO ("object %p is prerolled", media);
1247
1248   return TRUE;
1249
1250   /* OK */
1251 was_prepared:
1252   {
1253     GST_LOG ("media %p was prepared", media);
1254     g_rec_mutex_unlock (&media->state_lock);
1255     return TRUE;
1256   }
1257   /* ERRORS */
1258 not_unprepared:
1259   {
1260     GST_WARNING ("media %p was not unprepared", media);
1261     g_rec_mutex_unlock (&media->state_lock);
1262     return FALSE;
1263   }
1264 is_reused:
1265   {
1266     g_rec_mutex_unlock (&media->state_lock);
1267     GST_WARNING ("can not reuse media %p", media);
1268     return FALSE;
1269   }
1270 no_rtpbin:
1271   {
1272     g_rec_mutex_unlock (&media->state_lock);
1273     GST_WARNING ("no rtpbin element");
1274     g_warning ("failed to create element 'rtpbin', check your installation");
1275     return FALSE;
1276   }
1277 state_failed:
1278   {
1279     GST_WARNING ("failed to preroll pipeline");
1280     gst_rtsp_media_unprepare (media);
1281     g_rec_mutex_unlock (&media->state_lock);
1282     return FALSE;
1283   }
1284 }
1285
1286 /* must be called with state-lock */
1287 static void
1288 finish_unprepare (GstRTSPMedia * media)
1289 {
1290   gint i;
1291
1292   GST_DEBUG ("shutting down");
1293
1294   gst_element_set_state (media->pipeline, GST_STATE_NULL);
1295
1296   for (i = 0; i < media->streams->len; i++) {
1297     GstRTSPStream *stream;
1298
1299     GST_INFO ("Removing elements of stream %d from pipeline", i);
1300
1301     stream = g_ptr_array_index (media->streams, i);
1302
1303     gst_rtsp_stream_leave_bin (stream, GST_BIN (media->pipeline),
1304         media->rtpbin);
1305   }
1306   g_ptr_array_set_size (media->streams, 0);
1307
1308   gst_bin_remove (GST_BIN (media->pipeline), media->rtpbin);
1309   media->rtpbin = NULL;
1310
1311   gst_object_unref (media->pipeline);
1312   media->pipeline = NULL;
1313
1314   media->reused = TRUE;
1315   media->status = GST_RTSP_MEDIA_STATUS_UNPREPARED;
1316
1317   /* when the media is not reusable, this will effectively unref the media and
1318    * recreate it */
1319   g_signal_emit (media, gst_rtsp_media_signals[SIGNAL_UNPREPARED], 0, NULL);
1320 }
1321
1322 /* called with state-lock */
1323 static gboolean
1324 default_unprepare (GstRTSPMedia * media)
1325 {
1326   if (media->eos_shutdown) {
1327     GST_DEBUG ("sending EOS for shutdown");
1328     /* ref so that we don't disappear */
1329     g_object_ref (media);
1330     gst_element_send_event (media->pipeline, gst_event_new_eos ());
1331     /* we need to go to playing again for the EOS to propagate, normally in this
1332      * state, nothing is receiving data from us anymore so this is ok. */
1333     gst_element_set_state (media->pipeline, GST_STATE_PLAYING);
1334     media->status = GST_RTSP_MEDIA_STATUS_UNPREPARING;
1335   } else {
1336     finish_unprepare (media);
1337   }
1338   return TRUE;
1339 }
1340
1341 /**
1342  * gst_rtsp_media_unprepare:
1343  * @media: a #GstRTSPMedia
1344  *
1345  * Unprepare @media. After this call, the media should be prepared again before
1346  * it can be used again. If the media is set to be non-reusable, a new instance
1347  * must be created.
1348  *
1349  * Returns: %TRUE on success.
1350  */
1351 gboolean
1352 gst_rtsp_media_unprepare (GstRTSPMedia * media)
1353 {
1354   gboolean success;
1355
1356   g_rec_mutex_lock (&media->state_lock);
1357   if (media->status == GST_RTSP_MEDIA_STATUS_UNPREPARED)
1358     goto was_unprepared;
1359
1360   GST_INFO ("unprepare media %p", media);
1361   media->target_state = GST_STATE_NULL;
1362   success = TRUE;
1363
1364   if (media->status == GST_RTSP_MEDIA_STATUS_PREPARED) {
1365     GstRTSPMediaClass *klass;
1366
1367     klass = GST_RTSP_MEDIA_GET_CLASS (media);
1368     if (klass->unprepare)
1369       success = klass->unprepare (media);
1370   } else {
1371     finish_unprepare (media);
1372   }
1373   g_rec_mutex_unlock (&media->state_lock);
1374
1375   return success;
1376
1377 was_unprepared:
1378   {
1379     g_rec_mutex_unlock (&media->state_lock);
1380     GST_INFO ("media %p was already unprepared", media);
1381     return TRUE;
1382   }
1383 }
1384
1385 /**
1386  * gst_rtsp_media_set_state:
1387  * @media: a #GstRTSPMedia
1388  * @state: the target state of the media
1389  * @transports: a #GPtrArray of #GstRTSPStreamTransport pointers
1390  *
1391  * Set the state of @media to @state and for the transports in @transports.
1392  *
1393  * Returns: %TRUE on success.
1394  */
1395 gboolean
1396 gst_rtsp_media_set_state (GstRTSPMedia * media, GstState state,
1397     GPtrArray * transports)
1398 {
1399   gint i;
1400   gboolean add, remove, do_state;
1401   gint old_active;
1402
1403   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
1404   g_return_val_if_fail (transports != NULL, FALSE);
1405
1406   g_rec_mutex_lock (&media->state_lock);
1407
1408   /* NULL and READY are the same */
1409   if (state == GST_STATE_READY)
1410     state = GST_STATE_NULL;
1411
1412   add = remove = FALSE;
1413
1414   GST_INFO ("going to state %s media %p", gst_element_state_get_name (state),
1415       media);
1416
1417   switch (state) {
1418     case GST_STATE_NULL:
1419     case GST_STATE_PAUSED:
1420       /* we're going from PLAYING to PAUSED, READY or NULL, remove */
1421       if (media->target_state == GST_STATE_PLAYING)
1422         remove = TRUE;
1423       break;
1424     case GST_STATE_PLAYING:
1425       /* we're going to PLAYING, add */
1426       add = TRUE;
1427       break;
1428     default:
1429       break;
1430   }
1431   old_active = media->n_active;
1432
1433   for (i = 0; i < transports->len; i++) {
1434     GstRTSPStreamTransport *trans;
1435
1436     /* we need a non-NULL entry in the array */
1437     trans = g_ptr_array_index (transports, i);
1438     if (trans == NULL)
1439       continue;
1440
1441     /* we need a transport */
1442     if (!trans->transport)
1443       continue;
1444
1445     if (add) {
1446       if (gst_rtsp_stream_add_transport (trans->stream, trans))
1447         media->n_active++;
1448     } else if (remove) {
1449       if (gst_rtsp_stream_remove_transport (trans->stream, trans))
1450         media->n_active--;
1451     }
1452   }
1453
1454   /* we just added the first media, do the playing state change */
1455   if (old_active == 0 && add)
1456     do_state = TRUE;
1457   /* if we have no more active media, do the downward state changes */
1458   else if (media->n_active == 0)
1459     do_state = TRUE;
1460   else
1461     do_state = FALSE;
1462
1463   GST_INFO ("state %d active %d media %p do_state %d", state, media->n_active,
1464       media, do_state);
1465
1466   if (media->target_state != state) {
1467     if (do_state) {
1468       if (state == GST_STATE_NULL) {
1469         gst_rtsp_media_unprepare (media);
1470       } else {
1471         GST_INFO ("state %s media %p", gst_element_state_get_name (state),
1472             media);
1473         media->target_state = state;
1474         gst_element_set_state (media->pipeline, state);
1475       }
1476     }
1477     g_signal_emit (media, gst_rtsp_media_signals[SIGNAL_NEW_STATE], 0, state,
1478         NULL);
1479   }
1480
1481   /* remember where we are */
1482   if (state != GST_STATE_NULL && (state == GST_STATE_PAUSED ||
1483           old_active != media->n_active))
1484     collect_media_stats (media);
1485
1486   g_rec_mutex_unlock (&media->state_lock);
1487
1488   return TRUE;
1489 }