Rework the way we handle transports for streams
authorWim Taymans <wim.taymans@collabora.co.uk>
Tue, 3 Feb 2009 18:32:38 +0000 (19:32 +0100)
committerWim Taymans <wim.taymans@collabora.co.uk>
Tue, 3 Feb 2009 18:32:38 +0000 (19:32 +0100)
Make the media accept an array of transports for the streams that we have
configured for the play/pause requests.

Implement server states for a client and its media.

Require 0.10.22.1 (git HEAD) of gstreamer.

configure.ac
gst/rtsp-server/rtsp-client.c
gst/rtsp-server/rtsp-media.c
gst/rtsp-server/rtsp-media.h
gst/rtsp-server/rtsp-session.c
gst/rtsp-server/rtsp-session.h

index 9fdd1a1..a5eb524 100644 (file)
@@ -37,8 +37,8 @@ AC_SUBST(GST_MAJORMINOR)
 AM_PROG_LIBTOOL
 
 dnl *** required versions of GStreamer stuff ***
-GST_REQ=0.10.20
-GSTPB_REQ=0.10.20
+GST_REQ=0.10.22.1
+GSTPB_REQ=0.10.22.1
 
 dnl export for .pc files
 AC_SUBST([GST_REQ])
index 465b2ec..99c92fa 100644 (file)
@@ -287,8 +287,12 @@ handle_pause_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *re
   if (!media)
     goto not_found;
 
+  /* the session state must be playing or recording */
+  if (media->state != GST_RTSP_STATE_PLAYING &&
+      media->state != GST_RTSP_STATE_RECORDING)
+    goto invalid_state;
+
   gst_rtsp_session_media_pause (media);
-  g_object_unref (session);
 
   /* construct the response now */
   code = GST_RTSP_STS_OK;
@@ -296,6 +300,10 @@ handle_pause_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *re
 
   send_response (client, &response);
 
+  /* the state is now READY */
+  media->state = GST_RTSP_STATE_READY;
+  g_object_unref (session);
+
   return FALSE;
 
   /* ERRORS */
@@ -308,6 +316,11 @@ not_found:
     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
     return FALSE;
   }
+invalid_state:
+  {
+    send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
+    return FALSE;
+  }
 }
 
 static gboolean
@@ -329,6 +342,11 @@ handle_play_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *req
   if (!media)
     goto not_found;
 
+  /* the session state must be playing or ready */
+  if (media->state != GST_RTSP_STATE_PLAYING &&
+      media->state != GST_RTSP_STATE_READY)
+    goto invalid_state;
+
   /* grab RTPInfo from the payloaders now */
   rtpinfo = g_string_new ("");
 
@@ -362,6 +380,8 @@ handle_play_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *req
 
   /* start playing after sending the request */
   gst_rtsp_session_media_play (media);
+
+  media->state = GST_RTSP_STATE_PLAYING;
   g_object_unref (session);
 
   return FALSE;
@@ -377,6 +397,11 @@ not_found:
     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
     return FALSE;
   }
+invalid_state:
+  {
+    send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
+    return FALSE;
+  }
 }
 
 static gboolean
@@ -508,10 +533,23 @@ handle_setup_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *re
 
   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
   g_free (trans_str);
-  g_object_unref (session);
 
   send_response (client, &response);
 
+  /* update the state */
+  switch (media->state) {
+    case GST_RTSP_STATE_PLAYING:
+    case GST_RTSP_STATE_RECORDING:
+    case GST_RTSP_STATE_READY:
+      /* no state change */
+      break;
+    default:
+      media->state = GST_RTSP_STATE_READY;
+      break;
+  }
+
+  g_object_unref (session);
+
   return TRUE;
 
   /* ERRORS */
index 6178e21..b2e4cd6 100644 (file)
@@ -538,100 +538,125 @@ state_failed:
   }
 }
 
