gst/rtsp/: Allow url to be NULL to be able to use it for server connections.

Original commit message from CVS:
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.
This commit is contained in:
Peter Kjellerstedt 2007-01-10 15:19:48 +00:00 committed by Wim Taymans
parent 8f7c1775d9
commit 12ab127d12
11 changed files with 720 additions and 317 deletions

View file

@ -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>

23
gst/rtsp/COPYING.MIT Normal file
View file

@ -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.
*/

View file

@ -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);
/* 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;
/* see if we have TCP left to try */
if (src->cur_protocols & RTSP_LOWER_TRANS_TCP) {
gchar *url, *pos;
/* pause to prepare for a restart */
gst_rtspsrc_pause (src);
/* 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"));
}
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;
@ -1668,6 +1823,193 @@ failed:
}
}
static gboolean
gst_rtspsrc_setup_streams (GstRTSPSrc * src)
{
GList *walk;
RTSPResult res;
RTSPMessage request = { 0 };
RTSPMessage response = { 0 };
GstRTSPStream *stream = NULL;
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;
/* reset some state */
src->free_channel = 0;
src->interleaved = FALSE;
for (walk = src->streams; walk; walk = g_list_next (walk)) {
gchar *transports;
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 %p, disabled by extension",
stream);
continue;
}
}
/* merge/overwrite global caps */
if (stream->caps) {
guint j, num;
GstStructure *s;
s = gst_caps_get_structure (stream->caps, 0);
num = gst_structure_n_fields (src->props);
for (j = 0; j < num; j++) {
const gchar *name;
const GValue *val;
name = gst_structure_nth_field_name (src->props, j);
val = gst_structure_get_value (src->props, name);
gst_structure_set_value (s, name, val);
GST_DEBUG_OBJECT (src, "copied %s", name);
}
}
/* skip setup if we have no URL for it */
if (stream->setup_url == NULL) {
GST_DEBUG_OBJECT (src, "skipping stream %p, no setup", stream);
continue;
}
GST_DEBUG_OBJECT (src, "doing setup of stream %p with %s", stream,
stream->setup_url);
/* 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, 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;
/* 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);
if (!gst_rtspsrc_send (src, &request, &response, NULL))
goto send_error;
/* parse response transport */
{
gchar *resptrans = NULL;
RTSPTransport transport = { 0 };
rtsp_message_get_header (&response, RTSP_HDR_TRANSPORT, &resptrans);
if (!resptrans)
goto no_transport;
/* parse transport */
if (rtsp_transport_parse (resptrans, &transport) != RTSP_OK)
continue;
/* update allowed transports for other streams. once the transport of
* one stream has been determined, we make sure that all other streams
* are configured in the same way */
switch (transport.lower_transport) {
case RTSP_LOWER_TRANS_TCP:
GST_DEBUG_OBJECT (src, "stream %p as TCP interleaved", stream);
protocols = RTSP_LOWER_TRANS_TCP;
src->interleaved = TRUE;
/* update free channels */
src->free_channel =
MAX (transport.interleaved.min, src->free_channel);
src->free_channel =
MAX (transport.interleaved.max, src->free_channel);
src->free_channel++;
break;
case RTSP_LOWER_TRANS_UDP_MCAST:
/* only allow multicast for other streams */
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 %p as UDP unicast", stream);
protocols = RTSP_LOWER_TRANS_UDP;
break;
default:
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 selected transport */
if (!gst_rtspsrc_stream_configure_transport (stream, &transport)) {
GST_DEBUG_OBJECT (src,
"could not configure stream %p transport, skipping stream",
stream);
}
}
/* clean up our transport struct */
rtsp_transport_init (&transport);
}
}
if (src->extension && src->extension->stream_select)
src->extension->stream_select (src->extension);
/* 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)
{
@ -1678,13 +2020,10 @@ gst_rtspsrc_open (GstRTSPSrc * src)
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 */
@ -1758,139 +2097,14 @@ gst_rtspsrc_open (GstRTSPSrc * src)
if (src->extension && src->extension->parse_sdp)
src->extension->parse_sdp (src->extension, &sdp);
/* 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 */
/* create streams */
n_streams = sdp_message_medias_len (&sdp);
for (i = 0; i < n_streams; i++) {
gchar *transports;
/* create stream from the media, can never return NULL */
stream = gst_rtspsrc_create_stream (src, &sdp, i);
/* 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);
continue;
}
}
/* merge/overwrite global caps */
if (stream->caps) {
guint j, num;
GstStructure *s;
s = gst_caps_get_structure (stream->caps, 0);
num = gst_structure_n_fields (src->props);
for (j = 0; j < num; j++) {
const gchar *name;
const GValue *val;
name = gst_structure_nth_field_name (src->props, j);
val = gst_structure_get_value (src->props, name);
gst_structure_set_value (s, name, val);
GST_DEBUG_OBJECT (src, "copied %s", name);
}
}
/* skip setup if we have no URL for it */
if (stream->setup_url == NULL) {
GST_DEBUG_OBJECT (src, "skipping stream %d, no setup", i);
continue;
}
GST_DEBUG_OBJECT (src, "doing setup of stream %d with %s", i,
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);
if (res < 0)
goto setup_transport_failed;
/* select transport, copy is made when adding to header */
rtsp_message_add_header (&request, RTSP_HDR_TRANSPORT, transports);
g_free (transports);
if (!gst_rtspsrc_send (src, &request, &response, NULL))
goto send_error;
/* parse response transport */
{
gchar *resptrans = NULL;
RTSPTransport transport = { 0 };
rtsp_message_get_header (&response, RTSP_HDR_TRANSPORT, &resptrans);
if (!resptrans)
goto no_transport;
/* parse transport */
if (rtsp_transport_parse (resptrans, &transport) != RTSP_OK)
continue;
/* update allowed transports for other streams. once the transport of
* one stream has been determined, we make sure that all other streams
* 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);
protocols = RTSP_LOWER_TRANS_TCP;
src->interleaved = TRUE;
/* update free channels */
src->free_channel =
MAX (transport.interleaved.min, src->free_channel);
src->free_channel =
MAX (transport.interleaved.max, src->free_channel);
src->free_channel++;
break;
case RTSP_LOWER_TRANS_UDP_MCAST:
/* only allow multicast for other streams */
GST_DEBUG_OBJECT (src, "stream %d as UDP multicast", i);
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);
protocols = RTSP_LOWER_TRANS_UDP;
break;
default:
GST_DEBUG_OBJECT (src, "stream %d unknown transport %d", i,
transport.lower_transport);
break;
}
if (!stream->container || !src->interleaved) {
/* now configure the stream with the transport */
if (!gst_rtspsrc_stream_configure_transport (stream, &transport)) {
GST_DEBUG_OBJECT (src,
"could not configure stream %d transport, skipping stream", i);
}
}
/* clean up our transport struct */
rtsp_transport_init (&transport);
}
}
if (src->extension && src->extension->stream_select)
src->extension->stream_select (src->extension);
/* if we got here all was configured. We have dynamic pads so we notify that
* we are done */
gst_element_no_more_pads (GST_ELEMENT_CAST (src));
/* 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 */

View file

@ -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;
};

View file

@ -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

View file

@ -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;
}

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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 (RTSPMessage **msg);
RTSPResult rtsp_message_init (RTSPMessage *msg);
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_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_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, 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_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);

View file

@ -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++) {