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