-gboolean
-gst_rtsp_media_stream_add (GstRTSPMediaStream *stream, GstRTSPTransport *ct)
-{
-  g_return_val_if_fail (stream != NULL, FALSE);
-  g_return_val_if_fail (ct != NULL, FALSE);
-  g_return_val_if_fail (stream->prepared, FALSE);
-
-  g_message ("adding %s:%d", ct->destination, ct->client_port.min);
-
-  g_signal_emit_by_name (stream->udpsink[0], "add", ct->destination, ct->client_port.min, NULL);
-  g_signal_emit_by_name (stream->udpsink[1], "add", ct->destination, ct->client_port.max, NULL);
-
-  return TRUE;
-}
-
-gboolean
-gst_rtsp_media_stream_remove (GstRTSPMediaStream *stream, GstRTSPTransport *ct)
-{
-  g_return_val_if_fail (stream != NULL, FALSE);
-  g_return_val_if_fail (ct != NULL, FALSE);
-  g_return_val_if_fail (stream->prepared, FALSE);
-
-  g_message ("removing %s:%d", ct->destination, ct->client_port.min);
-
-  g_signal_emit_by_name (stream->udpsink[0], "remove", ct->destination, ct->client_port.min, NULL);
-  g_signal_emit_by_name (stream->udpsink[1], "remove", ct->destination, ct->client_port.max, NULL);
-
-  return TRUE;
-}
-
 /**
  * gst_rtsp_media_play:
  * @media: a #GstRTSPMedia
+ * @transports: a GArray of #GstRTSPMediaTrans pointers
  *
- * Tell the @media to start playing and streaming to the client.
+ * Start playing @media for to the transports in @transports.
  *
- * Returns: a #GstStateChangeReturn
+ * Returns: %TRUE on success.
  */
-GstStateChangeReturn
-gst_rtsp_media_play (GstRTSPMedia *media)
+gboolean
+gst_rtsp_media_play (GstRTSPMedia *media, GArray *transports)
 {
+  gint i;
   GstStateChangeReturn ret;
 
-  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), GST_STATE_CHANGE_FAILURE);
-  g_return_val_if_fail (media->prepared, GST_STATE_CHANGE_FAILURE);
+  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
+  g_return_val_if_fail (transports != NULL, FALSE);
+  g_return_val_if_fail (media->prepared, FALSE);
+
+  for (i = 0; i < transports->len; i++) {
+    GstRTSPMediaTrans *tr;
+    GstRTSPMediaStream *stream;
+    GstRTSPTransport *trans;
+
+    /* we need a non-NULL entry in the array */
+    tr = g_array_index (transports, GstRTSPMediaTrans *, i);
+    if (tr == NULL)
+      continue;
+
+    /* we need a transport */
+    if (!(trans = tr->transport))
+      continue;
+
+    /* get the stream and add the destinations */
+    stream = gst_rtsp_media_get_stream (media, tr->idx);
+
+    g_message ("adding %s:%d-%d", trans->destination, trans->client_port.min, trans->client_port.max);
+
+    g_signal_emit_by_name (stream->udpsink[0], "add", trans->destination, trans->client_port.min, NULL);
+    g_signal_emit_by_name (stream->udpsink[1], "add", trans->destination, trans->client_port.max, NULL);
+  }
 
   g_message ("playing");
   ret = gst_element_set_state (media->pipeline, GST_STATE_PLAYING);
 
-  return ret;
+  return TRUE;
 }
 
 /**
  * gst_rtsp_media_pause:
  * @media: a #GstRTSPMedia
+ * @transports: a array of #GstRTSPTransport pointers
  *
- * Tell the @media to pause.
+ * Pause playing @media for to the transports in @transports.
  *
- * Returns: a #GstStateChangeReturn
+ * Returns: %TRUE on success.
  */
