Allow setting a custom media factory for a server
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-session.c
1 /* GStreamer
2  * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "rtsp-session.h"
21
22 #undef DEBUG
23
24 static void gst_rtsp_session_finalize (GObject * obj);
25
26 G_DEFINE_TYPE (GstRTSPSession, gst_rtsp_session, G_TYPE_OBJECT);
27
28 static void
29 gst_rtsp_session_class_init (GstRTSPSessionClass * klass)
30 {
31   GObjectClass *gobject_class;
32
33   gobject_class = G_OBJECT_CLASS (klass);
34
35   gobject_class->finalize = gst_rtsp_session_finalize;
36 }
37
38 static void
39 gst_rtsp_session_init (GstRTSPSession * session)
40 {
41 }
42
43 static void
44 gst_rtsp_session_free_stream (GstRTSPSessionStream *stream)
45 {
46   if (stream->client_trans)
47     gst_rtsp_transport_free (stream->client_trans);
48   g_free (stream->destination);
49   if (stream->server_trans)
50     gst_rtsp_transport_free (stream->server_trans);
51
52   if (stream->udpsrc[0])
53     gst_object_unref (stream->udpsrc[0]);
54
55   g_free (stream);
56 }
57
58 static void
59 gst_rtsp_session_free_media (GstRTSPSessionMedia *media)
60 {
61   GList *walk;
62
63   gst_element_set_state (media->pipeline, GST_STATE_NULL);
64
65   if (media->media)
66     g_object_unref (media->media);
67
68   for (walk = media->streams; walk; walk = g_list_next (walk)) {
69     GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data; 
70
71     gst_rtsp_session_free_stream (stream);
72   }
73   if (media->pipeline)
74     gst_object_unref (media->pipeline);
75   g_list_free (media->streams);
76 }
77
78 static void
79 gst_rtsp_session_finalize (GObject * obj)
80 {
81   GstRTSPSession *session;
82   GList *walk;
83
84   session = GST_RTSP_SESSION (obj);
85
86   g_free (session->sessionid);
87
88   for (walk = session->medias; walk; walk = g_list_next (walk)) {
89     GstRTSPSessionMedia *media = (GstRTSPSessionMedia *) walk->data; 
90
91     gst_rtsp_session_free_media (media);
92   }
93   g_list_free (session->medias);
94
95   G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj);
96 }
97
98 /**
99  * gst_rtsp_session_get_media:
100  * @sess: a #GstRTSPSession
101  * @media: a #GstRTSPSessionMedia
102  *
103  * Get or create the session information for @media.
104  *
105  * Returns: the configuration for @media in @sess.
106  */
107 GstRTSPSessionMedia *
108 gst_rtsp_session_get_media (GstRTSPSession *sess, GstRTSPMedia *media)
109 {
110   GstRTSPSessionMedia *result;
111   GList *walk;
112
113   result = NULL;
114
115   for (walk = sess->medias; walk; walk = g_list_next (walk)) {
116     result = (GstRTSPSessionMedia *) walk->data; 
117
118     if (result->media == media)
119       break;
120
121     result = NULL;
122   }
123   if (result == NULL) {
124     result = g_new0 (GstRTSPSessionMedia, 1);
125     result->media = media;
126     result->pipeline = gst_pipeline_new ("pipeline");
127
128     /* prepare media into the pipeline */
129     if (!gst_rtsp_media_prepare (media, GST_BIN (result->pipeline)))
130       goto no_media;
131
132     result->rtpbin = gst_element_factory_make ("gstrtpbin", "rtpbin");
133
134     /* add stuf to the bin */
135     gst_bin_add (GST_BIN (result->pipeline), result->rtpbin);
136
137     gst_element_set_state (result->pipeline, GST_STATE_READY);
138
139     sess->medias = g_list_prepend (sess->medias, result);
140   }
141   return result;
142
143   /* ERRORS */
144 no_media:
145   {
146     gst_rtsp_session_free_media (result);
147     return NULL;
148   }
149 }
150
151 /**
152  * gst_rtsp_session_get_stream:
153  * @media: a #GstRTSPSessionMedia
154  * @idx: the stream index
155  *
156  * Get a previously created or create a new #GstRTSPSessionStream at @idx.
157  *
158  * Returns: a #GstRTSPSessionStream that is valid until the session of @media
159  * is unreffed.
160  */
161 GstRTSPSessionStream *
162 gst_rtsp_session_get_stream (GstRTSPSessionMedia *media, guint idx)
163 {
164   GstRTSPSessionStream *result;
165   GList *walk;
166
167   result = NULL;
168
169   for (walk = media->streams; walk; walk = g_list_next (walk)) {
170     result = (GstRTSPSessionStream *) walk->data; 
171
172     if (result->idx == idx)
173       break;
174
175     result = NULL;
176   }
177   if (result == NULL) {
178     result = g_new0 (GstRTSPSessionStream, 1);
179     result->idx = idx;
180     result->media = media;
181     result->media_stream = gst_rtsp_media_get_stream (media->media, idx);
182
183     media->streams = g_list_prepend (media->streams, result);
184   }
185   return result;
186 }
187
188 /**
189  * gst_rtsp_session_new:
190  *
191  * Create a new #GstRTSPSession instance.
192  */
193 GstRTSPSession *
194 gst_rtsp_session_new (const gchar *sessionid)
195 {
196   GstRTSPSession *result;
197
198   result = g_object_new (GST_TYPE_RTSP_SESSION, NULL);
199   result->sessionid = g_strdup (sessionid);
200
201   return result;
202 }
203
204 static gboolean
205 alloc_udp_ports (GstRTSPSessionStream * stream)
206 {
207   GstStateChangeReturn ret;
208   GstElement *udpsrc0, *udpsrc1;
209   GstElement *udpsink0, *udpsink1;
210   gint tmp_rtp, tmp_rtcp;
211   guint count;
212   gint rtpport, rtcpport, sockfd;
213   gchar *name;
214
215   udpsrc0 = NULL;
216   udpsrc1 = NULL;
217   udpsink0 = NULL;
218   udpsink1 = NULL;
219   count = 0;
220
221   /* Start with random port */
222   tmp_rtp = 0;
223
224   /* try to allocate 2 UDP ports, the RTP port should be an even
225    * number and the RTCP port should be the next (uneven) port */
226 again:
227   udpsrc0 = gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0", NULL);
228   if (udpsrc0 == NULL)
229     goto no_udp_protocol;
230   g_object_set (G_OBJECT (udpsrc0), "port", tmp_rtp, NULL);
231
232   ret = gst_element_set_state (udpsrc0, GST_STATE_PAUSED);
233   if (ret == GST_STATE_CHANGE_FAILURE) {
234     if (tmp_rtp != 0) {
235       tmp_rtp += 2;
236       if (++count > 20)
237         goto no_ports;
238
239       gst_element_set_state (udpsrc0, GST_STATE_NULL);
240       gst_object_unref (udpsrc0);
241
242       goto again;
243     }
244     goto no_udp_protocol;
245   }
246
247   g_object_get (G_OBJECT (udpsrc0), "port", &tmp_rtp, NULL);
248
249   /* check if port is even */
250   if ((tmp_rtp & 1) != 0) {
251     /* port not even, close and allocate another */
252     if (++count > 20)
253       goto no_ports;
254
255     gst_element_set_state (udpsrc0, GST_STATE_NULL);
256     gst_object_unref (udpsrc0);
257
258     tmp_rtp++;
259     goto again;
260   }
261
262   /* allocate port+1 for RTCP now */
263   udpsrc1 = gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0", NULL);
264   if (udpsrc1 == NULL)
265     goto no_udp_rtcp_protocol;
266
267   /* set port */
268   tmp_rtcp = tmp_rtp + 1;
269   g_object_set (G_OBJECT (udpsrc1), "port", tmp_rtcp, NULL);
270
271   ret = gst_element_set_state (udpsrc1, GST_STATE_PAUSED);
272   /* tmp_rtcp port is busy already : retry to make rtp/rtcp pair */
273   if (ret == GST_STATE_CHANGE_FAILURE) {
274
275     if (++count > 20)
276       goto no_ports;
277
278     gst_element_set_state (udpsrc0, GST_STATE_NULL);
279     gst_object_unref (udpsrc0);
280
281     gst_element_set_state (udpsrc1, GST_STATE_NULL);
282     gst_object_unref (udpsrc1);
283
284     tmp_rtp += 2;
285     goto again;
286   }
287
288   /* all fine, do port check */
289   g_object_get (G_OBJECT (udpsrc0), "port", &rtpport, NULL);
290   g_object_get (G_OBJECT (udpsrc1), "port", &rtcpport, NULL);
291
292   /* this should not happen... */
293   if (rtpport != tmp_rtp || rtcpport != tmp_rtcp)
294     goto port_error;
295
296   name = g_strdup_printf ("udp://%s:%d", stream->destination, stream->client_trans->client_port.min);
297   udpsink0 = gst_element_make_from_uri (GST_URI_SINK, name, NULL);
298   g_free (name);
299
300   if (!udpsink0)
301     goto no_udp_protocol;
302
303   g_object_get (G_OBJECT (udpsrc0), "sock", &sockfd, NULL);
304   g_object_set (G_OBJECT (udpsink0), "sockfd", sockfd, NULL);
305   g_object_set (G_OBJECT (udpsink0), "closefd", FALSE, NULL);
306
307   name = g_strdup_printf ("udp://%s:%d", stream->destination, stream->client_trans->client_port.max);
308   udpsink1 = gst_element_make_from_uri (GST_URI_SINK, name, NULL);
309   g_free (name);
310
311   if (!udpsink1)
312     goto no_udp_protocol;
313
314   g_object_get (G_OBJECT (udpsrc1), "sock", &sockfd, NULL);
315   g_object_set (G_OBJECT (udpsink1), "sockfd", sockfd, NULL);
316   g_object_set (G_OBJECT (udpsink1), "closefd", FALSE, NULL);
317   g_object_set (G_OBJECT (udpsink1), "sync", FALSE, NULL);
318   g_object_set (G_OBJECT (udpsink1), "async", FALSE, NULL);
319
320
321   /* we keep these elements, we configure all in configure_transport when the
322    * server told us to really use the UDP ports. */
323   stream->udpsrc[0] = gst_object_ref (udpsrc0);
324   stream->udpsrc[1] = gst_object_ref (udpsrc1);
325   stream->udpsink[0] = gst_object_ref (udpsink0);
326   stream->udpsink[1] = gst_object_ref (udpsink1);
327   stream->server_trans->server_port.min = rtpport;
328   stream->server_trans->server_port.max = rtcpport;
329
330   /* they are ours now */
331   gst_object_sink (udpsrc0);
332   gst_object_sink (udpsrc1);
333   gst_object_sink (udpsink0);
334   gst_object_sink (udpsink1);
335
336   return TRUE;
337
338   /* ERRORS */
339 no_udp_protocol:
340   {
341     goto cleanup;
342   }
343 no_ports:
344   {
345     goto cleanup;
346   }
347 no_udp_rtcp_protocol:
348   {
349     goto cleanup;
350   }
351 port_error:
352   {
353     goto cleanup;
354   }
355 cleanup:
356   {
357     if (udpsrc0) {
358       gst_element_set_state (udpsrc0, GST_STATE_NULL);
359       gst_object_unref (udpsrc0);
360     }
361     if (udpsrc1) {
362       gst_element_set_state (udpsrc1, GST_STATE_NULL);
363       gst_object_unref (udpsrc1);
364     }
365     if (udpsink0) {
366       gst_element_set_state (udpsink0, GST_STATE_NULL);
367       gst_object_unref (udpsink0);
368     }
369     if (udpsink1) {
370       gst_element_set_state (udpsink1, GST_STATE_NULL);
371       gst_object_unref (udpsink1);
372     }
373     return FALSE;
374   }
375 }
376
377
378 /**
379  * gst_rtsp_session_stream_init_udp:
380  * @stream: a #GstRTSPSessionStream
381  * @ct: a client #GstRTSPTransport
382  *
383  * Set @ct as the client transport and create and return a matching server
384  * transport. After this call the needed ports and elements will be created and
385  * initialized.
386  * 
387  * Returns: a server transport or NULL if something went wrong.
388  */
389 GstRTSPTransport *
390 gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream, 
391     const gchar *destination, GstRTSPTransport *ct)
392 {
393   GstRTSPTransport *st;
394   GstPad *pad;
395   gchar *name;
396   GstRTSPSessionMedia *media;
397
398   media = stream->media;
399
400   /* prepare the server transport */
401   gst_rtsp_transport_new (&st);
402
403   st->trans = ct->trans;
404   st->profile = ct->profile;
405   st->lower_transport = ct->lower_transport;
406   st->client_port = ct->client_port;
407
408   /* keep track of the transports */
409   g_free (stream->destination);
410   stream->destination = g_strdup (destination);
411   if (stream->client_trans)
412     gst_rtsp_transport_free (stream->client_trans);
413   stream->client_trans = ct;
414   if (stream->server_trans)
415     gst_rtsp_transport_free (stream->server_trans);
416   stream->server_trans = st;
417
418   alloc_udp_ports (stream);
419
420   gst_bin_add (GST_BIN (media->pipeline), stream->udpsink[0]);
421   gst_bin_add (GST_BIN (media->pipeline), stream->udpsink[1]);
422   gst_bin_add (GST_BIN (media->pipeline), stream->udpsrc[1]);
423
424   /* hook up the stream to the RTP session elements. */
425   name = g_strdup_printf ("send_rtp_sink_%d", stream->idx);
426   stream->send_rtp_sink = gst_element_get_request_pad (media->rtpbin, name);
427   g_free (name);
428   name = g_strdup_printf ("send_rtp_src_%d", stream->idx);
429   stream->send_rtp_src = gst_element_get_static_pad (media->rtpbin, name);
430   g_free (name);
431   name = g_strdup_printf ("send_rtcp_src_%d", stream->idx);
432   stream->send_rtcp_src = gst_element_get_request_pad (media->rtpbin, name);
433   g_free (name);
434   name = g_strdup_printf ("recv_rtcp_sink_%d", stream->idx);
435   stream->recv_rtcp_sink = gst_element_get_request_pad (media->rtpbin, name);
436   g_free (name);
437
438   gst_pad_link (stream->media_stream->srcpad, stream->send_rtp_sink);
439   pad = gst_element_get_static_pad (stream->udpsink[0], "sink");
440   gst_pad_link (stream->send_rtp_src, pad);
441   gst_object_unref (pad);
442   pad = gst_element_get_static_pad (stream->udpsink[1], "sink");
443   gst_pad_link (stream->send_rtcp_src, pad);
444   gst_object_unref (pad);
445   pad = gst_element_get_static_pad (stream->udpsrc[1], "src");
446   gst_pad_link (pad, stream->recv_rtcp_sink);
447   gst_object_unref (pad);
448
449   return st;
450 }
451
452 /**
453  * gst_rtsp_session_media_play:
454  * @media: a #GstRTSPSessionMedia
455  *
456  * Tell the media object @media to start playing and streaming to the client.
457  *
458  * Returns: a #GstStateChangeReturn
459  */
460 GstStateChangeReturn
461 gst_rtsp_session_media_play (GstRTSPSessionMedia *media)
462 {
463   GstStateChangeReturn ret;
464
465   ret = gst_element_set_state (media->pipeline, GST_STATE_PLAYING);
466
467   return ret;
468 }
469
470 /**
471  * gst_rtsp_session_media_pause:
472  * @media: a #GstRTSPSessionMedia
473  *
474  * Tell the media object @media to pause.
475  *
476  * Returns: a #GstStateChangeReturn
477  */
478 GstStateChangeReturn
479 gst_rtsp_session_media_pause (GstRTSPSessionMedia *media)
480 {
481   GstStateChangeReturn ret;
482
483   ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED);
484
485   return ret;
486 }
487
488 /**
489  * gst_rtsp_session_media_stop:
490  * @media: a #GstRTSPSessionMedia
491  *
492  * Tell the media object @media to stop playing. After this call the media
493  * cannot be played or paused anymore
494  *
495  * Returns: a #GstStateChangeReturn
496  */
497 GstStateChangeReturn
498 gst_rtsp_session_media_stop (GstRTSPSessionMedia *media)
499 {
500   GstStateChangeReturn ret;
501
502   ret = gst_element_set_state (media->pipeline, GST_STATE_NULL);
503
504   return ret;
505 }
506
507