diff options
-rw-r--r-- | ChangeLog | 58 | ||||
-rw-r--r-- | gst/rtsp/COPYING.MIT | 23 | ||||
-rw-r--r-- | gst/rtsp/gstrtspsrc.c | 625 | ||||
-rw-r--r-- | gst/rtsp/gstrtspsrc.h | 6 | ||||
-rw-r--r-- | gst/rtsp/rtspconnection.c | 43 | ||||
-rw-r--r-- | gst/rtsp/rtspdefs.c | 35 | ||||
-rw-r--r-- | gst/rtsp/rtspdefs.h | 3 | ||||
-rw-r--r-- | gst/rtsp/rtspextwms.c | 6 | ||||
-rw-r--r-- | gst/rtsp/rtspmessage.c | 116 | ||||
-rw-r--r-- | gst/rtsp/rtspmessage.h | 58 | ||||
-rw-r--r-- | gst/rtsp/sdpmessage.c | 28 |
11 files changed, 702 insertions, 299 deletions
@@ -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++) { |