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