2 * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
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.
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.
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.
21 #include "rtsp-session.h"
25 #define DEFAULT_TIMEOUT 60
35 GST_DEBUG_CATEGORY_STATIC (rtsp_session_debug);
36 #define GST_CAT_DEFAULT rtsp_session_debug
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);
44 G_DEFINE_TYPE (GstRTSPSession, gst_rtsp_session, G_TYPE_OBJECT);
47 gst_rtsp_session_class_init (GstRTSPSessionClass * klass)
49 GObjectClass *gobject_class;
51 gobject_class = G_OBJECT_CLASS (klass);
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;
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));
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));
67 GST_DEBUG_CATEGORY_INIT (rtsp_session_debug, "rtspsession", 0,
72 gst_rtsp_session_init (GstRTSPSession * session)
74 session->timeout = DEFAULT_TIMEOUT;
75 g_get_current_time (&session->create_time);
76 gst_rtsp_session_touch (session);
80 gst_rtsp_session_free_stream (GstRTSPSessionStream * stream)
82 GST_INFO ("free session stream %p", stream);
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);
88 gst_rtsp_media_trans_cleanup (&stream->trans);
94 gst_rtsp_session_free_media (GstRTSPSessionMedia * media,
95 GstRTSPSession * session)
99 size = media->streams->len;
101 GST_INFO ("free session media %p", media);
103 gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
105 for (i = 0; i < size; i++) {
106 GstRTSPSessionStream *stream;
108 stream = g_array_index (media->streams, GstRTSPSessionStream *, i);
111 gst_rtsp_session_free_stream (stream);
113 g_array_free (media->streams, TRUE);
116 gst_rtsp_url_free (media->url);
119 g_object_unref (media->media);
125 gst_rtsp_session_finalize (GObject * obj)
127 GstRTSPSession *session;
129 session = GST_RTSP_SESSION (obj);
131 GST_INFO ("finalize session %p", session);
134 g_list_foreach (session->medias, (GFunc) gst_rtsp_session_free_media,
136 g_list_free (session->medias);
138 /* free session id */
139 g_free (session->sessionid);
141 G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj);
145 gst_rtsp_session_get_property (GObject * object, guint propid,
146 GValue * value, GParamSpec * pspec)
148 GstRTSPSession *session = GST_RTSP_SESSION (object);
152 g_value_set_string (value, session->sessionid);
155 g_value_set_uint (value, gst_rtsp_session_get_timeout (session));
158 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
163 gst_rtsp_session_set_property (GObject * object, guint propid,
164 const GValue * value, GParamSpec * pspec)
166 GstRTSPSession *session = GST_RTSP_SESSION (object);
170 g_free (session->sessionid);
171 session->sessionid = g_value_dup_string (value);
174 gst_rtsp_session_set_timeout (session, g_value_get_uint (value));
177 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
182 * gst_rtsp_session_manage_media:
183 * @sess: a #GstRTSPSession
184 * @uri: the uri for the media
185 * @media: (transfer full): a #GstRTSPMedia
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().
190 * Ownership is taken from @media.
192 * Returns: a new @GstRTSPSessionMedia object.
194 GstRTSPSessionMedia *
195 gst_rtsp_session_manage_media (GstRTSPSession * sess, const GstRTSPUrl * uri,
196 GstRTSPMedia * media)
198 GstRTSPSessionMedia *result;
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);
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;
211 /* prealloc the streams now, filled with NULL */
212 n_streams = gst_rtsp_media_n_streams (media);
214 g_array_sized_new (FALSE, TRUE, sizeof (GstRTSPSessionStream *),
216 g_array_set_size (result->streams, n_streams);
218 sess->medias = g_list_prepend (sess->medias, result);
220 GST_INFO ("manage new media %p in session %p", media, result);
226 * gst_rtsp_session_release_media:
227 * @sess: a #GstRTSPSession
228 * @media: a #GstRTSPMedia
230 * Release the managed @media in @sess, freeing the memory allocated by it.
232 * Returns: %TRUE if there are more media session left in @sess.
235 gst_rtsp_session_release_media (GstRTSPSession * sess,
236 GstRTSPSessionMedia * media)
240 g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
241 g_return_val_if_fail (media != NULL, FALSE);
243 for (walk = sess->medias; walk;) {
244 GstRTSPSessionMedia *find;
246 find = (GstRTSPSessionMedia *) walk->data;
247 next = g_list_next (walk);
250 sess->medias = g_list_delete_link (sess->medias, walk);
252 gst_rtsp_session_free_media (find, sess);
257 return (sess->medias != NULL);
261 * gst_rtsp_session_get_media:
262 * @sess: a #GstRTSPSession
263 * @url: the url for the media
265 * Get the session media of the @url.
267 * Returns: the configuration for @url in @sess.
269 GstRTSPSessionMedia *
270 gst_rtsp_session_get_media (GstRTSPSession * sess, const GstRTSPUrl * url)
272 GstRTSPSessionMedia *result;
275 g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
276 g_return_val_if_fail (url != NULL, NULL);
280 for (walk = sess->medias; walk; walk = g_list_next (walk)) {
281 result = (GstRTSPSessionMedia *) walk->data;
283 if (strcmp (result->url->abspath, url->abspath) == 0)
292 * gst_rtsp_session_media_get_stream:
293 * @media: a #GstRTSPSessionMedia
294 * @idx: the stream index
296 * Get a previously created or create a new #GstRTSPSessionStream at @idx.
298 * Returns: a #GstRTSPSessionStream that is valid until the session of @media
301 GstRTSPSessionStream *
302 gst_rtsp_session_media_get_stream (GstRTSPSessionMedia * media, guint idx)
304 GstRTSPSessionStream *result;
306 g_return_val_if_fail (media != NULL, NULL);
307 g_return_val_if_fail (media->media != NULL, NULL);
309 if (idx >= media->streams->len)
312 result = g_array_index (media->streams, GstRTSPSessionStream *, idx);
313 if (result == NULL) {
314 GstRTSPMediaStream *media_stream;
316 media_stream = gst_rtsp_media_get_stream (media->media, idx);
317 if (media_stream == NULL)
320 result = g_new0 (GstRTSPSessionStream, 1);
321 result->trans.idx = idx;
322 result->trans.transport = NULL;
323 result->media_stream = media_stream;
325 g_array_index (media->streams, GstRTSPSessionStream *, idx) = result;
337 gst_rtsp_session_media_alloc_channels (GstRTSPSessionMedia * media,
338 GstRTSPRange * range)
340 range->min = media->counter++;
341 range->max = media->counter++;
347 * gst_rtsp_session_new:
349 * Create a new #GstRTSPSession instance.
352 gst_rtsp_session_new (const gchar * sessionid)
354 GstRTSPSession *result;
356 g_return_val_if_fail (sessionid != NULL, NULL);
358 result = g_object_new (GST_TYPE_RTSP_SESSION, "sessionid", sessionid, NULL);
364 * gst_rtsp_session_get_sessionid:
365 * @session: a #GstRTSPSession
367 * Get the sessionid of @session.
369 * Returns: the sessionid of @session. The value remains valid as long as
373 gst_rtsp_session_get_sessionid (GstRTSPSession * session)
375 g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
377 return session->sessionid;
381 * gst_rtsp_session_set_timeout:
382 * @session: a #GstRTSPSession
383 * @timeout: the new timeout
385 * Configure @session for a timeout of @timeout seconds. The session will be
386 * cleaned up when there is no activity for @timeout seconds.
389 gst_rtsp_session_set_timeout (GstRTSPSession * session, guint timeout)
391 g_return_if_fail (GST_IS_RTSP_SESSION (session));
393 session->timeout = timeout;
397 * gst_rtsp_session_get_timeout:
398 * @session: a #GstRTSPSession
400 * Get the timeout value of @session.
402 * Returns: the timeout of @session in seconds.
405 gst_rtsp_session_get_timeout (GstRTSPSession * session)
407 g_return_val_if_fail (GST_IS_RTSP_SESSION (session), 0);
409 return session->timeout;
413 * gst_rtsp_session_touch:
414 * @session: a #GstRTSPSession
416 * Update the last_access time of the session to the current time.
419 gst_rtsp_session_touch (GstRTSPSession * session)
421 g_return_if_fail (GST_IS_RTSP_SESSION (session));
423 g_get_current_time (&session->last_access);
427 gst_rtsp_session_prevent_expire (GstRTSPSession * session)
429 g_return_if_fail (GST_IS_RTSP_SESSION (session));
431 g_atomic_int_add (&session->expire_count, 1);
435 gst_rtsp_session_allow_expire (GstRTSPSession * session)
437 g_atomic_int_add (&session->expire_count, -1);
441 * gst_rtsp_session_next_timeout:
442 * @session: a #GstRTSPSession
443 * @now: the current system time
445 * Get the amount of milliseconds till the session will expire.
447 * Returns: the amount of milliseconds since the session will time out.
450 gst_rtsp_session_next_timeout (GstRTSPSession * session, GTimeVal * now)
453 GstClockTime last_access, now_ns;
455 g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
456 g_return_val_if_fail (now != NULL, -1);
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);
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);
467 now_ns = GST_TIMEVAL_TO_TIME (*now);
469 if (last_access > now_ns)
470 res = GST_TIME_AS_MSECONDS (last_access - now_ns);
478 * gst_rtsp_session_is_expired:
479 * @session: a #GstRTSPSession
480 * @now: the current system time
482 * Check if @session timeout out.
484 * Returns: %TRUE if @session timed out
487 gst_rtsp_session_is_expired (GstRTSPSession * session, GTimeVal * now)
491 res = (gst_rtsp_session_next_timeout (session, now) == 0);
497 * gst_rtsp_session_stream_init_udp:
498 * @stream: a #GstRTSPSessionStream
499 * @ct: a client #GstRTSPTransport
501 * Set @ct as the client transport and create and return a matching server
502 * transport. This function takes ownership of the passed @ct.
504 * Returns: a server transport or NULL if something went wrong. Use
505 * gst_rtsp_transport_free () after usage.
508 gst_rtsp_session_stream_set_transport (GstRTSPSessionStream * stream,
509 GstRTSPTransport * ct)
511 GstRTSPTransport *st;
513 g_return_val_if_fail (stream != NULL, NULL);
514 g_return_val_if_fail (ct != NULL, NULL);
516 /* prepare the server transport */
517 gst_rtsp_transport_new (&st);
519 st->trans = ct->trans;
520 st->profile = ct->profile;
521 st->lower_transport = ct->lower_transport;
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;
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);
533 case GST_RTSP_LOWER_TRANS_TCP:
534 st->interleaved = ct->interleaved;
539 if (stream->media_stream->session)
540 g_object_get (stream->media_stream->session, "internal-ssrc", &st->ssrc,
543 /* keep track of the transports in the stream. */
544 if (stream->trans.transport)
545 gst_rtsp_transport_free (stream->trans.transport);
546 stream->trans.transport = ct;
552 * gst_rtsp_session_stream_set_callbacks:
553 * @stream: a #GstRTSPSessionStream
554 * @send_rtp: (scope notified): a callback called when RTP should be sent
555 * @send_rtcp: (scope notified): a callback called when RTCP should be sent
556 * @user_data: user data passed to callbacks
557 * @notify: called with the user_data when no longer needed.
559 * Install callbacks that will be called when data for a stream should be sent
560 * to a client. This is usually used when sending RTP/RTCP over TCP.
563 gst_rtsp_session_stream_set_callbacks (GstRTSPSessionStream * stream,
564 GstRTSPSendFunc send_rtp, GstRTSPSendFunc send_rtcp,
565 gpointer user_data, GDestroyNotify notify)
567 stream->trans.send_rtp = send_rtp;
568 stream->trans.send_rtcp = send_rtcp;
569 if (stream->trans.notify)
570 stream->trans.notify (stream->trans.user_data);
571 stream->trans.user_data = user_data;
572 stream->trans.notify = notify;
576 * gst_rtsp_session_stream_set_keepalive:
577 * @stream: a #GstRTSPSessionStream
578 * @keep_alive: a callback called when the receiver is active
579 * @user_data: user data passed to callback
580 * @notify: called with the user_data when no longer needed.
582 * Install callbacks that will be called when RTCP packets are received from the
583 * receiver of @stream.
586 gst_rtsp_session_stream_set_keepalive (GstRTSPSessionStream * stream,
587 GstRTSPKeepAliveFunc keep_alive, gpointer user_data, GDestroyNotify notify)
589 stream->trans.keep_alive = keep_alive;
590 if (stream->trans.ka_notify)
591 stream->trans.ka_notify (stream->trans.ka_user_data);
592 stream->trans.ka_user_data = user_data;
593 stream->trans.ka_notify = notify;
597 * gst_rtsp_session_media_set_state:
598 * @media: a #GstRTSPSessionMedia
599 * @state: the new state
601 * Tell the media object @media to change to @state.
603 * Returns: %TRUE on success.
606 gst_rtsp_session_media_set_state (GstRTSPSessionMedia * media, GstState state)
610 g_return_val_if_fail (media != NULL, FALSE);
612 ret = gst_rtsp_media_set_state (media->media, state, media->streams);