Merge branch 'master' of git+ssh://git.collabora.co.uk/git/gst-rtsp-server

This commit is contained in:
Wim Taymans 2009-02-04 20:13:32 +01:00 committed by Wim Taymans
commit 734dedaeac
8 changed files with 539 additions and 36 deletions

View file

@ -49,6 +49,8 @@ gst_rtsp_client_finalize (GObject * obj)
{ {
GstRTSPClient *client = GST_RTSP_CLIENT (obj); GstRTSPClient *client = GST_RTSP_CLIENT (obj);
g_message ("finalize client %p", client);
gst_rtsp_connection_free (client->connection); gst_rtsp_connection_free (client->connection);
if (client->session_pool) if (client->session_pool)
g_object_unref (client->session_pool); g_object_unref (client->session_pool);
@ -81,6 +83,8 @@ gst_rtsp_client_new (void)
static void static void
send_response (GstRTSPClient *client, GstRTSPMessage *response) send_response (GstRTSPClient *client, GstRTSPMessage *response)
{ {
gst_rtsp_message_add_header (response, GST_RTSP_HDR_SERVER, "GStreamer RTSP server");
#ifdef DEBUG #ifdef DEBUG
gst_rtsp_message_dump (response); gst_rtsp_message_dump (response);
#endif #endif
@ -149,14 +153,17 @@ find_media (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request)
/* now keep track of the uri and the media */ /* now keep track of the uri and the media */
client->uri = gst_rtsp_url_copy (uri); client->uri = gst_rtsp_url_copy (uri);
client->media = g_object_ref (media); client->media = media;
} }
else { else {
/* we have seen this uri before, used cached media */ /* we have seen this uri before, used cached media */
media = g_object_ref (client->media); media = client->media;
g_message ("reusing cached media %p", media); g_message ("reusing cached media %p", media);
} }
if (media)
g_object_ref (media);
return media; return media;
/* ERRORS */ /* ERRORS */
@ -243,12 +250,17 @@ handle_teardown_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage
gst_rtsp_session_media_stop (media); gst_rtsp_session_media_stop (media);
/* unmanage the media in the session, returns false if all media session
* are torn down. */
if (!gst_rtsp_session_release_media (session, media)) {
/* remove the session */
gst_rtsp_session_pool_remove (client->session_pool, session); gst_rtsp_session_pool_remove (client->session_pool, session);
g_object_unref (session);
/* remove the session id from the request, which will also remove it from the /* remove the session id from the request, which will also remove it from the
* response */ * response */
gst_rtsp_message_remove_header (request, GST_RTSP_HDR_SESSION, -1); gst_rtsp_message_remove_header (request, GST_RTSP_HDR_SESSION, -1);
}
g_object_unref (session);
/* construct the response now */ /* construct the response now */
code = GST_RTSP_STS_OK; code = GST_RTSP_STS_OK;
@ -314,11 +326,13 @@ no_session:
not_found: not_found:
{ {
send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request); send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
g_object_unref (session);
return FALSE; return FALSE;
} }
invalid_state: invalid_state:
{ {
send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request); send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
g_object_unref (session);
return FALSE; return FALSE;
} }
} }
@ -333,6 +347,7 @@ handle_play_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *req
GString *rtpinfo; GString *rtpinfo;
guint n_streams, i; guint n_streams, i;
guint timestamp, seqnum; guint timestamp, seqnum;
gchar *str;
if (!(session = ensure_session (client, request))) if (!(session = ensure_session (client, request)))
goto no_session; goto no_session;
@ -373,8 +388,12 @@ handle_play_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *req
gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request); gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
/* add the RTP-Info header */ /* add the RTP-Info header */
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_RTP_INFO, rtpinfo->str); str = g_string_free (rtpinfo, FALSE);
g_string_free (rtpinfo, TRUE); gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RTP_INFO, str);
/* add the range */
str = gst_rtsp_range_to_string (&media->media->range);
gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RANGE, str);
send_response (client, &response); send_response (client, &response);
@ -395,11 +414,13 @@ no_session:
not_found: not_found:
{ {
send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request); send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
g_object_unref (session);
return FALSE; return FALSE;
} }
invalid_state: invalid_state:
{ {
send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request); send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
g_object_unref (session);
return FALSE; return FALSE;
} }
} }
@ -490,6 +511,11 @@ handle_setup_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *re
/* we had a session in the request, find it again */ /* we had a session in the request, find it again */
if (!(session = gst_rtsp_session_pool_find (client->session_pool, sessid))) if (!(session = gst_rtsp_session_pool_find (client->session_pool, sessid)))
goto session_not_found; goto session_not_found;
/* get a handle to the configuration of the media in the session, this can
* return NULL if this is a new url to manage in this session. */
media = gst_rtsp_session_get_media (session, uri);
need_session = FALSE; need_session = FALSE;
} }
else { else {
@ -497,10 +523,15 @@ handle_setup_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *re
* something. */ * something. */
if (!(session = gst_rtsp_session_pool_create (client->session_pool))) if (!(session = gst_rtsp_session_pool_create (client->session_pool)))
goto service_unavailable; goto service_unavailable;
/* we need a new media configuration in this session */
media = NULL;
need_session = TRUE; need_session = TRUE;
} }
if (need_session) { /* we have no media, find one and manage it */
if (media == NULL) {
GstRTSPMedia *m; GstRTSPMedia *m;
/* get a handle to the configuration of the media in the session */ /* get a handle to the configuration of the media in the session */
@ -509,8 +540,9 @@ handle_setup_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *re
media = gst_rtsp_session_manage_media (session, uri, m); media = gst_rtsp_session_manage_media (session, uri, m);
} }
} }
/* get a handle to the configuration of the media in the session */
if (!(media = gst_rtsp_session_get_media (session, uri))) /* if we stil have no media, error */
if (media == NULL)
goto not_found; goto not_found;
/* get a handle to the stream in the media */ /* get a handle to the stream in the media */
@ -528,8 +560,17 @@ handle_setup_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *re
code = GST_RTSP_STS_OK; code = GST_RTSP_STS_OK;
gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request); gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
if (need_session) /* add the new session header for new session ids */
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_SESSION, session->sessionid); if (need_session) {
gchar *str;
if (session->timeout != 60)
str = g_strdup_printf ("%s; timeout=%d", session->sessionid, session->timeout);
else
str = g_strdup (session->sessionid);
gst_rtsp_message_take_header (&response, GST_RTSP_HDR_SESSION, str);
}
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str); gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
g_free (trans_str); g_free (trans_str);
@ -566,6 +607,7 @@ not_found:
no_stream: no_stream:
{ {
send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request); send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
g_object_unref (media);
return FALSE; return FALSE;
} }
session_not_found: session_not_found:
@ -628,6 +670,8 @@ handle_describe_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage
if (!(sdp = gst_rtsp_sdp_from_media (media))) if (!(sdp = gst_rtsp_sdp_from_media (media)))
goto no_sdp; goto no_sdp;
g_object_unref (media);
gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
gst_rtsp_status_as_text (GST_RTSP_STS_OK), request); gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);

