#include "gstrtspsrc.h"
#include "sdp.h"
-#if 0
-#define WITH_EXT_REAL
-#endif
+/* define for experimental real support */
+#undef WITH_EXT_REAL
#include "rtspextwms.h"
#ifdef WITH_EXT_REAL
static void gst_rtspsrc_base_init (gpointer g_class);
static void gst_rtspsrc_finalize (GObject * object);
+static void gst_rtspsrc_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_rtspsrc_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
static void gst_rtspsrc_uri_handler_init (gpointer g_iface,
gpointer iface_data);
static GstCaps *gst_rtspsrc_media_to_caps (gint pt, SDPMedia * media);
GstStateChange transition);
static void gst_rtspsrc_handle_message (GstBin * bin, GstMessage * message);
-static void gst_rtspsrc_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec);
-static void gst_rtspsrc_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec);
-
static gboolean gst_rtspsrc_open (GstRTSPSrc * src);
static gboolean gst_rtspsrc_play (GstRTSPSrc * src);
static gboolean gst_rtspsrc_pause (GstRTSPSrc * src);
static gboolean gst_rtspsrc_uri_set_uri (GstURIHandler * handler,
const gchar * uri);
+static gboolean gst_rtspsrc_activate_streams (GstRTSPSrc * src);
static void gst_rtspsrc_loop (GstRTSPSrc * src);
/* commands we send to out loop to notify it of events */
/* we mark the pad as not linked, we will mark it as OK when we add the pad to
* the element. */
stream->last_ret = GST_FLOW_NOT_LINKED;
+ stream->added = FALSE;
stream->id = src->numstreams++;
/* we must have a payload. No payload means we cannot create caps */
return stream;
}
-#if 0
static void
-gst_rtspsrc_free_stream (GstRTSPSrc * src, GstRTSPStream * stream)
+gst_rtspsrc_stream_free (GstRTSPSrc * src, GstRTSPStream * stream)
{
- if (stream->caps) {
+ gint i;
+
+ GST_DEBUG_OBJECT (src, "free stream %p", stream);
+
+ if (stream->caps)
gst_caps_unref (stream->caps);
- }
+
g_free (stream->setup_url);
- src->streams = g_list_remove (src->streams, stream);
- src->numstreams--;
+ for (i = 0; i < 2; i++) {
+ GstElement *udpsrc = stream->udpsrc[i];
+
+ if (udpsrc) {
+ GstPad *pad;
+
+ /* unlink the pad */
+ pad = gst_element_get_pad (udpsrc, "src");
+ if (stream->channelpad[i]) {
+ gst_pad_unlink (pad, stream->channelpad[i]);
+ gst_object_unref (stream->channelpad[i]);
+ stream->channelpad[i] = NULL;
+ }
+ gst_element_set_state (udpsrc, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN_CAST (src), udpsrc);
+ gst_object_unref (stream->udpsrc[i]);
+ stream->udpsrc[i] = NULL;
+ }
+ }
+ if (stream->sess) {
+ gst_element_set_state (stream->sess, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN_CAST (src), stream->sess);
+ stream->sess = NULL;
+ }
+ if (stream->srcpad) {
+ gst_pad_set_active (stream->srcpad, FALSE);
+ if (stream->added) {
+ gst_element_remove_pad (GST_ELEMENT_CAST (src), stream->srcpad);
+ stream->added = FALSE;
+ }
+ stream->srcpad = NULL;
+ }
g_free (stream);
}
-#endif
+
+static void
+gst_rtspsrc_cleanup (GstRTSPSrc * src)
+{
+ GList *walk;
+
+ GST_DEBUG_OBJECT (src, "cleanup");
+
+ for (walk = src->streams; walk; walk = g_list_next (walk)) {
+ GstRTSPStream *stream = (GstRTSPStream *) walk->data;
+
+ gst_rtspsrc_stream_free (src, stream);
+ }
+ g_list_free (src->streams);
+ src->streams = NULL;
+ src->numstreams = 0;
+ if (src->props)
+ gst_structure_free (src->props);
+ src->props = NULL;
+}
+
#define PARSE_INT(p, del, res) \
G_STMT_START { \
/* 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->udpsrc[0] = gst_object_ref (udpsrc0);
+ stream->udpsrc[1] = gst_object_ref (udpsrc1);
+
+ /* they are ours now */
+ gst_object_sink (udpsrc0);
+ gst_object_sink (udpsrc1);
return TRUE;
}
}
+static void
+pad_unblocked (GstPad * pad, gboolean blocked, GstRTSPSrc * src)
+{
+}
+
+static void
+pad_blocked (GstPad * pad, gboolean blocked, GstRTSPSrc * src)
+{
+ GST_DEBUG_OBJECT (src, "pad %s:%s blocked, activating streams",
+ GST_DEBUG_PAD_NAME (pad));
+
+ gst_pad_set_blocked_async (pad, FALSE, (GstPadBlockCallback) pad_unblocked,
+ src);
+
+ /* activate the streams */
+ GST_OBJECT_LOCK (src);
+ if (!src->need_activate)
+ goto was_ok;
+
+ src->need_activate = FALSE;
+ GST_OBJECT_UNLOCK (src);
+
+ gst_rtspsrc_activate_streams (src);
+
+ return;
+
+was_ok:
+ {
+ GST_OBJECT_UNLOCK (src);
+ return;
+ }
+}
+
+/* sets up all elements needed for streaming over the specified transport.
+ * Does not yet expose the element pads, this will be done when there is actuall
+ * dataflow detected, which might never happen when UDP is blocked in a
+ * firewall, for example.
+ */
static gboolean
gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
RTSPTransport * transport)
s = gst_caps_get_structure (stream->caps, 0);
+ /* get the proper mime type for this stream now */
if ((res = rtsp_transport_get_mime (transport->trans, &mime)) < 0)
goto no_mime;
if (!mime)
goto no_mime;
- GST_DEBUG_OBJECT (src, "setting mime to %s", mime);
/* configure the final mime type */
+ GST_DEBUG_OBJECT (src, "setting mime to %s", mime);
gst_structure_set_name (s, mime);
+ /* find a manager */
if ((res = rtsp_transport_get_manager (transport->trans, &manager)) < 0)
goto no_manager;
if (ret != GST_STATE_CHANGE_SUCCESS)
goto start_session_failure;
+ /* we stream directly to the manager, FIXME, pad names should not be
+ * hardcoded. */
stream->channelpad[0] = gst_element_get_pad (stream->sess, "sinkrtp");
stream->channelpad[1] = gst_element_get_pad (stream->sess, "sinkrtcp");
}
stream->udpsrc[i] = NULL;
}
}
+
/* no session manager, send data to srcpad directly */
if (!stream->channelpad[0]) {
GST_DEBUG_OBJECT (src, "no manager, creating pad");
+ /* create a new pad we will use to stream to */
name = g_strdup_printf ("stream%d", stream->id);
template = gst_static_pad_template_get (&rtptemplate);
- stream->srcpad = gst_pad_new_from_template (template, name);
+ stream->channelpad[0] = gst_pad_new_from_template (template, name);
gst_object_unref (template);
g_free (name);
- gst_pad_use_fixed_caps (stream->srcpad);
- gst_pad_set_caps (stream->srcpad, stream->caps);
+ /* set caps and activate */
+ gst_pad_use_fixed_caps (stream->channelpad[0]);
+ gst_pad_set_caps (stream->channelpad[0], stream->caps);
+ gst_pad_set_active (stream->channelpad[0], TRUE);
- stream->channelpad[0] = gst_object_ref (stream->srcpad);
- gst_pad_set_active (stream->srcpad, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (src), stream->srcpad);
+ outpad = gst_object_ref (stream->channelpad[0]);
} else {
GST_DEBUG_OBJECT (src, "using manager source pad");
outpad = gst_element_get_pad (stream->sess, "srcrtp");
if (stream->udpsrc[0] == NULL)
goto no_element;
+ /* take ownership */
+ gst_object_ref (stream->udpsrc[0]);
+ gst_object_sink (stream->udpsrc[0]);
+
/* change state */
gst_element_set_state (stream->udpsrc[0], GST_STATE_PAUSED);
}
if (stream->udpsrc[1] == NULL)
goto no_element;
+ /* take ownership */
+ gst_object_ref (stream->udpsrc[0]);
+ gst_object_sink (stream->udpsrc[0]);
+
gst_element_set_state (stream->udpsrc[1], GST_STATE_PAUSED);
}
}
/* we manage the UDP elements now. For unicast, the UDP sources where
* allocated in the stream when we suggested a transport. */
if (stream->udpsrc[0]) {
- GstPad *pad;
-
gst_bin_add (GST_BIN_CAST (src), stream->udpsrc[0]);
GST_DEBUG_OBJECT (src, "setting up UDP source");
/* set caps */
g_object_set (G_OBJECT (stream->udpsrc[0]), "caps", stream->caps, NULL);
- /* configure a timeout on the UDP port */
+ /* configure a timeout on the UDP port. When the timeout message is
+ * posted, we assume UDP transport is not possible. We reconnect using TCP
+ * if we can. */
g_object_set (G_OBJECT (stream->udpsrc[0]), "timeout", src->timeout,
NULL);
+ /* get output pad of the UDP source. */
+ outpad = gst_element_get_pad (stream->udpsrc[0], "src");
+
+ /* configure pad block on the pad. As soon as there is dataflow on the
+ * UDP source, we know that UDP is not blocked by a firewall and we can
+ * configure all the streams to let the application autoplug decoders. */
+ gst_pad_set_blocked_async (outpad, TRUE,
+ (GstPadBlockCallback) pad_blocked, src);
+
if (stream->channelpad[0]) {
GST_DEBUG_OBJECT (src, "connecting UDP source 0 to manager");
/* configure for UDP delivery, we need to connect the UDP pads to
* the session plugin. */
- pad = gst_element_get_pad (stream->udpsrc[0], "src");
- gst_pad_link (pad, stream->channelpad[0]);
- gst_object_unref (pad);
-
+ gst_pad_link (outpad, stream->channelpad[0]);
+ gst_object_unref (outpad);
+ /* the real output pad is that of the session manager */
outpad = gst_element_get_pad (stream->sess, "srcrtp");
} else {
GST_DEBUG_OBJECT (src, "using UDP src pad as output");
- outpad = gst_element_get_pad (stream->udpsrc[0], "src");
}
}
}
}
- if (outpad && !stream->srcpad) {
+ if (outpad) {
GST_DEBUG_OBJECT (src, "creating ghostpad");
gst_pad_use_fixed_caps (outpad);
gst_pad_set_caps (outpad, stream->caps);
- /* create ghostpad */
+ /* create ghostpad, don't add just yet, this will be done when we activate
+ * the stream. */
name = g_strdup_printf ("stream%d", stream->id);
template = gst_static_pad_template_get (&rtptemplate);
stream->srcpad = gst_ghost_pad_new_from_template (name, outpad, template);
g_free (name);
gst_object_unref (outpad);
-
- /* and add */
- gst_pad_set_active (stream->srcpad, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (src), stream->srcpad);
}
/* mark pad as ok */
stream->last_ret = GST_FLOW_OK;
}
}
+/* Adds the source pads of all configured streams to the element.
+ * This code is performed when we detected dataflow.
+ *
+ * We detect dataflow from either the _loop function or with pad probes on the
+ * udp sources.
+ */
+static gboolean
+gst_rtspsrc_activate_streams (GstRTSPSrc * src)
+{
+ GList *walk;
+
+ for (walk = src->streams; walk; walk = g_list_next (walk)) {
+ GstRTSPStream *stream = (GstRTSPStream *) walk->data;
+
+ gst_pad_set_active (stream->srcpad, TRUE);
+ /* add the pad */
+ if (!stream->added) {
+ gst_element_add_pad (GST_ELEMENT_CAST (src), stream->srcpad);
+ stream->added = TRUE;
+ }
+ }
+
+ /* if we got here all was configured. We have dynamic pads so we notify that
+ * we are done */
+ gst_element_no_more_pads (GST_ELEMENT_CAST (src));
+
+ return TRUE;
+}
+
static gint
find_stream_by_channel (GstRTSPStream * stream, gconstpointer a)
{
GST_DEBUG_OBJECT (src, "pushing data of size %d on channel %d", size,
channel);
+ if (src->need_activate) {
+ gst_rtspsrc_activate_streams (src);
+ src->need_activate = FALSE;
+ }
+
/* chain to the peer pad */
if (GST_PAD_IS_SINK (outpad))
ret = gst_pad_chain (outpad, buf);
}
GST_OBJECT_UNLOCK (src);
- if (restart) {
- gst_rtspsrc_pause (src);
+ /* no need to restart, we're done */
+ if (!restart)
+ goto done;
- if (src->task) {
- /* stop task, we cannot join as this would deadlock */
- gst_task_stop (src->task);
- /* and free the task so that _close will not stop/join it again. */
- gst_object_unref (GST_OBJECT (src->task));
- src->task = NULL;
- }
- gst_rtspsrc_close (src);
-
- /* see if we have TCP left to try */
- if (src->cur_protocols & RTSP_LOWER_TRANS_TCP) {
- gchar *url, *pos;
-
- /* We post a warning message now to inform the user
- * that nothing happened. It's most likely a firewall thing. */
- GST_ELEMENT_WARNING (src, RESOURCE, READ, (NULL),
- ("Could not receive any UDP packets for %.4f seconds, maybe your "
- "firewall is blocking it. Retrying using a TCP connection.",
- (gdouble) src->timeout / 1000000));
- /* we can try only TCP now */
- src->cur_protocols = RTSP_LOWER_TRANS_TCP;
-
- pos = strstr (src->location, "://");
- if (!pos)
- goto weird_url;
-
- url = g_strdup_printf ("rtspt://%s", pos + 3);
-
- gst_element_post_message (GST_ELEMENT_CAST (src),
- gst_message_new_element (GST_OBJECT_CAST (src),
- gst_structure_new ("redirect",
- "new-location", G_TYPE_STRING, url, NULL)));
- g_free (url);
- } else {
- src->cur_protocols = 0;
- /* no transport possible, post an error and stop */
- GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
- ("Could not connect to server, no protocols left"));
- }
+ /* We post a warning message now to inform the user
+ * that nothing happened. It's most likely a firewall thing. */
+ GST_ELEMENT_WARNING (src, RESOURCE, READ, (NULL),
+ ("Could not receive any UDP packets for %.4f seconds, maybe your "
+ "firewall is blocking it. Retrying using a TCP connection.",
+ (gdouble) src->timeout / 1000000));
+ /* we can try only TCP now */
+ src->cur_protocols = RTSP_LOWER_TRANS_TCP;
+
+ /* pause to prepare for a restart */
+ gst_rtspsrc_pause (src);
+
+ if (src->task) {
+ /* stop task, we cannot join as this would deadlock */
+ gst_task_stop (src->task);
+ /* and free the task so that _close will not stop/join it again. */
+ gst_object_unref (GST_OBJECT (src->task));
+ src->task = NULL;
}
+ /* close and cleanup our state */
+ gst_rtspsrc_close (src);
+
+ /* see if we have TCP left to try */
+ if (!(src->cur_protocols & RTSP_LOWER_TRANS_TCP))
+ goto no_protocols;
+
+ /* open new connection using tcp */
+ if (!gst_rtspsrc_open (src))
+ goto open_failed;
+
+ /* start playback */
+ if (!gst_rtspsrc_play (src))
+ goto play_failed;
+
+done:
return;
/* ERRORS */
gst_task_pause (src->task);
return;
}
-weird_url:
+no_protocols:
{
+ src->cur_protocols = 0;
+ /* no transport possible, post an error and stop */
GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
- ("Could not redirect, location %s is invalid", src->location));
+ ("Could not connect to server, no protocols left"));
+ return;
+ }
+open_failed:
+ {
+ GST_DEBUG_OBJECT (src, "open failed");
+ return;
+ }
+play_failed:
+ {
+ GST_DEBUG_OBJECT (src, "play failed");
return;
}
}
/* store new content base if any */
rtsp_message_get_header (response, RTSP_HDR_CONTENT_BASE, &content_base);
- if (content_base) {
- g_free (src->content_base);
- src->content_base = g_strdup (content_base);
- }
+ g_free (src->content_base);
+ src->content_base = g_strdup (content_base);
if (src->extension && src->extension->after_send)
src->extension->after_send (src->extension, request, response);
method = rtsp_find_method (stripped);
/* keep bitfield of supported methods */
- if (method != -1)
+ if (method != RTSP_INVALID)
src->methods |= method;
}
g_strfreev (options);
if (*transports != NULL)
return RTSP_OK;
+ /* the default RTSP transports */
result = g_strdup ("");
if (protocols & RTSP_LOWER_TRANS_UDP) {
gchar *new;
}
static RTSPResult
-gst_rtspsrc_configure_transports (GstRTSPStream * stream, gchar ** transports)
+gst_rtspsrc_prepare_transports (GstRTSPStream * stream, gchar ** transports)
{
GstRTSPSrc *src;
gint nr_udp, nr_int;
}
static gboolean
-gst_rtspsrc_open (GstRTSPSrc * src)
+gst_rtspsrc_setup_streams (GstRTSPSrc * src)
{
+ GList *walk;
RTSPResult res;
RTSPMessage request = { 0 };
RTSPMessage response = { 0 };
- guint8 *data;
- guint size;
- gint i, n_streams;
- SDPMessage sdp = { 0 };
- RTSPLowerTrans protocols;
GstRTSPStream *stream = NULL;
- gchar *respcont = NULL;
-
- /* reset our state */
- src->free_channel = 0;
- src->interleaved = FALSE;
- gst_segment_init (&src->segment, GST_FORMAT_TIME);
-
- /* can't continue without a valid url */
- if (G_UNLIKELY (src->url == NULL))
- goto no_url;
-
- /* create connection */
- GST_DEBUG_OBJECT (src, "creating connection (%s)...", src->location);
- if ((res = rtsp_connection_create (src->url, &src->connection)) < 0)
- goto could_not_create;
-
- /* connect */
- GST_DEBUG_OBJECT (src, "connecting (%s)...", src->location);
- if ((res = rtsp_connection_connect (src->connection)) < 0)
- goto could_not_connect;
-
- /* create OPTIONS */
- GST_DEBUG_OBJECT (src, "create options...");
- res = rtsp_message_init_request (&request, RTSP_OPTIONS, src->location);
- if (res < 0)
- goto create_request_failed;
-
- /* send OPTIONS */
- GST_DEBUG_OBJECT (src, "send options...");
- if (!gst_rtspsrc_send (src, &request, &response, NULL))
- goto send_error;
-
- /* parse OPTIONS */
- if (!gst_rtspsrc_parse_methods (src, &response))
- goto methods_error;
-
- /* create DESCRIBE */
- GST_DEBUG_OBJECT (src, "create describe...");
- res = rtsp_message_init_request (&request, RTSP_DESCRIBE, src->location);
- if (res < 0)
- goto create_request_failed;
-
- /* we only accept SDP for now */
- rtsp_message_add_header (&request, RTSP_HDR_ACCEPT, "application/sdp");
-
- /* prepare global stream caps properties */
- if (src->props)
- gst_structure_remove_all_fields (src->props);
- else
- src->props = gst_structure_empty_new ("RTSP Properties");
-
- /* send DESCRIBE */
- GST_DEBUG_OBJECT (src, "send describe...");
- if (!gst_rtspsrc_send (src, &request, &response, NULL))
- goto send_error;
-
- /* check if reply is SDP */
- rtsp_message_get_header (&response, RTSP_HDR_CONTENT_TYPE, &respcont);
- /* could not be set but since the request returned OK, we assume it
- * was SDP, else check it. */
- if (respcont) {
- if (!g_ascii_strcasecmp (respcont, "application/sdp") == 0)
- goto wrong_content_type;
- }
-
- /* get message body and parse as SDP */
- rtsp_message_get_body (&response, &data, &size);
-
- GST_DEBUG_OBJECT (src, "parse SDP...");
- sdp_message_init (&sdp);
- sdp_message_parse_buffer (data, size, &sdp);
-
- if (src->debug)
- sdp_message_dump (&sdp);
-
- if (src->extension && src->extension->parse_sdp)
- src->extension->parse_sdp (src->extension, &sdp);
+ RTSPLowerTrans protocols;
/* we initially allow all configured lower transports. based on the URL
* transports and the replies from the server we narrow them down. */
protocols = src->url->transports & src->cur_protocols;
- /* setup streams */
- n_streams = sdp_message_medias_len (&sdp);
- for (i = 0; i < n_streams; i++) {
+ /* reset some state */
+ src->free_channel = 0;
+ src->interleaved = FALSE;
+
+ for (walk = src->streams; walk; walk = g_list_next (walk)) {
gchar *transports;
- /* create stream from the media, can never return NULL */
- stream = gst_rtspsrc_create_stream (src, &sdp, i);
+ stream = (GstRTSPStream *) walk->data;
/* see if we need to configure this stream */
if (src->extension && src->extension->configure_stream) {
if (!src->extension->configure_stream (src->extension, stream)) {
- GST_DEBUG_OBJECT (src, "skipping stream %d, disabled by extension", i);
+ GST_DEBUG_OBJECT (src, "skipping stream %p, disabled by extension",
+ stream);
continue;
}
}
/* skip setup if we have no URL for it */
if (stream->setup_url == NULL) {
- GST_DEBUG_OBJECT (src, "skipping stream %d, no setup", i);
+ GST_DEBUG_OBJECT (src, "skipping stream %p, no setup", stream);
continue;
}
- GST_DEBUG_OBJECT (src, "doing setup of stream %d with %s", i,
+ GST_DEBUG_OBJECT (src, "doing setup of stream %p with %s", stream,
stream->setup_url);
- /* create SETUP request */
- res = rtsp_message_init_request (&request, RTSP_SETUP, stream->setup_url);
- if (res < 0)
- goto create_request_failed;
-
/* create a string with all the transports */
res = gst_rtspsrc_create_transports_string (src, protocols, &transports);
if (res < 0)
goto setup_transport_failed;
- /* replace placeholders with real values */
- res = gst_rtspsrc_configure_transports (stream, &transports);
+ /* replace placeholders with real values, this function will optionally
+ * allocate UDP ports and other info needed to execute the setup request */
+ res = gst_rtspsrc_prepare_transports (stream, &transports);
if (res < 0)
goto setup_transport_failed;
- /* select transport, copy is made when adding to header */
+ /* create SETUP request */
+ res = rtsp_message_init_request (&request, RTSP_SETUP, stream->setup_url);
+ if (res < 0)
+ goto create_request_failed;
+
+ /* select transport, copy is made when adding to header so we can free it. */
rtsp_message_add_header (&request, RTSP_HDR_TRANSPORT, transports);
g_free (transports);
* are configured in the same way */
switch (transport.lower_transport) {
case RTSP_LOWER_TRANS_TCP:
- GST_DEBUG_OBJECT (src, "stream %d as TCP interleaved", i);
+ GST_DEBUG_OBJECT (src, "stream %p as TCP interleaved", stream);
protocols = RTSP_LOWER_TRANS_TCP;
src->interleaved = TRUE;
/* update free channels */
break;
case RTSP_LOWER_TRANS_UDP_MCAST:
/* only allow multicast for other streams */
- GST_DEBUG_OBJECT (src, "stream %d as UDP multicast", i);
+ GST_DEBUG_OBJECT (src, "stream %p as UDP multicast", stream);
protocols = RTSP_LOWER_TRANS_UDP_MCAST;
break;
case RTSP_LOWER_TRANS_UDP:
/* only allow unicast for other streams */
- GST_DEBUG_OBJECT (src, "stream %d as UDP unicast", i);
+ GST_DEBUG_OBJECT (src, "stream %p as UDP unicast", stream);
protocols = RTSP_LOWER_TRANS_UDP;
break;
default:
- GST_DEBUG_OBJECT (src, "stream %d unknown transport %d", i,
+ GST_DEBUG_OBJECT (src, "stream %p unknown transport %d", stream,
transport.lower_transport);
break;
}
if (!stream->container || !src->interleaved) {
- /* now configure the stream with the transport */
+ /* now configure the stream with the selected transport */
if (!gst_rtspsrc_stream_configure_transport (stream, &transport)) {
GST_DEBUG_OBJECT (src,
- "could not configure stream %d transport, skipping stream", i);
+ "could not configure stream %p transport, skipping stream",
+ stream);
}
}
-
/* clean up our transport struct */
rtsp_transport_init (&transport);
}
if (src->extension && src->extension->stream_select)
src->extension->stream_select (src->extension);
- /* if we got here all was configured. We have dynamic pads so we notify that
- * we are done */
- gst_element_no_more_pads (GST_ELEMENT_CAST (src));
+ /* we need to activate the streams when we detect activity */
+ src->need_activate = TRUE;
+
+ return TRUE;
+
+ /* ERRORS */
+create_request_failed:
+ {
+ gchar *str = rtsp_strresult (res);
+
+ GST_ELEMENT_ERROR (src, LIBRARY, INIT, (NULL),
+ ("Could not create request. (%s)", str));
+ g_free (str);
+ goto cleanup_error;
+ }
+setup_transport_failed:
+ {
+ GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL),
+ ("Could not setup transport."));
+ goto cleanup_error;
+ }
+send_error:
+ {
+ gchar *str = rtsp_strresult (res);
+
+ GST_ELEMENT_ERROR (src, RESOURCE, WRITE, (NULL),
+ ("Could not send message. (%s)", str));
+ g_free (str);
+ goto cleanup_error;
+ }
+no_transport:
+ {
+ GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL),
+ ("Server did not select transport."));
+ goto cleanup_error;
+ }
+cleanup_error:
+ {
+ rtsp_message_unset (&request);
+ rtsp_message_unset (&response);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_rtspsrc_open (GstRTSPSrc * src)
+{
+ RTSPResult res;
+ RTSPMessage request = { 0 };
+ RTSPMessage response = { 0 };
+ guint8 *data;
+ guint size;
+ gint i, n_streams;
+ SDPMessage sdp = { 0 };
+ GstRTSPStream *stream = NULL;
+ gchar *respcont = NULL;
+
+ /* reset our state */
+ gst_segment_init (&src->segment, GST_FORMAT_TIME);
+
+ /* can't continue without a valid url */
+ if (G_UNLIKELY (src->url == NULL))
+ goto no_url;
+
+ /* create connection */
+ GST_DEBUG_OBJECT (src, "creating connection (%s)...", src->location);
+ if ((res = rtsp_connection_create (src->url, &src->connection)) < 0)
+ goto could_not_create;
+
+ /* connect */
+ GST_DEBUG_OBJECT (src, "connecting (%s)...", src->location);
+ if ((res = rtsp_connection_connect (src->connection)) < 0)
+ goto could_not_connect;
+
+ /* create OPTIONS */
+ GST_DEBUG_OBJECT (src, "create options...");
+ res = rtsp_message_init_request (&request, RTSP_OPTIONS, src->location);
+ if (res < 0)
+ goto create_request_failed;
+
+ /* send OPTIONS */
+ GST_DEBUG_OBJECT (src, "send options...");
+ if (!gst_rtspsrc_send (src, &request, &response, NULL))
+ goto send_error;
+
+ /* parse OPTIONS */
+ if (!gst_rtspsrc_parse_methods (src, &response))
+ goto methods_error;
+
+ /* create DESCRIBE */
+ GST_DEBUG_OBJECT (src, "create describe...");
+ res = rtsp_message_init_request (&request, RTSP_DESCRIBE, src->location);
+ if (res < 0)
+ goto create_request_failed;
+
+ /* we only accept SDP for now */
+ rtsp_message_add_header (&request, RTSP_HDR_ACCEPT, "application/sdp");
+
+ /* prepare global stream caps properties */
+ if (src->props)
+ gst_structure_remove_all_fields (src->props);
+ else
+ src->props = gst_structure_empty_new ("RTSP Properties");
+
+ /* send DESCRIBE */
+ GST_DEBUG_OBJECT (src, "send describe...");
+ if (!gst_rtspsrc_send (src, &request, &response, NULL))
+ goto send_error;
+
+ /* check if reply is SDP */
+ rtsp_message_get_header (&response, RTSP_HDR_CONTENT_TYPE, &respcont);
+ /* could not be set but since the request returned OK, we assume it
+ * was SDP, else check it. */
+ if (respcont) {
+ if (!g_ascii_strcasecmp (respcont, "application/sdp") == 0)
+ goto wrong_content_type;
+ }
+
+ /* get message body and parse as SDP */
+ rtsp_message_get_body (&response, &data, &size);
+
+ GST_DEBUG_OBJECT (src, "parse SDP...");
+ sdp_message_init (&sdp);
+ sdp_message_parse_buffer (data, size, &sdp);
+
+ if (src->debug)
+ sdp_message_dump (&sdp);
+
+ if (src->extension && src->extension->parse_sdp)
+ src->extension->parse_sdp (src->extension, &sdp);
+
+ /* create streams */
+ n_streams = sdp_message_medias_len (&sdp);
+ for (i = 0; i < n_streams; i++) {
+ stream = gst_rtspsrc_create_stream (src, &sdp, i);
+ }
+
+ /* setup streams */
+ gst_rtspsrc_setup_streams (src);
/* clean up any messages */
rtsp_message_unset (&request);
("Server does not support SDP, got %s.", respcont));
goto cleanup_error;
}
-setup_transport_failed:
- {
- GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL),
- ("Could not setup transport."));
- goto cleanup_error;
- }
-no_transport:
- {
- GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL),
- ("Server did not select transport."));
- goto cleanup_error;
- }
cleanup_error:
{
rtsp_message_unset (&request);
if ((res = rtsp_connection_close (src->connection)) < 0)
goto close_failed;
+ /* free connection */
+ rtsp_connection_free (src->connection);
+ src->connection = NULL;
+
+ /* cleanup */
+ gst_rtspsrc_cleanup (src);
+
return TRUE;
/* ERRORS */
* SOFTWARE.
*/
+#include <string.h>
+
#include "rtspmessage.h"
RTSPResult
-rtsp_message_new_request (RTSPMessage ** msg, RTSPMethod method, gchar * uri)
+rtsp_message_new (RTSPMessage ** msg)
+{
+ RTSPMessage *newmsg;
+
+ g_return_val_if_fail (msg != NULL, RTSP_EINVAL);
+
+ newmsg = g_new0 (RTSPMessage, 1);
+
+ *msg = newmsg;
+
+ return rtsp_message_init (newmsg);
+}
+
+RTSPResult
+rtsp_message_init (RTSPMessage * msg)
+{
+ g_return_val_if_fail (msg != NULL, RTSP_EINVAL);
+
+ rtsp_message_unset (msg);
+
+ msg->type = RTSP_MESSAGE_INVALID;
+ msg->hdr_fields =
+ g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
+
+ return RTSP_OK;
+}
+
+RTSPResult
+rtsp_message_new_request (RTSPMessage ** msg, RTSPMethod method,
+ const gchar * uri)
{
RTSPMessage *newmsg;
}
RTSPResult
-rtsp_message_init_request (RTSPMessage * msg, RTSPMethod method, gchar * uri)
+rtsp_message_init_request (RTSPMessage * msg, RTSPMethod method,
+ const gchar * uri)
{
- g_return_val_if_fail (uri != NULL, RTSP_EINVAL);
g_return_val_if_fail (msg != NULL, RTSP_EINVAL);
+ g_return_val_if_fail (uri != NULL, RTSP_EINVAL);
rtsp_message_unset (msg);
RTSPResult
rtsp_message_new_response (RTSPMessage ** msg, RTSPStatusCode code,
- gchar * reason, RTSPMessage * request)
+ const gchar * reason, const RTSPMessage * request)
{
RTSPMessage *newmsg;
- g_return_val_if_fail (reason != NULL, RTSP_EINVAL);
g_return_val_if_fail (msg != NULL, RTSP_EINVAL);
- g_return_val_if_fail (request != NULL, RTSP_EINVAL);
newmsg = g_new0 (RTSPMessage, 1);
RTSPResult
rtsp_message_init_response (RTSPMessage * msg, RTSPStatusCode code,
- gchar * reason, RTSPMessage * request)
+ const gchar * reason, const RTSPMessage * request)
{
g_return_val_if_fail (msg != NULL, RTSP_EINVAL);
- g_return_val_if_fail (reason != NULL, RTSP_EINVAL);
rtsp_message_unset (msg);
+ if (reason == NULL)
+ reason = rtsp_status_as_text (code);
+
msg->type = RTSP_MESSAGE_RESPONSE;
msg->type_data.response.code = code;
msg->type_data.response.reason = g_strdup (reason);
g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
if (request) {
- /* FIXME copy headers */
+ gchar *header;
+
+ if (rtsp_message_get_header (request, RTSP_HDR_CSEQ, &header) == RTSP_OK) {
+ rtsp_message_add_header (msg, RTSP_HDR_CSEQ, header);
+ }
+
+ if (rtsp_message_get_header (request, RTSP_HDR_SESSION, &header) == RTSP_OK) {
+ char *pos;
+
+ header = g_strdup (header);
+ if ((pos = strchr (header, ';'))) {
+ *pos = '\0';
+ }
+ g_strchomp (header);
+ rtsp_message_add_header (msg, RTSP_HDR_SESSION, header);
+ g_free (header);
+ }
+
+ /* FIXME copy more headers? */
}
return RTSP_OK;
{
g_return_val_if_fail (msg != NULL, RTSP_EINVAL);
- msg->type = RTSP_MESSAGE_INVALID;
- msg->type_data.request.method = RTSP_INVALID;
- g_free (msg->type_data.request.uri);
- msg->type_data.request.uri = NULL;
-
- msg->type_data.response.code = RTSP_STS_INVALID;
- g_free (msg->type_data.response.reason);
- msg->type_data.response.reason = NULL;
+ switch (msg->type) {
+ case RTSP_MESSAGE_INVALID:
+ break;
+ case RTSP_MESSAGE_REQUEST:
+ g_free (msg->type_data.request.uri);
+ break;
+ case RTSP_MESSAGE_RESPONSE:
+ g_free (msg->type_data.response.reason);
+ break;
+ case RTSP_MESSAGE_DATA:
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
if (msg->hdr_fields != NULL)
g_hash_table_destroy (msg->hdr_fields);
- msg->hdr_fields = NULL;
g_free (msg->body);
- msg->body = NULL;
- msg->body_size = 0;
+
+ memset (msg, 0, sizeof *msg);
return RTSP_OK;
}
RTSPResult
rtsp_message_add_header (RTSPMessage * msg, RTSPHeaderField field,
- gchar * value)
+ const gchar * value)
{
g_return_val_if_fail (msg != NULL, RTSP_EINVAL);
g_return_val_if_fail (value != NULL, RTSP_EINVAL);
}
RTSPResult
-rtsp_message_get_header (RTSPMessage * msg, RTSPHeaderField field,
+rtsp_message_get_header (const RTSPMessage * msg, RTSPHeaderField field,
gchar ** value)
{
gchar *val;
g_return_val_if_fail (msg != NULL, RTSP_EINVAL);
- g_return_val_if_fail (value != NULL, RTSP_EINVAL);
val = g_hash_table_lookup (msg->hdr_fields, GINT_TO_POINTER (field));
if (val == NULL)
return RTSP_ENOTIMPL;
- *value = val;
+ if (value)
+ *value = val;
return RTSP_OK;
}
RTSPResult
-rtsp_message_set_body (RTSPMessage * msg, guint8 * data, guint size)
+rtsp_message_set_body (RTSPMessage * msg, const guint8 * data, guint size)
{
g_return_val_if_fail (msg != NULL, RTSP_EINVAL);
}
RTSPResult
-rtsp_message_get_body (RTSPMessage * msg, guint8 ** data, guint * size)
+rtsp_message_get_body (const RTSPMessage * msg, guint8 ** data, guint * size)
{
g_return_val_if_fail (msg != NULL, RTSP_EINVAL);
g_return_val_if_fail (data != NULL, RTSP_EINVAL);
}
static void
-dump_mem (guint8 * mem, gint size)
+dump_mem (guint8 * mem, guint size)
{
guint i, j;
GString *string = g_string_sized_new (50);
switch (msg->type) {
case RTSP_MESSAGE_REQUEST:
- g_print ("request message %p\n", msg);
+ g_print ("RTSP request message %p\n", msg);
g_print (" request line:\n");
g_print (" method: '%s'\n",
rtsp_method_as_text (msg->type_data.request.method));
dump_mem (data, size);
break;
case RTSP_MESSAGE_RESPONSE:
- g_print ("response message %p\n", msg);
+ g_print ("RTSP response message %p\n", msg);
g_print (" status line:\n");
g_print (" code: '%d'\n", msg->type_data.response.code);
g_print (" reason: '%s'\n", msg->type_data.response.reason);
dump_mem (data, size);
break;
case RTSP_MESSAGE_DATA:
- g_print ("data message %p\n", msg);
+ g_print ("RTSP data message %p\n", msg);
g_print (" channel: '%d'\n", msg->type_data.data.channel);
g_print (" size: '%d'\n", msg->body_size);
rtsp_message_get_body (msg, &data, &size);