Cleanup of sessions and more
authorWim Taymans <wim.taymans@collabora.co.uk>
Wed, 4 Feb 2009 16:00:42 +0000 (17:00 +0100)
committerWim Taymans <wim.taymans@collabora.co.uk>
Wed, 4 Feb 2009 16:00:42 +0000 (17:00 +0100)
Fix the refcounting of media and sessions in the client. Properly clean up the
session data when the client performs a teardown.

Add Server header to responses.

Allow for multiple uri setups in one session.

Add Range header to the PLAY response and add the range attribute to the SDP
message.

Fix the session pool remove method, it used the wrong key in the hashtable. Also
give the ownership of the sessionid to the session object.

gst/rtsp-server/rtsp-client.c
gst/rtsp-server/rtsp-media.c
gst/rtsp-server/rtsp-media.h
gst/rtsp-server/rtsp-sdp.c
gst/rtsp-server/rtsp-session-pool.c
gst/rtsp-server/rtsp-session.c
gst/rtsp-server/rtsp-session.h

index 99c92fa..0f18d21 100644 (file)
@@ -49,6 +49,8 @@ gst_rtsp_client_finalize (GObject * obj)
 {
   GstRTSPClient *client = GST_RTSP_CLIENT (obj);
 
+  g_message ("finalize client %p", client);
+
   gst_rtsp_connection_free (client->connection);
   if (client->session_pool)
     g_object_unref (client->session_pool);
@@ -81,6 +83,8 @@ gst_rtsp_client_new (void)
 static void
 send_response (GstRTSPClient *client, GstRTSPMessage *response)
 {
+  gst_rtsp_message_add_header (response, GST_RTSP_HDR_SERVER, "GStreamer RTSP server");
+
 #ifdef DEBUG
   gst_rtsp_message_dump (response);
 #endif
@@ -149,14 +153,17 @@ find_media (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request)
 
     /* now keep track of the uri and the media */
     client->uri = gst_rtsp_url_copy (uri);
-    client->media = g_object_ref (media);
+    client->media = media;
   }
   else {
     /* 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); 
   }
 
+  if (media)
+    g_object_ref (media);
+
   return media;
 
   /* ERRORS */
@@ -243,12 +250,17 @@ handle_teardown_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage
 
   gst_rtsp_session_media_stop (media);
 
-  gst_rtsp_session_pool_remove (client->session_pool, session);
-  g_object_unref (session);
+  /* 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);
 
-  /* remove the session id from the request, which will also remove it from the
-   * response */
-  gst_rtsp_message_remove_header (request, GST_RTSP_HDR_SESSION, -1);
+    /* remove the session id from the request, which will also remove it from the
+     * response */
+    gst_rtsp_message_remove_header (request, GST_RTSP_HDR_SESSION, -1);
+  }
+  g_object_unref (session);
 
   /* construct the response now */
   code = GST_RTSP_STS_OK;
@@ -314,11 +326,13 @@ no_session:
 not_found:
   {
     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
+    g_object_unref (session);
     return FALSE;
   }
 invalid_state:
   {
     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
+    g_object_unref (session);
     return FALSE;
   }
 }
@@ -333,6 +347,7 @@ handle_play_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *req
   GString *rtpinfo;
   guint n_streams, i;
   guint timestamp, seqnum;
+  gchar *str;
 
   if (!(session = ensure_session (client, request)))
     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);
 
   /* add the RTP-Info header */
-  gst_rtsp_message_add_header (&response, GST_RTSP_HDR_RTP_INFO, rtpinfo->str);
-  g_string_free (rtpinfo, TRUE);
+  str = g_string_free (rtpinfo, FALSE);
+  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);
 
@@ -395,11 +414,13 @@ no_session:
 not_found:
   {
     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
+    g_object_unref (session);
     return FALSE;
   }
 invalid_state:
   {
     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
+    g_object_unref (session);
     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 */
     if (!(session = gst_rtsp_session_pool_find (client->session_pool, sessid)))
       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;
   }
   else {
@@ -497,10 +523,15 @@ handle_setup_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *re
      * something. */
     if (!(session = gst_rtsp_session_pool_create (client->session_pool)))
       goto service_unavailable;
+
+    /* we need a new media configuration in this session */
+    media = NULL;
+
     need_session = TRUE;
   }
 
