Make the server handle arbitrary pipelines
[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->factory)
66     g_object_unref (media->factory);
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  * @location: the url for the media
102  * @factory: a #GstRTSPMediaFactory
103  *
104  * Get or create the session information for @factory.
105  *
106  * Returns: the configuration for @factory in @sess.
107  */
108 GstRTSPSessionMedia *
109 gst_rtsp_session_get_media (GstRTSPSession *sess, const gchar *location, GstRTSPMediaFactory *factory)
110 {
111   GstRTSPSessionMedia *result;
112   GList *walk;
113
114   result = NULL;
115
116   for (walk = sess->medias; walk; walk = g_list_next (walk)) {
117     result = (GstRTSPSessionMedia *) walk->data; 
118
119     if (result->factory == factory)
120       break;
121
122     result = NULL;
123   }
124   if (result == NULL) {
125     result = g_new0 (GstRTSPSessionMedia, 1);
126     result->factory = factory;
127     result->pipeline = gst_pipeline_new ("pipeline");
128
129     /* construct media and add to the pipeline */
130     result->mediabin = gst_rtsp_media_factory_construct (factory, location);
131     if (result->mediabin == NULL)
132       goto no_media;
133     
134     gst_bin_add (GST_BIN_CAST (result->pipeline), result->mediabin->element);
135
136     result->rtpbin = gst_element_factory_make ("gstrtpbin", "rtpbin");
137
138     /* add stuf to the bin */
139     gst_bin_add (GST_BIN (result->pipeline), result->rtpbin);
140
141     gst_element_set_state (result->pipeline, GST_STATE_READY);
142
143     sess->medias = g_list_prepend (sess->medias, result);
144   }
145   return result;
146
147   /* ERRORS */
148 no_media:
149   {
150     gst_rtsp_session_free_media (result);
151     return NULL;
152   }
153 }
154
155 /**
156  * gst_rtsp_session_media_get_stream:
157  * @media: a #GstRTSPSessionMedia
158  * @idx: the stream index
159  *
160  * Get a previously created or create a new #GstRTSPSessionStream at @idx.
161  *
162  * Returns: a #GstRTSPSessionStream that is valid until the session of @media
163  * is unreffed.
164  */
165 GstRTSPSessionStream *
166 gst_rtsp_session_media_get_stream (GstRTSPSessionMedia *media, guint idx)
167 {
168   GstRTSPSessionStream *result;
169   GList *walk;
170
171   result = NULL;
172
173   for (walk = media->streams; walk; walk = g_list_next (walk)) {
174     result = (GstRTSPSessionStream *) walk->data; 
175
176     if (result->idx == idx)
177       break;
178
179     result = NULL;
180   }
181   if (result == NULL) {
182     result = g_new0 (GstRTSPSessionStream, 1);
183     result->idx = idx;
184     result->media = media;
185     result->media_stream = gst_rtsp_media_bin_get_stream (media->mediabin, idx);
186
187     media->streams = g_list_prepend (media->streams, result);
188   }
189   return result;
190 }
191
192 /**
193  * gst_rtsp_session_new:
194  *
195  * Create a new #GstRTSPSession instance.
196  */
197 GstRTSPSession *
198 gst_rtsp_session_new (const gchar *sessionid)
199 {
200   GstRTSPSession *result;
201
202   result = g_object_new (GST_TYPE_RTSP_SESSION, NULL);
203   result->sessionid = g_strdup (sessionid);
204
205   return result;
206 }
207
208 static gboolean
209 alloc_udp_ports (GstRTSPSessionStream * stream)
210 {
211   GstStateChangeReturn ret;
212   GstElement *udpsrc0, *udpsrc1;
213   GstElement *udpsink0, *udpsink1;
214   gint tmp_rtp, tmp_rtcp;
215   guint count;
216   gint rtpport, rtcpport, sockfd;
217   gchar *name;
218
219   udpsrc0 = NULL;
220   udpsrc1 = NULL;
221   udpsink0 = NULL;
222   udpsink1 = NULL;
223   count = 0;
224
225   /* Start with random port */
226   tmp_rtp = 0;
227
228   /* try to allocate 2 UDP ports, the RTP port should be an even
229    * number and the RTCP port should be the next (uneven) port */
230 again:
231   udpsrc0 = gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0", NULL);
232   if (udpsrc0 == NULL)
233     goto no_udp_protocol;
234   g_object_set (G_OBJECT (udpsrc0), "port", tmp_rtp, NULL);
235
236   ret = gst_element_set_state (udpsrc0, GST_STATE_PAUSED);
237   if (ret == GST_STATE_CHANGE_FAILURE) {
238     if (tmp_rtp != 0) {
239       tmp_rtp += 2;
240       if (++count > 20)
241         goto no_ports;
242
243       gst_element_set_state (udpsrc0, GST_STATE_NULL);
244       gst_object_unref (udpsrc0);
245
246       goto again;
247     }
248     goto no_udp_protocol;
249   }
250
251   g_object_get (G_OBJECT (udpsrc0), "port", &tmp_rtp, NULL);
252
253   /* check if port is even */
254   if ((tmp_rtp & 1) != 0) {
255     /* port not even, close and allocate another */
256     if (++count > 20)
257       goto no_ports;
258
259     gst_element_set_state (udpsrc0, GST_STATE_NULL);
260     gst_object_unref (udpsrc0);
261
262     tmp_rtp++;
263     goto again;
264   }
265
266   /* allocate port+1 for RTCP now */
267   udpsrc1 = gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0", NULL);
268   if (udpsrc1 == NULL)
269     goto no_udp_rtcp_protocol;
270
271   /* set port */
272   tmp_rtcp = tmp_rtp + 1;
273   g_object_set (G_OBJECT (udpsrc1), "port", tmp_rtcp, NULL);
274
275   ret = gst_element_set_state (udpsrc1, GST_STATE_PAUSED);
276   /* tmp_rtcp port is busy already : retry to make rtp/rtcp pair */
277   if (ret == GST_STATE_CHANGE_FAILURE) {
278
279     if (++count > 20)
280       goto no_ports;
281
282     gst_element_set_state (udpsrc0, GST_STATE_NULL);
283     gst_object_unref (udpsrc0);
284
285     gst_element_set_state (udpsrc1, GST_STATE_NULL);
286     gst_object_unref (udpsrc1);
287
288     tmp_rtp += 2;
289     goto again;
290   }
291
292   /* all fine, do port check */
293   g_object_get (G_OBJECT (udpsrc0), "port", &rtpport, NULL);
294   g_object_get (G_OBJECT (udpsrc1), "port", &rtcpport, NULL);
295
296   /* this should not happen... */
297   if (rtpport != tmp_rtp || rtcpport != tmp_rtcp)
298     goto port_error;
299
300   name = g_strdup_printf ("udp://%s:%d", stream->destination, stream->client_trans->client_port.min);
301   udpsink0 = gst_element_make_from_uri (GST_URI_SINK, name, NULL);
302   g_free (name);
303
304   if (!udpsink0)
305     goto no_udp_protocol;
306
307   g_object_get (G_OBJECT (udpsrc0), "sock", &sockfd, NULL);
308   g_object_set (G_OBJECT (udpsink0), "sockfd", sockfd, NULL);
309   g_object_set (G_OBJECT (udpsink0), "closefd", FALSE, NULL);
310
311   name = g_strdup_printf ("udp://%s:%d", stream->destination, stream->client_trans->client_port.max);
312   udpsink1 = gst_element_make_from_uri (GST_URI_SINK, name, NULL);
313   g_free (name);
314
315   if (!udpsink1)
316     goto no_udp_protocol;
317
318   g_object_get (G_OBJECT (udpsrc1), "sock", &sockfd, NULL);
319   g_object_set (G_OBJECT (udpsink1), "sockfd", sockfd, NULL);
320   g_object_set (G_OBJECT (udpsink1), "closefd", FALSE, NULL);
321   g_object_set (G_OBJECT (udpsink1), "sync", FALSE, NULL);
322   g_object_set (G_OBJECT (udpsink1), "async", FALSE, NULL);
323
324
325   /* we keep these elements, we configure all in configure_transport when the
326    * server told us to really use the UDP ports. */
327   stream->udpsrc[0] = gst_object_ref (udpsrc0);
328   stream->udpsrc[1] = gst_object_ref (udpsrc1);
329   stream->udpsink[0] = gst_object_ref (udpsink0);
330   stream->udpsink[1] = gst_object_ref (udpsink1);
331   stream->server_trans->server_port.min = rtpport;
332   stream->server_trans->server_port.max = rtcpport;
333
334   /* they are ours now */
335   gst_object_sink (udpsrc0);
336   gst_object_sink (udpsrc1);
337   gst_object_sink (udpsink0);
338   gst_object_sink (udpsink1);
339
340   return TRUE;
341
342   /* ERRORS */
343 no_udp_protocol:
344   {
345     goto cleanup;
346   }
347 no_ports:
348   {
349     goto cleanup;
350   }
351 no_udp_rtcp_protocol:
352   {
353     goto cleanup;
354   }
355 port_error:
356   {
357     goto cleanup;
358   }
359 cleanup:
360   {
361     if (udpsrc0) {
362       gst_element_set_state (udpsrc0, GST_STATE_NULL);
363       gst_object_unref (udpsrc0);
364     }
365     if (udpsrc1) {
366       gst_element_set_state (udpsrc1, GST_STATE_NULL);
367       gst_object_unref (udpsrc1);
368     }
369     if (udpsink0) {
370       gst_element_set_state (udpsink0, GST_STATE_NULL);
371       gst_object_unref (udpsink0);
372     }
373     if (udpsink1) {
374       gst_element_set_state (udpsink1, GST_STATE_NULL);
375       gst_object_unref (udpsink1);
376     }
377     return FALSE;
378   }
379 }
380
381
382 /**
383  * gst_rtsp_session_stream_init_udp:
384  * @stream: a #GstRTSPSessionStream
385  * @ct: a client #GstRTSPTransport
386  *
387  * Set @ct as the client transport and create and return a matching server
388  * transport. After this call the needed ports and elements will be created and
389  * initialized.
390  * 
391  * Returns: a server transport or NULL if something went wrong.
392  */
393 GstRTSPTransport *
394 gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream, 
395     const gchar *destination, GstRTSPTransport *ct)
396 {
397   GstRTSPTransport *st;
398   GstPad *pad;
399   gchar *name;
400   GstRTSPSessionMedia *media;
401
402   media = stream->media;
403
404   /* prepare the server transport */
405   gst_rtsp_transport_new (&st);
406
407   st->trans = ct->trans;
408   st->profile = ct->profile;
409   st->lower_transport = ct->lower_transport;
410   st->client_port = ct->client_port;
411
412   /* keep track of the transports */
413   g_free (stream->destination);
414   stream->destination = g_strdup (destination);
415   if (stream->client_trans)
416     gst_rtsp_transport_free (stream->client_trans);
417   stream->client_trans = ct;
418   if (stream->server_trans)
419     gst_rtsp_transport_free (stream->server_trans);
420   stream->server_trans = st;
421
422   alloc_udp_ports (stream);
423
424   gst_bin_add (GST_BIN (media->pipeline), stream->udpsink[0]);
425   gst_bin_add (GST_BIN (media->pipeline), stream->udpsink[1]);
426   gst_bin_add (GST_BIN (media->pipeline), stream->udpsrc[1]);
427
428   /* hook up the stream to the RTP session elements. */
429   name = g_strdup_printf ("send_rtp_sink_%d", stream->idx);
430   stream->send_rtp_sink = gst_element_get_request_pad (media->rtpbin, name);
431   g_free (name);
432   name = g_strdup_printf ("send_rtp_src_%d", stream->idx);
433   stream->send_rtp_src = gst_element_get_static_pad (media->rtpbin, name);
434   g_free (name);
435   name = g_strdup_printf ("send_rtcp_src_%d", stream->idx);
436   stream->send_rtcp_src = gst_element_get_request_pad (media->rtpbin, name);
437   g_free (name);
438   name = g_strdup_printf ("recv_rtcp_sink_%d", stream->idx);
439   stream->recv_rtcp_sink = gst_element_get_request_pad (media->rtpbin, name);
440   g_free (name);
441
442   gst_pad_link (stream->media_stream->srcpad, stream->send_rtp_sink);
443   pad = gst_element_get_static_pad (stream->udpsink[0], "sink");
444   gst_pad_link (stream->send_rtp_src, pad);
445   gst_object_unref (pad);
446   pad = gst_element_get_static_pad (stream->udpsink[1], "sink");
447   gst_pad_link (stream->send_rtcp_src, pad);
448   gst_object_unref (pad);
449   pad = gst_element_get_static_pad (stream->udpsrc[1], "src");
450   gst_pad_link (pad, stream->recv_rtcp_sink);
451   gst_object_unref (pad);
452
453   return st;
454 }
455
456 /**
457  * gst_rtsp_session_media_play:
458  * @media: a #GstRTSPSessionMedia
459  *
460  * Tell the media object @media to start playing and streaming to the client.
461  *
462  * Returns: a #GstStateChangeReturn
463  */
464 GstStateChangeReturn
465 gst_rtsp_session_media_play (GstRTSPSessionMedia *media)
466 {
467   GstStateChangeReturn ret;
468
469   ret = gst_element_set_state (media->pipeline, GST_STATE_PLAYING);
470
471   return ret;
472 }
473
474 /**
475  * gst_rtsp_session_media_pause:
476  * @media: a #GstRTSPSessionMedia
477  *
478  * Tell the media object @media to pause.
479  *
480  * Returns: a #GstStateChangeReturn
481  */
482 GstStateChangeReturn
483 gst_rtsp_session_media_pause (GstRTSPSessionMedia *media)
484 {
485   GstStateChangeReturn ret;
486
487   ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED);
488
489   return ret;
490 }
491
492 /**
493  * gst_rtsp_session_media_stop:
494  * @media: a #GstRTSPSessionMedia
495  *
496  * Tell the media object @media to stop playing. After this call the media
497  * cannot be played or paused anymore
498  *
499  * Returns: a #GstStateChangeReturn
500  */
501 GstStateChangeReturn
502 gst_rtsp_session_media_stop (GstRTSPSessionMedia *media)
503 {
504   GstStateChangeReturn ret;
505
506   ret = gst_element_set_state (media->pipeline, GST_STATE_NULL);
507
508   return ret;
509 }
510
511