rtsp-server: Run gst-indent
[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 #include <string.h>
20
21 #include "rtsp-session.h"
22
23 #undef DEBUG
24
25 #define DEFAULT_TIMEOUT 60
26
27 enum
28 {
29   PROP_0,
30   PROP_SESSIONID,
31   PROP_TIMEOUT,
32   PROP_LAST
33 };
34
35 GST_DEBUG_CATEGORY_EXTERN (rtsp_session_debug);
36 #define GST_CAT_DEFAULT rtsp_session_debug
37
38 static void gst_rtsp_session_get_property (GObject * object, guint propid,
39     GValue * value, GParamSpec * pspec);
40 static void gst_rtsp_session_set_property (GObject * object, guint propid,
41     const GValue * value, GParamSpec * pspec);
42 static void gst_rtsp_session_finalize (GObject * obj);
43
44 G_DEFINE_TYPE (GstRTSPSession, gst_rtsp_session, G_TYPE_OBJECT);
45
46 static void
47 gst_rtsp_session_class_init (GstRTSPSessionClass * klass)
48 {
49   GObjectClass *gobject_class;
50
51   gobject_class = G_OBJECT_CLASS (klass);
52
53   gobject_class->get_property = gst_rtsp_session_get_property;
54   gobject_class->set_property = gst_rtsp_session_set_property;
55   gobject_class->finalize = gst_rtsp_session_finalize;
56
57   g_object_class_install_property (gobject_class, PROP_SESSIONID,
58       g_param_spec_string ("sessionid", "Sessionid", "the session id",
59           NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
60           G_PARAM_STATIC_STRINGS));
61
62   g_object_class_install_property (gobject_class, PROP_TIMEOUT,
63       g_param_spec_uint ("timeout", "timeout",
64           "the timeout of the session (0 = never)", 0, G_MAXUINT,
65           DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
66 }
67
68 static void
69 gst_rtsp_session_init (GstRTSPSession * session)
70 {
71   session->timeout = DEFAULT_TIMEOUT;
72   g_get_current_time (&session->create_time);
73   gst_rtsp_session_touch (session);
74 }
75
76 static void
77 gst_rtsp_session_free_stream (GstRTSPSessionStream * stream)
78 {
79   GST_INFO ("free session stream %p", stream);
80
81   /* remove callbacks now */
82   gst_rtsp_session_stream_set_callbacks (stream, NULL, NULL, NULL, NULL);
83   gst_rtsp_session_stream_set_keepalive (stream, NULL, NULL, NULL);
84
85   gst_rtsp_media_trans_cleanup (&stream->trans);
86
87   g_free (stream);
88 }
89
90 static void
91 gst_rtsp_session_free_media (GstRTSPSessionMedia * media,
92     GstRTSPSession * session)
93 {
94   guint size, i;
95
96   size = media->streams->len;
97
98   GST_INFO ("free session media %p", media);
99
100   gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
101
102   for (i = 0; i < size; i++) {
103     GstRTSPSessionStream *stream;
104
105     stream = g_array_index (media->streams, GstRTSPSessionStream *, i);
106
107     if (stream)
108       gst_rtsp_session_free_stream (stream);
109   }
110   g_array_free (media->streams, TRUE);
111
112   if (media->url)
113     gst_rtsp_url_free (media->url);
114
115   if (media->media)
116     g_object_unref (media->media);
117
118   g_free (media);
119 }
120
121 static void
122 gst_rtsp_session_finalize (GObject * obj)
123 {
124   GstRTSPSession *session;
125
126   session = GST_RTSP_SESSION (obj);
127
128   GST_INFO ("finalize session %p", session);
129
130   /* free all media */
131   g_list_foreach (session->medias, (GFunc) gst_rtsp_session_free_media,
132       session);
133   g_list_free (session->medias);
134
135   /* free session id */
136   g_free (session->sessionid);
137
138   G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj);
139 }
140
141 static void
142 gst_rtsp_session_get_property (GObject * object, guint propid,
143     GValue * value, GParamSpec * pspec)
144 {
145   GstRTSPSession *session = GST_RTSP_SESSION (object);
146
147   switch (propid) {
148     case PROP_SESSIONID:
149       g_value_set_string (value, session->sessionid);
150       break;
151     case PROP_TIMEOUT:
152       g_value_set_uint (value, gst_rtsp_session_get_timeout (session));
153       break;
154     default:
155       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
156   }
157 }
158
159 static void
160 gst_rtsp_session_set_property (GObject * object, guint propid,
161     const GValue * value, GParamSpec * pspec)
162 {
163   GstRTSPSession *session = GST_RTSP_SESSION (object);
164
165   switch (propid) {
166     case PROP_SESSIONID:
167       g_free (session->sessionid);
168       session->sessionid = g_value_dup_string (value);
169       break;
170     case PROP_TIMEOUT:
171       gst_rtsp_session_set_timeout (session, g_value_get_uint (value));
172       break;
173     default:
174       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
175   }
176 }
177
178 /**
179  * gst_rtsp_session_manage_media:
180  * @sess: a #GstRTSPSession
181  * @uri: the uri for the media
182  * @media: a #GstRTSPMedia
183  *
184  * Manage the media object @obj in @sess. @uri will be used to retrieve this
185  * media from the session with gst_rtsp_session_get_media().
186  *
187  * Ownership is taken from @media.
188  *
189  * Returns: a new @GstRTSPSessionMedia object.
190  */
191 GstRTSPSessionMedia *
192 gst_rtsp_session_manage_media (GstRTSPSession * sess, const GstRTSPUrl * uri,
193     GstRTSPMedia * media)
194 {
195   GstRTSPSessionMedia *result;
196   guint n_streams;
197
198   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
199   g_return_val_if_fail (uri != NULL, NULL);
200   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
201   g_return_val_if_fail (media->status == GST_RTSP_MEDIA_STATUS_PREPARED, NULL);
202
203   result = g_new0 (GstRTSPSessionMedia, 1);
204   result->media = media;
205   result->url = gst_rtsp_url_copy ((GstRTSPUrl *) uri);
206   result->state = GST_RTSP_STATE_INIT;
207
208   /* prealloc the streams now, filled with NULL */
209   n_streams = gst_rtsp_media_n_streams (media);
210   result->streams =
211       g_array_sized_new (FALSE, TRUE, sizeof (GstRTSPSessionStream *),
212       n_streams);
213   g_array_set_size (result->streams, n_streams);
214
215   sess->medias = g_list_prepend (sess->medias, result);
216
217   GST_INFO ("manage new media %p in session %p", media, result);
218
219   return result;
220 }
221
222 /**
223  * gst_rtsp_session_release_media:
224  * @sess: a #GstRTSPSession
225  * @media: a #GstRTSPMedia
226  *
227  * Release the managed @media in @sess, freeing the memory allocated by it.
228  *
229  * Returns: %TRUE if there are more media session left in @sess.
230  */
231 gboolean
232 gst_rtsp_session_release_media (GstRTSPSession * sess,
233     GstRTSPSessionMedia * media)
234 {
235   GList *walk, *next;
236
237   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
238   g_return_val_if_fail (media != NULL, FALSE);
239
240   for (walk = sess->medias; walk;) {
241     GstRTSPSessionMedia *find;
242
243     find = (GstRTSPSessionMedia *) walk->data;
244     next = g_list_next (walk);
245
246     if (find == media) {
247       sess->medias = g_list_delete_link (sess->medias, walk);
248
249       gst_rtsp_session_free_media (find, sess);
250       break;
251     }
252     walk = next;
253   }
254   return (sess->medias != NULL);
255 }
256
257 /**
258  * gst_rtsp_session_get_media:
259  * @sess: a #GstRTSPSession
260  * @url: the url for the media
261  *
262  * Get the session media of the @url.
263  *
264  * Returns: the configuration for @url in @sess.
265  */
266 GstRTSPSessionMedia *
267 gst_rtsp_session_get_media (GstRTSPSession * sess, const GstRTSPUrl * url)
268 {
269   GstRTSPSessionMedia *result;
270   GList *walk;
271
272   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
273   g_return_val_if_fail (url != NULL, NULL);
274
275   result = NULL;
276
277   for (walk = sess->medias; walk; walk = g_list_next (walk)) {
278     result = (GstRTSPSessionMedia *) walk->data;
279
280     if (strcmp (result->url->abspath, url->abspath) == 0)
281       break;
282
283     result = NULL;
284   }
285   return result;
286 }
287
288 /**
289  * gst_rtsp_session_media_get_stream:
290  * @media: a #GstRTSPSessionMedia
291  * @idx: the stream index
292  *
293  * Get a previously created or create a new #GstRTSPSessionStream at @idx.
294  *
295  * Returns: a #GstRTSPSessionStream that is valid until the session of @media
296  * is unreffed.
297  */
298 GstRTSPSessionStream *
299 gst_rtsp_session_media_get_stream (GstRTSPSessionMedia * media, guint idx)
300 {
301   GstRTSPSessionStream *result;
302
303   g_return_val_if_fail (media != NULL, NULL);
304   g_return_val_if_fail (media->media != NULL, NULL);
305
306   if (idx >= media->streams->len)
307     return NULL;
308
309   result = g_array_index (media->streams, GstRTSPSessionStream *, idx);
310   if (result == NULL) {
311     GstRTSPMediaStream *media_stream;
312
313     media_stream = gst_rtsp_media_get_stream (media->media, idx);
314     if (media_stream == NULL)
315       goto no_media;
316
317     result = g_new0 (GstRTSPSessionStream, 1);
318     result->trans.idx = idx;
319     result->trans.transport = NULL;
320     result->media_stream = media_stream;
321
322     g_array_index (media->streams, GstRTSPSessionStream *, idx) = result;
323   }
324   return result;
325
326   /* ERRORS */
327 no_media:
328   {
329     return NULL;
330   }
331 }
332
333 gboolean
334 gst_rtsp_session_media_alloc_channels (GstRTSPSessionMedia * media,
335     GstRTSPRange * range)
336 {
337   range->min = media->counter++;
338   range->max = media->counter++;
339
340   return TRUE;
341 }
342
343 /**
344  * gst_rtsp_session_new:
345  *
346  * Create a new #GstRTSPSession instance.
347  */
348 GstRTSPSession *
349 gst_rtsp_session_new (const gchar * sessionid)
350 {
351   GstRTSPSession *result;
352
353   g_return_val_if_fail (sessionid != NULL, NULL);
354
355   result = g_object_new (GST_TYPE_RTSP_SESSION, "sessionid", sessionid, NULL);
356
357   return result;
358 }
359
360 /**
361  * gst_rtsp_session_get_sessionid:
362  * @session: a #GstRTSPSession
363  *
364  * Get the sessionid of @session.
365  *
366  * Returns: the sessionid of @session. The value remains valid as long as
367  * @session is alive.
368  */
369 const gchar *
370 gst_rtsp_session_get_sessionid (GstRTSPSession * session)
371 {
372   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
373
374   return session->sessionid;
375 }
376
377 /**
378  * gst_rtsp_session_set_timeout:
379  * @session: a #GstRTSPSession
380  * @timeout: the new timeout
381  *
382  * Configure @session for a timeout of @timeout seconds. The session will be
383  * cleaned up when there is no activity for @timeout seconds.
384  */
385 void
386 gst_rtsp_session_set_timeout (GstRTSPSession * session, guint timeout)
387 {
388   g_return_if_fail (GST_IS_RTSP_SESSION (session));
389
390   session->timeout = timeout;
391 }
392
393 /**
394  * gst_rtsp_session_get_timeout:
395  * @session: a #GstRTSPSession
396  *
397  * Get the timeout value of @session.
398  *
399  * Returns: the timeout of @session in seconds.
400  */
401 guint
402 gst_rtsp_session_get_timeout (GstRTSPSession * session)
403 {
404   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), 0);
405
406   return session->timeout;
407 }
408
409 /**
410  * gst_rtsp_session_touch:
411  * @session: a #GstRTSPSession
412  *
413  * Update the last_access time of the session to the current time.
414  */
415 void
416 gst_rtsp_session_touch (GstRTSPSession * session)
417 {
418   g_return_if_fail (GST_IS_RTSP_SESSION (session));
419
420   g_get_current_time (&session->last_access);
421 }
422
423 void
424 gst_rtsp_session_prevent_expire (GstRTSPSession * session)
425 {
426   g_return_if_fail (GST_IS_RTSP_SESSION (session));
427
428   g_atomic_int_add (&session->expire_count, 1);
429 }
430
431 void
432 gst_rtsp_session_allow_expire (GstRTSPSession * session)
433 {
434   g_atomic_int_add (&session->expire_count, -1);
435 }
436
437 /**
438  * gst_rtsp_session_next_timeout:
439  * @session: a #GstRTSPSession
440  * @now: the current system time
441  *
442  * Get the amount of milliseconds till the session will expire.
443  *
444  * Returns: the amount of milliseconds since the session will time out.
445  */
446 gint
447 gst_rtsp_session_next_timeout (GstRTSPSession * session, GTimeVal * now)
448 {
449   gint res;
450   GstClockTime last_access, now_ns;
451
452   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
453   g_return_val_if_fail (now != NULL, -1);
454
455   if (g_atomic_int_get (&session->expire_count) != 0) {
456     /* touch session when the expire count is not 0 */
457     g_get_current_time (&session->last_access);
458   }
459
460   last_access = GST_TIMEVAL_TO_TIME (session->last_access);
461   /* add timeout allow for 5 seconds of extra time */
462   last_access += session->timeout * GST_SECOND + (5 * GST_SECOND);
463
464   now_ns = GST_TIMEVAL_TO_TIME (*now);
465
466   if (last_access > now_ns)
467     res = GST_TIME_AS_MSECONDS (last_access - now_ns);
468   else
469     res = 0;
470
471   return res;
472 }
473
474 /**
475  * gst_rtsp_session_is_expired:
476  * @session: a #GstRTSPSession
477  * @now: the current system time
478  *
479  * Check if @session timeout out.
480  *
481  * Returns: %TRUE if @session timed out
482  */
483 gboolean
484 gst_rtsp_session_is_expired (GstRTSPSession * session, GTimeVal * now)
485 {
486   gboolean res;
487
488   res = (gst_rtsp_session_next_timeout (session, now) == 0);
489
490   return res;
491 }
492
493 /**
494  * gst_rtsp_session_stream_init_udp:
495  * @stream: a #GstRTSPSessionStream
496  * @ct: a client #GstRTSPTransport
497  *
498  * Set @ct as the client transport and create and return a matching server
499  * transport. This function takes ownership of the passed @ct.
500  *
501  * Returns: a server transport or NULL if something went wrong. Use
502  * gst_rtsp_transport_free () after usage.
503  */
504 GstRTSPTransport *
505 gst_rtsp_session_stream_set_transport (GstRTSPSessionStream * stream,
506     GstRTSPTransport * ct)
507 {
508   GstRTSPTransport *st;
509
510   g_return_val_if_fail (stream != NULL, NULL);
511   g_return_val_if_fail (ct != NULL, NULL);
512
513   /* prepare the server transport */
514   gst_rtsp_transport_new (&st);
515
516   st->trans = ct->trans;
517   st->profile = ct->profile;
518   st->lower_transport = ct->lower_transport;
519
520   switch (st->lower_transport) {
521     case GST_RTSP_LOWER_TRANS_UDP:
522       st->client_port = ct->client_port;
523       st->server_port = stream->media_stream->server_port;
524       break;
525     case GST_RTSP_LOWER_TRANS_UDP_MCAST:
526       ct->port = st->port = stream->media_stream->server_port;
527       st->destination = g_strdup (ct->destination);
528       break;
529     case GST_RTSP_LOWER_TRANS_TCP:
530       st->interleaved = ct->interleaved;
531     default:
532       break;
533   }
534
535   if (stream->media_stream->session)
536     g_object_get (stream->media_stream->session, "internal-ssrc", &st->ssrc,
537         NULL);
538
539   /* keep track of the transports in the stream. */
540   if (stream->trans.transport)
541     gst_rtsp_transport_free (stream->trans.transport);
542   stream->trans.transport = ct;
543
544   return st;
545 }
546
547 /**
548  * gst_rtsp_session_stream_set_callbacks:
549  * @stream: a #GstRTSPSessionStream
550  * @send_rtp: a callback called when RTP should be sent
551  * @send_rtcp: a callback called when RTCP should be sent
552  * @user_data: user data passed to callbacks
553  * @notify: called with the user_data when no longer needed.
554  *
555  * Install callbacks that will be called when data for a stream should be sent
556  * to a client. This is usually used when sending RTP/RTCP over TCP.
557  */
558 void
559 gst_rtsp_session_stream_set_callbacks (GstRTSPSessionStream * stream,
560     GstRTSPSendFunc send_rtp, GstRTSPSendFunc send_rtcp, gpointer user_data,
561     GDestroyNotify notify)
562 {
563   stream->trans.send_rtp = send_rtp;
564   stream->trans.send_rtcp = send_rtcp;
565   if (stream->trans.notify)
566     stream->trans.notify (stream->trans.user_data);
567   stream->trans.user_data = user_data;
568   stream->trans.notify = notify;
569 }
570
571 /**
572  * gst_rtsp_session_stream_set_keepalive:
573  * @stream: a #GstRTSPSessionStream
574  * @keep_alive: a callback called when the receiver is active
575  * @user_data: user data passed to callback
576  * @notify: called with the user_data when no longer needed.
577  *
578  * Install callbacks that will be called when RTCP packets are received from the
579  * receiver of @stream.
580  */
581 void
582 gst_rtsp_session_stream_set_keepalive (GstRTSPSessionStream * stream,
583     GstRTSPKeepAliveFunc keep_alive, gpointer user_data, GDestroyNotify notify)
584 {
585   stream->trans.keep_alive = keep_alive;
586   if (stream->trans.ka_notify)
587     stream->trans.ka_notify (stream->trans.ka_user_data);
588   stream->trans.ka_user_data = user_data;
589   stream->trans.ka_notify = notify;
590 }
591
592 /**
593  * gst_rtsp_session_media_set_state:
594  * @media: a #GstRTSPSessionMedia
595  * @state: the new state
596  *
597  * Tell the media object @media to change to @state.
598  *
599  * Returns: %TRUE on success.
600  */
601 gboolean
602 gst_rtsp_session_media_set_state (GstRTSPSessionMedia * media, GstState state)
603 {
604   gboolean ret;
605
606   g_return_val_if_fail (media != NULL, FALSE);
607
608   ret = gst_rtsp_media_set_state (media->media, state, media->streams);
609
610   return ret;
611 }