Remove if statement that is always true
[platform/upstream/gst-rtsp-server.git] / gst / rtsp-server / rtsp-media-ext.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  * SECTION:rtsp-media
21  * @short_description: The media pipeline
22  * @see_also: #GstRTSPMediaFactory, #GstRTSPStream, #GstRTSPSession,
23  *     #GstRTSPSessionMedia
24  *
25  * a #GstRTSPMedia contains the complete GStreamer pipeline to manage the
26  * streaming to the clients. The actual data transfer is done by the
27  * #GstRTSPStream objects that are created and exposed by the #GstRTSPMedia.
28  *
29  * The #GstRTSPMedia is usually created from a #GstRTSPMediaFactory when the
30  * client does a DESCRIBE or SETUP of a resource.
31  *
32  * A media is created with gst_rtsp_media_new() that takes the element that will
33  * provide the streaming elements. For each of the streams, a new #GstRTSPStream
34  * object needs to be made with the gst_rtsp_media_create_stream() which takes
35  * the payloader element and the source pad that produces the RTP stream.
36  *
37  * The pipeline of the media is set to PAUSED with gst_rtsp_media_prepare(). The
38  * prepare method will add rtpbin and sinks and sources to send and receive RTP
39  * and RTCP packets from the clients. Each stream srcpad is connected to an
40  * input into the internal rtpbin.
41  *
42  * It is also possible to dynamically create #GstRTSPStream objects during the
43  * prepare phase. With gst_rtsp_media_get_status() you can check the status of
44  * the prepare phase.
45  *
46  * After the media is prepared, it is ready for streaming. It will usually be
47  * managed in a session with gst_rtsp_session_manage_media(). See
48  * #GstRTSPSession and #GstRTSPSessionMedia.
49  *
50  * The state of the media can be controlled with gst_rtsp_media_set_state ().
51  * Seeking can be done with gst_rtsp_media_seek().
52  *
53  * With gst_rtsp_media_unprepare() the pipeline is stopped and shut down. When
54  * gst_rtsp_media_set_eos_shutdown() an EOS will be sent to the pipeline to
55  * cleanly shut down.
56  *
57  * With gst_rtsp_media_set_shared(), the media can be shared between multiple
58  * clients. With gst_rtsp_media_set_reusable() you can control if the pipeline
59  * can be prepared again after an unprepare.
60  *
61  * Last reviewed on 2013-07-11 (1.0.0)
62  */
63
64 #ifdef HAVE_CONFIG_H
65 #include "config.h"
66 #endif
67
68 #include <string.h>
69 #include <stdlib.h>
70
71 #include <gst/app/gstappsrc.h>
72 #include <gst/app/gstappsink.h>
73
74 #include "rtsp-media-ext.h"
75
76 #define GST_RTSP_MEDIA_EXT_GET_PRIVATE(obj)  \
77      (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_MEDIA_EXT, GstRTSPMediaExtPrivate))
78
79 #define RTP_RETRANS_PORT 19120
80
81 typedef struct _GstRTSPMediaExtRTPResender GstRTSPMediaExtRTPResender;
82
83 struct _GstRTSPMediaExtRTPResender
84 {
85   /* sinks used for sending and receiving RTP and RTCP over ipv4, they share
86    * sockets */
87   GstElement *udpsrc_v4;
88
89   /* for TCP transport */
90   GstElement *appsrc;
91   GstElement *funnel;
92   GstElement *resender;
93   GstElement *resend_sink;
94 };
95
96 struct _GstRTSPMediaExtPrivate
97 {
98   GMutex lock;
99   GstRTSPMediaExtMode mode;
100
101   GstRTSPMediaExtRTPResender rtp_resender;
102   GstElement *fecenc;
103   gboolean is_joined;
104
105   /* pads on the rtpbin */
106   GstPad *send_src;
107
108   guint retransmit_port;
109   guint max_size_k;
110   guint max_size_p;
111   GstRTSPMediaExtLatency latency_mode;
112 #ifdef FORCE_DROP
113   GstElement *identity;
114 #endif
115 };
116
117 GST_DEBUG_CATEGORY_STATIC (rtsp_media_ext_debug);
118 #define GST_CAT_DEFAULT rtsp_media_ext_debug
119
120 static void gst_rtsp_media_ext_get_property (GObject * object, guint propid,
121     GValue * value, GParamSpec * pspec);
122 static void gst_rtsp_media_ext_set_property (GObject * object, guint propid,
123     const GValue * value, GParamSpec * pspec);
124 static void gst_rtsp_media_ext_finalize (GObject * obj);
125
126 static void ext_preparing (GstRTSPMedia * media, GstRTSPStream * stream,
127     guint idx);
128 static void ext_unpreparing (GstRTSPMedia * media, GstRTSPStream * stream,
129     guint idx);
130
131 G_DEFINE_TYPE (GstRTSPMediaExt, gst_rtsp_media_ext, GST_TYPE_RTSP_MEDIA);
132
133 static void
134 gst_rtsp_media_ext_class_init (GstRTSPMediaExtClass * klass)
135 {
136   GObjectClass *gobject_class;
137   GstRTSPMediaClass *rtsp_media_class;
138
139   g_type_class_add_private (klass, sizeof (GstRTSPMediaExtPrivate));
140
141   gobject_class = G_OBJECT_CLASS (klass);
142   rtsp_media_class = GST_RTSP_MEDIA_CLASS (klass);
143
144   gobject_class->get_property = gst_rtsp_media_ext_get_property;
145   gobject_class->set_property = gst_rtsp_media_ext_set_property;
146   gobject_class->finalize = gst_rtsp_media_ext_finalize;
147
148   GST_DEBUG_CATEGORY_INIT (rtsp_media_ext_debug, "rtspmediaext", 0,
149       "GstRTSPMediaExt");
150
151   rtsp_media_class->preparing = ext_preparing;
152   rtsp_media_class->unpreparing = ext_unpreparing;
153 }
154
155 static void
156 gst_rtsp_media_ext_init (GstRTSPMediaExt * media)
157 {
158   GstRTSPMediaExtPrivate *priv = GST_RTSP_MEDIA_EXT_GET_PRIVATE (media);
159
160   media->priv = priv;
161   priv->is_joined = FALSE;
162   priv->mode = MEDIA_EXT_MODE_RESEND;
163   priv->retransmit_port = RTP_RETRANS_PORT;
164   priv->max_size_k = 10;
165   priv->max_size_p = 10;
166   priv->latency_mode = MEDIA_EXT_LATENCY_LOW;
167   memset (&priv->rtp_resender, 0x00, sizeof (GstRTSPMediaExtRTPResender));
168   g_mutex_init (&priv->lock);
169 }
170
171 static void
172 gst_rtsp_media_ext_finalize (GObject * obj)
173 {
174   GstRTSPMediaExtPrivate *priv;
175   GstRTSPMediaExt *media;
176
177   media = GST_RTSP_MEDIA_EXT (obj);
178   priv = media->priv;
179   g_mutex_clear (&priv->lock);
180
181   G_OBJECT_CLASS (gst_rtsp_media_ext_parent_class)->finalize (obj);
182 }
183
184 static void
185 gst_rtsp_media_ext_get_property (GObject * object, guint propid, GValue * value,
186     GParamSpec * pspec)
187 {
188 }
189
190 static void
191 gst_rtsp_media_ext_set_property (GObject * object, guint propid,
192     const GValue * value, GParamSpec * pspec)
193 {
194 }
195
196 GstRTSPMediaExt *
197 gst_rtsp_media_ext_new (GstElement * element)
198 {
199   GstRTSPMediaExt *result;
200
201   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
202
203   result = g_object_new (GST_TYPE_RTSP_MEDIA_EXT, "element", element, NULL);
204
205   return result;
206 }
207
208 static gint in_idle_probe = FALSE;
209
210 static gboolean
211 alloc_ports (GstRTSPMediaExt * media)
212 {
213   GstStateChangeReturn ret;
214   GstElement *udpsrc;
215   GstElement *udpsink;
216
217   gint tmp_feedback_rtcp;
218   gint feedback_rtcpport;
219
220   GInetAddress *inetaddr = NULL;
221   GSocketAddress *feedback_rtcp_sockaddr = NULL;
222   GSocket *feedback_rtp_socket;
223   GSocketFamily family = G_SOCKET_FAMILY_IPV4;
224   const gchar *sink_socket = "socket";
225   gchar *resend_uri = NULL;
226
227   GstRTSPMediaExtPrivate *priv;
228   priv = media->priv;
229
230   g_return_val_if_fail (priv != NULL, GST_PAD_PROBE_REMOVE);
231
232   udpsrc = NULL;
233   udpsink = NULL;
234
235   /* Start with random port */
236   tmp_feedback_rtcp = priv->retransmit_port + 1;
237
238   feedback_rtp_socket =
239       g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP,
240       NULL);
241
242   if (!feedback_rtp_socket)
243     goto no_udp_protocol;
244
245   inetaddr = g_inet_address_new_any (family);
246
247   feedback_rtcp_sockaddr =
248       g_inet_socket_address_new (inetaddr, tmp_feedback_rtcp);
249
250   g_object_unref (inetaddr);
251   inetaddr = NULL;
252
253   if (!g_socket_bind (feedback_rtp_socket, feedback_rtcp_sockaddr, FALSE, NULL)) {
254     g_object_unref (feedback_rtcp_sockaddr);
255     goto port_error;
256   }
257   g_object_unref (feedback_rtcp_sockaddr);
258
259   udpsrc = gst_element_factory_make ("udpsrc", NULL);
260
261   if (udpsrc == NULL)
262     goto no_udp_protocol;
263
264   g_object_set (G_OBJECT (udpsrc), "socket", feedback_rtp_socket, NULL);
265
266   ret = gst_element_set_state (udpsrc, GST_STATE_READY);
267   if (ret == GST_STATE_CHANGE_FAILURE)
268     goto element_error;
269
270   /* all fine, do port check */
271   g_object_get (G_OBJECT (udpsrc), "port", &feedback_rtcpport, NULL);
272
273   /* this should not happen... */
274   if (feedback_rtcpport != tmp_feedback_rtcp)
275     goto port_error;
276
277   resend_uri = g_strdup_printf ("udp://localhost:%d", priv->retransmit_port);
278   if (resend_uri) {
279     udpsink = gst_element_make_from_uri (GST_URI_SINK, resend_uri, NULL, NULL);
280     g_free (resend_uri);
281   }
282
283   if (!udpsink)
284     goto no_udp_protocol;
285
286   g_object_set (G_OBJECT (udpsink), "close-socket", FALSE, NULL);
287   g_object_set (G_OBJECT (udpsink), sink_socket, feedback_rtp_socket, NULL);
288   g_object_set (G_OBJECT (udpsink), "sync", FALSE, NULL);
289   g_object_set (G_OBJECT (udpsink), "async", FALSE, NULL);
290   g_object_set (G_OBJECT (udpsink), "send-duplicates", FALSE, NULL);
291   g_object_set (G_OBJECT (udpsink), "auto-multicast", FALSE, NULL);
292   g_object_set (G_OBJECT (udpsink), "loop", FALSE, NULL);
293
294   priv->rtp_resender.resend_sink = udpsink;
295   priv->rtp_resender.udpsrc_v4 = udpsrc;
296
297   return TRUE;
298
299   /* ERRORS */
300 no_udp_protocol:
301   {
302     goto cleanup;
303   }
304 port_error:
305   {
306     goto cleanup;
307   }
308 element_error:
309   {
310     goto cleanup;
311   }
312 cleanup:
313   {
314     if (udpsrc) {
315       gst_element_set_state (udpsrc, GST_STATE_NULL);
316       gst_object_unref (udpsrc);
317     }
318     if (udpsink) {
319       gst_element_set_state (udpsink, GST_STATE_NULL);
320       gst_object_unref (udpsink);
321     }
322     if (inetaddr)
323       g_object_unref (inetaddr);
324     return FALSE;
325   }
326 }
327
328 static GstPadProbeReturn
329 pad_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
330 {
331   GstPad *sinkpad, *resend_pad, *fecpad;
332   GstRTSPMediaExt *media = NULL;
333   GstRTSPMediaExtPrivate *priv;
334
335   if (!g_atomic_int_compare_and_exchange (&in_idle_probe, FALSE, TRUE))
336     return GST_PAD_PROBE_OK;
337
338   media = (GstRTSPMediaExt *) user_data;
339
340   priv = media->priv;
341
342   g_return_val_if_fail (priv != NULL, GST_PAD_PROBE_REMOVE);
343
344   sinkpad = gst_pad_get_peer (priv->send_src);
345   gst_pad_unlink (priv->send_src, sinkpad);
346
347   if (priv->mode & MEDIA_EXT_MODE_RESEND) {
348     GST_INFO_OBJECT (media, "joining resender");
349     resend_pad =
350         gst_element_get_static_pad (priv->rtp_resender.resender, "rtp_sink");
351     gst_pad_link (priv->send_src, resend_pad);
352     gst_object_unref (resend_pad);
353
354 #ifdef FORCE_DROP
355     {
356       GstPad *identity_src, *identity_sink;
357       identity_src = gst_element_get_static_pad (priv->identity, "src");
358       identity_sink = gst_element_get_static_pad (priv->identity, "sink");
359       resend_pad =
360           gst_element_get_static_pad (priv->rtp_resender.resender, "send_src");
361       gst_pad_link (resend_pad, identity_sink);
362       gst_pad_link (identity_src, sinkpad);
363       gst_object_unref (identity_sink);
364       gst_object_unref (identity_src);
365     }
366 #else
367     resend_pad =
368         gst_element_get_static_pad (priv->rtp_resender.resender, "send_src");
369     gst_pad_link (resend_pad, sinkpad);
370 #endif
371     gst_object_unref (resend_pad);
372   } else if (priv->mode & MEDIA_EXT_MODE_FEC) {
373     GST_INFO_OBJECT (media, "joining fec encoder");
374     fecpad = gst_element_get_static_pad (priv->fecenc, "sink");
375     gst_pad_link (priv->send_src, fecpad);
376     gst_object_unref (fecpad);
377
378 #ifdef FORCE_DROP
379     {
380       GstPad *identity_src, *identity_sink;
381       identity_src = gst_element_get_static_pad (priv->identity, "src");
382       identity_sink = gst_element_get_static_pad (priv->identity, "sink");
383
384       fecpad = gst_element_get_static_pad (priv->fecenc, "src");
385
386       gst_pad_link (fecpad, identity_sink);
387       gst_pad_link (identity_src, sinkpad);
388       gst_object_unref (identity_sink);
389       gst_object_unref (identity_src);
390     }
391 #else
392     fecpad = gst_element_get_static_pad (priv->fecenc, "src");
393     gst_pad_link (fecpad, sinkpad);
394 #endif
395     gst_object_unref (fecpad);
396   }
397
398   gst_object_unref (sinkpad);
399
400   return GST_PAD_PROBE_REMOVE;
401 }
402
403 static gboolean
404 gst_rtsp_media_ext_join_extended_plugin (GstRTSPMediaExt * media, GstBin * bin,
405     GstElement * rtpbin, GstState state, guint idx)
406 {
407   GstRTSPMediaExtPrivate *priv;
408   gchar *name;
409   GstPad *pad, *sinkpad, *selpad;
410   GstPad *resenderpad;
411
412   g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
413   g_return_val_if_fail (GST_IS_ELEMENT (rtpbin), FALSE);
414
415   priv = media->priv;
416   g_return_val_if_fail (priv != NULL, FALSE);
417
418   g_mutex_lock (&priv->lock);
419   if (priv->is_joined)
420     goto was_joined;
421
422   GST_INFO ("media %p joining rtp resender %u", media, idx);
423
424   /* get pads from the RTP session element for sending and receiving
425    * RTP/RTCP*/
426   name = g_strdup_printf ("send_rtp_src_%u", idx);
427   priv->send_src = gst_element_get_static_pad (rtpbin, name);
428   g_free (name);
429
430   /* make resender for RTP and link to stream */
431   priv->rtp_resender.resender = gst_element_factory_make ("rtpresender", NULL);
432   gst_bin_add (bin, priv->rtp_resender.resender);
433
434   gst_element_sync_state_with_parent (priv->rtp_resender.resender);
435
436   if (!alloc_ports (media))
437     goto no_ports;
438
439   /* For the sender we create this bit of pipeline for both
440    * RTP and RTCP. Sync and preroll are enabled on udpsink so
441    * we need to add a queue before appsink to make the pipeline
442    * not block. For the TCP case, we want to pump data to the
443    * client as fast as possible anyway.
444    *
445    * .--------.      .-----.    .---------.
446    * | rtpbin |      | tee |    | udpsink |
447    * |       send->sink   src->sink       |
448    * '--------'      |     |    '---------'
449    *                 |     |    .---------.    .---------.
450    *                 |     |    |  queue  |    | appsink |
451    *                 |    src->sink      src->sink       |
452    *                 '-----'    '---------'    '---------'
453    *
454    * When only UDP is allowed, we skip the tee, queue and appsink and link the
455    * udpsink directly to the session.
456    */
457   /* add udpsink */
458   gst_bin_add (bin, priv->rtp_resender.resend_sink);
459   sinkpad = gst_element_get_static_pad (priv->rtp_resender.resend_sink, "sink");
460   resenderpad =
461       gst_element_get_static_pad (priv->rtp_resender.resender, "resend_src");
462
463   gst_pad_link (resenderpad, sinkpad);
464   gst_object_unref (resenderpad);
465   gst_object_unref (sinkpad);
466
467   /* For the receiver we create this bit of pipeline for both
468    * RTP and RTCP. We receive RTP/RTCP on appsrc and udpsrc
469    * and it is all funneled into the rtpbin receive pad.
470    *
471    * .--------.     .--------.    .--------.
472    * | udpsrc |     | funnel |    | rtpbin |
473    * |       src->sink      src->sink      |
474    * '--------'     |        |    '--------'
475    * .--------.     |        |
476    * | appsrc |     |        |
477    * |       src->sink       |
478    * '--------'     '--------'
479    */
480   /* make funnel for the RTP/RTCP receivers */
481   priv->rtp_resender.funnel = gst_element_factory_make ("funnel", NULL);
482   gst_bin_add (bin, priv->rtp_resender.funnel);
483
484   resenderpad =
485       gst_element_get_static_pad (priv->rtp_resender.resender, "rtcp_sink");
486   pad = gst_element_get_static_pad (priv->rtp_resender.funnel, "src");
487   gst_pad_link (pad, resenderpad);
488   gst_object_unref (resenderpad);
489   gst_object_unref (pad);
490
491   if (priv->rtp_resender.udpsrc_v4) {
492     /* we set and keep these to playing so that they don't cause NO_PREROLL return
493      * values */
494     gst_element_set_state (priv->rtp_resender.udpsrc_v4, GST_STATE_PLAYING);
495     gst_element_set_locked_state (priv->rtp_resender.udpsrc_v4, TRUE);
496     /* add udpsrc */
497     gst_bin_add (bin, priv->rtp_resender.udpsrc_v4);
498
499     /* and link to the funnel v4 */
500     selpad = gst_element_get_request_pad (priv->rtp_resender.funnel, "sink_%u");
501     pad = gst_element_get_static_pad (priv->rtp_resender.udpsrc_v4, "src");
502     gst_pad_link (pad, selpad);
503     gst_object_unref (pad);
504     gst_object_unref (selpad);
505   }
506
507   /* make and add appsrc */
508   priv->rtp_resender.appsrc = gst_element_factory_make ("appsrc", NULL);
509   gst_bin_add (bin, priv->rtp_resender.appsrc);
510   /* and link to the funnel */
511   selpad = gst_element_get_request_pad (priv->rtp_resender.funnel, "sink_%u");
512   pad = gst_element_get_static_pad (priv->rtp_resender.appsrc, "src");
513   gst_pad_link (pad, selpad);
514   gst_object_unref (pad);
515   gst_object_unref (selpad);
516
517   /* check if we need to set to a special state */
518   if (state != GST_STATE_NULL) {
519     if (priv->rtp_resender.resend_sink)
520       gst_element_set_state (priv->rtp_resender.resend_sink, state);
521     if (priv->rtp_resender.funnel)
522       gst_element_set_state (priv->rtp_resender.funnel, state);
523     if (priv->rtp_resender.appsrc)
524       gst_element_set_state (priv->rtp_resender.appsrc, state);
525   }
526
527   /* make alfec encoder for RTP and link to stream */
528   priv->fecenc = gst_element_factory_make ("alfecencoder", NULL);
529   g_object_set (G_OBJECT (priv->fecenc), "max-size-k", priv->max_size_k, NULL);
530   g_object_set (G_OBJECT (priv->fecenc), "max-size-p", priv->max_size_p, NULL);
531   GST_DEBUG ("k:%d, p:%d", priv->max_size_k, priv->max_size_p);
532   g_object_set (G_OBJECT (priv->fecenc), "next-k", priv->max_size_k, NULL);
533   g_object_set (G_OBJECT (priv->fecenc), "next-p", priv->max_size_p, NULL);
534   g_object_set (G_OBJECT (priv->fecenc), "symbol-length", 1500, NULL);
535   gst_bin_add (bin, priv->fecenc);
536
537   gst_element_sync_state_with_parent (priv->fecenc);
538
539 #ifdef FORCE_DROP
540   priv->identity = gst_element_factory_make ("identity", NULL);
541   g_object_set (G_OBJECT (priv->identity), "drop-probability", 0.05, NULL);
542   gst_bin_add (bin, priv->identity);
543
544   gst_element_sync_state_with_parent(priv->identity);
545 #endif
546
547   in_idle_probe = FALSE;
548   gst_pad_add_probe (priv->send_src, GST_PAD_PROBE_TYPE_IDLE, pad_probe_cb,
549       media, NULL);
550
551   priv->is_joined = TRUE;
552   g_mutex_unlock (&priv->lock);
553
554   return TRUE;
555
556   /* ERRORS */
557 was_joined:
558   {
559     g_mutex_unlock (&priv->lock);
560     return TRUE;
561   }
562 no_ports:
563   {
564     g_mutex_unlock (&priv->lock);
565     GST_WARNING ("failed to allocate ports %u", idx);
566     return FALSE;
567   }
568 }
569
570
571 static void
572 ext_preparing (GstRTSPMedia * media, GstRTSPStream * stream, guint idx)
573 {
574   gboolean ret = FALSE;
575   GstElement *rtpbin = NULL;
576   GstElement *pipeline = NULL;
577   GstRTSPMediaExt *_media = GST_RTSP_MEDIA_EXT (media);
578   GstRTSPMediaExtPrivate *priv;
579
580   priv = _media->priv;
581   g_return_if_fail (priv != NULL);
582
583   pipeline = gst_rtsp_media_get_pipeline (media);
584   rtpbin = gst_rtsp_media_get_rtpbin (media);
585
586   ret =
587       gst_rtsp_media_ext_join_extended_plugin (_media, GST_BIN (pipeline),
588       rtpbin, GST_STATE_NULL, idx);
589   if (!ret)
590     GST_ERROR_OBJECT (_media, "Fatal error to join resender");
591
592   g_object_unref (pipeline);
593   g_object_unref (rtpbin);
594
595   return;
596 }
597
598 static gboolean
599 gst_rtsp_media_ext_leave_extended_plugin (GstRTSPMediaExt * media, GstBin * bin,
600     GstElement * rtpbin)
601 {
602   GstRTSPMediaExtPrivate *priv;
603
604   g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
605   g_return_val_if_fail (GST_IS_ELEMENT (rtpbin), FALSE);
606
607   priv = media->priv;
608   g_return_val_if_fail (priv != NULL, FALSE);
609
610   g_mutex_lock (&priv->lock);
611   if (!priv->is_joined)
612     goto was_not_joined;
613
614   GST_INFO ("media %p leaving rtp resender", media);
615
616   if (priv->rtp_resender.resend_sink)
617     gst_element_set_state (priv->rtp_resender.resend_sink, GST_STATE_NULL);
618   if (priv->rtp_resender.funnel)
619     gst_element_set_state (priv->rtp_resender.funnel, GST_STATE_NULL);
620   if (priv->rtp_resender.appsrc)
621     gst_element_set_state (priv->rtp_resender.appsrc, GST_STATE_NULL);
622
623   if (priv->rtp_resender.udpsrc_v4) {
624     /* and set udpsrc to NULL now before removing */
625     gst_element_set_locked_state (priv->rtp_resender.udpsrc_v4, FALSE);
626     gst_element_set_state (priv->rtp_resender.udpsrc_v4, GST_STATE_NULL);
627     /* removing them should also nicely release the request
628      * pads when they finalize */
629     gst_bin_remove (bin, priv->rtp_resender.udpsrc_v4);
630   }
631
632   if (priv->rtp_resender.resend_sink)
633     gst_bin_remove (bin, priv->rtp_resender.resend_sink);
634   if (priv->rtp_resender.appsrc)
635     gst_bin_remove (bin, priv->rtp_resender.appsrc);
636   if (priv->rtp_resender.funnel)
637     gst_bin_remove (bin, priv->rtp_resender.funnel);
638
639   priv->rtp_resender.udpsrc_v4 = NULL;
640   priv->rtp_resender.resend_sink = NULL;
641   priv->rtp_resender.appsrc = NULL;
642   priv->rtp_resender.funnel = NULL;
643
644   GST_INFO ("media %p leaving fec encoder", media);
645
646   if (priv->fecenc) {
647     gst_element_set_state (priv->fecenc, GST_STATE_NULL);
648     priv->fecenc = NULL;
649   }
650
651   gst_object_unref (priv->send_src);
652   priv->send_src = NULL;
653   priv->is_joined = FALSE;
654   g_mutex_unlock (&priv->lock);
655
656   return TRUE;
657
658 was_not_joined:
659   {
660     g_mutex_unlock (&priv->lock);
661     return TRUE;
662   }
663 }
664
665
666 static void
667 ext_unpreparing (GstRTSPMedia * media, GstRTSPStream * stream, guint idx)
668 {
669   gboolean ret = FALSE;
670   GstElement *rtpbin = NULL;
671   GstElement *pipeline = NULL;
672   GstRTSPMediaExt *_media = GST_RTSP_MEDIA_EXT (media);
673   GstRTSPMediaExtPrivate *priv;
674
675   priv = _media->priv;
676   g_return_if_fail (priv != NULL);
677
678   pipeline = gst_rtsp_media_get_pipeline (media);
679   rtpbin = gst_rtsp_media_get_rtpbin (media);
680
681   ret =
682       gst_rtsp_media_ext_leave_extended_plugin (_media, GST_BIN (pipeline),
683       rtpbin);
684
685   if (!ret)
686     GST_ERROR_OBJECT (_media, "Fatal error to leave resender");
687
688   g_object_unref (pipeline);
689   g_object_unref (rtpbin);
690
691   return;
692 }
693
694 guint
695 gst_rtsp_media_ext_get_resent_packets (GstRTSPMediaExt * media)
696 {
697   guint resent_packets = 0;
698   GstRTSPMediaExtPrivate *priv;
699
700   g_return_val_if_fail (GST_IS_RTSP_MEDIA_EXT (media), 0);
701
702   priv = media->priv;
703   g_return_val_if_fail (priv != NULL, 0);
704
705   g_object_get (G_OBJECT (priv->rtp_resender.resender), "rtp-packets-resend",
706       &resent_packets, NULL);
707
708   return resent_packets;
709 }
710
711 void
712 gst_rtsp_media_ext_set_extended_mode (GstRTSPMediaExt * media,
713     GstRTSPMediaExtMode mode)
714 {
715   GstRTSPMediaExtPrivate *priv;
716
717   g_return_if_fail (GST_IS_RTSP_MEDIA_EXT (media));
718
719   priv = media->priv;
720   g_return_if_fail (priv != NULL);
721
722   priv->mode = mode;
723 }
724
725 void
726 gst_rtsp_media_ext_set_retrans_port (GstRTSPMediaExt * media, guint port)
727 {
728   GstRTSPMediaExtPrivate *priv;
729
730   g_return_if_fail (GST_IS_RTSP_MEDIA_EXT (media));
731
732   priv = media->priv;
733   g_return_if_fail (priv != NULL);
734
735   priv->retransmit_port = port;
736 }
737
738 void
739 gst_rtsp_media_ext_set_fec_value (GstRTSPMediaExt * media, guint max_k,
740     guint max_p)
741 {
742   GstRTSPMediaExtPrivate *priv;
743
744   g_return_if_fail (GST_IS_RTSP_MEDIA_EXT (media));
745
746   priv = media->priv;
747   g_return_if_fail (priv != NULL);
748
749   priv->max_size_k = max_k;
750   priv->max_size_p = max_p;
751 }
752
753 void
754 gst_rtsp_media_ext_set_latency_mode (GstRTSPMediaExt * media,
755     GstRTSPMediaExtLatency latency)
756 {
757   GstRTSPMediaExtPrivate *priv;
758
759   g_return_if_fail (GST_IS_RTSP_MEDIA_EXT (media));
760
761   priv = media->priv;
762   g_return_if_fail (priv != NULL);
763
764   priv->latency_mode = latency;
765 }
766
767 void
768 gst_rtsp_media_ext_set_next_param (GstRTSPMediaExt * media, gint32 next_k,
769     gint32 next_p)
770 {
771   GstRTSPMediaExtPrivate *priv;
772
773   g_return_if_fail (GST_IS_RTSP_MEDIA_EXT (media));
774
775   priv = media->priv;
776   g_return_if_fail (priv != NULL);
777
778   g_object_set (G_OBJECT (priv->fecenc), "next-k", next_k, NULL);
779   g_object_set (G_OBJECT (priv->fecenc), "next-p", next_p, NULL);
780 }