View file

@ -76,6 +76,8 @@ gst_rtsp_media_finalize (GObject * obj)
media = GST_RTSP_MEDIA (obj); media = GST_RTSP_MEDIA (obj);
g_message ("finalize media %p", media);
for (i = 0; i < media->streams->len; i++) { for (i = 0; i < media->streams->len; i++) {
GstRTSPMediaStream *stream; GstRTSPMediaStream *stream;
@ -447,6 +449,31 @@ setup_stream (GstRTSPMediaStream *stream, guint idx, GstRTSPMedia *media)
return TRUE; return TRUE;
} }
static void
collect_media_stats (GstRTSPMedia *media)
{
GstFormat format;
gint64 duration;
media->range.unit = GST_RTSP_RANGE_NPT;
media->range.min.type = GST_RTSP_TIME_SECONDS;
media->range.min.seconds = 0.0;
/* get the duration */
format = GST_FORMAT_TIME;
if (!gst_element_query_duration (media->pipeline, &format, &duration))
duration = -1;
if (duration == -1) {
media->range.max.type = GST_RTSP_TIME_END;
media->range.max.seconds = -1;
}
else {
media->range.max.type = GST_RTSP_TIME_SECONDS;
media->range.max.seconds = ((gdouble)duration) / GST_SECOND;
}
}
/** /**
* gst_rtsp_media_prepare: * gst_rtsp_media_prepare:
* @obj: a #GstRTSPMedia * @obj: a #GstRTSPMedia
@ -509,6 +536,9 @@ gst_rtsp_media_prepare (GstRTSPMedia *media)
/* and back to PAUSED for live pipelines */ /* and back to PAUSED for live pipelines */
ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED); ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED);
/* collect stats about the media */
collect_media_stats (media);
/* unlock the udp src elements */ /* unlock the udp src elements */
n_streams = gst_rtsp_media_n_streams (media); n_streams = gst_rtsp_media_n_streams (media);
for (i = 0; i < n_streams; i++) { for (i = 0; i < n_streams; i++) {

View file

@ -18,6 +18,7 @@
*/ */
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/rtsp/gstrtsprange.h>
#include <gst/rtsp/gstrtspurl.h> #include <gst/rtsp/gstrtspurl.h>
#ifndef __GST_RTSP_MEDIA_H__ #ifndef __GST_RTSP_MEDIA_H__
@ -129,6 +130,9 @@ struct _GstRTSPMedia {
/* for TCP transport */ /* for TCP transport */
GstElement *multifdsink; GstElement *multifdsink;
/* the range of media */
GstRTSPTimeRange range;
}; };
struct _GstRTSPMediaClass { struct _GstRTSPMediaClass {

View file

@ -33,6 +33,7 @@ gst_rtsp_sdp_from_media (GstRTSPMedia *media)
{ {
GstSDPMessage *sdp; GstSDPMessage *sdp;
guint i, n_streams; guint i, n_streams;
gchar *rangestr;
n_streams = gst_rtsp_media_n_streams (media); n_streams = gst_rtsp_media_n_streams (media);
@ -46,6 +47,9 @@ gst_rtsp_sdp_from_media (GstRTSPMedia *media)
gst_sdp_message_add_time (sdp, "0", "0", NULL); gst_sdp_message_add_time (sdp, "0", "0", NULL);
gst_sdp_message_add_attribute (sdp, "tool", "GStreamer"); gst_sdp_message_add_attribute (sdp, "tool", "GStreamer");
gst_sdp_message_add_attribute (sdp, "type", "broadcast"); gst_sdp_message_add_attribute (sdp, "type", "broadcast");
rangestr = gst_rtsp_range_to_string (&media->range);
gst_sdp_message_add_attribute (sdp, "range", rangestr);
g_free (rangestr);
for (i = 0; i < n_streams; i++) { for (i = 0; i < n_streams; i++) {
GstRTSPMediaStream *stream; GstRTSPMediaStream *stream;

View file

@ -21,6 +21,19 @@
#undef DEBUG #undef DEBUG
#define DEFAULT_MAX_SESSIONS 0
enum
{
PROP_0,
PROP_MAX_SESSIONS,
PROP_LAST
};
static void gst_rtsp_session_pool_get_property (GObject *object, guint propid,
GValue *value, GParamSpec *pspec);
static void gst_rtsp_session_pool_set_property (GObject *object, guint propid,
const GValue *value, GParamSpec *pspec);
static void gst_rtsp_session_pool_finalize (GObject * object); static void gst_rtsp_session_pool_finalize (GObject * object);
static gchar * create_session_id (GstRTSPSessionPool *pool); static gchar * create_session_id (GstRTSPSessionPool *pool);
@ -34,8 +47,16 @@ gst_rtsp_session_pool_class_init (GstRTSPSessionPoolClass * klass)
gobject_class = G_OBJECT_CLASS (klass); gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = gst_rtsp_session_pool_get_property;
gobject_class->set_property = gst_rtsp_session_pool_set_property;
gobject_class->finalize = gst_rtsp_session_pool_finalize; gobject_class->finalize = gst_rtsp_session_pool_finalize;
g_object_class_install_property (gobject_class, PROP_MAX_SESSIONS,
g_param_spec_uint ("max-sessions", "Max Sessions",
"the maximum amount of sessions (0 = unlimited)",
0, G_MAXUINT, DEFAULT_MAX_SESSIONS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
klass->create_session_id = create_session_id; klass->create_session_id = create_session_id;
} }
@ -44,7 +65,8 @@ gst_rtsp_session_pool_init (GstRTSPSessionPool * pool)
{ {
pool->lock = g_mutex_new (); pool->lock = g_mutex_new ();
pool->sessions = g_hash_table_new_full (g_str_hash, g_str_equal, pool->sessions = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_object_unref); NULL, g_object_unref);
pool->max_sessions = DEFAULT_MAX_SESSIONS;
} }
static void static void
@ -58,6 +80,42 @@ gst_rtsp_session_pool_finalize (GObject * object)
G_OBJECT_CLASS (gst_rtsp_session_pool_parent_class)->finalize (object); G_OBJECT_CLASS (gst_rtsp_session_pool_parent_class)->finalize (object);
} }
static void
gst_rtsp_session_pool_get_property (GObject *object, guint propid,
GValue *value, GParamSpec *pspec)
{
GstRTSPSessionPool *pool = GST_RTSP_SESSION_POOL (object);
switch (propid) {
case PROP_MAX_SESSIONS:
g_mutex_lock (pool->lock);
g_value_set_uint (value, pool->max_sessions);
g_mutex_unlock (pool->lock);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
break;
}
}
static void
gst_rtsp_session_pool_set_property (GObject *object, guint propid,
const GValue *value, GParamSpec *pspec)
{
GstRTSPSessionPool *pool = GST_RTSP_SESSION_POOL (object);
switch (propid) {
case PROP_MAX_SESSIONS:
g_mutex_lock (pool->lock);
pool->max_sessions = g_value_get_uint (value);
g_mutex_unlock (pool->lock);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
break;
}
}
/** /**
* gst_rtsp_session_pool_new: * gst_rtsp_session_pool_new:
* *
@ -75,12 +133,76 @@ gst_rtsp_session_pool_new (void)
return result; return result;
} }
/**
* gst_rtsp_session_pool_set_max_sessions:
* @pool: a #GstRTSPSessionPool
* @max: the maximum number of sessions
*
* Configure the maximum allowed number of sessions in @pool to @max.
* A value of 0 means an unlimited amount of sessions.
*/
void
gst_rtsp_session_pool_set_max_sessions (GstRTSPSessionPool *pool, guint max)
{
g_return_if_fail (GST_IS_RTSP_SESSION_POOL (pool));
g_mutex_lock (pool->lock);
pool->max_sessions = max;
g_mutex_unlock (pool->lock);
}
/**
* gst_rtsp_session_pool_get_max_sessions:
* @pool: a #GstRTSPSessionPool
*
* Get the maximum allowed number of sessions in @pool. 0 means an unlimited
* amount of sessions.
*
* Returns: the maximum allowed number of sessions.
*/
guint
gst_rtsp_session_pool_get_max_sessions (GstRTSPSessionPool *pool)
{
guint result;
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
g_mutex_lock (pool->lock);
result = pool->max_sessions;
g_mutex_unlock (pool->lock);
return result;
}
/**
* gst_rtsp_session_pool_get_n_sessions:
* @pool: a #GstRTSPSessionPool
*
* Get the amount of active sessions in @pool.
*
* Returns: the amount of active sessions in @pool.
*/
guint
gst_rtsp_session_pool_get_n_sessions (GstRTSPSessionPool *pool)
{
guint result;
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
g_mutex_lock (pool->lock);
result = g_hash_table_size (pool->sessions);
g_mutex_unlock (pool->lock);
return result;
}
/** /**
* gst_rtsp_session_pool_find: * gst_rtsp_session_pool_find:
* @pool: the pool to search * @pool: the pool to search
* @sessionid: the session id * @sessionid: the session id
* *
* Find the session with @sessionid in @pool. * Find the session with @sessionid in @pool. The access time of the session
* will be updated with gst_rtsp_session_touch().
* *
* Returns: the #GstRTSPSession with @sessionid or %NULL when the session did * Returns: the #GstRTSPSession with @sessionid or %NULL when the session did
* not exist. g_object_unref() after usage. * not exist. g_object_unref() after usage.
@ -99,6 +221,9 @@ gst_rtsp_session_pool_find (GstRTSPSessionPool *pool, const gchar *sessionid)
g_object_ref (result); g_object_ref (result);
g_mutex_unlock (pool->lock); g_mutex_unlock (pool->lock);
if (result)
gst_rtsp_session_touch (result);
return result; return result;
} }
@ -129,11 +254,13 @@ gst_rtsp_session_pool_create (GstRTSPSessionPool *pool)
GstRTSPSession *result = NULL; GstRTSPSession *result = NULL;
GstRTSPSessionPoolClass *klass; GstRTSPSessionPoolClass *klass;
gchar *id = NULL; gchar *id = NULL;
guint retry;
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL); g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL);
klass = GST_RTSP_SESSION_POOL_GET_CLASS (pool); klass = GST_RTSP_SESSION_POOL_GET_CLASS (pool);
retry = 0;
do { do {
/* start by creating a new random session id, we assume that this is random /* start by creating a new random session id, we assume that this is random
* enough to not cause a collision, which we will check later */ * enough to not cause a collision, which we will check later */
@ -146,11 +273,19 @@ gst_rtsp_session_pool_create (GstRTSPSessionPool *pool)
goto no_session; goto no_session;
g_mutex_lock (pool->lock); g_mutex_lock (pool->lock);
/* check session limit */
if (pool->max_sessions > 0) {
if (g_hash_table_size (pool->sessions) >= pool->max_sessions)
goto too_many_sessions;
}
/* check if the sessionid existed */ /* check if the sessionid existed */
result = g_hash_table_lookup (pool->sessions, id); result = g_hash_table_lookup (pool->sessions, id);
if (result) { if (result) {
/* found, retry with a different session id */ /* found, retry with a different session id */
result = NULL; result = NULL;
retry++;
if (retry > 100)
goto collision;
} }
else { else {
/* not found, create session and insert it in the pool */ /* not found, create session and insert it in the pool */
@ -177,6 +312,20 @@ no_session:
g_warning ("can't create session id with GstRTSPSessionPool %p", pool); g_warning ("can't create session id with GstRTSPSessionPool %p", pool);
return NULL; return NULL;
} }
collision:
{
g_warning ("can't find unique sessionid for GstRTSPSessionPool %p", pool);
g_mutex_unlock (pool->lock);
g_free (id);
return NULL;
}
too_many_sessions:
{
g_warning ("session pool reached max sessions of %d", pool->max_sessions);
g_mutex_unlock (pool->lock);
g_free (id);
return NULL;
}
} }
/** /**
@ -184,20 +333,50 @@ no_session:
* @pool: a #GstRTSPSessionPool * @pool: a #GstRTSPSessionPool
* @sess: a #GstRTSPSession * @sess: a #GstRTSPSession
* *
* Remove @sess from @pool and Clean it up. * Remove @sess from @pool, releasing the ref that the pool has on @sess.
* *
* Returns: a new #GstRTSPSession. * Returns: %TRUE if the session was found and removed.
*/ */
void gboolean
gst_rtsp_session_pool_remove (GstRTSPSessionPool *pool, GstRTSPSession *sess) gst_rtsp_session_pool_remove (GstRTSPSessionPool *pool, GstRTSPSession *sess)
{ {
gboolean found; gboolean found;
g_return_if_fail (GST_IS_RTSP_SESSION_POOL (pool)); g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), FALSE);
g_return_if_fail (GST_IS_RTSP_SESSION (sess)); g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
g_mutex_lock (pool->lock); g_mutex_lock (pool->lock);
found = g_hash_table_remove (pool->sessions, sess); found = g_hash_table_remove (pool->sessions, sess->sessionid);
g_mutex_unlock (pool->lock); g_mutex_unlock (pool->lock);
return found;
} }
static gboolean
cleanup_func (gchar *sessionid, GstRTSPSession *sess, GstRTSPSessionPool *pool)
{
return gst_rtsp_session_is_expired (sess);
}
/**
* gst_rtsp_session_pool_cleanup:
* @pool: a #GstRTSPSessionPool
*
* Inspect all the sessions in @pool and remove the sessions that are inactive
* for more than their timeout.
*
* Returns: the amount of sessions that got removed.
*/
guint
gst_rtsp_session_pool_cleanup (GstRTSPSessionPool *pool)
{
guint result;
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
g_mutex_lock (pool->lock);
result = g_hash_table_foreach_remove (pool->sessions, (GHRFunc) cleanup_func, pool);
g_mutex_unlock (pool->lock);
return result;
}

