rtsp: massive refactoring
authorWim Taymans <wim.taymans@collabora.co.uk>
Thu, 25 Oct 2012 19:29:58 +0000 (21:29 +0200)
committerWim Taymans <wim.taymans@collabora.co.uk>
Thu, 25 Oct 2012 19:29:58 +0000 (21:29 +0200)
Make GObjects from the remaining simple structures.
Remove GstRTSPSessionStream, it's not needed.
Rename GstRTSPMediaStream -> GstRTSPStream: It is shorter
Rename GstRTSPMediaTrans -> GstRTSPStreamTransport: It describes how
  a GstRTSPStream should be transported to a client.
Rename GstRTSPMediaFactory::get_element -> create_element because that
  more accurately describes what it does.
Make nice methods instead of poking in the structures.
Move some methods inside the relevant object source code.
Use GPtrArray to store objects instead of plain arrays, it is more
  natural and allows us to more easily clean up.
Move the allocation of udp ports to the Stream object. The Stream object
  contains the elements needed to stream the media to a client.
Improve the prepare and unprepare methods. Unprepare should now undo
  everything prepare did. Improve also async unprepare when doing EOS on
  shutdown. Make sure we always unprepare correctly.

17 files changed:
gst/rtsp-server/Makefile.am
gst/rtsp-server/rtsp-client.c
gst/rtsp-server/rtsp-client.h
gst/rtsp-server/rtsp-media-factory-uri.c
gst/rtsp-server/rtsp-media-factory.c
gst/rtsp-server/rtsp-media-factory.h
gst/rtsp-server/rtsp-media.c
gst/rtsp-server/rtsp-media.h
gst/rtsp-server/rtsp-sdp.c
gst/rtsp-server/rtsp-session-media.c [new file with mode: 0644]
gst/rtsp-server/rtsp-session-media.h [new file with mode: 0644]
gst/rtsp-server/rtsp-session.c
gst/rtsp-server/rtsp-session.h
gst/rtsp-server/rtsp-stream-transport.c [new file with mode: 0644]
gst/rtsp-server/rtsp-stream-transport.h [new file with mode: 0644]
gst/rtsp-server/rtsp-stream.c [new file with mode: 0644]
gst/rtsp-server/rtsp-stream.h [new file with mode: 0644]

index bbc6632..2d6a603 100644 (file)
@@ -6,7 +6,10 @@ public_headers = \
                rtsp-media-factory.h \
                rtsp-media-factory-uri.h \
                rtsp-media-mapping.h \
+               rtsp-stream.h \
+               rtsp-stream-transport.h \
                rtsp-session.h \
+               rtsp-session-media.h \
                rtsp-session-pool.h \
                rtsp-client.h \
                rtsp-server.h
@@ -19,7 +22,10 @@ c_sources = \
        rtsp-media-factory.c \
        rtsp-media-factory-uri.c \
        rtsp-media-mapping.c \
+       rtsp-stream.c \
+       rtsp-stream-transport.c \
        rtsp-session.c \
+       rtsp-session-media.c \
        rtsp-session-pool.c \
        rtsp-client.c \
        rtsp-server.c
index 659b499..c78aedd 100644 (file)
@@ -69,7 +69,7 @@ static void gst_rtsp_client_finalize (GObject * obj);
 static GstSDPMessage *create_sdp (GstRTSPClient * client, GstRTSPMedia * media);
 static void client_session_finalized (GstRTSPClient * client,
     GstRTSPSession * session);
-static void unlink_session_streams (GstRTSPClient * client,
+static void unlink_session_transports (GstRTSPClient * client,
     GstRTSPSession * session, GstRTSPSessionMedia * media);
 
 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
@@ -184,7 +184,7 @@ client_unlink_session (GstRTSPClient * client, GstRTSPSession * session)
     GstRTSPSessionMedia *media = g_list_first (session->medias)->data;
 
     gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
-    unlink_session_streams (client, session, media);
+    unlink_session_transports (client, session, media);
     /* unmanage the media in the session. this will modify session->medias */
     gst_rtsp_session_release_media (session, media);
   }
@@ -480,6 +480,7 @@ do_send_data (GstBuffer * buffer, guint8 channel, GstRTSPClient * client)
 
   gst_rtsp_message_init_data (&message, channel);
 
+  /* FIXME, need some sort of iovec RTSPMessage here */
   if (!gst_buffer_map (buffer, &map_info, GST_MAP_READ))
     return FALSE;
 
@@ -498,48 +499,49 @@ do_send_data (GstBuffer * buffer, guint8 channel, GstRTSPClient * client)
 }
 
 static void
