summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog58
-rw-r--r--gst/rtsp/COPYING.MIT23
-rw-r--r--gst/rtsp/gstrtspsrc.c625
-rw-r--r--gst/rtsp/gstrtspsrc.h6
-rw-r--r--gst/rtsp/rtspconnection.c43
-rw-r--r--gst/rtsp/rtspdefs.c35
-rw-r--r--gst/rtsp/rtspdefs.h3
-rw-r--r--gst/rtsp/rtspextwms.c6
-rw-r--r--gst/rtsp/rtspmessage.c116
-rw-r--r--gst/rtsp/rtspmessage.h58
-rw-r--r--gst/rtsp/sdpmessage.c28
11 files changed, 702 insertions, 299 deletions
diff --git a/ChangeLog b/ChangeLog
index cb2e1e1d..7ce3f28b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,61 @@
+2007-01-10 Wim Taymans <wim@fluendo.com>
+
+ Patch by: Peter Kjellerstedt <pkj at axis com>
+
+ * gst/rtsp/COPYING.MIT:
+ * gst/rtsp/gstrtspsrc.c: (gst_rtspsrc_create_stream),
+ (gst_rtspsrc_stream_free), (gst_rtspsrc_cleanup),
+ (gst_rtspsrc_alloc_udp_ports), (pad_unblocked), (pad_blocked),
+ (gst_rtspsrc_stream_configure_transport),
+ (gst_rtspsrc_activate_streams), (gst_rtspsrc_loop_interleaved),
+ (gst_rtspsrc_loop_udp), (gst_rtspsrc_send),
+ (gst_rtspsrc_parse_methods),
+ (gst_rtspsrc_create_transports_string),
+ (gst_rtspsrc_prepare_transports), (gst_rtspsrc_setup_streams),
+ (gst_rtspsrc_open), (gst_rtspsrc_close):
+ * gst/rtsp/gstrtspsrc.h:
+ * gst/rtsp/rtspconnection.c: (rtsp_connection_create),
+ (rtsp_connection_connect), (rtsp_connection_send), (read_line),
+ (parse_request_line), (parse_line), (rtsp_connection_read),
+ (rtsp_connection_close):
+ * gst/rtsp/rtspdefs.c: (rtsp_init_status), (rtsp_strresult),
+ (rtsp_method_as_text), (rtsp_header_as_text),
+ (rtsp_status_as_text), (rtsp_find_header_field),
+ (rtsp_find_method):
+ * gst/rtsp/rtspdefs.h:
+ * gst/rtsp/rtspextwms.c: (rtsp_ext_wms_after_send),
+ (rtsp_ext_wms_configure_stream):
+ * gst/rtsp/rtspmessage.c: (rtsp_message_new), (rtsp_message_init),
+ (rtsp_message_new_request), (rtsp_message_init_request),
+ (rtsp_message_new_response), (rtsp_message_init_response),
+ (rtsp_message_init_data), (rtsp_message_unset),
+ (rtsp_message_free), (rtsp_message_add_header),
+ (rtsp_message_get_header), (rtsp_message_set_body),
+ (rtsp_message_get_body), (dump_mem), (rtsp_message_dump):
+ * gst/rtsp/rtspmessage.h:
+ * gst/rtsp/sdpmessage.c: (sdp_message_get_attribute_val_n),
+ (sdp_media_get_attribute_val_n), (read_string), (read_string_del),
+ (sdp_parse_line), (sdp_message_parse_buffer), (print_media),
+ (sdp_message_dump):
+ Allow url to be NULL to be able to use it for server connections.
+ Can now send responses as well as requests.
+ No longer hangs in an endless loop if EOF is received.
+ Can now convert a status code to a text string.
+ Return RTSP_HDR_INVALID for unknown headers.
+ Return RTSP_INVALID for unknown methods.
+ Copy CSeq and Session headers from the request.
+ Only free memory corresponding to the currently set message type.
+ Added const to function arguments as appropriate.
+ Avoid a compiler warning when initializing nmedia.
+ Use guint rather than gint to avoid compiler warnings.
+ Fix crasher in wms extension.
+ Factor out stream setup from open_connection.
+ Delay activation of streams when actual data is received from the
+ server, this prepares us to do proper protocol switching.
+ Added new license.
+ Fixes #380895.
+
+
2007-01-10 Tim-Philipp Müller <tim at centricular dot net>
Patch by: Sebastian Dröge <slomo ubuntu com>
diff --git a/gst/rtsp/COPYING.MIT b/gst/rtsp/COPYING.MIT
new file mode 100644
index 00000000..7e8010b4
--- /dev/null
+++ b/gst/rtsp/COPYING.MIT
@@ -0,0 +1,23 @@
+/*
+ * Unless otherwise indicated, Source Code is licensed under MIT license.
+ * See further explanation attached in License Statement (distributed in the file
+ * LICENSE).
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c
index 6af4c68b..1ec70b88 100644
--- a/gst/rtsp/gstrtspsrc.c
+++ b/gst/rtsp/gstrtspsrc.c
@@ -95,9 +95,8 @@
#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
@@ -165,6 +164,11 @@ gst_rtsp_lower_trans_get_type (void)
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);
@@ -173,11 +177,6 @@ static GstStateChangeReturn gst_rtspsrc_change_state (GstElement * element,
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);
@@ -186,6 +185,7 @@ static gboolean gst_rtspsrc_close (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 */
@@ -397,6 +397,7 @@ gst_rtspsrc_create_stream (GstRTSPSrc * src, SDPMessage * sdp, gint idx)
/* 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 */
@@ -448,21 +449,74 @@ gst_rtspsrc_create_stream (GstRTSPSrc * src, SDPMessage * sdp, gint idx)
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 { \
@@ -724,8 +778,12 @@ again:
/* 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;
@@ -780,6 +838,44 @@ cleanup:
}
}
+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)
@@ -799,15 +895,17 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
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;
@@ -824,6 +922,8 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
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");
}
@@ -847,22 +947,24 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
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");
@@ -884,6 +986,10 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
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);
}
@@ -897,6 +1003,10 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
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);
}
}
@@ -904,8 +1014,6 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
/* 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");
@@ -913,22 +1021,31 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
/* 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");
}
}
@@ -947,13 +1064,14 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
}
}
- 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);
@@ -961,10 +1079,6 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
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;
@@ -994,6 +1108,35 @@ start_session_failure:
}
}
+/* 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)
{
@@ -1144,6 +1287,11 @@ gst_rtspsrc_loop_interleaved (GstRTSPSrc * src)
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);
@@ -1239,49 +1387,45 @@ gst_rtspsrc_loop_udp (GstRTSPSrc * src)
}
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 */
@@ -1292,10 +1436,22 @@ stopping:
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;
}
}
@@ -1414,10 +1570,8 @@ next:
/* 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);
@@ -1509,7 +1663,7 @@ gst_rtspsrc_parse_methods (GstRTSPSrc * src, RTSPMessage * response)
method = rtsp_find_method (stripped);
/* keep bitfield of supported methods */
- if (method != -1)
+ if (method != RTSP_INVALID)
src->methods |= method;
}
g_strfreev (options);
@@ -1556,6 +1710,7 @@ gst_rtspsrc_create_transports_string (GstRTSPSrc * src,
if (*transports != NULL)
return RTSP_OK;
+ /* the default RTSP transports */
result = g_strdup ("");
if (protocols & RTSP_LOWER_TRANS_UDP) {
gchar *new;
@@ -1602,7 +1757,7 @@ failed:
}
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;
@@ -1669,111 +1824,33 @@ failed:
}
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;
}
}
@@ -1800,29 +1877,30 @@ gst_rtspsrc_open (GstRTSPSrc * src)
/* 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);
@@ -1847,7 +1925,7 @@ gst_rtspsrc_open (GstRTSPSrc * src)
* 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 */
@@ -1859,28 +1937,28 @@ gst_rtspsrc_open (GstRTSPSrc * src)
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);
}
@@ -1888,9 +1966,145 @@ gst_rtspsrc_open (GstRTSPSrc * src)
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);
@@ -1952,18 +2166,6 @@ wrong_content_type:
("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);
@@ -2018,6 +2220,13 @@ gst_rtspsrc_close (GstRTSPSrc * src)
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 */
diff --git a/gst/rtsp/gstrtspsrc.h b/gst/rtsp/gstrtspsrc.h
index cdc7c7cd..fa030b48 100644
--- a/gst/rtsp/gstrtspsrc.h
+++ b/gst/rtsp/gstrtspsrc.h
@@ -78,11 +78,12 @@ typedef struct _GstRTSPStream GstRTSPStream;
struct _GstRTSPStream {
gint id;
- GstRTSPSrc *parent;
+ GstRTSPSrc *parent; /* parent, no extra ref to parent is taken */
/* pad we expose or NULL when it does not have an actual pad */
GstPad *srcpad;
GstFlowReturn last_ret;
+ gboolean added;
/* for interleaved mode */
gint channel[2];
@@ -124,6 +125,7 @@ struct _GstRTSPSrc {
gint numstreams;
GList *streams;
GstStructure *props;
+ gboolean need_activate;
/* properties */
gchar *location;
@@ -141,8 +143,6 @@ struct _GstRTSPSrc {
gint methods;
RTSPConnection *connection;
- RTSPMessage *request;
- RTSPMessage *response;
RTSPExtensionCtx *extension;
};
diff --git a/gst/rtsp/rtspconnection.c b/gst/rtsp/rtspconnection.c
index a0603911..69454069 100644
--- a/gst/rtsp/rtspconnection.c
+++ b/gst/rtsp/rtspconnection.c
@@ -119,7 +119,6 @@ rtsp_connection_create (RTSPUrl * url, RTSPConnection ** conn)
gint ret;
RTSPConnection *newconn;
- g_return_val_if_fail (url != NULL, RTSP_EINVAL);
g_return_val_if_fail (conn != NULL, RTSP_EINVAL);
newconn = g_new (RTSPConnection, 1);
@@ -139,6 +138,7 @@ rtsp_connection_create (RTSPUrl * url, RTSPConnection ** conn)
#endif
newconn->url = url;
+ newconn->fd = -1;
newconn->cseq = 0;
newconn->session_id[0] = 0;
newconn->state = RTSP_STATE_INIT;
@@ -169,6 +169,8 @@ rtsp_connection_connect (RTSPConnection * conn)
RTSPUrl *url;
g_return_val_if_fail (conn != NULL, RTSP_EINVAL);
+ g_return_val_if_fail (conn->url != NULL, RTSP_EINVAL);
+ g_return_val_if_fail (conn->fd < 0, RTSP_EINVAL);
url = conn->url;
@@ -252,11 +254,23 @@ rtsp_connection_send (RTSPConnection * conn, RTSPMessage * message)
str = g_string_new ("");
- /* create request string, add CSeq */
- g_string_append_printf (str, "%s %s RTSP/1.0\r\n"
- "CSeq: %d\r\n",
- rtsp_method_as_text (message->type_data.request.method),
- message->type_data.request.uri, conn->cseq);
+ switch (message->type) {
+ case RTSP_MESSAGE_REQUEST:
+ /* create request string, add CSeq */
+ g_string_append_printf (str, "%s %s RTSP/1.0\r\n"
+ "CSeq: %d\r\n",
+ rtsp_method_as_text (message->type_data.request.method),
+ message->type_data.request.uri, conn->cseq++);
+ break;
+ case RTSP_MESSAGE_RESPONSE:
+ /* create response string */
+ g_string_append_printf (str, "RTSP/1.0 %d %s\r\n",
+ message->type_data.response.code, message->type_data.response.reason);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
/* append session id if we have one */
if (conn->session_id[0] != '\0') {
@@ -300,8 +314,6 @@ rtsp_connection_send (RTSPConnection * conn, RTSPMessage * message)
}
g_string_free (str, TRUE);
- conn->cseq++;
-
return RTSP_OK;
#ifdef G_OS_WIN32
@@ -328,14 +340,16 @@ write_error:
static RTSPResult
read_line (gint fd, gchar * buffer, guint size)
{
- gint idx;
+ guint idx;
gchar c;
gint r;
idx = 0;
while (TRUE) {
r = read (fd, &c, 1);
- if (r < 1) {
+ if (r == 0) {
+ goto eof;
+ } else if (r < 0) {
if (errno != EAGAIN && errno != EINTR)
goto read_error;
} else {
@@ -352,6 +366,10 @@ read_line (gint fd, gchar * buffer, guint size)
return RTSP_OK;
+eof:
+ {
+ return RTSP_EEOF;
+ }
read_error:
{
return RTSP_ESYS;
@@ -435,7 +453,7 @@ parse_request_line (gchar * buffer, RTSPMessage * msg)
read_string (methodstr, sizeof (methodstr), &bptr);
method = rtsp_find_method (methodstr);
- if (method == -1)
+ if (method == RTSP_INVALID)
goto wrong_method;
read_string (urlstr, sizeof (urlstr), &bptr);
@@ -476,7 +494,7 @@ parse_line (gchar * buffer, RTSPMessage * msg)
bptr++;
field = rtsp_find_header_field (key);
- if (field != -1) {
+ if (field != RTSP_HDR_INVALID) {
while (g_ascii_isspace (*bptr))
bptr++;
rtsp_message_add_header (msg, field, bptr);
@@ -737,6 +755,7 @@ rtsp_connection_close (RTSPConnection * conn)
gint res;
g_return_val_if_fail (conn != NULL, RTSP_EINVAL);
+ g_return_val_if_fail (conn->fd >= 0, RTSP_EINVAL);
res = CLOSE_SOCKET (conn->fd);
#ifdef G_OS_WIN32
diff --git a/gst/rtsp/rtspdefs.c b/gst/rtsp/rtspdefs.c
index 63b68a3f..6c96e1f4 100644
--- a/gst/rtsp/rtspdefs.c
+++ b/gst/rtsp/rtspdefs.c
@@ -57,7 +57,7 @@ static const gchar *rtsp_results[] = {
"Out of memory",
"Cannot resolve host",
"Function not implemented",
- "System error: '%s'",
+ "System error: %s",
"Parse error",
"Error on WSAStartup",
"Windows sockets are not version 0x202",
@@ -142,11 +142,14 @@ static const gchar *rtsp_headers[] = {
NULL
};
-#define DEF_STATUS(c,t)
+#define DEF_STATUS(c, t) \
+ g_hash_table_insert (statuses, GUINT_TO_POINTER(c), t)
-void
+GHashTable *
rtsp_init_status (void)
{
+ GHashTable *statuses = g_hash_table_new (NULL, NULL);
+
DEF_STATUS (RTSP_STS_CONTINUE, "Continue");
DEF_STATUS (RTSP_STS_OK, "OK");
DEF_STATUS (RTSP_STS_CREATED, "Created");
@@ -196,6 +199,8 @@ rtsp_init_status (void)
DEF_STATUS (RTSP_STS_RTSP_VERSION_NOT_SUPPORTED,
"RTSP Version not supported");
DEF_STATUS (RTSP_STS_OPTION_NOT_SUPPORTED, "Option not supported");
+
+ return statuses;
}
gchar *
@@ -229,7 +234,7 @@ rtsp_method_as_text (RTSPMethod method)
{
gint i;
- if (method == 0)
+ if (method == RTSP_INVALID)
return NULL;
i = 0;
@@ -243,19 +248,21 @@ rtsp_method_as_text (RTSPMethod method)
const gchar *
rtsp_header_as_text (RTSPHeaderField field)
{
- return rtsp_headers[field];
+ if (field == RTSP_HDR_INVALID)
+ return NULL;
+ else
+ return rtsp_headers[field - 1];
}
const gchar *
rtsp_status_as_text (RTSPStatusCode code)
{
- return NULL;
-}
+ static GHashTable *statuses;
-const gchar *
-rtsp_status_to_string (RTSPStatusCode code)
-{
- return NULL;
+ if (G_UNLIKELY (statuses == NULL))
+ statuses = rtsp_init_status ();
+
+ return g_hash_table_lookup (statuses, GUINT_TO_POINTER (code));
}
RTSPHeaderField
@@ -265,10 +272,10 @@ rtsp_find_header_field (gchar * header)
for (idx = 0; rtsp_headers[idx]; idx++) {
if (g_ascii_strcasecmp (rtsp_headers[idx], header) == 0) {
- return idx;
+ return idx + 1;
}
}
- return -1;
+ return RTSP_HDR_INVALID;
}
RTSPMethod
@@ -281,5 +288,5 @@ rtsp_find_method (gchar * method)
return (1 << idx);
}
}
- return -1;
+ return RTSP_INVALID;
}
diff --git a/gst/rtsp/rtspdefs.h b/gst/rtsp/rtspdefs.h
index 61ec3bd1..43436188 100644
--- a/gst/rtsp/rtspdefs.h
+++ b/gst/rtsp/rtspdefs.h
@@ -100,6 +100,8 @@ typedef enum {
} RTSPMethod;
typedef enum {
+ RTSP_HDR_INVALID,
+
/*
* R = Request
* r = response
@@ -217,7 +219,6 @@ gchar* rtsp_strresult (RTSPResult result);
const gchar* rtsp_method_as_text (RTSPMethod method);
const gchar* rtsp_header_as_text (RTSPHeaderField field);
const gchar* rtsp_status_as_text (RTSPStatusCode code);
-const gchar* rtsp_status_to_string (RTSPStatusCode code);
RTSPHeaderField rtsp_find_header_field (gchar *header);
RTSPMethod rtsp_find_method (gchar *method);
diff --git a/gst/rtsp/rtspextwms.c b/gst/rtsp/rtspextwms.c
index 44776d67..2f1fbdaa 100644
--- a/gst/rtsp/rtspextwms.c
+++ b/gst/rtsp/rtspextwms.c
@@ -87,7 +87,7 @@ rtsp_ext_wms_after_send (RTSPExtensionCtx * ctx, RTSPMessage * req,
gchar *server = NULL;
rtsp_message_get_header (resp, RTSP_HDR_SERVER, &server);
- if (g_str_has_prefix (server, SERVER_PREFIX))
+ if (server && g_str_has_prefix (server, SERVER_PREFIX))
rext->active = TRUE;
else
rext->active = FALSE;
@@ -152,9 +152,13 @@ rtsp_ext_wms_configure_stream (RTSPExtensionCtx * ctx, GstRTSPStream * stream)
s = gst_caps_get_structure (stream->caps, 0);
encoding = gst_structure_get_string (s, "encoding-name");
+ if (!encoding)
+ return TRUE;
+
GST_DEBUG_OBJECT (src, "%" GST_PTR_FORMAT " encoding-name: %s", stream->caps,
encoding);
+ /* rtx streams do not need to be configured */
if (!strcmp (encoding, "x-wms-rtx"))
return FALSE;
diff --git a/gst/rtsp/rtspmessage.c b/gst/rtsp/rtspmessage.c
index 515ccb46..ac227ac2 100644
--- a/gst/rtsp/rtspmessage.c
+++ b/gst/rtsp/rtspmessage.c
@@ -41,10 +41,41 @@
* 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;
@@ -59,10 +90,11 @@ rtsp_message_new_request (RTSPMessage ** msg, RTSPMethod method, gchar * uri)
}
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);
@@ -77,13 +109,11 @@ rtsp_message_init_request (RTSPMessage * msg, RTSPMethod method, gchar * uri)
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);
@@ -94,13 +124,15 @@ rtsp_message_new_response (RTSPMessage ** msg, RTSPStatusCode code,
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);
@@ -108,7 +140,25 @@ rtsp_message_init_response (RTSPMessage * msg, RTSPStatusCode code,
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;
@@ -132,22 +182,28 @@ rtsp_message_unset (RTSPMessage * msg)
{
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;
}
@@ -168,7 +224,7 @@ rtsp_message_free (RTSPMessage * msg)
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);
@@ -190,25 +246,25 @@ rtsp_message_remove_header (RTSPMessage * msg, RTSPHeaderField field)
}
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);
@@ -231,7 +287,7 @@ rtsp_message_take_body (RTSPMessage * msg, guint8 * data, guint size)
}
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);
@@ -260,7 +316,7 @@ rtsp_message_steal_body (RTSPMessage * msg, guint8 ** data, guint * size)
}
static void
-dump_mem (guint8 * mem, gint size)
+dump_mem (guint8 * mem, guint size)
{
guint i, j;
GString *string = g_string_sized_new (50);
@@ -309,7 +365,7 @@ rtsp_message_dump (RTSPMessage * msg)
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));
@@ -321,7 +377,7 @@ rtsp_message_dump (RTSPMessage * msg)
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);
@@ -332,7 +388,7 @@ rtsp_message_dump (RTSPMessage * msg)
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);
diff --git a/gst/rtsp/rtspmessage.h b/gst/rtsp/rtspmessage.h
index 111f4fe8..2b91432d 100644
--- a/gst/rtsp/rtspmessage.h
+++ b/gst/rtsp/rtspmessage.h
@@ -82,27 +82,53 @@ typedef struct _RTSPMessage
} RTSPMessage;
-RTSPResult rtsp_message_new_request (RTSPMessage **msg, RTSPMethod method, gchar *uri);
-RTSPResult rtsp_message_init_request (RTSPMessage *msg, RTSPMethod method, gchar *uri);
-
-RTSPResult rtsp_message_new_response (RTSPMessage **msg, RTSPStatusCode code, gchar *reason,
- RTSPMessage *request);
-RTSPResult rtsp_message_init_response (RTSPMessage *msg, RTSPStatusCode code, gchar *reason,
- RTSPMessage *request);
-RTSPResult rtsp_message_init_data (RTSPMessage *msg, gint channel);
+RTSPResult rtsp_message_new (RTSPMessage **msg);
+RTSPResult rtsp_message_init (RTSPMessage *msg);
+
+RTSPResult rtsp_message_new_request (RTSPMessage **msg,
+ RTSPMethod method,
+ const gchar *uri);
+RTSPResult rtsp_message_init_request (RTSPMessage *msg,
+ RTSPMethod method,
+ const gchar *uri);
+
+RTSPResult rtsp_message_new_response (RTSPMessage **msg,
+ RTSPStatusCode code,
+ const gchar *reason,
+ const RTSPMessage *request);
+RTSPResult rtsp_message_init_response (RTSPMessage *msg,
+ RTSPStatusCode code,
+ const gchar *reason,
+ const RTSPMessage *request);
+
+RTSPResult rtsp_message_init_data (RTSPMessage *msg,
+ gint channel);
RTSPResult rtsp_message_unset (RTSPMessage *msg);
RTSPResult rtsp_message_free (RTSPMessage *msg);
-RTSPResult rtsp_message_add_header (RTSPMessage *msg, RTSPHeaderField field, gchar *value);
-RTSPResult rtsp_message_remove_header (RTSPMessage *msg, RTSPHeaderField field);
-RTSPResult rtsp_message_get_header (RTSPMessage *msg, RTSPHeaderField field, gchar **value);
-
-RTSPResult rtsp_message_set_body (RTSPMessage *msg, guint8 *data, guint size);
-RTSPResult rtsp_message_take_body (RTSPMessage *msg, guint8 *data, guint size);
-RTSPResult rtsp_message_get_body (RTSPMessage *msg, guint8 **data, guint *size);
-RTSPResult rtsp_message_steal_body (RTSPMessage *msg, guint8 **data, guint *size);
+RTSPResult rtsp_message_add_header (RTSPMessage *msg,
+ RTSPHeaderField field,
+ const gchar *value);
+RTSPResult rtsp_message_remove_header (RTSPMessage *msg,
+ RTSPHeaderField field);
+RTSPResult rtsp_message_get_header (const RTSPMessage *msg,
+ RTSPHeaderField field,
+ gchar **value);
+
+RTSPResult rtsp_message_set_body (RTSPMessage *msg,
+ const guint8 *data,
+ guint size);
+RTSPResult rtsp_message_take_body (RTSPMessage *msg,
+ guint8 *data,
+ guint size);
+RTSPResult rtsp_message_get_body (const RTSPMessage *msg,
+ guint8 **data,
+ guint *size);
+RTSPResult rtsp_message_steal_body (RTSPMessage *msg,
+ guint8 **data,
+ guint *size);
RTSPResult rtsp_message_dump (RTSPMessage *msg);
diff --git a/gst/rtsp/sdpmessage.c b/gst/rtsp/sdpmessage.c
index a9430cf0..e7d2d7eb 100644
--- a/gst/rtsp/sdpmessage.c
+++ b/gst/rtsp/sdpmessage.c
@@ -322,7 +322,7 @@ DEFINE_ARRAY_P_GETTER (attribute, attributes, SDPAttribute);
gchar *
sdp_message_get_attribute_val_n (SDPMessage * msg, gchar * key, guint nth)
{
- gint i;
+ guint i;
for (i = 0; i < msg->attributes->len; i++) {
SDPAttribute *attr;
@@ -423,7 +423,7 @@ sdp_media_get_attribute (SDPMedia * media, guint idx)
gchar *
sdp_media_get_attribute_val_n (SDPMedia * media, gchar * key, guint nth)
{
- gint i;
+ guint i;
for (i = 0; i < media->attributes->len; i++) {
SDPAttribute *attr;
@@ -454,9 +454,9 @@ sdp_media_get_format (SDPMedia * media, guint idx)
}
static void
-read_string (gchar * dest, gint size, gchar ** src)
+read_string (gchar * dest, guint size, gchar ** src)
{
- gint idx;
+ guint idx;
idx = 0;
/* skip spaces */
@@ -473,9 +473,9 @@ read_string (gchar * dest, gint size, gchar ** src)
}
static void
-read_string_del (gchar * dest, gint size, gchar del, gchar ** src)
+read_string_del (gchar * dest, guint size, gchar del, gchar ** src)
{
- gint idx;
+ guint idx;
idx = 0;
/* skip spaces */
@@ -582,7 +582,7 @@ sdp_parse_line (SDPContext * c, gchar type, gchar * buffer)
case 'm':
{
gchar *slash;
- SDPMedia nmedia = { 0 };
+ SDPMedia nmedia = {.media = NULL };
c->state = SDP_MEDIA;
sdp_media_init (&nmedia);
@@ -622,7 +622,7 @@ sdp_message_parse_buffer (guint8 * data, guint size, SDPMessage * msg)
SDPContext c;
gchar type;
gchar buffer[MAX_LINE_LEN];
- gint idx = 0;
+ guint idx = 0;
g_return_val_if_fail (msg != NULL, RTSP_EINVAL);
g_return_val_if_fail (data != NULL, RTSP_EINVAL);
@@ -672,7 +672,7 @@ print_media (SDPMedia * media)
g_print (" num_ports: '%d'\n", media->num_ports);
g_print (" proto: '%s'\n", media->proto);
if (media->fmts->len > 0) {
- gint i;
+ guint i;
g_print (" formats:\n");
for (i = 0; i < media->fmts->len; i++) {
@@ -684,7 +684,7 @@ print_media (SDPMedia * media)
g_print (" type: '%s'\n", media->key.type);
g_print (" data: '%s'\n", media->key.data);
if (media->attributes->len > 0) {
- gint i;
+ guint i;
g_print (" attributes:\n");
for (i = 0; i < media->attributes->len; i++) {
@@ -714,7 +714,7 @@ sdp_message_dump (SDPMessage * msg)
g_print (" uri: '%s'\n", msg->uri);
if (msg->emails->len > 0) {
- gint i;
+ guint i;
g_print (" emails:\n");
for (i = 0; i < msg->emails->len; i++) {
@@ -722,7 +722,7 @@ sdp_message_dump (SDPMessage * msg)
}
}
if (msg->phones->len > 0) {
- gint i;
+ guint i;
g_print (" phones:\n");
for (i = 0; i < msg->phones->len; i++) {
@@ -739,7 +739,7 @@ sdp_message_dump (SDPMessage * msg)
g_print (" type: '%s'\n", msg->key.type);
g_print (" data: '%s'\n", msg->key.data);
if (msg->attributes->len > 0) {
- gint i;
+ guint i;
g_print (" attributes:\n");
for (i = 0; i < msg->attributes->len; i++) {
@@ -749,7 +749,7 @@ sdp_message_dump (SDPMessage * msg)
}
}
if (msg->medias->len > 0) {
- gint i;
+ guint i;
g_print (" medias:\n");
for (i = 0; i < msg->medias->len; i++) {