-  if (need_session) {
+  /* we have no media, find one and manage it */
+  if (media == NULL) {
     GstRTSPMedia *m;
 
     /* 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);
     }
   }
-  /* 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;
 
   /* get a handle to the stream in the media */
@@ -528,6 +560,7 @@ handle_setup_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *re
   code = GST_RTSP_STS_OK;
   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
 
+  /* add the new session header for new session ids */
   if (need_session)
     gst_rtsp_message_add_header (&response, GST_RTSP_HDR_SESSION, session->sessionid);
 
@@ -566,6 +599,7 @@ not_found:
 no_stream:
   {
     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
+    g_object_unref (media);
     return FALSE;
   }
 session_not_found:
@@ -628,6 +662,8 @@ handle_describe_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage
   if (!(sdp = gst_rtsp_sdp_from_media (media)))
     goto no_sdp;
 
+  g_object_unref (media);
+
   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, 
        gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
 
index b2e4cd6..d0f8ba0 100644 (file)
@@ -76,6 +76,8 @@ gst_rtsp_media_finalize (GObject * obj)
 
   media = GST_RTSP_MEDIA (obj);
 
+  g_message ("finalize media %p", media);
+
   for (i = 0; i < media->streams->len; i++) {
     GstRTSPMediaStream *stream;
 
@@ -447,6 +449,31 @@ setup_stream (GstRTSPMediaStream *stream, guint idx, GstRTSPMedia *media)
   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:
  * @obj: a #GstRTSPMedia
@@ -509,6 +536,9 @@ gst_rtsp_media_prepare (GstRTSPMedia *media)
   /* and back to PAUSED for live pipelines */
   ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED);
 
+  /* collect stats about the media */
+  collect_media_stats (media);
+
   /* unlock the udp src elements */
   n_streams = gst_rtsp_media_n_streams (media);
   for (i = 0; i < n_streams; i++) {
index dfb6e89..c00b165 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <gst/gst.h>
+#include <gst/rtsp/gstrtsprange.h>
 #include <gst/rtsp/gstrtspurl.h>
 
 #ifndef __GST_RTSP_MEDIA_H__
@@ -129,6 +130,9 @@ struct _GstRTSPMedia {
 
   /* for TCP transport */
   GstElement   *multifdsink;
+
+  /* the range of media */
+  GstRTSPTimeRange range;
 };
 
 struct _GstRTSPMediaClass {
index 52acf6e..74baa10 100644 (file)
@@ -33,6 +33,7 @@ gst_rtsp_sdp_from_media (GstRTSPMedia *media)
 {
   GstSDPMessage *sdp;
   guint i, n_streams;
+  gchar *rangestr;
 
   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_attribute (sdp, "tool", "GStreamer");
   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++) {
     GstRTSPMediaStream *stream;
index 968d241..4260498 100644 (file)
@@ -44,7 +44,7 @@ gst_rtsp_session_pool_init (GstRTSPSessionPool * pool)
 {
   pool->lock = g_mutex_new ();
   pool->sessions = g_hash_table_new_full (g_str_hash, g_str_equal,
-                 g_free, g_object_unref);
+                 NULL, g_object_unref);
 }
 
 static void
@@ -197,7 +197,7 @@ gst_rtsp_session_pool_remove (GstRTSPSessionPool *pool, GstRTSPSession *sess)
   g_return_if_fail (GST_IS_RTSP_SESSION (sess));
 
   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);
 }
 
index 8ccd733..8596ea7 100644 (file)
@@ -44,6 +44,8 @@ gst_rtsp_session_init (GstRTSPSession * session)
 static void
 gst_rtsp_session_free_stream (GstRTSPSessionStream *stream)
 {
+  g_message ("free session stream %p", stream);
+
   if (stream->trans.transport)
     gst_rtsp_transport_free (stream->trans.transport);
 
@@ -57,6 +59,8 @@ gst_rtsp_session_free_media (GstRTSPSessionMedia *media, GstRTSPSession *session
 
   size = media->streams->len;
 
+  g_message ("free session media %p", media);
+
   for (i = 0; i < size; i++) {
     GstRTSPSessionStream *stream;
 
@@ -83,6 +87,8 @@ gst_rtsp_session_finalize (GObject * obj)
 
   session = GST_RTSP_SESSION (obj);
 
+  g_message ("finalize session %p", session);
+
   /* free all media */
   g_list_foreach (session->medias, (GFunc) gst_rtsp_session_free_media,
                  session);
@@ -98,10 +104,12 @@ gst_rtsp_session_finalize (GObject * obj)
  * gst_rtsp_session_manage_media:
  * @sess: a #GstRTSPSession
  * @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
- * 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.
  */
@@ -129,19 +137,54 @@ gst_rtsp_session_manage_media (GstRTSPSession *sess, const GstRTSPUrl *uri,
 
   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;
 }
 
 /**
+ * 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:
  * @sess: a #GstRTSPSession
  * @url: the url for the media
  *
  * Get the session media of the @url.
  *
- * Returns: the configuration for @url in @sess.
+ * Returns: the configuration for @url in @sess. 
  */
 GstRTSPSessionMedia *
 gst_rtsp_session_get_media (GstRTSPSession *sess, const GstRTSPUrl *url)
index 150434a..08104f1 100644 (file)
@@ -107,10 +107,11 @@ GstRTSPSession *       gst_rtsp_session_new                  (const gchar *sessi
 GstRTSPSessionMedia *  gst_rtsp_session_manage_media         (GstRTSPSession *sess,
                                                               const GstRTSPUrl *uri,
                                                              GstRTSPMedia *media);
+gboolean               gst_rtsp_session_release_media        (GstRTSPSession *sess,
+                                                              GstRTSPSessionMedia *media);
 /* get media in a session */
 GstRTSPSessionMedia *  gst_rtsp_session_get_media            (GstRTSPSession *sess,
                                                               const GstRTSPUrl *uri);
-
 /* control media */
 gboolean               gst_rtsp_session_media_play           (GstRTSPSessionMedia *media);
 gboolean               gst_rtsp_session_media_pause          (GstRTSPSessionMedia *media);