-GstStateChangeReturn
-gst_rtsp_media_pause (GstRTSPMedia *media)
+gboolean
+gst_rtsp_media_pause (GstRTSPMedia *media, GArray *transports)
 {
+  gint i;
   GstStateChangeReturn ret;
 
-  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), GST_STATE_CHANGE_FAILURE);
-  g_return_val_if_fail (media->prepared, GST_STATE_CHANGE_FAILURE);
+  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
+  g_return_val_if_fail (transports != NULL, FALSE);
+  g_return_val_if_fail (media->prepared, FALSE);
+
+  for (i = 0; i < transports->len; i++) {
+    GstRTSPMediaTrans *tr;
+    GstRTSPMediaStream *stream;
+    GstRTSPTransport *trans;
+
+    /* we need a non-NULL entry in the array */
+    tr = g_array_index (transports, GstRTSPMediaTrans *, i);
+    if (tr == NULL)
+      continue;
 
-  g_message ("paused");
+    /* we need a transport */
+    if (!(trans = tr->transport))
+      continue;
+
+    /* get the stream and add the destinations */
+    stream = gst_rtsp_media_get_stream (media, tr->idx);
+
+    g_message ("removing %s:%d-%d", trans->destination, trans->client_port.min, trans->client_port.max);
+
+    g_signal_emit_by_name (stream->udpsink[0], "remove", trans->destination, trans->client_port.min, NULL);
+    g_signal_emit_by_name (stream->udpsink[1], "remove", trans->destination, trans->client_port.max, NULL);
+  }
+
+  g_message ("pause");
   ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED);
 
-  return ret;
+  return TRUE;
 }
 
 /**
- * gst_rtsp_media_stop:
+ * gst_rtsp_media_stream_stop:
  * @media: a #GstRTSPMedia
+ * @transports: a GArray of #GstRTSPMediaTrans pointers
  *
- * Tell the @media to stop playing. After this call the media
- * cannot be played or paused anymore
+ * Stop playing @media for to the transports in @transports.
  *
- * Returns: a #GstStateChangeReturn
+ * Returns: %TRUE on success.
  */
-GstStateChangeReturn
-gst_rtsp_media_stop (GstRTSPMedia *media)
+gboolean
+gst_rtsp_media_stop (GstRTSPMedia *media, GArray *transports)
 {
   GstStateChangeReturn ret;
 
-  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), GST_STATE_CHANGE_FAILURE);
-  g_return_val_if_fail (media->prepared, GST_STATE_CHANGE_FAILURE);
+  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
+  g_return_val_if_fail (transports != NULL, FALSE);
+  g_return_val_if_fail (media->prepared, FALSE);
+
+  gst_rtsp_media_pause (media, transports);
 
   g_message ("stop");
   ret = gst_element_set_state (media->pipeline, GST_STATE_NULL);
 
-  return ret;
+  return TRUE;
 }
 
index cf744fa..dfb6e89 100644 (file)
@@ -38,6 +38,20 @@ G_BEGIN_DECLS
 typedef struct _GstRTSPMediaStream GstRTSPMediaStream;
 typedef struct _GstRTSPMedia GstRTSPMedia;
 typedef struct _GstRTSPMediaClass GstRTSPMediaClass;
+typedef struct _GstRTSPMediaTrans GstRTSPMediaTrans;
+
+/**
+ * GstRTSPMediaTrans:
+ * @idx: a stream index
+ * @transport: a transport description
+ *
+ * A Transport description for stream @idx
+ */
+struct _GstRTSPMediaTrans {
+  guint idx;
+
+  GstRTSPTransport *transport;
+};
 
 /**
  * GstRTSPMediaStream:
@@ -136,13 +150,9 @@ gboolean              gst_rtsp_media_prepare          (GstRTSPMedia *media);
 guint                 gst_rtsp_media_n_streams        (GstRTSPMedia *media);
 GstRTSPMediaStream *  gst_rtsp_media_get_stream       (GstRTSPMedia *media, guint idx);
 
-/* add destinations to a stream */
-gboolean              gst_rtsp_media_stream_add       (GstRTSPMediaStream *stream, GstRTSPTransport *ct);
-gboolean              gst_rtsp_media_stream_remove    (GstRTSPMediaStream *stream, GstRTSPTransport *ct);
-
-GstStateChangeReturn  gst_rtsp_media_play             (GstRTSPMedia *media);
-GstStateChangeReturn  gst_rtsp_media_pause            (GstRTSPMedia *media);
-GstStateChangeReturn  gst_rtsp_media_stop             (GstRTSPMedia *media);
+gboolean              gst_rtsp_media_play             (GstRTSPMedia *media, GArray *trans);
+gboolean              gst_rtsp_media_pause            (GstRTSPMedia *media, GArray *trans);
+gboolean              gst_rtsp_media_stop             (GstRTSPMedia *media, GArray *trans);
 
 G_END_DECLS
 