View file

@ -40,7 +40,7 @@ typedef struct _GstRTSPSessionPoolClass GstRTSPSessionPoolClass;
/** /**
* GstRTSPSessionPool: * GstRTSPSessionPool:
* * @max_sessions: the maximum number of sessions.
* @lock: locking the session hashtable * @lock: locking the session hashtable
* @session: hashtable of sessions indexed by the session id. * @session: hashtable of sessions indexed by the session id.
* *
@ -50,6 +50,8 @@ typedef struct _GstRTSPSessionPoolClass GstRTSPSessionPoolClass;
struct _GstRTSPSessionPool { struct _GstRTSPSessionPool {
GObject parent; GObject parent;
guint max_sessions;
GMutex *lock; GMutex *lock;
GHashTable *sessions; GHashTable *sessions;
}; };
@ -67,14 +69,25 @@ struct _GstRTSPSessionPoolClass {
GType gst_rtsp_session_pool_get_type (void); GType gst_rtsp_session_pool_get_type (void);
/* creating a session pool */
GstRTSPSessionPool * gst_rtsp_session_pool_new (void); GstRTSPSessionPool * gst_rtsp_session_pool_new (void);
/* counting sessionss */
void gst_rtsp_session_pool_set_max_sessions (GstRTSPSessionPool *pool, guint max);
guint gst_rtsp_session_pool_get_max_sessions (GstRTSPSessionPool *pool);
guint gst_rtsp_session_pool_get_n_sessions (GstRTSPSessionPool *pool);
/* managing sessions */
GstRTSPSession * gst_rtsp_session_pool_create (GstRTSPSessionPool *pool);
GstRTSPSession * gst_rtsp_session_pool_find (GstRTSPSessionPool *pool, GstRTSPSession * gst_rtsp_session_pool_find (GstRTSPSessionPool *pool,
const gchar *sessionid); const gchar *sessionid);
GstRTSPSession * gst_rtsp_session_pool_create (GstRTSPSessionPool *pool); gboolean gst_rtsp_session_pool_remove (GstRTSPSessionPool *pool,
void gst_rtsp_session_pool_remove (GstRTSPSessionPool *pool,
GstRTSPSession *sess); GstRTSPSession *sess);
/* perform session maintenance */
guint gst_rtsp_session_pool_cleanup (GstRTSPSessionPool *pool);
G_END_DECLS G_END_DECLS
#endif /* __GST_RTSP_SESSION_POOL_H__ */ #endif /* __GST_RTSP_SESSION_POOL_H__ */

