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