index cd5fd85..8ccd733 100644 (file)
@@ -42,10 +42,10 @@ gst_rtsp_session_init (GstRTSPSession * session)
 }
 
 static void
-gst_rtsp_session_free_stream (GstRTSPSessionStream *stream, GstRTSPSessionMedia *media)
+gst_rtsp_session_free_stream (GstRTSPSessionStream *stream)
 {
-  if (stream->client_trans)
-    gst_rtsp_transport_free (stream->client_trans);
+  if (stream->trans.transport)
+    gst_rtsp_transport_free (stream->trans.transport);
 
   g_free (stream);
 }
@@ -53,9 +53,19 @@ gst_rtsp_session_free_stream (GstRTSPSessionStream *stream, GstRTSPSessionMedia
 static void
 gst_rtsp_session_free_media (GstRTSPSessionMedia *media, GstRTSPSession *session)
 {
-  g_list_foreach (media->streams, (GFunc) gst_rtsp_session_free_stream,
-                 media);
-  g_list_free (media->streams);
+  guint size, i;
+
+  size = media->streams->len;
+
+  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);
@@ -100,10 +110,22 @@ 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->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);
 
   sess->medias = g_list_prepend (sess->medias, result);
 
@@ -158,29 +180,25 @@ gst_rtsp_session_media_get_stream (GstRTSPSessionMedia *media, guint idx)
 {
   GstRTSPSessionStream *result;
   GstRTSPMediaStream *media_stream;
-  GList *walk;
 
   g_return_val_if_fail (media != NULL, NULL);
   g_return_val_if_fail (media->media != NULL, NULL);
 
-  media_stream = gst_rtsp_media_get_stream (media->media, idx);
-  if (media_stream == NULL)
-    goto no_media;
-
-  result = NULL;
-  for (walk = media->streams; walk; walk = g_list_next (walk)) {
-    result = (GstRTSPSessionStream *) walk->data; 
-
-    if (result->media_stream == media_stream)
-      break;
+  if (idx >= media->streams->len)
+    return NULL;
 
-    result = NULL;
-  }
+  result = g_array_index (media->streams, GstRTSPSessionStream *, idx);
   if (result == NULL) {
+    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;
 
-    media->streams = g_list_prepend (media->streams, result);
+    g_array_insert_val (media->streams, idx, result);
   }
   return result;
 
@@ -232,9 +250,9 @@ gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream,
   st->client_port = ct->client_port;
 
   /* keep track of the transports */
-  if (stream->client_trans)
-    gst_rtsp_transport_free (stream->client_trans);
-  stream->client_trans = ct;
+  if (stream->trans.transport)
+    gst_rtsp_transport_free (stream->trans.transport);
+  stream->trans.transport = ct;
 
   st->server_port.min = stream->media_stream->server_port.min;
   st->server_port.max = stream->media_stream->server_port.max;
@@ -242,28 +260,20 @@ gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream,
   return st;
 }
 
-
 /**
  * gst_rtsp_session_media_play:
  * @media: a #GstRTSPSessionMedia
  *
  * Tell the media object @media to start playing and streaming to the client.
  *
- * Returns: a #GstStateChangeReturn
+ * Returns: %TRUE on success.
  */