View file

@ -22,6 +22,20 @@
#undef DEBUG #undef DEBUG
#define DEFAULT_TIMEOUT 60
enum
{
PROP_0,
PROP_SESSIONID,
PROP_TIMEOUT,
PROP_LAST
};
static void gst_rtsp_session_get_property (GObject *object, guint propid,
GValue *value, GParamSpec *pspec);
static void gst_rtsp_session_set_property (GObject *object, guint propid,
const GValue *value, GParamSpec *pspec);
static void gst_rtsp_session_finalize (GObject * obj); static void gst_rtsp_session_finalize (GObject * obj);
G_DEFINE_TYPE (GstRTSPSession, gst_rtsp_session, G_TYPE_OBJECT); G_DEFINE_TYPE (GstRTSPSession, gst_rtsp_session, G_TYPE_OBJECT);
@ -33,17 +47,33 @@ gst_rtsp_session_class_init (GstRTSPSessionClass * klass)
gobject_class = G_OBJECT_CLASS (klass); gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = gst_rtsp_session_get_property;
gobject_class->set_property = gst_rtsp_session_set_property;
gobject_class->finalize = gst_rtsp_session_finalize; gobject_class->finalize = gst_rtsp_session_finalize;
g_object_class_install_property (gobject_class, PROP_SESSIONID,
g_param_spec_string ("sessionid", "Sessionid", "the session id",
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_TIMEOUT,
g_param_spec_uint ("timeout", "timeout", "the timeout of the session (0 = never)",
0, G_MAXUINT, DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
} }
static void static void
gst_rtsp_session_init (GstRTSPSession * session) gst_rtsp_session_init (GstRTSPSession * session)
{ {
session->timeout = DEFAULT_TIMEOUT;
g_get_current_time (&session->create_time);
gst_rtsp_session_touch (session);
} }
static void static void
gst_rtsp_session_free_stream (GstRTSPSessionStream *stream) gst_rtsp_session_free_stream (GstRTSPSessionStream *stream)
{ {
g_message ("free session stream %p", stream);
if (stream->trans.transport) if (stream->trans.transport)
gst_rtsp_transport_free (stream->trans.transport); gst_rtsp_transport_free (stream->trans.transport);
@ -57,6 +87,8 @@ gst_rtsp_session_free_media (GstRTSPSessionMedia *media, GstRTSPSession *session
size = media->streams->len; size = media->streams->len;
g_message ("free session media %p", media);
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
GstRTSPSessionStream *stream; GstRTSPSessionStream *stream;
@ -83,6 +115,8 @@ gst_rtsp_session_finalize (GObject * obj)
session = GST_RTSP_SESSION (obj); session = GST_RTSP_SESSION (obj);
g_message ("finalize session %p", session);
/* free all media */ /* free all media */
g_list_foreach (session->medias, (GFunc) gst_rtsp_session_free_media, g_list_foreach (session->medias, (GFunc) gst_rtsp_session_free_media,
session); session);
@ -94,14 +128,53 @@ gst_rtsp_session_finalize (GObject * obj)
G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj); G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj);
} }
static void
gst_rtsp_session_get_property (GObject *object, guint propid,
GValue *value, GParamSpec *pspec)
{
GstRTSPSession *session = GST_RTSP_SESSION (object);
switch (propid) {
case PROP_SESSIONID:
g_value_set_string (value, session->sessionid);
break;
case PROP_TIMEOUT:
g_value_set_uint (value, gst_rtsp_session_get_timeout (session));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
}
}
static void
gst_rtsp_session_set_property (GObject *object, guint propid,
const GValue *value, GParamSpec *pspec)
{
GstRTSPSession *session = GST_RTSP_SESSION (object);
switch (propid) {
case PROP_SESSIONID:
g_free (session->sessionid);
session->sessionid = g_value_dup_string (value);
break;
case PROP_TIMEOUT:
gst_rtsp_session_set_timeout (session, g_value_get_uint (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
}
}
/** /**
* gst_rtsp_session_manage_media: * gst_rtsp_session_manage_media:
* @sess: a #GstRTSPSession * @sess: a #GstRTSPSession
* @url: the url for the media * @url: the url for the media
* @obj: a #GstRTSPMediaObject * @media: a #GstRTSPMediaObject
* *
* Manage the media object @obj in @sess. @url will be used to retrieve this * Manage the media object @obj in @sess. @url will be used to retrieve this
* media object from the session with gst_rtsp_session_get_media(). * media from the session with gst_rtsp_session_get_media().
*
* Ownership is taken from @media.
* *
* Returns: a new @GstRTSPSessionMedia object. * Returns: a new @GstRTSPSessionMedia object.
*/ */
@ -129,11 +202,46 @@ gst_rtsp_session_manage_media (GstRTSPSession *sess, const GstRTSPUrl *uri,
sess->medias = g_list_prepend (sess->medias, result); sess->medias = g_list_prepend (sess->medias, result);
g_message ("manage new media %p in session %p", media, sess); g_message ("manage new media %p in session %p", media, result);
return result; return result;
} }
/**
* gst_rtsp_session_release_media:
* @sess: a #GstRTSPSession
* @media: a #GstRTSPMediaObject
*
* Release the managed @media in @sess, freeing the memory allocated by it.
*
* Returns: %TRUE if there are more media session left in @sess.
*/
gboolean
gst_rtsp_session_release_media (GstRTSPSession *sess,
GstRTSPSessionMedia *media)
{
GList *walk, *next;
g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
g_return_val_if_fail (media != NULL, FALSE);
for (walk = sess->medias; walk;) {
GstRTSPSessionMedia *find;
find = (GstRTSPSessionMedia *) walk->data;
next = g_list_next (walk);
if (find == media) {
sess->medias = g_list_delete_link (sess->medias, walk);
gst_rtsp_session_free_media (find, sess);
break;
}
walk = next;
}
return (sess->medias != NULL);
}
/** /**
* gst_rtsp_session_get_media: * gst_rtsp_session_get_media:
* @sess: a #GstRTSPSession * @sess: a #GstRTSPSession
@ -219,12 +327,105 @@ gst_rtsp_session_new (const gchar *sessionid)
{ {
GstRTSPSession *result; GstRTSPSession *result;
result = g_object_new (GST_TYPE_RTSP_SESSION, NULL); g_return_val_if_fail (sessionid != NULL, NULL);
result->sessionid = g_strdup (sessionid);
result = g_object_new (GST_TYPE_RTSP_SESSION, "sessionid", sessionid, NULL);
return result; return result;
} }
/**
* gst_rtsp_session_get_sessionid:
* @session: a #GstRTSPSession
*
* Get the sessionid of @session.
*
* Returns: the sessionid of @session. The value remains valid as long as
* @session is alive.
*/
const gchar *
gst_rtsp_session_get_sessionid (GstRTSPSession *session)
{
g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
return session->sessionid;
}
/**
* gst_rtsp_session_set_timeout:
* @session: a #GstRTSPSession
* @timeout: the new timeout
*
* Configure @session for a timeout of @timeout seconds. The session will be
* cleaned up when there is no activity for @timeout seconds.
*/
void
gst_rtsp_session_set_timeout (GstRTSPSession *session, guint timeout)
{
g_return_if_fail (GST_IS_RTSP_SESSION (session));
session->timeout = timeout;
}
/**
* gst_rtsp_session_get_timeout:
* @session: a #GstRTSPSession
*
* Get the timeout value of @session.
*
* Returns: the timeout of @session in seconds.
*/
guint
gst_rtsp_session_get_timeout (GstRTSPSession *session)
{
g_return_val_if_fail (GST_IS_RTSP_SESSION (session), 0);
return session->timeout;
}
/**
* gst_rtsp_session_touch:
* @session: a #GstRTSPSession
*
* Update the last_access time of the session to the current time.
*/
void
gst_rtsp_session_touch (GstRTSPSession *session)
{
g_return_if_fail (GST_IS_RTSP_SESSION (session));
g_get_current_time (&session->last_access);
}
/**
* gst_rtsp_session_is_expired:
* @session: a #GstRTSPSession
*
* Check if @session timeout out.
*
* Returns: %TRUE if @session timed out
*/
gboolean
gst_rtsp_session_is_expired (GstRTSPSession *session)
{
gboolean res;
GstClockTime last_access, now_ns;
GTimeVal now;
g_return_val_if_fail (GST_IS_RTSP_SESSION (session), FALSE);
last_access = GST_TIMEVAL_TO_TIME (session->last_access);
/* add timeout */
last_access += session->timeout * GST_SECOND;
g_get_current_time (&now);
now_ns = GST_TIMEVAL_TO_TIME (now);
res = now_ns > last_access;
return res;
}
/** /**
* gst_rtsp_session_stream_init_udp: * gst_rtsp_session_stream_init_udp:
* @stream: a #GstRTSPSessionStream * @stream: a #GstRTSPSessionStream
@ -241,6 +442,9 @@ gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream,
{ {
GstRTSPTransport *st; GstRTSPTransport *st;
g_return_val_if_fail (stream != NULL, NULL);
g_return_val_if_fail (ct != NULL, NULL);
/* prepare the server transport */ /* prepare the server transport */
gst_rtsp_transport_new (&st); gst_rtsp_transport_new (&st);
@ -273,6 +477,8 @@ gst_rtsp_session_media_play (GstRTSPSessionMedia *media)
{ {
gboolean ret; gboolean ret;
g_return_val_if_fail (media != NULL, FALSE);
ret = gst_rtsp_media_play (media->media, media->streams); ret = gst_rtsp_media_play (media->media, media->streams);
return ret; return ret;
@ -291,6 +497,8 @@ gst_rtsp_session_media_pause (GstRTSPSessionMedia *media)
{ {
gboolean ret; gboolean ret;
g_return_val_if_fail (media != NULL, FALSE);
ret = gst_rtsp_media_pause (media->media, media->streams); ret = gst_rtsp_media_pause (media->media, media->streams);
return ret; return ret;
@ -310,6 +518,8 @@ gst_rtsp_session_media_stop (GstRTSPSessionMedia *media)
{ {
gboolean ret; gboolean ret;
g_return_val_if_fail (media != NULL, FALSE);
ret = gst_rtsp_media_stop (media->media, media->streams); ret = gst_rtsp_media_stop (media->media, media->streams);
return ret; return ret;

View file

@ -81,16 +81,25 @@ struct _GstRTSPSessionMedia
/** /**
* GstRTSPSession: * GstRTSPSession:
* @sessionid: the session id of the session
* @timeout: the timeout of the session
* @create_time: the time when the session was created
* @last_access: the time the session was last accessed
* @media: a list of #GstRTSPSessionMedia managed in this session
* *
* Session information kept by the server for a specific client. * Session information kept by the server for a specific client.
* One client session, identified with a session id, can handle multiple medias * One client session, identified with a session id, can handle multiple medias
* identified with the media object. * identified with the url of a media.
*/ */
struct _GstRTSPSession { struct _GstRTSPSession {
GObject parent; GObject parent;
gchar *sessionid; gchar *sessionid;
guint timeout;
GTimeVal create_time;
GTimeVal last_access;
GList *medias; GList *medias;
}; };
@ -103,14 +112,24 @@ GType gst_rtsp_session_get_type (void);
/* create a new session */ /* create a new session */
GstRTSPSession * gst_rtsp_session_new (const gchar *sessionid); GstRTSPSession * gst_rtsp_session_new (const gchar *sessionid);
const gchar * gst_rtsp_session_get_sessionid (GstRTSPSession *session);
void gst_rtsp_session_set_timeout (GstRTSPSession *session, guint timeout);
guint gst_rtsp_session_get_timeout (GstRTSPSession *session);
/* session timeout stuff */
void gst_rtsp_session_touch (GstRTSPSession *session);
gboolean gst_rtsp_session_is_expired (GstRTSPSession *session);
/* handle media in a session */ /* handle media in a session */
GstRTSPSessionMedia * gst_rtsp_session_manage_media (GstRTSPSession *sess, GstRTSPSessionMedia * gst_rtsp_session_manage_media (GstRTSPSession *sess,
const GstRTSPUrl *uri, const GstRTSPUrl *uri,
GstRTSPMedia *media); GstRTSPMedia *media);
gboolean gst_rtsp_session_release_media (GstRTSPSession *sess,
GstRTSPSessionMedia *media);
/* get media in a session */ /* get media in a session */
GstRTSPSessionMedia * gst_rtsp_session_get_media (GstRTSPSession *sess, GstRTSPSessionMedia * gst_rtsp_session_get_media (GstRTSPSession *sess,
const GstRTSPUrl *uri); const GstRTSPUrl *uri);
/* control media */ /* control media */
gboolean gst_rtsp_session_media_play (GstRTSPSessionMedia *media); gboolean gst_rtsp_session_media_play (GstRTSPSessionMedia *media);
gboolean gst_rtsp_session_media_pause (GstRTSPSessionMedia *media); gboolean gst_rtsp_session_media_pause (GstRTSPSessionMedia *media);