-link_stream (GstRTSPClient * client, GstRTSPSession * session,
-    GstRTSPSessionStream * stream)
+link_transport (GstRTSPClient * client, GstRTSPSession * session,
+    GstRTSPStreamTransport * trans)
 {
-  GST_DEBUG ("client %p: linking stream %p", client, stream);
-  gst_rtsp_session_stream_set_callbacks (stream, (GstRTSPSendFunc) do_send_data,
+  GST_DEBUG ("client %p: linking transport %p", client, trans);
+  gst_rtsp_stream_transport_set_callbacks (trans,
+      (GstRTSPSendFunc) do_send_data,
       (GstRTSPSendFunc) do_send_data, client, NULL);
-  client->streams = g_list_prepend (client->streams, stream);
+  client->transports = g_list_prepend (client->transports, trans);
   /* make sure our session can't expire */
   gst_rtsp_session_prevent_expire (session);
 }
 
 static void
-unlink_stream (GstRTSPClient * client, GstRTSPSession * session,
-    GstRTSPSessionStream * stream)
+unlink_transport (GstRTSPClient * client, GstRTSPSession * session,
+    GstRTSPStreamTransport * trans)
 {
-  GST_DEBUG ("client %p: unlinking stream %p", client, stream);
-  gst_rtsp_session_stream_set_callbacks (stream, NULL, NULL, NULL, NULL);
-  client->streams = g_list_remove (client->streams, stream);
+  GST_DEBUG ("client %p: unlinking transport %p", client, trans);
+  gst_rtsp_stream_transport_set_callbacks (trans, NULL, NULL, NULL, NULL);
+  client->transports = g_list_remove (client->transports, trans);
   /* our session can now expire */
   gst_rtsp_session_allow_expire (session);
 }
 
 static void
-unlink_session_streams (GstRTSPClient * client, GstRTSPSession * session,
+unlink_session_transports (GstRTSPClient * client, GstRTSPSession * session,
     GstRTSPSessionMedia * media)
 {
   guint n_streams, i;
 
   n_streams = gst_rtsp_media_n_streams (media->media);
   for (i = 0; i < n_streams; i++) {
-    GstRTSPSessionStream *sstream;
+    GstRTSPStreamTransport *trans;
     GstRTSPTransport *tr;
 
     /* get the stream as configured in the session */
-    sstream = gst_rtsp_session_media_get_stream (media, i);
+    trans = gst_rtsp_session_media_get_transport (media, i);
     /* get the transport, if there is no transport configured, skip this stream */
-    if (!(tr = sstream->trans.transport))
+    if (!(tr = trans->transport))
       continue;
 
     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
       /* for TCP, unlink the stream from the TCP connection of the client */
-      unlink_stream (client, session, sstream);
+      unlink_transport (client, session, trans);
     }
   }
 }
@@ -581,7 +583,7 @@ handle_teardown_request (GstRTSPClient * client, GstRTSPClientState * state)
   state->sessmedia = media;
 
   /* unlink the all TCP callbacks */
-  unlink_session_streams (client, session, media);
+  unlink_session_transports (client, session, media);
 
   /* remove the session from the watched sessions */
   g_object_weak_unref (G_OBJECT (session),
@@ -722,7 +724,7 @@ handle_pause_request (GstRTSPClient * client, GstRTSPClientState * state)
     goto invalid_state;
 
   /* unlink the all TCP callbacks */
-  unlink_session_streams (client, session, media);
+  unlink_session_transports (client, session, media);
 
   /* then pause sending */
   gst_rtsp_session_media_set_state (media, GST_STATE_PAUSED);
@@ -769,7 +771,6 @@ handle_play_request (GstRTSPClient * client, GstRTSPClientState * state)
   GstRTSPStatusCode code;
   GString *rtpinfo;
   guint n_streams, i, infocount;
-  guint timestamp, seqnum;
   gchar *str;
   GstRTSPTimeRange *range;
   GstRTSPResult res;
@@ -805,44 +806,31 @@ handle_play_request (GstRTSPClient * client, GstRTSPClientState * state)
 
   n_streams = gst_rtsp_media_n_streams (media->media);
   for (i = 0, infocount = 0; i < n_streams; i++) {
-    GstRTSPSessionStream *sstream;
-    GstRTSPMediaStream *stream;
+    GstRTSPStreamTransport *trans;
     GstRTSPTransport *tr;
-    GObjectClass *payobjclass;
     gchar *uristr;
+    guint rtptime, seq;
 
     /* get the stream as configured in the session */
-    sstream = gst_rtsp_session_media_get_stream (media, i);
+    trans = gst_rtsp_session_media_get_transport (media, i);
     /* get the transport, if there is no transport configured, skip this stream */
-    if (!(tr = sstream->trans.transport)) {
+    if (!(tr = trans->transport)) {
       GST_INFO ("stream %d is not configured", i);
       continue;
     }
 
     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
       /* for TCP, link the stream to the TCP connection of the client */
-      link_stream (client, session, sstream);
+      link_transport (client, session, trans);
     }
 
-    stream = sstream->media_stream;
-
-    payobjclass = G_OBJECT_GET_CLASS (stream->payloader);
-
-    if (g_object_class_find_property (payobjclass, "seqnum") &&
-        g_object_class_find_property (payobjclass, "timestamp")) {
-      GObject *payobj;
-
-      payobj = G_OBJECT (stream->payloader);
-
-      /* only add RTP-Info for streams with seqnum and timestamp */
-      g_object_get (payobj, "seqnum", &seqnum, "timestamp", &timestamp, NULL);
-
+    if (gst_rtsp_stream_get_rtpinfo (trans->stream, &rtptime, &seq)) {
       if (infocount > 0)
         g_string_append (rtpinfo, ", ");
 
       uristr = gst_rtsp_url_get_request_uri (state->uri);
       g_string_append_printf (rtpinfo, "url=%s/stream=%d;seq=%u;rtptime=%u",
-          uristr, i, seqnum, timestamp);
+          uristr, i, seq, rtptime);
       g_free (uristr);
 
       infocount++;
@@ -944,7 +932,7 @@ handle_setup_request (GstRTSPClient * client, GstRTSPClientState * state)
   GstRTSPLowerTrans supported;
   GstRTSPStatusCode code;
   GstRTSPSession *session;
-  GstRTSPSessionStream *stream;
+  GstRTSPStreamTransport *trans;
   gchar *trans_str, *pos;
   guint streamid;
   GstRTSPSessionMedia *media;
@@ -1086,14 +1074,14 @@ handle_setup_request (GstRTSPClient * client, GstRTSPClientState * state)
     }
   }
 
-  /* get a handle to the stream in the media */
-  if (!(stream = gst_rtsp_session_media_get_stream (media, streamid)))
-    goto no_stream;
+  /* get a handle to the transport of the media in this session */
+  if (!(trans = gst_rtsp_session_media_get_transport (media, streamid)))
+    goto no_stream_transport;
 
-  st = gst_rtsp_session_stream_set_transport (stream, ct);
+  st = gst_rtsp_stream_transport_set_transport (trans, ct);
 
   /* configure keepalive for this transport */
-  gst_rtsp_session_stream_set_keepalive (stream,
+  gst_rtsp_stream_transport_set_keepalive (trans,
       (GstRTSPKeepAliveFunc) do_keepalive, session, NULL);
 
   /* serialize the server transport */
@@ -1149,7 +1137,7 @@ invalid_blocksize:
     gst_rtsp_transport_free (ct);
     return FALSE;
   }
-no_stream:
+no_stream_transport:
   {
     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, state);
     g_object_unref (session);
@@ -1265,7 +1253,6 @@ handle_describe_request (GstRTSPClient * client, GstRTSPClientState * state)
   if (!(media = find_media (client, state)))
     goto no_media;
 
-
   /* create an SDP for the media object on this client */
   if (!(sdp = klass->create_sdp (client, media)))
     goto no_sdp;
@@ -1567,28 +1554,28 @@ handle_data (GstRTSPClient * client, GstRTSPMessage * message)
   buffer = gst_buffer_new_wrapped (data, size);
 
   handled = FALSE;
-  for (walk = client->streams; walk; walk = g_list_next (walk)) {
-    GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
-    GstRTSPMediaStream *mstream;
+  for (walk = client->transports; walk; walk = g_list_next (walk)) {
+    GstRTSPStreamTransport *trans = (GstRTSPStreamTransport *) walk->data;
+    GstRTSPStream *stream;
     GstRTSPTransport *tr;
 
     /* get the transport, if there is no transport configured, skip this stream */
-    if (!(tr = stream->trans.transport))
+    if (!(tr = trans->transport))
       continue;
 
     /* we also need a media stream */
-    if (!(mstream = stream->media_stream))
+    if (!(stream = trans->stream))
       continue;
 
     /* check for TCP transport */
     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
       /* dispatch to the stream based on the channel number */
       if (tr->interleaved.min == channel) {
-        gst_rtsp_media_stream_rtp (mstream, buffer);
+        gst_rtsp_stream_recv_rtp (stream, buffer);
         handled = TRUE;
         break;
       } else if (tr->interleaved.max == channel) {
-        gst_rtsp_media_stream_rtcp (mstream, buffer);
+        gst_rtsp_stream_recv_rtcp (stream, buffer);
         handled = TRUE;
         break;
       }
index 4712d1e..cd08f7e 100644 (file)
@@ -33,6 +33,7 @@ typedef struct _GstRTSPClientState GstRTSPClientState;
 #include "rtsp-media.h"
 #include "rtsp-media-mapping.h"
 #include "rtsp-session-pool.h"
+#include "rtsp-session-media.h"
 #include "rtsp-auth.h"
 #include "rtsp-sdp.h"
 
@@ -58,7 +59,7 @@ typedef struct _GstRTSPClientState GstRTSPClientState;
  *
  * Information passed around containing the client state of a request.
  */
-struct _GstRTSPClientState{
+struct _GstRTSPClientState {
   GstRTSPMessage      *request;
   GstRTSPUrl          *uri;
   GstRTSPMethod        method;
@@ -81,7 +82,7 @@ struct _GstRTSPClientState{
  * @media_mapping: handle to the media mapping used by the client.
  * @uri: cached uri
  * @media: cached media
- * @streams: a list of streams using @connection.
+ * @transports: a list of #GstRTSPStreamTransport using @connection.
  * @sessions: a list of sessions managed by @connection.
  *
  * The client structure.
@@ -104,7 +105,7 @@ struct _GstRTSPClient {
   GstRTSPUrl     *uri;
   GstRTSPMedia   *media;
 
-  GList *streams;
+  GList *transports;
   GList *sessions;
 };
 
index f4f1bde..620b53e 100644 (file)
@@ -66,7 +66,7 @@ static void gst_rtsp_media_factory_uri_set_property (GObject * object,
     guint propid, const GValue * value, GParamSpec * pspec);
 static void gst_rtsp_media_factory_uri_finalize (GObject * obj);
 
-static GstElement *rtsp_media_factory_uri_get_element (GstRTSPMediaFactory *
+static GstElement *rtsp_media_factory_uri_create_element (GstRTSPMediaFactory *
     factory, const GstRTSPUrl * url);
 
 G_DEFINE_TYPE (GstRTSPMediaFactoryURI, gst_rtsp_media_factory_uri,
@@ -105,7 +105,7 @@ gst_rtsp_media_factory_uri_class_init (GstRTSPMediaFactoryURIClass * klass)
           "Use the gstpay payloader to avoid decoding", DEFAULT_USE_GSTPAY,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
-  mediafactory_class->get_element = rtsp_media_factory_uri_get_element;
+  mediafactory_class->create_element = rtsp_media_factory_uri_create_element;
 
   GST_DEBUG_CATEGORY_INIT (rtsp_media_factory_uri_debug, "rtspmediafactoryuri",
       0, "GstRTSPMediaFactoryUri");
@@ -528,7 +528,7 @@ no_more_pads_cb (GstElement * uribin, GstElement * element)
 }
 
 static GstElement *
-rtsp_media_factory_uri_get_element (GstRTSPMediaFactory * factory,
+rtsp_media_factory_uri_create_element (GstRTSPMediaFactory * factory,
     const GstRTSPUrl * url)
 {
   GstElement *topbin, *element, *uribin;
index 86db9fd..fb7af92 100644 (file)
@@ -58,7 +58,7 @@ static void gst_rtsp_media_factory_finalize (GObject * obj);
 
 static gchar *default_gen_key (GstRTSPMediaFactory * factory,
     const GstRTSPUrl * url);
-static GstElement *default_get_element (GstRTSPMediaFactory * factory,
+static GstElement *default_create_element (GstRTSPMediaFactory * factory,
     const GstRTSPUrl * url);
 static GstRTSPMedia *default_construct (GstRTSPMediaFactory * factory,
     const GstRTSPUrl * url);
@@ -139,7 +139,7 @@ gst_rtsp_media_factory_class_init (GstRTSPMediaFactoryClass * klass)
       G_TYPE_NONE, 1, GST_TYPE_RTSP_MEDIA);
 
   klass->gen_key = default_gen_key;
-  klass->get_element = default_get_element;
+  klass->create_element = default_create_element;
   klass->construct = default_construct;
   klass->configure = default_configure;
   klass->create_pipeline = default_create_pipeline;
@@ -578,13 +578,15 @@ media_unprepared (GstRTSPMedia * media, GstRTSPMediaFactory * factory)
  * @factory: a #GstRTSPMediaFactory
  * @url: the url used
  *
- * Prepare the media object and create its streams. Implementations
+ * Construct the media object and create its streams. Implementations
  * should create the needed gstreamer elements and add them to the result
  * object. No state changes should be performed on them yet.
  *
- * One or more GstRTSPMediaStream objects should be added to the result with
- * the srcpad member set to a source pad that produces buffer of type 
- * application/x-rtp.
+ * One or more GstRTSPStream objects should be created from the result
+ * with gst_rtsp_media_create_stream ().
+ *
+ * After the media is constructed, it can be configured and then prepared
+ * with gst_rtsp_media_prepare ().
  *
  * Returns: (transfer full): a new #GstRTSPMedia if the media could be prepared.
  */
@@ -596,6 +598,9 @@ gst_rtsp_media_factory_construct (GstRTSPMediaFactory * factory,
   GstRTSPMedia *media;
   GstRTSPMediaFactoryClass *klass;
 
+  g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), NULL);
+  g_return_val_if_fail (url != NULL, NULL);
+
   klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
 
   /* convert the url to a key for the hashtable. NULL return or a NULL function
@@ -676,7 +681,7 @@ default_gen_key (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
 }
 
 static GstElement *
-default_get_element (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
+default_create_element (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
 {
   GstElement *element;
   GError *error = NULL;
@@ -718,63 +723,6 @@ parse_error:
   }
 }
 
-/* try to find all the payloader elements, they should be named 'pay%d'. for
- * each of the payloaders we will create a stream and collect the source pad. */
-void
-gst_rtsp_media_factory_collect_streams (GstRTSPMediaFactory * factory,
-    const GstRTSPUrl * url, GstRTSPMedia * media)
-{
-  GstElement *element, *elem;
-  GstPad *pad;
-  gint i;
-  GstRTSPMediaStream *stream;
-  gboolean have_elem;
-
-  element = media->element;
-
-  have_elem = TRUE;
-  for (i = 0; have_elem; i++) {
-    gchar *name;
-
-    have_elem = FALSE;
-
-    name = g_strdup_printf ("pay%d", i);
-    if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
-      /* create the stream */
-      stream = g_new0 (GstRTSPMediaStream, 1);
-      stream->payloader = elem;
-
-      GST_INFO ("found stream %d with payloader %p", i, elem);
-
-      pad = gst_element_get_static_pad (elem, "src");
-
-      /* ghost the pad of the payloader to the element */
-      stream->srcpad = gst_ghost_pad_new (name, pad);
-      g_object_unref (pad);
-      gst_pad_set_active (stream->srcpad, TRUE);
-      gst_element_add_pad (media->element, stream->srcpad);
-      gst_object_unref (elem);
-
-      /* add stream now */
-      g_array_append_val (media->streams, stream);
-      have_elem = TRUE;
-    }
-    g_free (name);
-
-    name = g_strdup_printf ("dynpay%d", i);
-    if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
-      /* a stream that will dynamically create pads to provide RTP packets */
-
-      GST_INFO ("found dynamic element %d, %p", i, elem);
-
-      media->dynamic = g_list_prepend (media->dynamic, elem);
-
-      have_elem = TRUE;
-    }
-    g_free (name);
-  }
-}
-
 static GstRTSPMedia *
 default_construct (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
 {
@@ -787,10 +735,7 @@ default_construct (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
   if (!klass->create_pipeline)
     goto no_create;
 
-  if (klass->get_element)
-    element = klass->get_element (factory, url);
-  else
-    element = NULL;
+  element = gst_rtsp_media_factory_create_element (factory, url);
   if (element == NULL)
     goto no_element;
 
@@ -798,12 +743,12 @@ default_construct (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
   media = gst_rtsp_media_new ();
   media->element = element;
 
+  gst_rtsp_media_collect_streams (media);
+
   media->pipeline = klass->create_pipeline (factory, media);
   if (media->pipeline == NULL)
     goto no_pipeline;
 
-  gst_rtsp_media_factory_collect_streams (factory, url, media);
-
   return media;
 
   /* ERRORS */
@@ -879,16 +824,34 @@ default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
 }
 
 /**
- * gst_rtsp_media_factory_get_element:
+ * gst_rtsp_media_factory_create_element:
  * @factory: a #GstRTSPMediaFactory
  * @url: the url used
  *
+ * Construct and return a #GstElement that is a #GstBin containing
+ * the elements to use for streaming the media.
+ *
+ * The bin should contain payloaders pay%d for each stream. The default
+ * implementation of this function returns the bin created from the
+ * launch parameter.
+ *
  * Returns: (transfer floating) a new #GstElement.
  */
 GstElement *
-gst_rtsp_media_factory_get_element (GstRTSPMediaFactory * factory,
+gst_rtsp_media_factory_create_element (GstRTSPMediaFactory * factory,
     const GstRTSPUrl * url)
 {
-  GstRTSPMediaFactoryClass *klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
-  return klass->get_element (factory, url);
+  GstRTSPMediaFactoryClass *klass;
+  GstElement *result;
+
+  g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), NULL);
+
+  klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
+
+  if (klass->create_element)
+    result = klass->create_element (factory, url);
+  else
+    result = NULL;
+
+  return result;
 }
index ede1e0c..d20e502 100644 (file)
@@ -82,13 +82,13 @@ struct _GstRTSPMediaFactory {
  * @gen_key: convert @url to a key for caching shared #GstRTSPMedia objects.
  *       The default implementation of this function will use the complete URL
  *       including the query parameters to return a key.
- * @get_element: Construct and return a #GstElement that is a #GstBin containing
+ * @create_element: Construct and return a #GstElement that is a #GstBin containing
  *       the elements to use for streaming the media. The bin should contain
  *       payloaders pay%d for each stream. The default implementation of this
  *       function returns the bin created from the launch parameter.
  * @construct: the vmethod that will be called when the factory has to create the
  *       #GstRTSPMedia for @url. The default implementation of this
- *       function calls get_element to retrieve an element and then looks for
+ *       function calls create_element to retrieve an element and then looks for
  *       pay%d to create the streams.
  * @configure: configure the media created with @construct. The default
  *       implementation will configure the 'shared' property of the media.
@@ -104,7 +104,7 @@ struct _GstRTSPMediaFactoryClass {
 
   gchar *         (*gen_key)            (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
 
-  GstElement *    (*get_element)        (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
+  GstElement *    (*create_element)     (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
   GstRTSPMedia *  (*construct)          (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
   void            (*configure)          (GstRTSPMediaFactory *factory, GstRTSPMedia *media);
   GstElement *    (*create_pipeline)    (GstRTSPMediaFactory *factory, GstRTSPMedia *media);
@@ -145,14 +145,11 @@ void                  gst_rtsp_media_factory_set_multicast_group (GstRTSPMediaFa
 gchar *               gst_rtsp_media_factory_get_multicast_group (GstRTSPMediaFactory * factory);
 
 /* creating the media from the factory and a url */
-GstRTSPMedia *        gst_rtsp_media_factory_construct    (GstRTSPMediaFactory *factory,
-                                                           const GstRTSPUrl *url);
-
-void                  gst_rtsp_media_factory_collect_streams (GstRTSPMediaFactory *factory,
-                                                              const GstRTSPUrl *url,
-                                                              GstRTSPMedia *media);
-GstElement *          gst_rtsp_media_factory_get_element     (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
+GstRTSPMedia *        gst_rtsp_media_factory_construct       (GstRTSPMediaFactory *factory,
+                                                              const GstRTSPUrl *url);
+
+GstElement *          gst_rtsp_media_factory_create_element  (GstRTSPMediaFactory *factory,
+                                                              const GstRTSPUrl *url);
 
 G_END_DECLS
 
index c5efd27..7987a97 100644 (file)
@@ -59,8 +59,6 @@ enum
 GST_DEBUG_CATEGORY_STATIC (rtsp_media_debug);
 #define GST_CAT_DEFAULT rtsp_media_debug
 
-static GQuark ssrc_stream_map_key;
-
 static void gst_rtsp_media_get_property (GObject * object, guint propid,
     GValue * value, GParamSpec * pspec);
 static void gst_rtsp_media_set_property (GObject * object, guint propid,
@@ -70,6 +68,7 @@ static void gst_rtsp_media_finalize (GObject * obj);
 static gpointer do_loop (GstRTSPMediaClass * klass);
 static gboolean default_handle_message (GstRTSPMedia * media,
     GstMessage * message);
+static void finish_unprepare (GstRTSPMedia * media);
 static gboolean default_unprepare (GstRTSPMedia * media);
 static void unlock_streams (GstRTSPMedia * media);
 static void default_handle_mtu (GstRTSPMedia * media, guint mtu);
@@ -144,14 +143,12 @@ gst_rtsp_media_class_init (GstRTSPMediaClass * klass)
   klass->handle_message = default_handle_message;
   klass->unprepare = default_unprepare;
   klass->handle_mtu = default_handle_mtu;
-
-  ssrc_stream_map_key = g_quark_from_static_string ("GstRTSPServer.stream");
 }
 
 static void
 gst_rtsp_media_init (GstRTSPMedia * media)
 {
-  media->streams = g_array_new (FALSE, TRUE, sizeof (GstRTSPMediaStream *));
+  media->streams = g_ptr_array_new_with_free_func (g_object_unref);
   g_mutex_init (&media->lock);
   g_cond_init (&media->cond);
 
@@ -163,71 +160,20 @@ gst_rtsp_media_init (GstRTSPMedia * media)
   media->multicast_group = g_strdup (DEFAULT_MULTICAST_GROUP);
 }
 
-void
-gst_rtsp_media_trans_cleanup (GstRTSPMediaTrans * trans)
-{
-  if (trans->transport) {
-    gst_rtsp_transport_free (trans->transport);
-    trans->transport = NULL;
-  }
-  if (trans->rtpsource) {
-    g_object_set_qdata (trans->rtpsource, ssrc_stream_map_key, NULL);
-    trans->rtpsource = NULL;
-  }
-}
-
-static void
-gst_rtsp_media_stream_free (GstRTSPMediaStream * stream)
-{
-  if (stream->session)
-    g_object_unref (stream->session);
-
-  if (stream->caps)
-    gst_caps_unref (stream->caps);
-
-  if (stream->send_rtp_sink)
-    gst_object_unref (stream->send_rtp_sink);
-  if (stream->send_rtp_src)
-    gst_object_unref (stream->send_rtp_src);
-  if (stream->send_rtcp_src)
-    gst_object_unref (stream->send_rtcp_src);
-  if (stream->recv_rtcp_sink)
-    gst_object_unref (stream->recv_rtcp_sink);
-  if (stream->recv_rtp_sink)
-    gst_object_unref (stream->recv_rtp_sink);
-
-  g_list_free (stream->transports);
-
-  g_free (stream);
-}
-
 static void
 gst_rtsp_media_finalize (GObject * obj)
 {
   GstRTSPMedia *media;
-  guint i;
 
   media = GST_RTSP_MEDIA (obj);
 
   GST_INFO ("finalize media %p", media);
 
-  if (media->pipeline) {
-    unlock_streams (media);
-    gst_element_set_state (media->pipeline, GST_STATE_NULL);
-    gst_object_unref (media->pipeline);
-  }
-
-  for (i = 0; i < media->streams->len; i++) {
-    GstRTSPMediaStream *stream;
+  gst_rtsp_media_unprepare (media);
 
-    stream = g_array_index (media->streams, GstRTSPMediaStream *, i);
+  g_ptr_array_unref (media->streams);
 
-    gst_rtsp_media_stream_free (stream);
-  }
-  g_array_free (media->streams, TRUE);
-
-  g_list_foreach (media->dynamic, (GFunc) gst_object_unref, NULL);
-  g_list_free (media->dynamic);
+  g_list_free_full (media->dynamic, gst_object_unref);
 
   if (media->source) {
     g_source_destroy (media->source);
@@ -624,6 +570,104 @@ gst_rtsp_media_get_auth (GstRTSPMedia * media)
   return result;
 }
 
+/**
+ * gst_rtsp_media_collect_streams:
+ * @media: a #GstRTSPMedia
+ *
+ * Find all payloader elements, they should be named pay%d in the
+ * element of @media, and create #GstRTSPStreams for them.
+ *
+ * Collect all dynamic elements, named dynpay%d, and add them to
+ * the list of dynamic elements.
+ */
+void
+gst_rtsp_media_collect_streams (GstRTSPMedia * media)
+{
+  GstElement *element, *elem;
+  GstPad *pad;
+  gint i;
+  gboolean have_elem;
+
+  g_return_if_fail (GST_IS_RTSP_MEDIA (media));
+
+  element = media->element;
+
+  have_elem = TRUE;
+  for (i = 0; have_elem; i++) {
+    gchar *name;
+
+    have_elem = FALSE;
+
+    name = g_strdup_printf ("pay%d", i);
+    if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
+      GST_INFO ("found stream %d with payloader %p", i, elem);
+
+      /* take the pad of the payloader */
+      pad = gst_element_get_static_pad (elem, "src");
+      /* create the stream */
+      gst_rtsp_media_create_stream (media, elem, pad);
+      g_object_unref (pad);
+
+      gst_object_unref (elem);
+
+      have_elem = TRUE;
+    }
+    g_free (name);
+
+    name = g_strdup_printf ("dynpay%d", i);
+    if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
+      /* a stream that will dynamically create pads to provide RTP packets */
+
+      GST_INFO ("found dynamic element %d, %p", i, elem);
+
+      media->dynamic = g_list_prepend (media->dynamic, elem);
+
+      have_elem = TRUE;
+    }
+    g_free (name);
+  }
+}
+
+/**
+ * gst_rtsp_media_create_stream:
+ * @media: a #GstRTSPMedia
+ * @payloader: a #GstElement
+ * @srcpad: a source #GstPad
+ *
+ * Create a new stream in @media that provides RTP data on @srcpad.
+ * @srcpad should be a pad of an element inside @media->element.
+ *
+ * Returns: a new #GstRTSPStream that remains valid for as long
+ *          as @media exists.
+ */
+GstRTSPStream *
+gst_rtsp_media_create_stream (GstRTSPMedia * media, GstElement * payloader,
+    GstPad * pad)
+{
+  GstRTSPStream *stream;
+  GstPad *srcpad;
+  gchar *name;
+  gint idx;
+
+  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
+  g_return_val_if_fail (GST_IS_ELEMENT (payloader), NULL);
+  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+  g_return_val_if_fail (GST_PAD_IS_SRC (pad), NULL);
+
+  idx = media->streams->len;
+
+  name = g_strdup_printf ("src_%u", idx);
+  srcpad = gst_ghost_pad_new (name, pad);
+  gst_pad_set_active (srcpad, TRUE);
+  gst_element_add_pad (media->element, srcpad);
+  g_free (name);
+
+  stream = gst_rtsp_stream_new (idx, payloader, srcpad);
+
+  g_ptr_array_add (media->streams, stream);
+
+  return stream;
+}
 
 /**
  * gst_rtsp_media_n_streams:
@@ -648,18 +692,18 @@ gst_rtsp_media_n_streams (GstRTSPMedia * media)
  *
  * Retrieve the stream with index @idx from @media.
  *
- * Returns: the #GstRTSPMediaStream at index @idx or %NULL when a stream with
+ * Returns: the #GstRTSPStream at index @idx or %NULL when a stream with
  * that index did not exist.
  */
-GstRTSPMediaStream *
+GstRTSPStream *
 gst_rtsp_media_get_stream (GstRTSPMedia * media, guint idx)
 {
-  GstRTSPMediaStream *res;
+  GstRTSPStream *res;
 
   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
 
   if (idx < media->streams->len)
-    res = g_array_index (media->streams, GstRTSPMediaStream *, idx);
+    res = g_ptr_array_index (media->streams, idx);
   else
     res = NULL;
 
@@ -684,7 +728,7 @@ gst_rtsp_media_get_range_string (GstRTSPMedia * media, gboolean play)
   /* make copy */
   range = media->range;
 
-  if (!play && media->active > 0) {
+  if (!play && media->n_active > 0) {
     range.min.type = GST_RTSP_TIME_NOW;
     range.min.seconds = -1;
   }
@@ -797,657 +841,16 @@ weird_type:
   }
 }
 
-/**
- * gst_rtsp_media_stream_rtp:
- * @stream: a #GstRTSPMediaStream
- * @buffer: a #GstBuffer
- *
- * Handle an RTP buffer for the stream. This method is usually called when a
- * message has been received from a client using the TCP transport.
- *
- * This function takes ownership of @buffer.
- *
- * Returns: a GstFlowReturn.
- */
-GstFlowReturn
-gst_rtsp_media_stream_rtp (GstRTSPMediaStream * stream, GstBuffer * buffer)
-{
-  GstFlowReturn ret;
-
-  ret = gst_app_src_push_buffer (GST_APP_SRC_CAST (stream->appsrc[0]), buffer);
-
-  return ret;
-}
-
-/**
- * gst_rtsp_media_stream_rtcp:
- * @stream: a #GstRTSPMediaStream
- * @buffer: a #GstBuffer
- *
- * Handle an RTCP buffer for the stream. This method is usually called when a
- * message has been received from a client using the TCP transport.
- *
- * This function takes ownership of @buffer.
- *
- * Returns: a GstFlowReturn.
- */
-GstFlowReturn
-gst_rtsp_media_stream_rtcp (GstRTSPMediaStream * stream, GstBuffer * buffer)
-{
-  GstFlowReturn ret;
-
-  ret = gst_app_src_push_buffer (GST_APP_SRC_CAST (stream->appsrc[1]), buffer);
-
-  return ret;
-}
-
-/* Allocate the udp ports and sockets */
-static gboolean
-alloc_udp_ports (GstRTSPMedia * media, GstRTSPMediaStream * stream)
-{
-  GstStateChangeReturn ret;
-  GstElement *udpsrc0, *udpsrc1;
-  GstElement *udpsink0, *udpsink1;
-  gint tmp_rtp, tmp_rtcp;
-  guint count;
-  gint rtpport, rtcpport;
-  GSocket *socket;
-  const gchar *host;
-
-  udpsrc0 = NULL;
-  udpsrc1 = NULL;
-  udpsink0 = NULL;
-  udpsink1 = NULL;
-  count = 0;
-
-  /* Start with random port */
-  tmp_rtp = 0;
-
-  if (media->is_ipv6)
-    host = "udp://[::0]";
-  else
-    host = "udp://0.0.0.0";
-
-  /* try to allocate 2 UDP ports, the RTP port should be an even
-   * number and the RTCP port should be the next (uneven) port */
-again:
-  udpsrc0 = gst_element_make_from_uri (GST_URI_SRC, host, NULL, NULL);
-  if (udpsrc0 == NULL)
-    goto no_udp_protocol;
-  g_object_set (G_OBJECT (udpsrc0), "port", tmp_rtp, NULL);
-
-  ret = gst_element_set_state (udpsrc0, GST_STATE_PAUSED);
-  if (ret == GST_STATE_CHANGE_FAILURE) {
-    if (tmp_rtp != 0) {
-      tmp_rtp += 2;
-      if (++count > 20)
-        goto no_ports;
-
-      gst_element_set_state (udpsrc0, GST_STATE_NULL);
-      gst_object_unref (udpsrc0);
-
-      goto again;
-    }
-    goto no_udp_protocol;
-  }
-
-  g_object_get (G_OBJECT (udpsrc0), "port", &tmp_rtp, NULL);
-
-  /* check if port is even */
-  if ((tmp_rtp & 1) != 0) {
-    /* port not even, close and allocate another */
-    if (++count > 20)
-      goto no_ports;
-
-    gst_element_set_state (udpsrc0, GST_STATE_NULL);
-    gst_object_unref (udpsrc0);
-
-    tmp_rtp++;
-    goto again;
-  }
-
-  /* allocate port+1 for RTCP now */
-  udpsrc1 = gst_element_make_from_uri (GST_URI_SRC, host, NULL, NULL);
-  if (udpsrc1 == NULL)
-    goto no_udp_rtcp_protocol;
-
-  /* set port */
-  tmp_rtcp = tmp_rtp + 1;
-  g_object_set (G_OBJECT (udpsrc1), "port", tmp_rtcp, NULL);
-
-  ret = gst_element_set_state (udpsrc1, GST_STATE_PAUSED);
-  /* tmp_rtcp port is busy already : retry to make rtp/rtcp pair */
-  if (ret == GST_STATE_CHANGE_FAILURE) {
-
-    if (++count > 20)
-      goto no_ports;
-
-    gst_element_set_state (udpsrc0, GST_STATE_NULL);
-    gst_object_unref (udpsrc0);
-
-    gst_element_set_state (udpsrc1, GST_STATE_NULL);
-    gst_object_unref (udpsrc1);
-
-    tmp_rtp += 2;
-    goto again;
-  }
-
-  /* all fine, do port check */
-  g_object_get (G_OBJECT (udpsrc0), "port", &rtpport, NULL);
-  g_object_get (G_OBJECT (udpsrc1), "port", &rtcpport, NULL);
-
-  /* this should not happen... */
-  if (rtpport != tmp_rtp || rtcpport != tmp_rtcp)
-    goto port_error;
-
-  udpsink0 = gst_element_factory_make ("multiudpsink", NULL);
-  if (!udpsink0)
-    goto no_udp_protocol;
-
-  g_object_get (G_OBJECT (udpsrc0), "socket", &socket, NULL);
-  g_object_set (G_OBJECT (udpsink0), "socket", socket, NULL);
-  g_object_set (G_OBJECT (udpsink0), "close-socket", FALSE, NULL);
-
-  udpsink1 = gst_element_factory_make ("multiudpsink", NULL);
-  if (!udpsink1)
-    goto no_udp_protocol;
-
-  if (g_object_class_find_property (G_OBJECT_GET_CLASS (udpsink0),
-          "send-duplicates")) {
-    g_object_set (G_OBJECT (udpsink0), "send-duplicates", FALSE, NULL);
-    g_object_set (G_OBJECT (udpsink1), "send-duplicates", FALSE, NULL);
-  } else {
-    g_warning
-        ("old multiudpsink version found without send-duplicates property");
-  }
-
-  if (g_object_class_find_property (G_OBJECT_GET_CLASS (udpsink0),
-          "buffer-size")) {
-    g_object_set (G_OBJECT (udpsink0), "buffer-size", media->buffer_size, NULL);
-  } else {
-    GST_WARNING ("multiudpsink version found without buffer-size property");
-  }
-
-  g_object_get (G_OBJECT (udpsrc1), "socket", &socket, NULL);
-  g_object_set (G_OBJECT (udpsink1), "socket", socket, NULL);
-  g_object_set (G_OBJECT (udpsink1), "close-socket", FALSE, NULL);
-  g_object_set (G_OBJECT (udpsink1), "sync", FALSE, NULL);
-  g_object_set (G_OBJECT (udpsink1), "async", FALSE, NULL);
-
-  g_object_set (G_OBJECT (udpsink0), "auto-multicast", FALSE, NULL);
-  g_object_set (G_OBJECT (udpsink0), "loop", FALSE, NULL);
-  g_object_set (G_OBJECT (udpsink1), "auto-multicast", FALSE, NULL);
-  g_object_set (G_OBJECT (udpsink1), "loop", FALSE, NULL);
-
-  /* we keep these elements, we configure all in configure_transport when the
-   * server told us to really use the UDP ports. */
-  stream->udpsrc[0] = udpsrc0;
-  stream->udpsrc[1] = udpsrc1;
-  stream->udpsink[0] = udpsink0;
-  stream->udpsink[1] = udpsink1;
-  stream->server_port.min = rtpport;
-  stream->server_port.max = rtcpport;
-
-  return TRUE;
-
-  /* ERRORS */
-no_udp_protocol:
-  {
-    goto cleanup;
-  }
-no_ports:
-  {
-    goto cleanup;
-  }
-no_udp_rtcp_protocol:
-  {
-    goto cleanup;
-  }
-port_error:
-  {
-    goto cleanup;
-  }
-cleanup:
-  {
-    if (udpsrc0) {
-      gst_element_set_state (udpsrc0, GST_STATE_NULL);
-      gst_object_unref (udpsrc0);
-    }
-    if (udpsrc1) {
-      gst_element_set_state (udpsrc1, GST_STATE_NULL);
-      gst_object_unref (udpsrc1);
-    }
-    if (udpsink0) {
-      gst_element_set_state (udpsink0, GST_STATE_NULL);
-      gst_object_unref (udpsink0);
-    }
-    if (udpsink1) {
-      gst_element_set_state (udpsink1, GST_STATE_NULL);
-      gst_object_unref (udpsink1);
-    }
-    return FALSE;
-  }
-}
-
-/* executed from streaming thread */
-static void
-caps_notify (GstPad * pad, GParamSpec * unused, GstRTSPMediaStream * stream)
-{
-  gchar *capsstr;
-  GstCaps *newcaps, *oldcaps;
-
-  newcaps = gst_pad_get_current_caps (pad);
-
-  oldcaps = stream->caps;
-  stream->caps = newcaps;
-
-  if (oldcaps)
-    gst_caps_unref (oldcaps);
-
-  capsstr = gst_caps_to_string (newcaps);
-  GST_INFO ("stream %p received caps %p, %s", stream, newcaps, capsstr);
-  g_free (capsstr);
-}
-
-static void
-dump_structure (const GstStructure * s)
-{
-  gchar *sstr;
-
-  sstr = gst_structure_to_string (s);
-  GST_INFO ("structure: %s", sstr);
-  g_free (sstr);
-}
-
-static GstRTSPMediaTrans *
-find_transport (GstRTSPMediaStream * stream, const gchar * rtcp_from)
-{
-  GList *walk;
-  GstRTSPMediaTrans *result = NULL;
-  const gchar *tmp;
-  gchar *dest;
-  guint port;
-
-  if (rtcp_from == NULL)
-    return NULL;
-
-  tmp = g_strrstr (rtcp_from, ":");
-  if (tmp == NULL)
-    return NULL;
-
-  port = atoi (tmp + 1);
-  dest = g_strndup (rtcp_from, tmp - rtcp_from);
-
-  GST_INFO ("finding %s:%d in %d transports", dest, port,
-      g_list_length (stream->transports));
-
-  for (walk = stream->transports; walk; walk = g_list_next (walk)) {
-    GstRTSPMediaTrans *trans = walk->data;
-    gint min, max;
-
-    min = trans->transport->client_port.min;
-    max = trans->transport->client_port.max;
-
-    if ((strcmp (trans->transport->destination, dest) == 0) && (min == port
-            || max == port)) {
-      result = trans;
-      break;
-    }
-  }
-  g_free (dest);
-
-  return result;
-}
-
-static GstRTSPMediaTrans *
-check_transport (GObject * source, GstRTSPMediaStream * stream)
-{
-  GstStructure *stats;
-  GstRTSPMediaTrans *trans;
-
-  /* see if we have a stream to match with the origin of the RTCP packet */
-  trans = g_object_get_qdata (source, ssrc_stream_map_key);
-  if (trans == NULL) {
-    g_object_get (source, "stats", &stats, NULL);
-    if (stats) {
-      const gchar *rtcp_from;
-
-      dump_structure (stats);
-
-      rtcp_from = gst_structure_get_string (stats, "rtcp-from");
-      if ((trans = find_transport (stream, rtcp_from))) {
-        GST_INFO ("%p: found transport %p for source  %p", stream, trans,
-            source);
-
-        /* keep ref to the source */
-        trans->rtpsource = source;
-
-        g_object_set_qdata (source, ssrc_stream_map_key, trans);
-      }
-      gst_structure_free (stats);
-    }
-  }
-
-  return trans;
-}
-
-static void
-on_new_ssrc (GObject * session, GObject * source, GstRTSPMediaStream * stream)
-{
-  GstRTSPMediaTrans *trans;
-
-  GST_INFO ("%p: new source %p", stream, source);
-
-  trans = check_transport (source, stream);
-
-  if (trans)
-    GST_INFO ("%p: source %p for transport %p", stream, source, trans);
-}
-
-static void
-on_ssrc_sdes (GObject * session, GObject * source, GstRTSPMediaStream * stream)
-{
-  GST_INFO ("%p: new SDES %p", stream, source);
-}
-
-static void
-on_ssrc_active (GObject * session, GObject * source,
-    GstRTSPMediaStream * stream)
-{
-  GstRTSPMediaTrans *trans;
-
-  trans = check_transport (source, stream);
-
-  if (trans)
-    GST_INFO ("%p: source %p in transport %p is active", stream, source, trans);
-
-  if (trans && trans->keep_alive)
-    trans->keep_alive (trans->ka_user_data);
-
-#ifdef DUMP_STATS
-  {
-    GstStructure *stats;
-    g_object_get (source, "stats", &stats, NULL);
-    if (stats) {
-      dump_structure (stats);
-      gst_structure_free (stats);
-    }
-  }
-#endif
-}
-
-static void
-on_bye_ssrc (GObject * session, GObject * source, GstRTSPMediaStream * stream)
-{
-  GST_INFO ("%p: source %p bye", stream, source);
-}
-
-static void
-on_bye_timeout (GObject * session, GObject * source,
-    GstRTSPMediaStream * stream)
-{
-  GstRTSPMediaTrans *trans;
-
-  GST_INFO ("%p: source %p bye timeout", stream, source);
-
-  if ((trans = g_object_get_qdata (source, ssrc_stream_map_key))) {
-    trans->rtpsource = NULL;
-    trans->timeout = TRUE;
-  }
-}
-
-static void
-on_timeout (GObject * session, GObject * source, GstRTSPMediaStream * stream)
-{
-  GstRTSPMediaTrans *trans;
-
-  GST_INFO ("%p: source %p timeout", stream, source);
-
-  if ((trans = g_object_get_qdata (source, ssrc_stream_map_key))) {
-    trans->rtpsource = NULL;
-    trans->timeout = TRUE;
-  }
-}
-
-static GstFlowReturn
-handle_new_sample (GstAppSink * sink, gpointer user_data)
-{
-  GList *walk;
-  GstSample *sample;
-  GstBuffer *buffer;
-  GstRTSPMediaStream *stream;
-
-  sample = gst_app_sink_pull_sample (sink);
-  if (!sample)
-    return GST_FLOW_OK;
-
-  stream = (GstRTSPMediaStream *) user_data;
-  buffer = gst_sample_get_buffer (sample);
-
-  for (walk = stream->transports; walk; walk = g_list_next (walk)) {
-    GstRTSPMediaTrans *tr = (GstRTSPMediaTrans *) walk->data;
-
-    if (GST_ELEMENT_CAST (sink) == stream->appsink[0]) {
-      if (tr->send_rtp)
-        tr->send_rtp (buffer, tr->transport->interleaved.min, tr->user_data);
-    } else {
-      if (tr->send_rtcp)
-        tr->send_rtcp (buffer, tr->transport->interleaved.max, tr->user_data);
-    }
-  }
-  gst_sample_unref (sample);
-
-  return GST_FLOW_OK;
-}
-
-static GstAppSinkCallbacks sink_cb = {
-  NULL,                         /* not interested in EOS */
-  NULL,                         /* not interested in preroll samples */
-  handle_new_sample,
-};
-
-/* prepare the pipeline objects to handle @stream in @media */
-static gboolean
-setup_stream (GstRTSPMediaStream * stream, guint idx, GstRTSPMedia * media)
-{
-  gchar *name;
-  GstPad *pad, *teepad, *queuepad, *selpad;
-  GstPadLinkReturn ret;
-  gint i;
-
-  /* allocate udp ports, we will have 4 of them, 2 for receiving RTP/RTCP and 2
-   * for sending RTP/RTCP. The sender and receiver ports are shared between the
-   * elements */
-  if (!alloc_udp_ports (media, stream))
-    return FALSE;
-
-  /* add the ports to the pipeline */
-  for (i = 0; i < 2; i++) {
-    gst_bin_add (GST_BIN_CAST (media->pipeline), stream->udpsink[i]);
-    gst_bin_add (GST_BIN_CAST (media->pipeline), stream->udpsrc[i]);
-  }
-
-  /* create elements for the TCP transfer */
-  for (i = 0; i < 2; i++) {
-    stream->appsrc[i] = gst_element_factory_make ("appsrc", NULL);
-    stream->appqueue[i] = gst_element_factory_make ("queue", NULL);
-    stream->appsink[i] = gst_element_factory_make ("appsink", NULL);
-    g_object_set (stream->appsink[i], "async", FALSE, "sync", FALSE, NULL);
-    g_object_set (stream->appsink[i], "emit-signals", FALSE, NULL);
-    gst_bin_add (GST_BIN_CAST (media->pipeline), stream->appqueue[i]);
-    gst_bin_add (GST_BIN_CAST (media->pipeline), stream->appsink[i]);
-    gst_bin_add (GST_BIN_CAST (media->pipeline), stream->appsrc[i]);
-    gst_app_sink_set_callbacks (GST_APP_SINK_CAST (stream->appsink[i]),
-        &sink_cb, stream, NULL);
-  }
-
-  /* hook up the stream to the RTP session elements. */
-  name = g_strdup_printf ("send_rtp_sink_%u", idx);
-  stream->send_rtp_sink = gst_element_get_request_pad (media->rtpbin, name);
-  g_free (name);
-  name = g_strdup_printf ("send_rtp_src_%u", idx);
-  stream->send_rtp_src = gst_element_get_static_pad (media->rtpbin, name);
-  g_free (name);
-  name = g_strdup_printf ("send_rtcp_src_%u", idx);
-  stream->send_rtcp_src = gst_element_get_request_pad (media->rtpbin, name);
-  g_free (name);
-  name = g_strdup_printf ("recv_rtcp_sink_%u", idx);
-  stream->recv_rtcp_sink = gst_element_get_request_pad (media->rtpbin, name);
-  g_free (name);
-  name = g_strdup_printf ("recv_rtp_sink_%u", idx);
-  stream->recv_rtp_sink = gst_element_get_request_pad (media->rtpbin, name);
-  g_free (name);
-
-  /* get the session */
-  g_signal_emit_by_name (media->rtpbin, "get-internal-session", idx,
-      &stream->session);
-
-  g_signal_connect (stream->session, "on-new-ssrc", (GCallback) on_new_ssrc,
-      stream);
-  g_signal_connect (stream->session, "on-ssrc-sdes", (GCallback) on_ssrc_sdes,
-      stream);
-  g_signal_connect (stream->session, "on-ssrc-active",
-      (GCallback) on_ssrc_active, stream);
-  g_signal_connect (stream->session, "on-bye-ssrc", (GCallback) on_bye_ssrc,
-      stream);
-  g_signal_connect (stream->session, "on-bye-timeout",
-      (GCallback) on_bye_timeout, stream);
-  g_signal_connect (stream->session, "on-timeout", (GCallback) on_timeout,
-      stream);
-
-  /* link the RTP pad to the session manager */
-  ret = gst_pad_link (stream->srcpad, stream->send_rtp_sink);
-  if (ret != GST_PAD_LINK_OK)
-    goto link_failed;
-
-  /* make tee for RTP and link to stream */
-  stream->tee[0] = gst_element_factory_make ("tee", NULL);
-  gst_bin_add (GST_BIN_CAST (media->pipeline), stream->tee[0]);
-
-  pad = gst_element_get_static_pad (stream->tee[0], "sink");
-  gst_pad_link (stream->send_rtp_src, pad);
-  gst_object_unref (pad);
-
-  /* link RTP sink, we're pretty sure this will work. */
-  teepad = gst_element_get_request_pad (stream->tee[0], "src_%u");
-  pad = gst_element_get_static_pad (stream->udpsink[0], "sink");
-  gst_pad_link (teepad, pad);
-  gst_object_unref (pad);
-  gst_object_unref (teepad);
-
-  teepad = gst_element_get_request_pad (stream->tee[0], "src_%u");
-  pad = gst_element_get_static_pad (stream->appqueue[0], "sink");
-  gst_pad_link (teepad, pad);
-  gst_object_unref (pad);
-  gst_object_unref (teepad);
-
-  queuepad = gst_element_get_static_pad (stream->appqueue[0], "src");
-  pad = gst_element_get_static_pad (stream->appsink[0], "sink");
-  gst_pad_link (queuepad, pad);
-  gst_object_unref (pad);
-  gst_object_unref (queuepad);
-
-  /* make tee for RTCP */
-  stream->tee[1] = gst_element_factory_make ("tee", NULL);
-  gst_bin_add (GST_BIN_CAST (media->pipeline), stream->tee[1]);
-
-  pad = gst_element_get_static_pad (stream->tee[1], "sink");
-  gst_pad_link (stream->send_rtcp_src, pad);
-  gst_object_unref (pad);
-
-  /* link RTCP elements */
-  teepad = gst_element_get_request_pad (stream->tee[1], "src_%u");
-  pad = gst_element_get_static_pad (stream->udpsink[1], "sink");
-  gst_pad_link (teepad, pad);
-  gst_object_unref (pad);
-  gst_object_unref (teepad);
-
-  teepad = gst_element_get_request_pad (stream->tee[1], "src_%u");
-  pad = gst_element_get_static_pad (stream->appqueue[1], "sink");
-  gst_pad_link (teepad, pad);
-  gst_object_unref (pad);
-  gst_object_unref (teepad);
-
-  queuepad = gst_element_get_static_pad (stream->appqueue[1], "src");
-  pad = gst_element_get_static_pad (stream->appsink[1], "sink");
-  gst_pad_link (queuepad, pad);
-  gst_object_unref (pad);
-  gst_object_unref (queuepad);
-
-  /* make selector for the RTP receivers */
-  stream->selector[0] = gst_element_factory_make ("funnel", NULL);
-  gst_bin_add (GST_BIN_CAST (media->pipeline), stream->selector[0]);
-
-  pad = gst_element_get_static_pad (stream->selector[0], "src");
-  gst_pad_link (pad, stream->recv_rtp_sink);
-  gst_object_unref (pad);
-
-  selpad = gst_element_get_request_pad (stream->selector[0], "sink_%u");
-  pad = gst_element_get_static_pad (stream->udpsrc[0], "src");
-  gst_pad_link (pad, selpad);
-  gst_object_unref (pad);
-  gst_object_unref (selpad);
-
-  selpad = gst_element_get_request_pad (stream->selector[0], "sink_%u");
-  pad = gst_element_get_static_pad (stream->appsrc[0], "src");
-  gst_pad_link (pad, selpad);
-  gst_object_unref (pad);
-  gst_object_unref (selpad);
-
-  /* make selector for the RTCP receivers */
-  stream->selector[1] = gst_element_factory_make ("funnel", NULL);
-  gst_bin_add (GST_BIN_CAST (media->pipeline), stream->selector[1]);
-
-  pad = gst_element_get_static_pad (stream->selector[1], "src");
-  gst_pad_link (pad, stream->recv_rtcp_sink);
-  gst_object_unref (pad);
-
-  selpad = gst_element_get_request_pad (stream->selector[1], "sink_%u");
-  pad = gst_element_get_static_pad (stream->udpsrc[1], "src");
-  gst_pad_link (pad, selpad);
-  gst_object_unref (pad);
-  gst_object_unref (selpad);
-
-  selpad = gst_element_get_request_pad (stream->selector[1], "sink_%u");
-  pad = gst_element_get_static_pad (stream->appsrc[1], "src");
-  gst_pad_link (pad, selpad);
-  gst_object_unref (pad);
-  gst_object_unref (selpad);
-
-  /* we set and keep these to playing so that they don't cause NO_PREROLL return
-   * values */
-  gst_element_set_state (stream->udpsrc[0], GST_STATE_PLAYING);
-  gst_element_set_state (stream->udpsrc[1], GST_STATE_PLAYING);
-  gst_element_set_locked_state (stream->udpsrc[0], TRUE);
-  gst_element_set_locked_state (stream->udpsrc[1], TRUE);
-
-  /* be notified of caps changes */
-  stream->caps_sig = g_signal_connect (stream->send_rtp_sink, "notify::caps",
-      (GCallback) caps_notify, stream);
-
-  stream->prepared = TRUE;
-
-  return TRUE;
-
-  /* ERRORS */
-link_failed:
-  {
-    GST_WARNING ("failed to link stream %d", idx);
-    return FALSE;
-  }
-}
-
 static void
 unlock_streams (GstRTSPMedia * media)
 {
-  guint i, n_streams;
+  guint i;
 
   /* unlock the udp src elements */
-  n_streams = gst_rtsp_media_n_streams (media);
-  for (i = 0; i < n_streams; i++) {
-    GstRTSPMediaStream *stream;
+  for (i = 0; i < media->streams->len; i++) {
+    GstRTSPStream *stream;
 
-    stream = gst_rtsp_media_get_stream (media, i);
+    stream = g_ptr_array_index (media->streams, i);
 
     gst_element_set_locked_state (stream->udpsrc[0], FALSE);
     gst_element_set_locked_state (stream->udpsrc[1], FALSE);
@@ -1583,10 +986,9 @@ default_handle_message (GstRTSPMedia * media, GstMessage * message)
       break;
     case GST_MESSAGE_EOS:
       GST_INFO ("%p: got EOS", media);
-      if (media->eos_pending) {
+      if (media->status == GST_RTSP_MEDIA_STATUS_UNPREPARING) {
         GST_DEBUG ("shutting down after EOS");
-        gst_element_set_state (media->pipeline, GST_STATE_NULL);
-        media->eos_pending = FALSE;
+        finish_unprepare (media);
         g_object_unref (media);
       }
       break;
@@ -1618,31 +1020,16 @@ bus_message (GstBus * bus, GstMessage * message, GstRTSPMedia * media)
 static void
 pad_added_cb (GstElement * element, GstPad * pad, GstRTSPMedia * media)
 {
-  GstRTSPMediaStream *stream;
-  gchar *name;
+  GstRTSPStream *stream;
   gint i;
 
-  i = media->streams->len + 1;
-
-  GST_INFO ("pad added %s:%s, stream %d", GST_DEBUG_PAD_NAME (pad), i);
-
-  stream = g_new0 (GstRTSPMediaStream, 1);
-  stream->payloader = element;
-
-  name = g_strdup_printf ("dynpay%d", i);
+  stream = gst_rtsp_media_create_stream (media, element, pad);
+  GST_INFO ("pad added %s:%s, stream %d", GST_DEBUG_PAD_NAME (pad),
+      stream->idx);
 
   media->adding = TRUE;
 
-  /* ghost the pad of the payloader to the element */
-  stream->srcpad = gst_ghost_pad_new (name, pad);
-  gst_pad_set_active (stream->srcpad, TRUE);
-  gst_element_add_pad (media->element, stream->srcpad);
-  g_free (name);
-
-  /* add stream now */
-  g_array_append_val (media->streams, stream);
-
-  setup_stream (stream, i, media);
+  gst_rtsp_stream_join_bin (stream, GST_BIN (media->pipeline), media->rtpbin);
 
   for (i = 0; i < 2; i++) {
     gst_element_set_state (stream->udpsink[i], GST_STATE_PAUSED);
@@ -1652,6 +1039,7 @@ pad_added_cb (GstElement * element, GstPad * pad, GstRTSPMedia * media)
     gst_element_set_state (stream->selector[i], GST_STATE_PAUSED);
     gst_element_set_state (stream->appsrc[i], GST_STATE_PAUSED);
   }
+
   media->adding = FALSE;
 }
 
@@ -1686,7 +1074,7 @@ gst_rtsp_media_prepare (GstRTSPMedia * media)
 {
   GstStateChangeReturn ret;
   GstRTSPMediaStatus status;
-  guint i, n_streams;
+  guint i;
   GstRTSPMediaClass *klass;
   GstBus *bus;
   GList *walk;
@@ -1726,13 +1114,12 @@ gst_rtsp_media_prepare (GstRTSPMedia * media)
 
   /* link streams we already have, other streams might appear when we have
    * dynamic elements */
-  n_streams = gst_rtsp_media_n_streams (media);
-  for (i = 0; i < n_streams; i++) {
-    GstRTSPMediaStream *stream;
+  for (i = 0; i < media->streams->len; i++) {
+    GstRTSPStream *stream;
 
-    stream = gst_rtsp_media_get_stream (media, i);
+    stream = g_ptr_array_index (media->streams, i);
 
-    setup_stream (stream, i, media);
+    gst_rtsp_stream_join_bin (stream, GST_BIN (media->pipeline), media->rtpbin);
   }
 
   for (walk = media->dynamic; walk; walk = g_list_next (walk)) {
@@ -1809,8 +1196,6 @@ no_rtpbin:
 state_failed:
   {
     GST_WARNING ("failed to preroll pipeline");
-    unlock_streams (media);
-    gst_element_set_state (media->pipeline, GST_STATE_NULL);
     gst_rtsp_media_unprepare (media);
     return FALSE;
   }
@@ -1829,7 +1214,6 @@ state_failed:
 gboolean
 gst_rtsp_media_unprepare (GstRTSPMedia * media)
 {
-  GstRTSPMediaClass *klass;
   gboolean success;
 
   if (media->status == GST_RTSP_MEDIA_STATUS_UNPREPARED)
@@ -1837,14 +1221,18 @@ gst_rtsp_media_unprepare (GstRTSPMedia * media)
 
   GST_INFO ("unprepare media %p", media);
   media->target_state = GST_STATE_NULL;
+  success = TRUE;
 
-  klass = GST_RTSP_MEDIA_GET_CLASS (media);
-  if (klass->unprepare)
-    success = klass->unprepare (media);
-  else
-    success = TRUE;
+  if (media->status == GST_RTSP_MEDIA_STATUS_PREPARED) {
+    GstRTSPMediaClass *klass;
+
+    klass = GST_RTSP_MEDIA_GET_CLASS (media);
+    if (klass->unprepare)
+      success = klass->unprepare (media);
+  } else {
+    finish_unprepare (media);
+  }
 
-  media->status = GST_RTSP_MEDIA_STATUS_UNPREPARED;
   media->reused = TRUE;
 
   /* when the media is not reusable, this will effectively unref the media and
@@ -1854,6 +1242,36 @@ gst_rtsp_media_unprepare (GstRTSPMedia * media)
   return success;
 }
 
+static void
+finish_unprepare (GstRTSPMedia * media)
+{
+  gint i;
+
+  GST_DEBUG ("shutting down");
+
+  unlock_streams (media);
+  gst_element_set_state (media->pipeline, GST_STATE_NULL);
+
+  for (i = 0; i < media->streams->len; i++) {
+    GstRTSPStream *stream;
+
+    GST_INFO ("Removing elements of stream %d from pipeline", i);
+
+    stream = g_ptr_array_index (media->streams, i);
+
+    gst_rtsp_stream_leave_bin (stream, GST_BIN (media->pipeline),
+        media->rtpbin);
+  }
+  g_ptr_array_set_size (media->streams, 0);
+
+  gst_bin_remove (GST_BIN (media->pipeline), media->rtpbin);
+
+  gst_object_unref (media->pipeline);
+  media->pipeline = NULL;
+
+  media->status = GST_RTSP_MEDIA_STATUS_UNPREPARED;
+}
+
 static gboolean
 default_unprepare (GstRTSPMedia * media)
 {
@@ -1861,49 +1279,22 @@ default_unprepare (GstRTSPMedia * media)
     GST_DEBUG ("sending EOS for shutdown");
     /* ref so that we don't disappear */
     g_object_ref (media);
-    media->eos_pending = TRUE;
     gst_element_send_event (media->pipeline, gst_event_new_eos ());
     /* we need to go to playing again for the EOS to propagate, normally in this
      * state, nothing is receiving data from us anymore so this is ok. */
     gst_element_set_state (media->pipeline, GST_STATE_PLAYING);
+    media->status = GST_RTSP_MEDIA_STATUS_UNPREPARING;
   } else {
-    GST_DEBUG ("shutting down");
-    gst_element_set_state (media->pipeline, GST_STATE_NULL);
+    finish_unprepare (media);
   }
   return TRUE;
 }
 
-static void
-add_udp_destination (GstRTSPMedia * media, GstRTSPMediaStream * stream,
-    gchar * dest, gint min, gint max)
-{
-  GST_INFO ("adding %s:%d-%d", dest, min, max);
-  g_signal_emit_by_name (stream->udpsink[0], "add", dest, min, NULL);
-  g_signal_emit_by_name (stream->udpsink[1], "add", dest, max, NULL);
-}
-
-static void
-remove_udp_destination (GstRTSPMedia * media, GstRTSPMediaStream * stream,
-    gchar * dest, gint min, gint max)
-{
-  GST_INFO ("removing %s:%d-%d", dest, min, max);
-  g_signal_emit_by_name (stream->udpsink[0], "remove", dest, min, NULL);
-  g_signal_emit_by_name (stream->udpsink[1], "remove", dest, max, NULL);
-}
-
-static void
-set_multicast_ttl (GstRTSPMedia * media, GstRTSPMediaStream * stream, guint ttl)
-{
-  GST_INFO ("setting ttl-mc %d", ttl);
-  g_object_set (G_OBJECT (stream->udpsink[0]), "ttl-mc", ttl, NULL);
-  g_object_set (G_OBJECT (stream->udpsink[1]), "ttl-mc", ttl, NULL);
-}
-
 /**
  * gst_rtsp_media_set_state:
  * @media: a #GstRTSPMedia
  * @state: the target state of the media
- * @transports: a #GArray of #GstRTSPMediaTrans pointers
+ * @transports: a #GPtrArray of #GstRTSPStreamTransport pointers
  *
  * Set the state of @media to @state and for the transports in @transports.
  *
@@ -1911,7 +1302,7 @@ set_multicast_ttl (GstRTSPMedia * media, GstRTSPMediaStream * stream, guint ttl)
  */
 gboolean
 gst_rtsp_media_set_state (GstRTSPMedia * media, GstState state,
-    GArray * transports)
+    GPtrArray * transports)
 {
   gint i;
   gboolean add, remove, do_state;
@@ -1946,74 +1337,26 @@ gst_rtsp_media_set_state (GstRTSPMedia * media, GstState state,
     default:
       break;
   }
-  old_active = media->active;
+  old_active = media->n_active;
 
   for (i = 0; i < transports->len; i++) {
-    GstRTSPMediaTrans *tr;
-    GstRTSPMediaStream *stream;
-    GstRTSPTransport *trans;
+    GstRTSPStreamTransport *trans;
 
     /* we need a non-NULL entry in the array */
-    tr = g_array_index (transports, GstRTSPMediaTrans *, i);
-    if (tr == NULL)
+    trans = g_ptr_array_index (transports, i);
+    if (trans == NULL)
       continue;
 
     /* we need a transport */
-    if (!(trans = tr->transport))
+    if (!trans->transport)
       continue;
 
-    /* get the stream and add the destinations */
-    stream = gst_rtsp_media_get_stream (media, tr->idx);
-    switch (trans->lower_transport) {
-      case GST_RTSP_LOWER_TRANS_UDP:
-      case GST_RTSP_LOWER_TRANS_UDP_MCAST:
-      {
-        gchar *dest;
-        gint min, max;
-        guint ttl = 0;
-
-        dest = trans->destination;
-        if (trans->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
-          min = trans->port.min;
-          max = trans->port.max;
-          ttl = trans->ttl;
-        } else {
-          min = trans->client_port.min;
-          max = trans->client_port.max;
-        }
-
-        if (add && !tr->active) {
-          add_udp_destination (media, stream, dest, min, max);
-          if (ttl > 0) {
-            set_multicast_ttl (media, stream, ttl);
-          }
-          stream->transports = g_list_prepend (stream->transports, tr);
-          tr->active = TRUE;
-          media->active++;
-        } else if (remove && tr->active) {
-          remove_udp_destination (media, stream, dest, min, max);
-          stream->transports = g_list_remove (stream->transports, tr);
-          tr->active = FALSE;
-          media->active--;
-        }
-        break;
-      }
-      case GST_RTSP_LOWER_TRANS_TCP:
-        if (add && !tr->active) {
-          GST_INFO ("adding TCP %s", trans->destination);
-          stream->transports = g_list_prepend (stream->transports, tr);
-          tr->active = TRUE;
-          media->active++;
-        } else if (remove && tr->active) {
-          GST_INFO ("removing TCP %s", trans->destination);
-          stream->transports = g_list_remove (stream->transports, tr);
-          tr->active = FALSE;
-          media->active--;
-        }
-        break;
-      default:
-        GST_INFO ("Unknown transport %d", trans->lower_transport);
-        break;
+    if (add) {
+      if (gst_rtsp_stream_add_transport (trans->stream, trans))
+        media->n_active++;
+    } else if (remove) {
+      if (gst_rtsp_stream_remove_transport (trans->stream, trans))
+        media->n_active--;
     }
   }
 
@@ -2021,12 +1364,12 @@ gst_rtsp_media_set_state (GstRTSPMedia * media, GstState state,
   if (old_active == 0 && add)
     do_state = TRUE;
   /* if we have no more active media, do the downward state changes */
-  else if (media->active == 0)
+  else if (media->n_active == 0)
     do_state = TRUE;
   else
     do_state = FALSE;
 
-  GST_INFO ("state %d active %d media %p do_state %d", state, media->active,
+  GST_INFO ("state %d active %d media %p do_state %d", state, media->n_active,
       media, do_state);
 
   if (media->target_state != state) {
@@ -2046,78 +1389,23 @@ gst_rtsp_media_set_state (GstRTSPMedia * media, GstState state,
 
   /* remember where we are */
   if (state != GST_STATE_NULL && (state == GST_STATE_PAUSED ||
-          old_active != media->active))
+          old_active != media->n_active))
     collect_media_stats (media);
 
   return TRUE;
 }
 
-/**
- * gst_rtsp_media_remove_elements:
- * @media: a #GstRTSPMedia
- *
- * Remove all elements and the pipeline controlled by @media.
- */
-void
-gst_rtsp_media_remove_elements (GstRTSPMedia * media)
-{
-  gint i, j;
-
-  unlock_streams (media);
-
-  for (i = 0; i < media->streams->len; i++) {
-    GstRTSPMediaStream *stream;
-
-    GST_INFO ("Removing elements of stream %d from pipeline", i);
-
-    stream = g_array_index (media->streams, GstRTSPMediaStream *, i);
-
-    gst_pad_unlink (stream->srcpad, stream->send_rtp_sink);
-
-    g_signal_handler_disconnect (stream->send_rtp_sink, stream->caps_sig);
-
-    for (j = 0; j < 2; j++) {
-      gst_element_set_state (stream->udpsrc[j], GST_STATE_NULL);
-      gst_element_set_state (stream->udpsink[j], GST_STATE_NULL);
-      gst_element_set_state (stream->appsrc[j], GST_STATE_NULL);
-      gst_element_set_state (stream->appsink[j], GST_STATE_NULL);
-      gst_element_set_state (stream->appqueue[j], GST_STATE_NULL);
-      gst_element_set_state (stream->tee[j], GST_STATE_NULL);
-      gst_element_set_state (stream->selector[j], GST_STATE_NULL);
-
-      gst_bin_remove (GST_BIN (media->pipeline), stream->udpsrc[j]);
-      gst_bin_remove (GST_BIN (media->pipeline), stream->udpsink[j]);
-      gst_bin_remove (GST_BIN (media->pipeline), stream->appsrc[j]);
-      gst_bin_remove (GST_BIN (media->pipeline), stream->appsink[j]);
-      gst_bin_remove (GST_BIN (media->pipeline), stream->appqueue[j]);
-      gst_bin_remove (GST_BIN (media->pipeline), stream->tee[j]);
-      gst_bin_remove (GST_BIN (media->pipeline), stream->selector[j]);
-    }
-    if (stream->caps)
-      gst_caps_unref (stream->caps);
-    stream->caps = NULL;
-    gst_rtsp_media_stream_free (stream);
-  }
-  g_array_remove_range (media->streams, 0, media->streams->len);
-
-  gst_element_set_state (media->rtpbin, GST_STATE_NULL);
-  gst_bin_remove (GST_BIN (media->pipeline), media->rtpbin);
-
-  gst_object_unref (media->pipeline);
-  media->pipeline = NULL;
-}
-
 static void
 default_handle_mtu (GstRTSPMedia * media, guint mtu)
 {
   gint i;
 
   for (i = 0; i < media->streams->len; i++) {
-    GstRTSPMediaStream *stream;
+    GstRTSPStream *stream;
 
     GST_INFO ("Setting mtu %d for stream %d", mtu, i);
 
-    stream = g_array_index (media->streams, GstRTSPMediaStream *, i);
+    stream = g_ptr_array_index (media->streams, i);
 
     g_object_set (G_OBJECT (stream->payloader), "mtu", mtu, NULL);
   }
index fb86a28..16b2cce 100644 (file)
@@ -36,115 +36,17 @@ G_BEGIN_DECLS
 #define GST_RTSP_MEDIA_CAST(obj)         ((GstRTSPMedia*)(obj))
 #define GST_RTSP_MEDIA_CLASS_CAST(klass) ((GstRTSPMediaClass*)(klass))
 
-typedef struct _GstRTSPMediaStream GstRTSPMediaStream;
 typedef struct _GstRTSPMedia GstRTSPMedia;
 typedef struct _GstRTSPMediaClass GstRTSPMediaClass;
-typedef struct _GstRTSPMediaTrans GstRTSPMediaTrans;
-
-typedef gboolean (*GstRTSPSendFunc)      (GstBuffer *buffer, guint8 channel, gpointer user_data);
-typedef void     (*GstRTSPKeepAliveFunc) (gpointer user_data);
-
-/**
- * GstRTSPMediaTrans:
- * @idx: a stream index
- * @send_rtp: callback for sending RTP messages
- * @send_rtcp: callback for sending RTCP messages
- * @send_rtp_list: callback for sending RTP messages
- * @send_rtcp_list: callback for sending RTCP messages
- * @user_data: user data passed in the callbacks
- * @notify: free function for the user_data.
- * @keep_alive: keep alive callback
- * @ka_user_data: data passed to @keep_alive
- * @ka_notify: called when @ka_user_data is freed
- * @active: if we are actively sending
- * @timeout: if we timed out
- * @transport: a transport description
- * @rtpsource: the receiver rtp source object
- *
- * A Transport description for stream @idx
- */
-struct _GstRTSPMediaTrans {
-  guint idx;
-
-  GstRTSPSendFunc      send_rtp;
-  GstRTSPSendFunc      send_rtcp;
-  gpointer             user_data;
-  GDestroyNotify       notify;
-
-  GstRTSPKeepAliveFunc keep_alive;
-  gpointer             ka_user_data;
-  GDestroyNotify       ka_notify;
-  gboolean             active;
-  gboolean             timeout;
-
-  GstRTSPTransport    *transport;
-
-  GObject             *rtpsource;
-};
 
+#include "rtsp-stream.h"
 #include "rtsp-auth.h"
 
 /**
- * GstRTSPMediaStream:
- * @srcpad: the srcpad of the stream
- * @payloader: the payloader of the format
- * @prepared: if the stream is prepared for streaming
- * @recv_rtp_sink: sinkpad for RTP buffers
- * @recv_rtcp_sink: sinkpad for RTCP buffers
- * @send_rtp_src: srcpad for RTP buffers
- * @send_rtcp_src: srcpad for RTCP buffers
- * @udpsrc: the udp source elements for RTP/RTCP
- * @udpsink: the udp sink elements for RTP/RTCP
- * @appsrc: the app source elements for RTP/RTCP
- * @appsink: the app sink elements for RTP/RTCP
- * @server_port: the server ports for this stream
- * @caps_sig: the signal id for detecting caps
- * @caps: the caps of the stream
- * @tranports: the current transports being streamed
- *
- * The definition of a media stream. The streams are identified by @id.
- */
-struct _GstRTSPMediaStream {
-  GstPad       *srcpad;
-  GstElement   *payloader;
-  gboolean      prepared;
-
-  /* pads on the rtpbin */
-  GstPad       *recv_rtcp_sink;
-  GstPad       *recv_rtp_sink;
-  GstPad       *send_rtp_sink;
-  GstPad       *send_rtp_src;
-  GstPad       *send_rtcp_src;
-
-  /* the RTPSession object */
-  GObject      *session;
-
-  /* sinks used for sending and receiving RTP and RTCP, they share
-   * sockets */
-  GstElement   *udpsrc[2];
-  GstElement   *udpsink[2];
-  /* for TCP transport */
-  GstElement   *appsrc[2];
-  GstElement   *appqueue[2];
-  GstElement   *appsink[2];
-
-  GstElement   *tee[2];
-  GstElement   *selector[2];
-
-  /* server ports for sending/receiving */
-  GstRTSPRange  server_port;
-
-  /* the caps of the stream */
-  gulong        caps_sig;
-  GstCaps      *caps;
-
-  /* transports we stream to */
-  GList        *transports;
-};
-
-/**
  * GstRTSPMediaStatus:
  * @GST_RTSP_MEDIA_STATUS_UNPREPARED: media pipeline not prerolled
+ * @GST_RTSP_MEDIA_STATUS_UNPREPARING: media pipeline is busy doing a clean
+ *                                     shutdown.
  * @GST_RTSP_MEDIA_STATUS_PREPARING: media pipeline is prerolling
  * @GST_RTSP_MEDIA_STATUS_PREPARED: media pipeline is prerolled
  * @GST_RTSP_MEDIA_STATUS_ERROR: media pipeline is in error
@@ -152,10 +54,11 @@ struct _GstRTSPMediaStream {
  * The state of the media pipeline.
  */
 typedef enum {
-  GST_RTSP_MEDIA_STATUS_UNPREPARED = 0,
-  GST_RTSP_MEDIA_STATUS_PREPARING  = 1,
-  GST_RTSP_MEDIA_STATUS_PREPARED   = 2,
-  GST_RTSP_MEDIA_STATUS_ERROR      = 3
+  GST_RTSP_MEDIA_STATUS_UNPREPARED  = 0,
+  GST_RTSP_MEDIA_STATUS_UNPREPARING = 1,
+  GST_RTSP_MEDIA_STATUS_PREPARING   = 2,
+  GST_RTSP_MEDIA_STATUS_PREPARED    = 3,
+  GST_RTSP_MEDIA_STATUS_ERROR       = 4
 } GstRTSPMediaStatus;
 
 /**
@@ -168,10 +71,10 @@ typedef enum {
  * @reused: if this media has been reused
  * @is_ipv6: if this media is using ipv6
  * @element: the data providing element
- * @streams: the different streams provided by @element
+ * @streams: the different #GstRTSPStream provided by @element
  * @dynamic: list of dynamic elements managed by @element
  * @status: the status of the media pipeline
- * @active: the number of active connections
+ * @n_active: the number of active connections
  * @pipeline: the toplevel pipeline
  * @fakesink: for making state changes async
  * @source: the bus watch for pipeline messages.
@@ -184,7 +87,7 @@ typedef enum {
  * @range: the range of the media being streamed
  *
  * A class that contains the GStreamer element along with a list of
- * #GstRTSPMediaStream objects that can produce data.
+ * #GstRTSPStream objects that can produce data.
  *
  * This object is usually created from a #GstRTSPMediaFactory.
  */
@@ -205,11 +108,10 @@ struct _GstRTSPMedia {
   gchar             *multicast_group;
 
   GstElement        *element;
-  GArray            *streams;
+  GPtrArray         *streams;
   GList             *dynamic;
   GstRTSPMediaStatus status;
-  gint               active;
-  gboolean           eos_pending;
+  gint               n_active;
   gboolean           adding;
 
   /* the pipeline for the media */
@@ -251,15 +153,15 @@ struct _GstRTSPMediaClass {
   GThread      *thread;
 
   /* vmethods */
-  gboolean     (*handle_message)  (GstRTSPMedia *media, GstMessage *message);
-  gboolean     (*unprepare)       (GstRTSPMedia *media);
-  void         (*handle_mtu)      (GstRTSPMedia *media, guint mtu);
+  gboolean        (*handle_message)  (GstRTSPMedia *media, GstMessage *message);
+  gboolean        (*unprepare)       (GstRTSPMedia *media);
+  void            (*handle_mtu)      (GstRTSPMedia *media, guint mtu);
 
   /* signals */
-  gboolean     (*prepared)        (GstRTSPMedia *media);
-  gboolean     (*unprepared)      (GstRTSPMedia *media);
+  gboolean        (*prepared)        (GstRTSPMedia *media);
+  gboolean        (*unprepared)      (GstRTSPMedia *media);
 
-  gboolean     (*new_state)       (GstRTSPMedia *media, GstState state);
+  gboolean        (*new_state)       (GstRTSPMedia *media, GstState state);
 };
 
 GType                 gst_rtsp_media_get_type         (void);
@@ -294,24 +196,24 @@ gboolean              gst_rtsp_media_prepare          (GstRTSPMedia *media);
 gboolean              gst_rtsp_media_is_prepared      (GstRTSPMedia *media);
 gboolean              gst_rtsp_media_unprepare        (GstRTSPMedia *media);
 
+/* creating streams */
+void                  gst_rtsp_media_collect_streams  (GstRTSPMedia *media);
+GstRTSPStream *       gst_rtsp_media_create_stream    (GstRTSPMedia *media,
+                                                       GstElement *payloader,
+                                                       GstPad *srcpad);
+
 /* dealing with the media */
 guint                 gst_rtsp_media_n_streams        (GstRTSPMedia *media);
-GstRTSPMediaStream *  gst_rtsp_media_get_stream       (GstRTSPMedia *media, guint idx);
+GstRTSPStream *       gst_rtsp_media_get_stream       (GstRTSPMedia *media, guint idx);
 
 gboolean              gst_rtsp_media_seek             (GstRTSPMedia *media, GstRTSPTimeRange *range);
 gchar *               gst_rtsp_media_get_range_string (GstRTSPMedia *media, gboolean play);
 
-GstFlowReturn         gst_rtsp_media_stream_rtp       (GstRTSPMediaStream *stream, GstBuffer *buffer);
-GstFlowReturn         gst_rtsp_media_stream_rtcp      (GstRTSPMediaStream *stream, GstBuffer *buffer);
-
-gboolean              gst_rtsp_media_set_state        (GstRTSPMedia *media, GstState state, GArray *transports);
-
-void                  gst_rtsp_media_remove_elements  (GstRTSPMedia *media);
+gboolean              gst_rtsp_media_set_state        (GstRTSPMedia *media, GstState state,
+                                                       GPtrArray *transports);
 
 void                  gst_rtsp_media_handle_mtu       (GstRTSPMedia *media, guint mtu);
 
-void                  gst_rtsp_media_trans_cleanup    (GstRTSPMediaTrans *trans);
-
 G_END_DECLS
 
 #endif /* __GST_RTSP_MEDIA_H__ */
index c6b379b..cfbe6f9 100644 (file)
@@ -48,7 +48,7 @@ gst_rtsp_sdp_from_media (GstSDPMessage * sdp, GstSDPInfo * info,
   g_free (rangestr);
 
   for (i = 0; i < n_streams; i++) {
-    GstRTSPMediaStream *stream;
+    GstRTSPStream *stream;
     GstSDPMedia *smedia;
     GstStructure *s;
     const gchar *caps_str, *caps_enc, *caps_params;
diff --git a/gst/rtsp-server/rtsp-session-media.c b/gst/rtsp-server/rtsp-session-media.c
new file mode 100644 (file)
index 0000000..12a01d4
--- /dev/null
@@ -0,0 +1,202 @@
+/* GStreamer
+ * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include <string.h>
+
+#include "rtsp-session.h"
+
+#undef DEBUG
+
+#define DEFAULT_TIMEOUT        60
+
+enum
+{
+  PROP_0,
+  PROP_LAST
+};
+
+GST_DEBUG_CATEGORY_STATIC (rtsp_session_media_debug);
+#define GST_CAT_DEFAULT rtsp_session_media_debug
+
+static void gst_rtsp_session_media_finalize (GObject * obj);
+
+G_DEFINE_TYPE (GstRTSPSessionMedia, gst_rtsp_session_media, G_TYPE_OBJECT);
+
+static void
+gst_rtsp_session_media_class_init (GstRTSPSessionMediaClass * klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = gst_rtsp_session_media_finalize;
+
+  GST_DEBUG_CATEGORY_INIT (rtsp_session_media_debug, "rtspsessionmedia", 0,
+      "GstRTSPSessionMedia");
+}
+
+static void
+gst_rtsp_session_media_init (GstRTSPSessionMedia * media)
+{
+  media->state = GST_RTSP_STATE_INIT;
+}
+
+static void
+gst_rtsp_session_media_finalize (GObject * obj)
+{
+  GstRTSPSessionMedia *media;
+
+  media = GST_RTSP_SESSION_MEDIA (obj);
+
+  GST_INFO ("free session media %p", media);
+
+  gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
+
+  g_ptr_array_unref (media->transports);
+
+  gst_rtsp_url_free (media->url);
+  g_object_unref (media->media);
+
+  G_OBJECT_CLASS (gst_rtsp_session_media_parent_class)->finalize (obj);
+}
+
+static void
+free_session_media (gpointer data)
+{
+  if (data)
+    g_object_unref (data);
+}
+
+/**
+ * gst_rtsp_session_media_new:
+ * @url: the #GstRTSPUrl
+ * @media: the #GstRTSPMedia
+ *
+ * Create a new #GstRTPSessionMedia that manages the streams
+ * in @media for @url. @media should be prepared.
+ *
+ * Ownership is taken of @media.
+ *
+ * Returns: a new #GstRTSPSessionMedia.
+ */
+GstRTSPSessionMedia *
+gst_rtsp_session_media_new (const GstRTSPUrl * url, GstRTSPMedia * media)
+{
+  GstRTSPSessionMedia *result;
+  guint n_streams;
+
+  g_return_val_if_fail (url != NULL, NULL);
+  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
+  g_return_val_if_fail (media->status == GST_RTSP_MEDIA_STATUS_PREPARED, NULL);
+
+  result = g_object_new (GST_TYPE_RTSP_SESSION_MEDIA, NULL);
+  result->url = gst_rtsp_url_copy ((GstRTSPUrl *) url);
+  result->media = media;
+
+  /* prealloc the streams now, filled with NULL */
+  n_streams = gst_rtsp_media_n_streams (media);
+  result->transports = g_ptr_array_new_full (n_streams, free_session_media);
+  g_ptr_array_set_size (result->transports, n_streams);
+
+  return result;
+}
+
+/**
+ * gst_rtsp_session_media_get_transport:
+ * @media: a #GstRTSPSessionMedia
+ * @idx: the stream index
+ *
+ * Get a previously created or create a new #GstRTSPStreamTransport at @idx.
+ *
+ * Returns: a #GstRTSPStreamTransport that is valid until the session of @media
+ * is unreffed.
+ */
+GstRTSPStreamTransport *
+gst_rtsp_session_media_get_transport (GstRTSPSessionMedia * media, guint idx)
+{
+  GstRTSPStreamTransport *result;
+
+  g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), NULL);
+  g_return_val_if_fail (media->media != NULL, NULL);
+
+  if (idx >= media->transports->len)
+    return NULL;
+
+  result = g_ptr_array_index (media->transports, idx);
+  if (result == NULL) {
+    GstRTSPStream *stream;
+
+    stream = gst_rtsp_media_get_stream (media->media, idx);
+    if (stream == NULL)
+      goto no_media;
+
+    result = gst_rtsp_stream_transport_new (stream);
+
+    g_ptr_array_index (media->transports, idx) = result;
+  }
+  return result;
+
+  /* ERRORS */
+no_media:
+  {
+    return NULL;
+  }
+}
+
+/**
+ * gst_rtsp_session_media_alloc_channels:
+ * @media: a #GstRTSPSessionMedia
+ * @range: a #GstRTSPRange
+ *
+ * Fill @range with the next available min and max channels for
+ * interleaved transport.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+gst_rtsp_session_media_alloc_channels (GstRTSPSessionMedia * media,
+    GstRTSPRange * range)
+{
+  g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), FALSE);
+
+  range->min = media->counter++;
+  range->max = media->counter++;
+
+  return TRUE;
+}
+
+/**
+ * gst_rtsp_session_media_set_state:
+ * @media: a #GstRTSPSessionMedia
+ * @state: the new state
+ *
+ * Tell the media object @media to change to @state.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+gst_rtsp_session_media_set_state (GstRTSPSessionMedia * media, GstState state)
+{
+  gboolean ret;
+
+  g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), FALSE);
+
+  ret = gst_rtsp_media_set_state (media->media, state, media->transports);
+
+  return ret;
+}
diff --git a/gst/rtsp-server/rtsp-session-media.h b/gst/rtsp-server/rtsp-session-media.h
new file mode 100644 (file)
index 0000000..ac9588f
--- /dev/null
@@ -0,0 +1,86 @@
+/* GStreamer
+ * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gst/gst.h>
+
+#include <gst/rtsp/gstrtsptransport.h>
+
+#ifndef __GST_RTSP_SESSION_MEDIA_H__
+#define __GST_RTSP_SESSION_MEDIA_H__
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_RTSP_SESSION_MEDIA              (gst_rtsp_session_media_get_type ())
+#define GST_IS_RTSP_SESSION_MEDIA(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_SESSION_MEDIA))
+#define GST_IS_RTSP_SESSION_MEDIA_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_SESSION_MEDIA))
+#define GST_RTSP_SESSION_MEDIA_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_SESSION_MEDIA, GstRTSPSessionMediaClass))
+#define GST_RTSP_SESSION_MEDIA(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_SESSION_MEDIA, GstRTSPSessionMedia))
+#define GST_RTSP_SESSION_MEDIA_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_SESSION_MEDIA, GstRTSPSessionMediaClass))
+#define GST_RTSP_SESSION_MEDIA_CAST(obj)         ((GstRTSPSessionMedia*)(obj))
+#define GST_RTSP_SESSION_MEDIA_CLASS_CAST(klass) ((GstRTSPSessionMediaClass*)(klass))
+
+typedef struct _GstRTSPSessionMedia GstRTSPSessionMedia;
+typedef struct _GstRTSPSessionMediaClass GstRTSPSessionMediaClass;
+
+/**
+ * GstRTSPSessionMedia:
+ * @url: the url of the media
+ * @media: the pipeline for the media
+ * @state: the server state
+ * @counter: counter for channels
+ * @transports: array of #GstRTSPStreamTransport with the configuration
+ *   for the transport for each selected stream from @media.
+ *
+ * State of a client session regarding a specific media identified by uri.
+ */
+struct _GstRTSPSessionMedia
+{
+  GObject  parent;
+
+  GstRTSPUrl   *url;
+  GstRTSPMedia *media;
+  GstRTSPState  state;
+  guint         counter;
+
+  GPtrArray    *transports;
+};
+
+struct _GstRTSPSessionMediaClass
+{
+  GObjectClass  parent_class;
+};
+
+GType                    gst_rtsp_session_media_get_type       (void);
+
+GstRTSPSessionMedia *    gst_rtsp_session_media_new            (const GstRTSPUrl *url,
+                                                                GstRTSPMedia *media);
+/* control media */
+gboolean                 gst_rtsp_session_media_set_state      (GstRTSPSessionMedia *media,
+                                                                GstState state);
+
+/* get stream transport config */
+GstRTSPStreamTransport * gst_rtsp_session_media_get_transport  (GstRTSPSessionMedia *media,
+                                                                guint idx);
+
+gboolean                 gst_rtsp_session_media_alloc_channels (GstRTSPSessionMedia *media,
+                                                                GstRTSPRange *range);
+
+G_END_DECLS
+
+#endif /* __GST_RTSP_SESSION_MEDIA_H__ */
index 3d76670..7b8cac0 100644 (file)
@@ -77,51 +77,6 @@ gst_rtsp_session_init (GstRTSPSession * session)
 }
 
 static void
-gst_rtsp_session_free_stream (GstRTSPSessionStream * stream)
-{
-  GST_INFO ("free session stream %p", stream);
-
-  /* remove callbacks now */
-  gst_rtsp_session_stream_set_callbacks (stream, NULL, NULL, NULL, NULL);
-  gst_rtsp_session_stream_set_keepalive (stream, NULL, NULL, NULL);
-
-  gst_rtsp_media_trans_cleanup (&stream->trans);
-
-  g_free (stream);
-}
-
-static void
-gst_rtsp_session_free_media (GstRTSPSessionMedia * media,
-    GstRTSPSession * session)
-{
-  guint size, i;
-
-  size = media->streams->len;
-
-  GST_INFO ("free session media %p", media);
-
-  gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
-
-  for (i = 0; i < size; i++) {
-    GstRTSPSessionStream *stream;
-
-    stream = g_array_index (media->streams, GstRTSPSessionStream *, i);
-
-    if (stream)
-      gst_rtsp_session_free_stream (stream);
-  }
-  g_array_free (media->streams, TRUE);
-
-  if (media->url)
-    gst_rtsp_url_free (media->url);
-
-  if (media->media)
-    g_object_unref (media->media);
-
-  g_free (media);
-}
-
-static void
 gst_rtsp_session_finalize (GObject * obj)
 {
   GstRTSPSession *session;
@@ -131,9 +86,7 @@ gst_rtsp_session_finalize (GObject * obj)
   GST_INFO ("finalize session %p", session);
 
   /* free all media */
-  g_list_foreach (session->medias, (GFunc) gst_rtsp_session_free_media,
-      session);
-  g_list_free (session->medias);
+  g_list_free_full (session->medias, g_object_unref);
 
   /* free session id */
   g_free (session->sessionid);
@@ -196,24 +149,13 @@ gst_rtsp_session_manage_media (GstRTSPSession * sess, const GstRTSPUrl * uri,
     GstRTSPMedia * media)
 {
   GstRTSPSessionMedia *result;
-  guint n_streams;
 
   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
   g_return_val_if_fail (uri != NULL, NULL);
   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
   g_return_val_if_fail (media->status == GST_RTSP_MEDIA_STATUS_PREPARED, NULL);
 
-  result = g_new0 (GstRTSPSessionMedia, 1);
-  result->media = media;
-  result->url = gst_rtsp_url_copy ((GstRTSPUrl *) uri);
-  result->state = GST_RTSP_STATE_INIT;
-
-  /* prealloc the streams now, filled with NULL */
-  n_streams = gst_rtsp_media_n_streams (media);
-  result->streams =
-      g_array_sized_new (FALSE, TRUE, sizeof (GstRTSPSessionStream *),
-      n_streams);
-  g_array_set_size (result->streams, n_streams);
+  result = gst_rtsp_session_media_new (uri, media);
 
   sess->medias = g_list_prepend (sess->medias, result);
 
@@ -235,24 +177,15 @@ gboolean
 gst_rtsp_session_release_media (GstRTSPSession * sess,
     GstRTSPSessionMedia * media)
 {
-  GList *walk, *next;
+  GList *find;
 
   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;
+  find = g_list_find (sess->medias, media);
+  if (find) {
+    g_object_unref (find->data);
+    sess->medias = g_list_delete_link (sess->medias, find);
   }
   return (sess->medias != NULL);
 }
@@ -280,7 +213,7 @@ gst_rtsp_session_get_media (GstRTSPSession * sess, const GstRTSPUrl * url)
   for (walk = sess->medias; walk; walk = g_list_next (walk)) {
     result = (GstRTSPSessionMedia *) walk->data;
 
-    if (strcmp (result->url->abspath, url->abspath) == 0)
+    if (g_str_equal (result->url->abspath, url->abspath))
       break;
 
     result = NULL;
@@ -289,61 +222,6 @@ gst_rtsp_session_get_media (GstRTSPSession * sess, const GstRTSPUrl * url)
 }
 
 /**
- * gst_rtsp_session_media_get_stream:
- * @media: a #GstRTSPSessionMedia
- * @idx: the stream index
- *
- * Get a previously created or create a new #GstRTSPSessionStream at @idx.
- *
- * Returns: a #GstRTSPSessionStream that is valid until the session of @media
- * is unreffed.
- */
-GstRTSPSessionStream *
-gst_rtsp_session_media_get_stream (GstRTSPSessionMedia * media, guint idx)
-{
-  GstRTSPSessionStream *result;
-
-  g_return_val_if_fail (media != NULL, NULL);
-  g_return_val_if_fail (media->media != NULL, NULL);
-
-  if (idx >= media->streams->len)
-    return NULL;
-
-  result = g_array_index (media->streams, GstRTSPSessionStream *, idx);
-  if (result == NULL) {
-    GstRTSPMediaStream *media_stream;
-
-    media_stream = gst_rtsp_media_get_stream (media->media, idx);
-    if (media_stream == NULL)
-      goto no_media;
-
-    result = g_new0 (GstRTSPSessionStream, 1);
-    result->trans.idx = idx;
-    result->trans.transport = NULL;
-    result->media_stream = media_stream;
-
-    g_array_index (media->streams, GstRTSPSessionStream *, idx) = result;
-  }
-  return result;
-
-  /* ERRORS */
-no_media:
-  {
-    return NULL;
-  }
-}
-
-gboolean
-gst_rtsp_session_media_alloc_channels (GstRTSPSessionMedia * media,
-    GstRTSPRange * range)
-{
-  range->min = media->counter++;
-  range->max = media->counter++;
-
-  return TRUE;
-}
-
-/**
  * gst_rtsp_session_new:
  *
  * Create a new #GstRTSPSession instance.
@@ -492,124 +370,3 @@ gst_rtsp_session_is_expired (GstRTSPSession * session, GTimeVal * now)
 
   return res;
 }
-
-/**
- * gst_rtsp_session_stream_init_udp:
- * @stream: a #GstRTSPSessionStream
- * @ct: a client #GstRTSPTransport
- *
- * Set @ct as the client transport and create and return a matching server
- * transport. This function takes ownership of the passed @ct.
- *
- * Returns: a server transport or NULL if something went wrong. Use
- * gst_rtsp_transport_free () after usage.
- */
-GstRTSPTransport *
-gst_rtsp_session_stream_set_transport (GstRTSPSessionStream * stream,
-    GstRTSPTransport * ct)
-{
-  GstRTSPTransport *st;
-
-  g_return_val_if_fail (stream != NULL, NULL);
-  g_return_val_if_fail (ct != NULL, NULL);
-
-  /* prepare the server transport */
-  gst_rtsp_transport_new (&st);
-
-  st->trans = ct->trans;
-  st->profile = ct->profile;
-  st->lower_transport = ct->lower_transport;
-
-  switch (st->lower_transport) {
-    case GST_RTSP_LOWER_TRANS_UDP:
-      st->client_port = ct->client_port;
-      st->server_port = stream->media_stream->server_port;
-      break;
-    case GST_RTSP_LOWER_TRANS_UDP_MCAST:
-      ct->port = st->port = stream->media_stream->server_port;
-      st->destination = g_strdup (ct->destination);
-      st->ttl = ct->ttl;
-      break;
-    case GST_RTSP_LOWER_TRANS_TCP:
-      st->interleaved = ct->interleaved;
-    default:
-      break;
-  }
-
-  if (stream->media_stream->session)
-    g_object_get (stream->media_stream->session, "internal-ssrc", &st->ssrc,
-        NULL);
-
-  /* keep track of the transports in the stream. */
-  if (stream->trans.transport)
-    gst_rtsp_transport_free (stream->trans.transport);
-  stream->trans.transport = ct;
-
-  return st;
-}
-
-/**
- * gst_rtsp_session_stream_set_callbacks:
- * @stream: a #GstRTSPSessionStream
- * @send_rtp: (scope notified): a callback called when RTP should be sent
- * @send_rtcp: (scope notified): a callback called when RTCP should be sent
- * @user_data: user data passed to callbacks
- * @notify: called with the user_data when no longer needed.
- *
- * Install callbacks that will be called when data for a stream should be sent
- * to a client. This is usually used when sending RTP/RTCP over TCP.
- */
-void
-gst_rtsp_session_stream_set_callbacks (GstRTSPSessionStream * stream,
-    GstRTSPSendFunc send_rtp, GstRTSPSendFunc send_rtcp,
-    gpointer user_data, GDestroyNotify notify)
-{
-  stream->trans.send_rtp = send_rtp;
-  stream->trans.send_rtcp = send_rtcp;
-  if (stream->trans.notify)
-    stream->trans.notify (stream->trans.user_data);
-  stream->trans.user_data = user_data;
-  stream->trans.notify = notify;
-}
-
-/**
- * gst_rtsp_session_stream_set_keepalive:
- * @stream: a #GstRTSPSessionStream
- * @keep_alive: a callback called when the receiver is active
- * @user_data: user data passed to callback
- * @notify: called with the user_data when no longer needed.
- *
- * Install callbacks that will be called when RTCP packets are received from the
- * receiver of @stream.
- */
-void
-gst_rtsp_session_stream_set_keepalive (GstRTSPSessionStream * stream,
-    GstRTSPKeepAliveFunc keep_alive, gpointer user_data, GDestroyNotify notify)
-{
-  stream->trans.keep_alive = keep_alive;
-  if (stream->trans.ka_notify)
-    stream->trans.ka_notify (stream->trans.ka_user_data);
-  stream->trans.ka_user_data = user_data;
-  stream->trans.ka_notify = notify;
-}
-
-/**
- * gst_rtsp_session_media_set_state:
- * @media: a #GstRTSPSessionMedia
- * @state: the new state
- *
- * Tell the media object @media to change to @state.
- *
- * Returns: %TRUE on success.
- */
-gboolean
-gst_rtsp_session_media_set_state (GstRTSPSessionMedia * media, GstState state)
-{
-  gboolean ret;
-
-  g_return_val_if_fail (media != NULL, FALSE);
-
-  ret = gst_rtsp_media_set_state (media->media, state, media->streams);
-
-  return ret;
-}
index 973f196..f7815c9 100644 (file)
@@ -38,49 +38,8 @@ G_BEGIN_DECLS
 typedef struct _GstRTSPSession GstRTSPSession;
 typedef struct _GstRTSPSessionClass GstRTSPSessionClass;
 
-typedef struct _GstRTSPSessionStream GstRTSPSessionStream;
-typedef struct _GstRTSPSessionMedia GstRTSPSessionMedia;
-
 #include "rtsp-media.h"
-
-/**
- * GstRTSPSessionStream:
- * @trans: the media transport
- * @media_stream: the controlled media stream
- *
- * Configuration of a stream. A stream is an audio or video stream related to a
- * media.
- */
-struct _GstRTSPSessionStream
-{
-  GstRTSPMediaTrans trans;
-
-  /* the stream of the media */
-  GstRTSPMediaStream *media_stream;
-};
-
-/**
- * GstRTSPSessionMedia:
- *
- * State of a client session regarding a specific media identified by uri.
- */
-struct _GstRTSPSessionMedia
-{
-  /* the url of the media */
-  GstRTSPUrl   *url;
-
-  /* the pipeline for the media */
-  GstRTSPMedia *media;
-
-  /* the server state */
-  GstRTSPState  state;
-
-  /* counter for channels */
-  guint         counter;
-
-  /* configuration for the different streams */
-  GArray       *streams;
-};
+#include "rtsp-session-media.h"
 
 /**
  * GstRTSPSession:
@@ -138,28 +97,6 @@ gboolean               gst_rtsp_session_release_media        (GstRTSPSession *se
 /* get media in a session */
 GstRTSPSessionMedia *  gst_rtsp_session_get_media            (GstRTSPSession *sess,
                                                               const GstRTSPUrl *url);
-/* control media */
-gboolean               gst_rtsp_session_media_set_state      (GstRTSPSessionMedia *media, GstState state);
-
-/* get stream config */
-GstRTSPSessionStream * gst_rtsp_session_media_get_stream     (GstRTSPSessionMedia *media,
-                                                              guint idx);
-
-gboolean               gst_rtsp_session_media_alloc_channels (GstRTSPSessionMedia *media,
-                                                              GstRTSPRange *range);
-
-/* configure transport */
-GstRTSPTransport *     gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream,
-                                                              GstRTSPTransport *ct);
-void                   gst_rtsp_session_stream_set_callbacks (GstRTSPSessionStream *stream,
-                                                              GstRTSPSendFunc send_rtp,
-                                                              GstRTSPSendFunc send_rtcp,
-                                                              gpointer user_data,
-                                                              GDestroyNotify  notify);
-void                   gst_rtsp_session_stream_set_keepalive (GstRTSPSessionStream *stream,
-                                                              GstRTSPKeepAliveFunc keep_alive,
-                                                              gpointer user_data,
-                                                              GDestroyNotify  notify);
 
 G_END_DECLS
 
diff --git a/gst/rtsp-server/rtsp-stream-transport.c b/gst/rtsp-server/rtsp-stream-transport.c
new file mode 100644 (file)
index 0000000..7b5609a
--- /dev/null
@@ -0,0 +1,202 @@
+/* GStreamer
+ * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <gst/app/gstappsrc.h>
+#include <gst/app/gstappsink.h>
+
+#include "rtsp-stream-transport.h"
+
+enum
+{
+  PROP_0,
+  PROP_LAST
+};
+
+GST_DEBUG_CATEGORY_STATIC (rtsp_stream_transport_debug);
+#define GST_CAT_DEFAULT rtsp_stream_transport_debug
+
+static void gst_rtsp_stream_transport_finalize (GObject * obj);
+
+G_DEFINE_TYPE (GstRTSPStreamTransport, gst_rtsp_stream_transport,
+    G_TYPE_OBJECT);
+
+static void
+gst_rtsp_stream_transport_class_init (GstRTSPStreamTransportClass * klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = gst_rtsp_stream_transport_finalize;
+
+  GST_DEBUG_CATEGORY_INIT (rtsp_stream_transport_debug, "rtspmediatransport",
+      0, "GstRTSPStreamTransport");
+}
+
+static void
+gst_rtsp_stream_transport_init (GstRTSPStreamTransport * trans)
+{
+}
+
+static void
+gst_rtsp_stream_transport_finalize (GObject * obj)
+{
+  GstRTSPStreamTransport *trans;
+
+  trans = GST_RTSP_STREAM_TRANSPORT (obj);
+
+  /* remove callbacks now */
+  gst_rtsp_stream_transport_set_callbacks (trans, NULL, NULL, NULL, NULL);
+  gst_rtsp_stream_transport_set_keepalive (trans, NULL, NULL, NULL);
+
+  if (trans->transport)
+    gst_rtsp_transport_free (trans->transport);
+
+#if 0
+  if (trans->rtpsource)
+    g_object_set_qdata (trans->rtpsource, ssrc_stream_map_key, NULL);
+#endif
+
+  G_OBJECT_CLASS (gst_rtsp_stream_transport_parent_class)->finalize (obj);
+}
+
+/**
+ * gst_rtsp_stream_transport_new:
+ * @stream: a #GstRTSPStream
+ *
+ * Create a new #GstRTSPStreamTransport that can be used for
+ * @stream.
+ *
+ * Returns: a new #GstRTSPStreamTransport
+ */
+GstRTSPStreamTransport *
+gst_rtsp_stream_transport_new (GstRTSPStream * stream)
+{
+  GstRTSPStreamTransport *trans;
+
+  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
+
+  trans = g_object_new (GST_TYPE_RTSP_STREAM_TRANSPORT, NULL);
+  trans->stream = stream;
+
+  return trans;
+}
+
+/**
+ * gst_rtsp_stream_transport_set_callbacks:
+ * @trans: a #GstRTSPStreamTransport
+ * @send_rtp: (scope notified): a callback called when RTP should be sent
+ * @send_rtcp: (scope notified): a callback called when RTCP should be sent
+ * @user_data: user data passed to callbacks
+ * @notify: called with the user_data when no longer needed.
+ *
+ * Install callbacks that will be called when data for a stream should be sent
+ * to a client. This is usually used when sending RTP/RTCP over TCP.
+ */
+void
+gst_rtsp_stream_transport_set_callbacks (GstRTSPStreamTransport * trans,
+    GstRTSPSendFunc send_rtp, GstRTSPSendFunc send_rtcp,
+    gpointer user_data, GDestroyNotify notify)
+{
+  trans->send_rtp = send_rtp;
+  trans->send_rtcp = send_rtcp;
+  if (trans->notify)
+    trans->notify (trans->user_data);
+  trans->user_data = user_data;
+  trans->notify = notify;
+}
+
+/**
+ * gst_rtsp_stream_transport_set_keepalive:
+ * @trans: a #GstRTSPStreamTransport
+ * @keep_alive: a callback called when the receiver is active
+ * @user_data: user data passed to callback
+ * @notify: called with the user_data when no longer needed.
+ *
+ * Install callbacks that will be called when RTCP packets are received from the
+ * receiver of @trans.
+ */
+void
+gst_rtsp_stream_transport_set_keepalive (GstRTSPStreamTransport * trans,
+    GstRTSPKeepAliveFunc keep_alive, gpointer user_data, GDestroyNotify notify)
+{
+  trans->keep_alive = keep_alive;
+  if (trans->ka_notify)
+    trans->ka_notify (trans->ka_user_data);
+  trans->ka_user_data = user_data;
+  trans->ka_notify = notify;
+}
+
+
+/**
+ * gst_rtsp_stream_transport_set_transport:
+ * @trans: a #GstRTSPStreamTransport
+ * @ct: a client #GstRTSPTransport
+ *
+ * Set @ct as the client transport and create and return a matching server
+ * transport. This function takes ownership of the passed @ct.
+ *
+ * Returns: a server transport or NULL if something went wrong. Use
+ * gst_rtsp_transport_free () after usage.
+ */
+GstRTSPTransport *
+gst_rtsp_stream_transport_set_transport (GstRTSPStreamTransport * trans,
+    GstRTSPTransport * ct)
+{
+  GstRTSPTransport *st;
+
+  g_return_val_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans), NULL);
+  g_return_val_if_fail (ct != NULL, NULL);
+
+  /* prepare the server transport */
+  gst_rtsp_transport_new (&st);
+
+  st->trans = ct->trans;
+  st->profile = ct->profile;
+  st->lower_transport = ct->lower_transport;
+
+  switch (st->lower_transport) {
+    case GST_RTSP_LOWER_TRANS_UDP:
+      st->client_port = ct->client_port;
+      st->server_port = trans->stream->server_port;
+      break;
+    case GST_RTSP_LOWER_TRANS_UDP_MCAST:
+      ct->port = st->port = trans->stream->server_port;
+      st->destination = g_strdup (ct->destination);
+      st->ttl = ct->ttl;
+      break;
+    case GST_RTSP_LOWER_TRANS_TCP:
+      st->interleaved = ct->interleaved;
+    default:
+      break;
+  }
+
+  if (trans->stream->session)
+    g_object_get (trans->stream->session, "internal-ssrc", &st->ssrc, NULL);
+
+  /* keep track of the transports in the stream. */
+  if (trans->transport)
+    gst_rtsp_transport_free (trans->transport);
+  trans->transport = ct;
+
+  return st;
+}
diff --git a/gst/rtsp-server/rtsp-stream-transport.h b/gst/rtsp-server/rtsp-stream-transport.h
new file mode 100644 (file)
index 0000000..cd17332
--- /dev/null
@@ -0,0 +1,111 @@
+/* GStreamer
+ * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gst/gst.h>
+#include <gst/rtsp/gstrtsprange.h>
+#include <gst/rtsp/gstrtspurl.h>
+
+#ifndef __GST_RTSP_STREAM_TRANSPORT_H__
+#define __GST_RTSP_STREAM_TRANSPORT_H__
+
+G_BEGIN_DECLS
+
+/* types for the media */
+#define GST_TYPE_RTSP_STREAM_TRANSPORT              (gst_rtsp_stream_transport_get_type ())
+#define GST_IS_RTSP_STREAM_TRANSPORT(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_STREAM_TRANSPORT))
+#define GST_IS_RTSP_STREAM_TRANSPORT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_STREAM_TRANSPORT))
+#define GST_RTSP_STREAM_TRANSPORT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_STREAM_TRANSPORT, GstRTSPStreamTransportClass))
+#define GST_RTSP_STREAM_TRANSPORT(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_STREAM_TRANSPORT, GstRTSPStreamTransport))
+#define GST_RTSP_STREAM_TRANSPORT_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_STREAM_TRANSPORT, GstRTSPStreamTransportClass))
+#define GST_RTSP_STREAM_TRANSPORT_CAST(obj)         ((GstRTSPStreamTransport*)(obj))
+#define GST_RTSP_STREAM_TRANSPORT_CLASS_CAST(klass) ((GstRTSPStreamTransportClass*)(klass))
+
+typedef struct _GstRTSPStreamTransport GstRTSPStreamTransport;
+typedef struct _GstRTSPStreamTransportClass GstRTSPStreamTransportClass;
+
+#include "rtsp-stream.h"
+
+typedef gboolean (*GstRTSPSendFunc)      (GstBuffer *buffer, guint8 channel, gpointer user_data);
+typedef void     (*GstRTSPKeepAliveFunc) (gpointer user_data);
+
+/**
+ * GstRTSPStreamTransport:
+ * @parent: parent instance
+ * @idx: the stream index
+ * @send_rtp: callback for sending RTP messages
+ * @send_rtcp: callback for sending RTCP messages
+ * @send_rtp_list: callback for sending RTP messages
+ * @send_rtcp_list: callback for sending RTCP messages
+ * @user_data: user data passed in the callbacks
+ * @notify: free function for the user_data.
+ * @keep_alive: keep alive callback
+ * @ka_user_data: data passed to @keep_alive
+ * @ka_notify: called when @ka_user_data is freed
+ * @active: if we are actively sending
+ * @timeout: if we timed out
+ * @transport: a transport description
+ * @rtpsource: the receiver rtp source object
+ *
+ * A Transport description for stream @idx
+ */
+struct _GstRTSPStreamTransport {
+  GObject              parent;
+
+  GstRTSPStream       *stream;
+
+  GstRTSPSendFunc      send_rtp;
+  GstRTSPSendFunc      send_rtcp;
+  gpointer             user_data;
+  GDestroyNotify       notify;
+
+  GstRTSPKeepAliveFunc keep_alive;
+  gpointer             ka_user_data;
+  GDestroyNotify       ka_notify;
+  gboolean             active;
+  gboolean             timeout;
+
+  GstRTSPTransport    *transport;
+
+  GObject             *rtpsource;
+};
+
+struct _GstRTSPStreamTransportClass {
+  GObjectClass parent_class;
+};
+
+GType                    gst_rtsp_stream_transport_get_type (void);
+
+GstRTSPStreamTransport * gst_rtsp_stream_transport_new           (GstRTSPStream *stream);
+
+GstRTSPTransport *       gst_rtsp_stream_transport_set_transport (GstRTSPStreamTransport *trans,
+                                                                  GstRTSPTransport * ct);
+
+void                     gst_rtsp_stream_transport_set_callbacks (GstRTSPStreamTransport *trans,
+                                                                  GstRTSPSendFunc send_rtp,
+                                                                  GstRTSPSendFunc send_rtcp,
+                                                                  gpointer user_data,
+                                                                  GDestroyNotify  notify);
+void                     gst_rtsp_stream_transport_set_keepalive (GstRTSPStreamTransport *trans,
+                                                                  GstRTSPKeepAliveFunc keep_alive,
+                                                                  gpointer user_data,
+                                                                  GDestroyNotify  notify);
+
+G_END_DECLS
+
+#endif /* __GST_RTSP_STREAM_TRANSPORT_H__ */
diff --git a/gst/rtsp-server/rtsp-stream.c b/gst/rtsp-server/rtsp-stream.c
new file mode 100644 (file)
index 0000000..9f5bd1e
--- /dev/null
@@ -0,0 +1,971 @@
+/* GStreamer
+ * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <gio/gio.h>
+
+#include <gst/app/gstappsrc.h>
+#include <gst/app/gstappsink.h>
+
+#include "rtsp-stream.h"
+
+enum
+{
+  PROP_0,
+  PROP_LAST
+};
+
+GST_DEBUG_CATEGORY_STATIC (rtsp_stream_debug);
+#define GST_CAT_DEFAULT rtsp_stream_debug
+
+static GQuark ssrc_stream_map_key;
+
+static void gst_rtsp_stream_finalize (GObject * obj);
+
+G_DEFINE_TYPE (GstRTSPStream, gst_rtsp_stream, G_TYPE_OBJECT);
+
+static void
+gst_rtsp_stream_class_init (GstRTSPStreamClass * klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = gst_rtsp_stream_finalize;
+
+  GST_DEBUG_CATEGORY_INIT (rtsp_stream_debug, "rtspstream", 0, "GstRTSPStream");
+
+  ssrc_stream_map_key = g_quark_from_static_string ("GstRTSPServer.stream");
+}
+
+static void
+gst_rtsp_stream_init (GstRTSPStream * media)
+{
+}
+
+static void
+gst_rtsp_stream_finalize (GObject * obj)
+{
+  GstRTSPStream *stream;
+
+  stream = GST_RTSP_STREAM (obj);
+
+  g_assert (!stream->is_joined);
+
+  gst_object_unref (stream->payloader);
+  gst_object_unref (stream->srcpad);
+
+  if (stream->session)
+    g_object_unref (stream->session);
+
+  if (stream->caps)
+    gst_caps_unref (stream->caps);
+
+  if (stream->send_rtp_sink)
+    gst_object_unref (stream->send_rtp_sink);
+  if (stream->send_rtp_src)
+    gst_object_unref (stream->send_rtp_src);
+  if (stream->send_rtcp_src)
+    gst_object_unref (stream->send_rtcp_src);
+  if (stream->recv_rtcp_sink)
+    gst_object_unref (stream->recv_rtcp_sink);
+  if (stream->recv_rtp_sink)
+    gst_object_unref (stream->recv_rtp_sink);
+
+  g_list_free (stream->transports);
+
+  G_OBJECT_CLASS (gst_rtsp_stream_parent_class)->finalize (obj);
+}
+
+/**
+ * gst_rtsp_stream_new:
+ * @idx: an index
+ * @srcpad: a #GstPad
+ * @payloader: a #GstElement
+ *
+ * Create a new media stream with index @idx that handles RTP data on
+ * @srcpad and has a payloader element @payloader.
+ *
+ * Returns: a new #GstRTSPStream
+ */
+GstRTSPStream *
+gst_rtsp_stream_new (guint idx, GstElement * payloader, GstPad * srcpad)
+{
+  GstRTSPStream *stream;
+
+  g_return_val_if_fail (GST_IS_ELEMENT (payloader), NULL);
+  g_return_val_if_fail (GST_IS_PAD (srcpad), NULL);
+  g_return_val_if_fail (GST_PAD_IS_SRC (srcpad), NULL);
+
+  stream = g_object_new (GST_TYPE_RTSP_STREAM, NULL);
+  stream->idx = idx;
+  stream->payloader = gst_object_ref (payloader);
+  stream->srcpad = gst_object_ref (srcpad);
+
+  return stream;
+}
+
+static gboolean
+alloc_ports (GstRTSPStream * stream)
+{
+  GstStateChangeReturn ret;
+  GstElement *udpsrc0, *udpsrc1;
+  GstElement *udpsink0, *udpsink1;
+  gint tmp_rtp, tmp_rtcp;
+  guint count;
+  gint rtpport, rtcpport;
+  GSocket *socket;
+  const gchar *host;
+
+  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
+
+  udpsrc0 = NULL;
+  udpsrc1 = NULL;
+  udpsink0 = NULL;
+  udpsink1 = NULL;
+  count = 0;
+
+  /* Start with random port */
+  tmp_rtp = 0;
+
+  if (stream->is_ipv6)
+    host = "udp://[::0]";
+  else
+    host = "udp://0.0.0.0";
+
+  /* try to allocate 2 UDP ports, the RTP port should be an even
+   * number and the RTCP port should be the next (uneven) port */
+again:
+  udpsrc0 = gst_element_make_from_uri (GST_URI_SRC, host, NULL, NULL);
+  if (udpsrc0 == NULL)
+    goto no_udp_protocol;
+  g_object_set (G_OBJECT (udpsrc0), "port", tmp_rtp, NULL);
+
+  ret = gst_element_set_state (udpsrc0, GST_STATE_PAUSED);
+  if (ret == GST_STATE_CHANGE_FAILURE) {
+    if (tmp_rtp != 0) {
+      tmp_rtp += 2;
+      if (++count > 20)
+        goto no_ports;
+
+      gst_element_set_state (udpsrc0, GST_STATE_NULL);
+      gst_object_unref (udpsrc0);
+
+      goto again;
+    }
+    goto no_udp_protocol;
+  }
+
+  g_object_get (G_OBJECT (udpsrc0), "port", &tmp_rtp, NULL);
+
+  /* check if port is even */
+  if ((tmp_rtp & 1) != 0) {
+    /* port not even, close and allocate another */
+    if (++count > 20)
+      goto no_ports;
+
+    gst_element_set_state (udpsrc0, GST_STATE_NULL);
+    gst_object_unref (udpsrc0);
+
+    tmp_rtp++;
+    goto again;
+  }
+
+  /* allocate port+1 for RTCP now */
+  udpsrc1 = gst_element_make_from_uri (GST_URI_SRC, host, NULL, NULL);
+  if (udpsrc1 == NULL)
+    goto no_udp_rtcp_protocol;
+
+  /* set port */
+  tmp_rtcp = tmp_rtp + 1;
+  g_object_set (G_OBJECT (udpsrc1), "port", tmp_rtcp, NULL);
+
+  ret = gst_element_set_state (udpsrc1, GST_STATE_PAUSED);
+  /* tmp_rtcp port is busy already : retry to make rtp/rtcp pair */
+  if (ret == GST_STATE_CHANGE_FAILURE) {
+
+    if (++count > 20)
+      goto no_ports;
+
+    gst_element_set_state (udpsrc0, GST_STATE_NULL);
+    gst_object_unref (udpsrc0);
+
+    gst_element_set_state (udpsrc1, GST_STATE_NULL);
+    gst_object_unref (udpsrc1);
+
+    tmp_rtp += 2;
+    goto again;
+  }
+  /* all fine, do port check */
+  g_object_get (G_OBJECT (udpsrc0), "port", &rtpport, NULL);
+  g_object_get (G_OBJECT (udpsrc1), "port", &rtcpport, NULL);
+
+  /* this should not happen... */
+  if (rtpport != tmp_rtp || rtcpport != tmp_rtcp)
+    goto port_error;
+
+  udpsink0 = gst_element_factory_make ("multiudpsink", NULL);
+  if (!udpsink0)
+    goto no_udp_protocol;
+
+  g_object_get (G_OBJECT (udpsrc0), "socket", &socket, NULL);
+  g_object_set (G_OBJECT (udpsink0), "socket", socket, NULL);
+  g_object_set (G_OBJECT (udpsink0), "close-socket", FALSE, NULL);
+
+  udpsink1 = gst_element_factory_make ("multiudpsink", NULL);
+  if (!udpsink1)
+    goto no_udp_protocol;
+
+  if (g_object_class_find_property (G_OBJECT_GET_CLASS (udpsink0),
+          "send-duplicates")) {
+    g_object_set (G_OBJECT (udpsink0), "send-duplicates", FALSE, NULL);
+    g_object_set (G_OBJECT (udpsink1), "send-duplicates", FALSE, NULL);
+  } else {
+    g_warning
+        ("old multiudpsink version found without send-duplicates property");
+  }
+
+  if (g_object_class_find_property (G_OBJECT_GET_CLASS (udpsink0),
+          "buffer-size")) {
+    g_object_set (G_OBJECT (udpsink0), "buffer-size", stream->buffer_size,
+        NULL);
+  } else {
+    GST_WARNING ("multiudpsink version found without buffer-size property");
+  }
+
+  g_object_get (G_OBJECT (udpsrc1), "socket", &socket, NULL);
+  g_object_set (G_OBJECT (udpsink1), "socket", socket, NULL);
+  g_object_set (G_OBJECT (udpsink1), "close-socket", FALSE, NULL);
+  g_object_set (G_OBJECT (udpsink1), "sync", FALSE, NULL);
+  g_object_set (G_OBJECT (udpsink1), "async", FALSE, NULL);
+  g_object_set (G_OBJECT (udpsink0), "auto-multicast", FALSE, NULL);
+  g_object_set (G_OBJECT (udpsink0), "loop", FALSE, NULL);
+  g_object_set (G_OBJECT (udpsink1), "auto-multicast", FALSE, NULL);
+  g_object_set (G_OBJECT (udpsink1), "loop", FALSE, NULL);
+
+  /* we keep these elements, we will further configure them when the
+   * client told us to really use the UDP ports. */
+  stream->udpsrc[0] = udpsrc0;
+  stream->udpsrc[1] = udpsrc1;
+  stream->udpsink[0] = udpsink0;
+  stream->udpsink[1] = udpsink1;
+  stream->server_port.min = rtpport;
+  stream->server_port.max = rtcpport;
+
+  return TRUE;
+
+  /* ERRORS */
+no_udp_protocol:
+  {
+    goto cleanup;
+  }
+no_ports:
+  {
+    goto cleanup;
+  }
+no_udp_rtcp_protocol:
+  {
+    goto cleanup;
+  }
+port_error:
+  {
+    goto cleanup;
+  }
+cleanup:
+  {
+    if (udpsrc0) {
+      gst_element_set_state (udpsrc0, GST_STATE_NULL);
+      gst_object_unref (udpsrc0);
+    }
+    if (udpsrc1) {
+      gst_element_set_state (udpsrc1, GST_STATE_NULL);
+      gst_object_unref (udpsrc1);
+    }
+    if (udpsink0) {
+      gst_element_set_state (udpsink0, GST_STATE_NULL);
+      gst_object_unref (udpsink0);
+    }
+    if (udpsink1) {
+      gst_element_set_state (udpsink1, GST_STATE_NULL);
+      gst_object_unref (udpsink1);
+    }
+    return FALSE;
+  }
+}
+
+/* executed from streaming thread */
+static void
+caps_notify (GstPad * pad, GParamSpec * unused, GstRTSPStream * stream)
+{
+  GstCaps *newcaps, *oldcaps;
+
+  newcaps = gst_pad_get_current_caps (pad);
+
+  oldcaps = stream->caps;
+  stream->caps = newcaps;
+
+  if (oldcaps)
+    gst_caps_unref (oldcaps);
+
+  GST_INFO ("stream %p received caps %p, %" GST_PTR_FORMAT, stream, newcaps,
+      newcaps);
+}
+
+static void
+dump_structure (const GstStructure * s)
+{
+  gchar *sstr;
+
+  sstr = gst_structure_to_string (s);
+  GST_INFO ("structure: %s", sstr);
+  g_free (sstr);
+}
+
+static GstRTSPStreamTransport *
+find_transport (GstRTSPStream * stream, const gchar * rtcp_from)
+{
+  GList *walk;
+  GstRTSPStreamTransport *result = NULL;
+  const gchar *tmp;
+  gchar *dest;
+  guint port;
+
+  if (rtcp_from == NULL)
+    return NULL;
+
+  tmp = g_strrstr (rtcp_from, ":");
+  if (tmp == NULL)
+    return NULL;
+
+  port = atoi (tmp + 1);
+  dest = g_strndup (rtcp_from, tmp - rtcp_from);
+
+  GST_INFO ("finding %s:%d in %d transports", dest, port,
+      g_list_length (stream->transports));
+
+  for (walk = stream->transports; walk; walk = g_list_next (walk)) {
+    GstRTSPStreamTransport *trans = walk->data;
+    gint min, max;
+
+    min = trans->transport->client_port.min;
+    max = trans->transport->client_port.max;
+
+    if ((strcmp (trans->transport->destination, dest) == 0) && (min == port
+            || max == port)) {
+      result = trans;
+      break;
+    }
+  }
+  g_free (dest);
+
+  return result;
+}
+
+static GstRTSPStreamTransport *
+check_transport (GObject * source, GstRTSPStream * stream)
+{
+  GstStructure *stats;
+  GstRTSPStreamTransport *trans;
+
+  /* see if we have a stream to match with the origin of the RTCP packet */
+  trans = g_object_get_qdata (source, ssrc_stream_map_key);
+  if (trans == NULL) {
+    g_object_get (source, "stats", &stats, NULL);
+    if (stats) {
+      const gchar *rtcp_from;
+
+      dump_structure (stats);
+
+      rtcp_from = gst_structure_get_string (stats, "rtcp-from");
+      if ((trans = find_transport (stream, rtcp_from))) {
+        GST_INFO ("%p: found transport %p for source  %p", stream, trans,
+            source);
+
+        /* keep ref to the source */
+        trans->rtpsource = source;
+
+        g_object_set_qdata (source, ssrc_stream_map_key, trans);
+      }
+      gst_structure_free (stats);
+    }
+  }
+
+  return trans;
+}
+
+
+static void
+on_new_ssrc (GObject * session, GObject * source, GstRTSPStream * stream)
+{
+  GstRTSPStreamTransport *trans;
+
+  GST_INFO ("%p: new source %p", stream, source);
+
+  trans = check_transport (source, stream);
+
+  if (trans)
+    GST_INFO ("%p: source %p for transport %p", stream, source, trans);
+}
+
+static void
+on_ssrc_sdes (GObject * session, GObject * source, GstRTSPStream * stream)
+{
+  GST_INFO ("%p: new SDES %p", stream, source);
+}
+
+static void
+on_ssrc_active (GObject * session, GObject * source, GstRTSPStream * stream)
+{
+  GstRTSPStreamTransport *trans;
+
+  trans = check_transport (source, stream);
+
+  if (trans)
+    GST_INFO ("%p: source %p in transport %p is active", stream, source, trans);
+
+  if (trans && trans->keep_alive)
+    trans->keep_alive (trans->ka_user_data);
+
+#ifdef DUMP_STATS
+  {
+    GstStructure *stats;
+    g_object_get (source, "stats", &stats, NULL);
+    if (stats) {
+      dump_structure (stats);
+      gst_structure_free (stats);
+    }
+  }
+#endif
+}
+
+static void
+on_bye_ssrc (GObject * session, GObject * source, GstRTSPStream * stream)
+{
+  GST_INFO ("%p: source %p bye", stream, source);
+}
+
+static void
+on_bye_timeout (GObject * session, GObject * source, GstRTSPStream * stream)
+{
+  GstRTSPStreamTransport *trans;
+
+  GST_INFO ("%p: source %p bye timeout", stream, source);
+
+  if ((trans = g_object_get_qdata (source, ssrc_stream_map_key))) {
+    trans->rtpsource = NULL;
+    trans->timeout = TRUE;
+  }
+}
+
+static void
+on_timeout (GObject * session, GObject * source, GstRTSPStream * stream)
+{
+  GstRTSPStreamTransport *trans;
+
+  GST_INFO ("%p: source %p timeout", stream, source);
+
+  if ((trans = g_object_get_qdata (source, ssrc_stream_map_key))) {
+    trans->rtpsource = NULL;
+    trans->timeout = TRUE;
+  }
+}
+
+static GstFlowReturn
+handle_new_sample (GstAppSink * sink, gpointer user_data)
+{
+  GList *walk;
+  GstSample *sample;
+  GstBuffer *buffer;
+  GstRTSPStream *stream;
+
+  sample = gst_app_sink_pull_sample (sink);
+  if (!sample)
+    return GST_FLOW_OK;
+
+  stream = (GstRTSPStream *) user_data;
+  buffer = gst_sample_get_buffer (sample);
+
+  for (walk = stream->transports; walk; walk = g_list_next (walk)) {
+    GstRTSPStreamTransport *tr = (GstRTSPStreamTransport *) walk->data;
+
+    if (GST_ELEMENT_CAST (sink) == stream->appsink[0]) {
+      if (tr->send_rtp)
+        tr->send_rtp (buffer, tr->transport->interleaved.min, tr->user_data);
+    } else {
+      if (tr->send_rtcp)
+        tr->send_rtcp (buffer, tr->transport->interleaved.max, tr->user_data);
+    }
+  }
+  gst_sample_unref (sample);
+
+  return GST_FLOW_OK;
+}
+
+static GstAppSinkCallbacks sink_cb = {
+  NULL,                         /* not interested in EOS */
+  NULL,                         /* not interested in preroll samples */
+  handle_new_sample,
+};
+
+/**
+ * gst_rtsp_stream_join_bin:
+ * @stream: a #GstRTSPStream
+ * @bin: a #GstBin to join
+ * @rtpbin: a rtpbin element in @bin
+ *
+ * Join the #Gstbin @bin that contains the element @rtpbin.
+ *
+ * @stream will link to @rtpbin, which must be inside @bin.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+gst_rtsp_stream_join_bin (GstRTSPStream * stream, GstBin * bin,
+    GstElement * rtpbin)
+{
+  gint i, idx;
+  gchar *name;
+  GstPad *pad, *teepad, *queuepad, *selpad;
+  GstPadLinkReturn ret;
+
+  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
+  g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
+  g_return_val_if_fail (GST_IS_ELEMENT (rtpbin), FALSE);
+
+  idx = stream->idx;
+
+  if (stream->is_joined)
+    return TRUE;
+
+  GST_INFO ("stream %p joining bin", stream);
+
+  if (!alloc_ports (stream))
+    goto no_ports;
+
+  /* add the ports to the pipeline */
+  for (i = 0; i < 2; i++) {
+    gst_bin_add (bin, stream->udpsink[i]);
+    gst_bin_add (bin, stream->udpsrc[i]);
+  }
+
+  /* create elements for the TCP transfer */
+  for (i = 0; i < 2; i++) {
+    stream->appsrc[i] = gst_element_factory_make ("appsrc", NULL);
+    stream->appqueue[i] = gst_element_factory_make ("queue", NULL);
+    stream->appsink[i] = gst_element_factory_make ("appsink", NULL);
+    g_object_set (stream->appsink[i], "async", FALSE, "sync", FALSE, NULL);
+    g_object_set (stream->appsink[i], "emit-signals", FALSE, NULL);
+    gst_bin_add (bin, stream->appqueue[i]);
+    gst_bin_add (bin, stream->appsink[i]);
+    gst_bin_add (bin, stream->appsrc[i]);
+    gst_app_sink_set_callbacks (GST_APP_SINK_CAST (stream->appsink[i]),
+        &sink_cb, stream, NULL);
+  }
+
+  /* hook up the stream to the RTP session elements. */
+  name = g_strdup_printf ("send_rtp_sink_%u", idx);
+  stream->send_rtp_sink = gst_element_get_request_pad (rtpbin, name);
+  g_free (name);
+  name = g_strdup_printf ("send_rtp_src_%u", idx);
+  stream->send_rtp_src = gst_element_get_static_pad (rtpbin, name);
+  g_free (name);
+  name = g_strdup_printf ("send_rtcp_src_%u", idx);
+  stream->send_rtcp_src = gst_element_get_request_pad (rtpbin, name);
+  g_free (name);
+  name = g_strdup_printf ("recv_rtcp_sink_%u", idx);
+  stream->recv_rtcp_sink = gst_element_get_request_pad (rtpbin, name);
+  g_free (name);
+  name = g_strdup_printf ("recv_rtp_sink_%u", idx);
+  stream->recv_rtp_sink = gst_element_get_request_pad (rtpbin, name);
+  g_free (name);
+  /* get the session */
+  g_signal_emit_by_name (rtpbin, "get-internal-session", idx, &stream->session);
+
+  g_signal_connect (stream->session, "on-new-ssrc", (GCallback) on_new_ssrc,
+      stream);
+  g_signal_connect (stream->session, "on-ssrc-sdes", (GCallback) on_ssrc_sdes,
+      stream);
+  g_signal_connect (stream->session, "on-ssrc-active",
+      (GCallback) on_ssrc_active, stream);
+  g_signal_connect (stream->session, "on-bye-ssrc", (GCallback) on_bye_ssrc,
+      stream);
+  g_signal_connect (stream->session, "on-bye-timeout",
+      (GCallback) on_bye_timeout, stream);
+  g_signal_connect (stream->session, "on-timeout", (GCallback) on_timeout,
+      stream);
+
+  /* link the RTP pad to the session manager */
+  ret = gst_pad_link (stream->srcpad, stream->send_rtp_sink);
+  if (ret != GST_PAD_LINK_OK)
+    goto link_failed;
+
+  /* make tee for RTP and link to stream */
+  stream->tee[0] = gst_element_factory_make ("tee", NULL);
+  gst_bin_add (bin, stream->tee[0]);
+
+  pad = gst_element_get_static_pad (stream->tee[0], "sink");
+  gst_pad_link (stream->send_rtp_src, pad);
+  gst_object_unref (pad);
+
+  /* link RTP sink, we're pretty sure this will work. */
+  teepad = gst_element_get_request_pad (stream->tee[0], "src_%u");
+  pad = gst_element_get_static_pad (stream->udpsink[0], "sink");
+  gst_pad_link (teepad, pad);
+  gst_object_unref (pad);
+  gst_object_unref (teepad);
+
+  teepad = gst_element_get_request_pad (stream->tee[0], "src_%u");
+  pad = gst_element_get_static_pad (stream->appqueue[0], "sink");
+  gst_pad_link (teepad, pad);
+  gst_object_unref (pad);
+  gst_object_unref (teepad);
+
+  queuepad = gst_element_get_static_pad (stream->appqueue[0], "src");
+  pad = gst_element_get_static_pad (stream->appsink[0], "sink");
+  gst_pad_link (queuepad, pad);
+  gst_object_unref (pad);
+  gst_object_unref (queuepad);
+
+  /* make tee for RTCP */
+  stream->tee[1] = gst_element_factory_make ("tee", NULL);
+  gst_bin_add (bin, stream->tee[1]);
+
+  pad = gst_element_get_static_pad (stream->tee[1], "sink");
+  gst_pad_link (stream->send_rtcp_src, pad);
+  gst_object_unref (pad);
+
+  /* link RTCP elements */
+  teepad = gst_element_get_request_pad (stream->tee[1], "src_%u");
+  pad = gst_element_get_static_pad (stream->udpsink[1], "sink");
+  gst_pad_link (teepad, pad);
+  gst_object_unref (pad);
+  gst_object_unref (teepad);
+
+  teepad = gst_element_get_request_pad (stream->tee[1], "src_%u");
+  pad = gst_element_get_static_pad (stream->appqueue[1], "sink");
+  gst_pad_link (teepad, pad);
+  gst_object_unref (pad);
+  gst_object_unref (teepad);
+
+  queuepad = gst_element_get_static_pad (stream->appqueue[1], "src");
+  pad = gst_element_get_static_pad (stream->appsink[1], "sink");
+  gst_pad_link (queuepad, pad);
+  gst_object_unref (pad);
+  gst_object_unref (queuepad);
+
+  /* make selector for the RTP receivers */
+  stream->selector[0] = gst_element_factory_make ("funnel", NULL);
+  gst_bin_add (bin, stream->selector[0]);
+
+  pad = gst_element_get_static_pad (stream->selector[0], "src");
+  gst_pad_link (pad, stream->recv_rtp_sink);
+  gst_object_unref (pad);
+
+  selpad = gst_element_get_request_pad (stream->selector[0], "sink_%u");
+  pad = gst_element_get_static_pad (stream->udpsrc[0], "src");
+  gst_pad_link (pad, selpad);
+  gst_object_unref (pad);
+  gst_object_unref (selpad);
+  selpad = gst_element_get_request_pad (stream->selector[0], "sink_%u");
+  pad = gst_element_get_static_pad (stream->appsrc[0], "src");
+  gst_pad_link (pad, selpad);
+  gst_object_unref (pad);
+  gst_object_unref (selpad);
+
+  /* make selector for the RTCP receivers */
+  stream->selector[1] = gst_element_factory_make ("funnel", NULL);
+  gst_bin_add (bin, stream->selector[1]);
+
+  pad = gst_element_get_static_pad (stream->selector[1], "src");
+  gst_pad_link (pad, stream->recv_rtcp_sink);
+  gst_object_unref (pad);
+
+  selpad = gst_element_get_request_pad (stream->selector[1], "sink_%u");
+  pad = gst_element_get_static_pad (stream->udpsrc[1], "src");
+  gst_pad_link (pad, selpad);
+  gst_object_unref (pad);
+  gst_object_unref (selpad);
+
+  selpad = gst_element_get_request_pad (stream->selector[1], "sink_%u");
+  pad = gst_element_get_static_pad (stream->appsrc[1], "src");
+  gst_pad_link (pad, selpad);
+  gst_object_unref (pad);
+  gst_object_unref (selpad);
+
+  /* we set and keep these to playing so that they don't cause NO_PREROLL return
+   * values */
+  gst_element_set_state (stream->udpsrc[0], GST_STATE_PLAYING);
+  gst_element_set_state (stream->udpsrc[1], GST_STATE_PLAYING);
+  gst_element_set_locked_state (stream->udpsrc[0], TRUE);
+  gst_element_set_locked_state (stream->udpsrc[1], TRUE);
+
+  /* be notified of caps changes */
+  stream->caps_sig = g_signal_connect (stream->send_rtp_sink, "notify::caps",
+      (GCallback) caps_notify, stream);
+
+  stream->is_joined = TRUE;
+
+  return TRUE;
+
+  /* ERRORS */
+no_ports:
+  {
+    GST_WARNING ("failed to allocate ports %d", idx);
+    return FALSE;
+  }
+link_failed:
+  {
+    GST_WARNING ("failed to link stream %d", idx);
+    return FALSE;
+  }
+}
+
+/**
+ * gst_rtsp_stream_leave_bin:
+ * @stream: a #GstRTSPStream
+ * @bin: a #GstBin
+ * @rtpbin: a rtpbin #GstElement
+ *
+ * Remove the elements of @stream from the bin
+ *
+ * Return: %TRUE on success.
+ */
+gboolean
+gst_rtsp_stream_leave_bin (GstRTSPStream * stream, GstBin * bin,
+    GstElement * rtpbin)
+{
+  gint i;
+
+  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
+  g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
+  g_return_val_if_fail (GST_IS_ELEMENT (rtpbin), FALSE);
+
+  if (!stream->is_joined)
+    return TRUE;
+
+  GST_INFO ("stream %p leaving bin", stream);
+
+  gst_pad_unlink (stream->srcpad, stream->send_rtp_sink);
+
+  g_signal_handler_disconnect (stream->send_rtp_sink, stream->caps_sig);
+
+  /* FIXME not entirely the opposite of join_bin */
+  for (i = 0; i < 2; i++) {
+    gst_bin_remove (bin, stream->udpsrc[i]);
+    gst_bin_remove (bin, stream->udpsink[i]);
+    gst_bin_remove (bin, stream->appsrc[i]);
+    gst_bin_remove (bin, stream->appsink[i]);
+    gst_bin_remove (bin, stream->appqueue[i]);
+    gst_bin_remove (bin, stream->tee[i]);
+    gst_bin_remove (bin, stream->selector[i]);
+  }
+  stream->is_joined = FALSE;
+
+  return TRUE;
+}
+
+/**
+ * gst_rtsp_stream_get_rtpinfo:
+ * @stream: a #GstRTSPStream
+ * @rtptime: result RTP timestamp
+ * @seq: result RTP seqnum
+ *
+ * Retrieve the current rtptime and seq. This is used to
+ * construct a RTPInfo reply header.
+ *
+ * Returns: %TRUE when rtptime and seq could be determined.
+ */
+gboolean
+gst_rtsp_stream_get_rtpinfo (GstRTSPStream * stream,
+    guint * rtptime, guint * seq)
+{
+  GObjectClass *payobjclass;
+
+  payobjclass = G_OBJECT_GET_CLASS (stream->payloader);
+
+  if (!g_object_class_find_property (payobjclass, "seqnum") ||
+      !g_object_class_find_property (payobjclass, "timestamp"))
+    return FALSE;
+
+  g_object_get (stream->payloader, "seqnum", seq, "timestamp", rtptime, NULL);
+
+  return TRUE;
+}
+
+/**
+ * gst_rtsp_stream_recv_rtp:
+ * @stream: a #GstRTSPStream
+ * @buffer: (transfer full): a #GstBuffer
+ *
+ * Handle an RTP buffer for the stream. This method is usually called when a
+ * message has been received from a client using the TCP transport.
+ *
+ * This function takes ownership of @buffer.
+ *
+ * Returns: a GstFlowReturn.
+ */
+GstFlowReturn
+gst_rtsp_stream_recv_rtp (GstRTSPStream * stream, GstBuffer * buffer)
+{
+  GstFlowReturn ret;
+
+  ret = gst_app_src_push_buffer (GST_APP_SRC_CAST (stream->appsrc[0]), buffer);
+
+  return ret;
+}
+
+/**
+ * gst_rtsp_stream_recv_rtcp:
+ * @stream: a #GstRTSPStream
+ * @buffer: (transfer full): a #GstBuffer
+ *
+ * Handle an RTCP buffer for the stream. This method is usually called when a
+ * message has been received from a client using the TCP transport.
+ *
+ * This function takes ownership of @buffer.
+ *
+ * Returns: a GstFlowReturn.
+ */
+GstFlowReturn
+gst_rtsp_stream_recv_rtcp (GstRTSPStream * stream, GstBuffer * buffer)
+{
+  GstFlowReturn ret;
+
+  ret = gst_app_src_push_buffer (GST_APP_SRC_CAST (stream->appsrc[1]), buffer);
+
+  return ret;
+}
+
+static gboolean
+update_transport (GstRTSPStream * stream, GstRTSPStreamTransport * trans,
+    gboolean add)
+{
+  GstRTSPTransport *tr;
+  gboolean updated;
+
+  updated = FALSE;
+
+  tr = trans->transport;
+
+  switch (tr->lower_transport) {
+    case GST_RTSP_LOWER_TRANS_UDP:
+    case GST_RTSP_LOWER_TRANS_UDP_MCAST:
+    {
+      gchar *dest;
+      gint min, max;
+      guint ttl = 0;
+
+      dest = tr->destination;
+      if (tr->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
+        min = tr->port.min;
+        max = tr->port.max;
+        ttl = tr->ttl;
+      } else {
+        min = tr->client_port.min;
+        max = tr->client_port.max;
+      }
+
+      if (add && !trans->active) {
+        GST_INFO ("adding %s:%d-%d", dest, min, max);
+        g_signal_emit_by_name (stream->udpsink[0], "add", dest, min, NULL);
+        g_signal_emit_by_name (stream->udpsink[1], "add", dest, max, NULL);
+        if (ttl > 0) {
+          GST_INFO ("setting ttl-mc %d", ttl);
+          g_object_set (G_OBJECT (stream->udpsink[0]), "ttl-mc", ttl, NULL);
+          g_object_set (G_OBJECT (stream->udpsink[1]), "ttl-mc", ttl, NULL);
+        }
+        stream->transports = g_list_prepend (stream->transports, trans);
+        trans->active = TRUE;
+        updated = TRUE;
+      } else if (trans->active) {
+        GST_INFO ("removing %s:%d-%d", dest, min, max);
+        g_signal_emit_by_name (stream->udpsink[0], "remove", dest, min, NULL);
+        g_signal_emit_by_name (stream->udpsink[1], "remove", dest, max, NULL);
+        stream->transports = g_list_remove (stream->transports, trans);
+        trans->active = FALSE;
+        updated = TRUE;
+      }
+      break;
+    }
+    case GST_RTSP_LOWER_TRANS_TCP:
+      if (add && !trans->active) {
+        GST_INFO ("adding TCP %s", tr->destination);
+        stream->transports = g_list_prepend (stream->transports, trans);
+        trans->active = TRUE;
+        updated = TRUE;
+      } else if (trans->active) {
+        GST_INFO ("removing TCP %s", tr->destination);
+        stream->transports = g_list_remove (stream->transports, trans);
+        trans->active = FALSE;
+        updated = TRUE;
+      }
+      break;
+    default:
+      GST_INFO ("Unknown transport %d", tr->lower_transport);
+      break;
+  }
+  return updated;
+}
+
+
+/**
+ * gst_rtsp_stream_add_transport:
+ * @stream: a #GstRTSPStream
+ * @trans: a #GstRTSPStreamTransport
+ *
+ * Add the transport in @trans to @stream. The media of @stream will
+ * then also be send to the values configured in @trans.
+ *
+ * @trans must contain a valid #GstRTSPTransport.
+ *
+ * Returns: %TRUE if @trans was added
+ */
+gboolean
+gst_rtsp_stream_add_transport (GstRTSPStream * stream,
+    GstRTSPStreamTransport * trans)
+{
+  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
+  g_return_val_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans), FALSE);
+  g_return_val_if_fail (trans->transport != NULL, FALSE);
+
+  return update_transport (stream, trans, TRUE);
+}
+
+/**
+ * gst_rtsp_stream_remove_transport:
+ * @stream: a #GstRTSPStream
+ * @trans: a #GstRTSPStreamTransport
+ *
+ * Remove the transport in @trans from @stream. The media of @stream will
+ * not be sent to the values configured in @trans.
+ *
+ * Returns: %TRUE if @trans was removed
+ */
+gboolean
+gst_rtsp_stream_remove_transport (GstRTSPStream * stream,
+    GstRTSPStreamTransport * trans)
+{
+  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
+  g_return_val_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans), FALSE);
+  g_return_val_if_fail (trans->transport != NULL, FALSE);
+
+  return update_transport (stream, trans, FALSE);
+}
diff --git a/gst/rtsp-server/rtsp-stream.h b/gst/rtsp-server/rtsp-stream.h
new file mode 100644 (file)
index 0000000..29a813b
--- /dev/null
@@ -0,0 +1,142 @@
+/* GStreamer
+ * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gst/gst.h>
+#include <gst/rtsp/gstrtsprange.h>
+#include <gst/rtsp/gstrtspurl.h>
+
+#ifndef __GST_RTSP_STREAM_H__
+#define __GST_RTSP_STREAM_H__
+
+G_BEGIN_DECLS
+
+/* types for the media stream */
+#define GST_TYPE_RTSP_STREAM              (gst_rtsp_stream_get_type ())
+#define GST_IS_RTSP_STREAM(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_STREAM))
+#define GST_IS_RTSP_STREAM_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_STREAM))
+#define GST_RTSP_STREAM_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_STREAM, GstRTSPStreamClass))
+#define GST_RTSP_STREAM(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_STREAM, GstRTSPStream))
+#define GST_RTSP_STREAM_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_STREAM, GstRTSPStreamClass))
+#define GST_RTSP_STREAM_CAST(obj)         ((GstRTSPStream*)(obj))
+#define GST_RTSP_STREAM_CLASS_CAST(klass) ((GstRTSPStreamClass*)(klass))
+
+typedef struct _GstRTSPStream GstRTSPStream;
+typedef struct _GstRTSPStreamClass GstRTSPStreamClass;
+
+#include "rtsp-stream-transport.h"
+
+/**
+ * GstRTSPStream:
+ * @parent: the parent instance
+ * @idx: the stream index
+ * @srcpad: the srcpad of the stream
+ * @payloader: the payloader of the format
+ * @is_ipv6: should this stream be IPv6
+ * @buffer_size: the UDP buffer size
+ * @is_joined: if the stream is joined in a bin
+ * @recv_rtp_sink: sinkpad for RTP buffers
+ * @recv_rtcp_sink: sinkpad for RTCP buffers
+ * @send_rtp_src: srcpad for RTP buffers
+ * @send_rtcp_src: srcpad for RTCP buffers
+ * @udpsrc: the udp source elements for RTP/RTCP
+ * @udpsink: the udp sink elements for RTP/RTCP
+ * @appsrc: the app source elements for RTP/RTCP
+ * @appsink: the app sink elements for RTP/RTCP
+ * @server_port: the server ports for this stream
+ * @caps_sig: the signal id for detecting caps
+ * @caps: the caps of the stream
+ * @n_active: the number of active transports in @transports
+ * @tranports: list of #GstStreamTransport being streamed to
+ *
+ * The definition of a media stream. The streams are identified by @id.
+ */
+struct _GstRTSPStream {
+  GObject       parent;
+
+  guint         idx;
+  GstPad       *srcpad;
+  GstElement   *payloader;
+  gboolean      is_ipv6;
+  guint         buffer_size;
+  gboolean      is_joined;
+
+  /* pads on the rtpbin */
+  GstPad       *recv_rtcp_sink;
+  GstPad       *recv_rtp_sink;
+  GstPad       *send_rtp_sink;
+  GstPad       *send_rtp_src;
+  GstPad       *send_rtcp_src;
+
+  /* the RTPSession object */
+  GObject      *session;
+
+  /* sinks used for sending and receiving RTP and RTCP, they share
+   * sockets */
+  GstElement   *udpsrc[2];
+  GstElement   *udpsink[2];
+  /* for TCP transport */
+  GstElement   *appsrc[2];
+  GstElement   *appqueue[2];
+  GstElement   *appsink[2];
+
+  GstElement   *tee[2];
+  GstElement   *selector[2];
+
+  /* server ports for sending/receiving */
+  GstRTSPRange  server_port;
+
+  /* the caps of the stream */
+  gulong        caps_sig;
+  GstCaps      *caps;
+
+  /* transports we stream to */
+  guint         n_active;
+  GList        *transports;
+};
+
+struct _GstRTSPStreamClass {
+  GObjectClass parent_class;
+};
+
+GType             gst_rtsp_stream_get_type         (void);
+
+GstRTSPStream *   gst_rtsp_stream_new              (guint idx, GstElement *payloader,
+                                                    GstPad *srcpad);
+
+gboolean          gst_rtsp_stream_join_bin         (GstRTSPStream * stream,
+                                                    GstBin *bin, GstElement *rtpbin);
+gboolean          gst_rtsp_stream_leave_bin        (GstRTSPStream * stream,
+                                                    GstBin *bin, GstElement *rtpbin);
+
+gboolean          gst_rtsp_stream_get_rtpinfo      (GstRTSPStream * stream,
+                                                    guint *rtptime, guint * seq);
+
+GstFlowReturn     gst_rtsp_stream_recv_rtp         (GstRTSPStream *stream,
+                                                    GstBuffer *buffer);
+GstFlowReturn     gst_rtsp_stream_recv_rtcp        (GstRTSPStream *stream,
+                                                    GstBuffer *buffer);
+
+gboolean          gst_rtsp_stream_add_transport    (GstRTSPStream *stream,
+                                                    GstRTSPStreamTransport *trans);
+gboolean          gst_rtsp_stream_remove_transport (GstRTSPStream *stream,
+                                                    GstRTSPStreamTransport *trans);
+
+G_END_DECLS
+
+#endif /* __GST_RTSP_STREAM_H__ */