-GstStateChangeReturn
+gboolean
 gst_rtsp_session_media_play (GstRTSPSessionMedia *media)
 {
-  GstStateChangeReturn ret;
-  GstRTSPSessionStream *stream;
-  GList *walk;
+  gboolean ret;
 
-  for (walk = media->streams; walk; walk = g_list_next (walk)) {
-    stream = (GstRTSPSessionStream *) walk->data; 
-
-    gst_rtsp_media_stream_add (stream->media_stream, stream->client_trans);
-  }
-  ret = gst_rtsp_media_play (media->media);
+  ret = gst_rtsp_media_play (media->media, media->streams);
 
   return ret;
 }
@@ -274,14 +284,14 @@ gst_rtsp_session_media_play (GstRTSPSessionMedia *media)
  *
  * Tell the media object @media to pause.
  *
- * Returns: a #GstStateChangeReturn
+ * Returns: %TRUE on success.
  */
-GstStateChangeReturn
+gboolean
 gst_rtsp_session_media_pause (GstRTSPSessionMedia *media)
 {
-  GstStateChangeReturn ret;
+  gboolean ret;
 
-  ret = gst_rtsp_media_pause (media->media);
+  ret = gst_rtsp_media_pause (media->media, media->streams);
 
   return ret;
 }
@@ -293,14 +303,14 @@ gst_rtsp_session_media_pause (GstRTSPSessionMedia *media)
  * Tell the media object @media to stop playing. After this call the media
  * cannot be played or paused anymore
  *
- * Returns: a #GstStateChangeReturn
+ * Returns: %TRUE on success.
  */
-GstStateChangeReturn
+gboolean
 gst_rtsp_session_media_stop (GstRTSPSessionMedia *media)
 {
-  GstStateChangeReturn ret;
+  gboolean ret;
 
-  ret = gst_rtsp_media_stop (media->media);
+  ret = gst_rtsp_media_stop (media->media, media->streams);
 
   return ret;
 }
index 3ff5ba0..150434a 100644 (file)
@@ -45,17 +45,18 @@ typedef struct _GstRTSPSessionMedia GstRTSPSessionMedia;
 
 /**
  * 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;
-
-  /* client and server transports */
-  GstRTSPTransport *client_trans;
 };
 
 /**
@@ -71,8 +72,11 @@ struct _GstRTSPSessionMedia
   /* the pipeline for the media */
   GstRTSPMedia *media;
 
+  /* the server state */
+  GstRTSPState  state;
+
   /* configuration for the different streams */
-  GList        *streams;
+  GArray       *streams;
 };
 
 /**
@@ -96,21 +100,27 @@ struct _GstRTSPSessionClass {
 
 GType                  gst_rtsp_session_get_type             (void);
 
+/* create a new session */
 GstRTSPSession *       gst_rtsp_session_new                  (const gchar *sessionid);
 
+/* handle media in a session */
 GstRTSPSessionMedia *  gst_rtsp_session_manage_media         (GstRTSPSession *sess,
                                                               const GstRTSPUrl *uri,
                                                              GstRTSPMedia *media);
+/* get media in a session */
 GstRTSPSessionMedia *  gst_rtsp_session_get_media            (GstRTSPSession *sess,
                                                               const GstRTSPUrl *uri);
 
-GstStateChangeReturn   gst_rtsp_session_media_play           (GstRTSPSessionMedia *media);
-GstStateChangeReturn   gst_rtsp_session_media_pause          (GstRTSPSessionMedia *media);
-GstStateChangeReturn   gst_rtsp_session_media_stop           (GstRTSPSessionMedia *media);
+/* control media */
+gboolean               gst_rtsp_session_media_play           (GstRTSPSessionMedia *media);
+gboolean               gst_rtsp_session_media_pause          (GstRTSPSessionMedia *media);
+gboolean               gst_rtsp_session_media_stop           (GstRTSPSessionMedia *media);
 
+/* get stream config */
 GstRTSPSessionStream * gst_rtsp_session_media_get_stream     (GstRTSPSessionMedia *media,
                                                               guint idx);
 
+/* configure transport */
 GstRTSPTransport *     gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream,
                                                               GstRTSPTransport *ct);