gst/rtsp/gstrtspsrc.*: Rework how the transport string is constructed, try to share channels and udp ports.

Original commit message from CVS:
* gst/rtsp/gstrtspsrc.c: (gst_rtsp_proto_get_type),
(gst_rtspsrc_class_init), (gst_rtspsrc_init),
(gst_rtspsrc_create_stream), (gst_rtspsrc_media_to_caps),
(gst_rtspsrc_alloc_udp_ports),
(gst_rtspsrc_stream_configure_transport), (find_stream_by_channel),
(gst_rtspsrc_push_event), (gst_rtspsrc_loop_interleaved),
(gst_rtspsrc_create_transports_string),
(gst_rtspsrc_configure_transports), (gst_rtspsrc_open),
(gst_rtspsrc_handle_message), (gst_rtspsrc_change_state):
* gst/rtsp/gstrtspsrc.h:
Rework how the transport string is constructed, try to share channels
and udp ports.
Make most of the stuff less dependant on RTP as we are also going to use
it for RDT.
Add support for transport specific session managers.
* gst/rtsp/rtspconnection.c: (rtsp_connection_flush):
Implement _flush().
* gst/rtsp/rtspdefs.c: (rtsp_strresult):
* gst/rtsp/rtspdefs.h:
Add generic error return code.
* gst/rtsp/rtspext.h:
Add support for pluggable tranport strings.
* gst/rtsp/rtspextwms.c: (rtsp_ext_wms_before_send),
(rtsp_ext_wms_after_send), (rtsp_ext_wms_parse_sdp),
(rtsp_ext_wms_get_context):
Detect WMServer and activate the extension.
* gst/rtsp/rtsptransport.c: (rtsp_transport_get_mime),
(rtsp_transport_get_manager), (rtsp_transport_parse):
* gst/rtsp/rtsptransport.h:
Added methods to get mime/manager for certain transports.
This commit is contained in:
Wim Taymans 2006-10-06 12:55:53 +00:00
parent 751222f5aa
commit a600d31120
10 changed files with 588 additions and 239 deletions

View file

@ -1,3 +1,41 @@
2006-10-06 Wim Taymans <wim@fluendo.com>
* gst/rtsp/gstrtspsrc.c: (gst_rtsp_proto_get_type),
(gst_rtspsrc_class_init), (gst_rtspsrc_init),
(gst_rtspsrc_create_stream), (gst_rtspsrc_media_to_caps),
(gst_rtspsrc_alloc_udp_ports),
(gst_rtspsrc_stream_configure_transport), (find_stream_by_channel),
(gst_rtspsrc_push_event), (gst_rtspsrc_loop_interleaved),
(gst_rtspsrc_create_transports_string),
(gst_rtspsrc_configure_transports), (gst_rtspsrc_open),
(gst_rtspsrc_handle_message), (gst_rtspsrc_change_state):
* gst/rtsp/gstrtspsrc.h:
Rework how the transport string is constructed, try to share channels
and udp ports.
Make most of the stuff less dependant on RTP as we are also going to use
it for RDT.
Add support for transport specific session managers.
* gst/rtsp/rtspconnection.c: (rtsp_connection_flush):
Implement _flush().
* gst/rtsp/rtspdefs.c: (rtsp_strresult):
* gst/rtsp/rtspdefs.h:
Add generic error return code.
* gst/rtsp/rtspext.h:
Add support for pluggable tranport strings.
* gst/rtsp/rtspextwms.c: (rtsp_ext_wms_before_send),
(rtsp_ext_wms_after_send), (rtsp_ext_wms_parse_sdp),
(rtsp_ext_wms_get_context):
Detect WMServer and activate the extension.
* gst/rtsp/rtsptransport.c: (rtsp_transport_get_mime),
(rtsp_transport_get_manager), (rtsp_transport_parse):
* gst/rtsp/rtsptransport.h:
Added methods to get mime/manager for certain transports.
2006-10-05 Tim-Philipp Müller <tim at centricular dot net>
* ext/cairo/gsttimeoverlay.c:

View file

@ -96,6 +96,9 @@
#include "sdp.h"
#include "rtspextwms.h"
#ifdef WITH_EXT_REAL
#include "rtspextreal.h"
#endif
GST_DEBUG_CATEGORY_STATIC (rtspsrc_debug);
#define GST_CAT_DEFAULT (rtspsrc_debug)
@ -109,11 +112,10 @@ GST_ELEMENT_DETAILS ("RTSP packet receiver",
"Thijs Vermeir <thijs.vermeir@barco.com>\n"
"Lutz Mueller <lutz@topfrose.de>");
static GstStaticPadTemplate rtptemplate =
GST_STATIC_PAD_TEMPLATE ("rtp_stream%d",
static GstStaticPadTemplate rtptemplate = GST_STATIC_PAD_TEMPLATE ("stream%d",
GST_PAD_SRC,
GST_PAD_SOMETIMES,
GST_STATIC_CAPS ("application/x-rtp"));
GST_STATIC_CAPS ("application/x-rtp; application/x-rdt"));
enum
{
@ -122,7 +124,7 @@ enum
};
#define DEFAULT_LOCATION NULL
#define DEFAULT_PROTOCOLS GST_RTSP_PROTO_UDP_UNICAST | GST_RTSP_PROTO_UDP_MULTICAST | GST_RTSP_PROTO_TCP
#define DEFAULT_PROTOCOLS RTSP_LOWER_TRANS_UDP | RTSP_LOWER_TRANS_UDP_MCAST | RTSP_LOWER_TRANS_TCP
#define DEFAULT_DEBUG FALSE
#define DEFAULT_RETRY 20
#define DEFAULT_TIMEOUT 5000000
@ -143,9 +145,9 @@ gst_rtsp_proto_get_type (void)
{
static GType rtsp_proto_type = 0;
static const GFlagsValue rtsp_proto[] = {
{GST_RTSP_PROTO_UDP_UNICAST, "UDP Unicast Mode", "udp-unicast"},
{GST_RTSP_PROTO_UDP_MULTICAST, "UDP Multicast Mode", "udp-multicast"},
{GST_RTSP_PROTO_TCP, "TCP interleaved mode", "tcp"},
{RTSP_LOWER_TRANS_UDP, "UDP Unicast Mode", "udp-unicast"},
{RTSP_LOWER_TRANS_UDP_MCAST, "UDP Multicast Mode", "udp-multicast"},
{RTSP_LOWER_TRANS_TCP, "TCP interleaved mode", "tcp"},
{0, NULL, NULL},
};
@ -233,9 +235,9 @@ gst_rtspsrc_class_init (GstRTSPSrcClass * klass)
DEFAULT_LOCATION, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_PROTOCOLS,
g_param_spec_flags ("protocols", "Protocols", "Allowed protocols",
GST_TYPE_RTSP_PROTO, DEFAULT_PROTOCOLS,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_param_spec_flags ("protocols", "Protocols",
"Allowed lower transport protocols", GST_TYPE_RTSP_PROTO,
DEFAULT_PROTOCOLS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (gobject_class, PROP_DEBUG,
g_param_spec_boolean ("debug", "Debug",
@ -272,6 +274,9 @@ gst_rtspsrc_init (GstRTSPSrc * src, GstRTSPSrcClass * g_class)
/* install WMS extension by default */
src->extension = rtsp_ext_wms_get_context ();
#ifdef WITH_EXT_REAL
src->extension = rtsp_ext_real_get_context ();
#endif
src->extension->src = (gpointer) src;
}
@ -414,8 +419,9 @@ gst_rtspsrc_create_stream (GstRTSPSrc * src, SDPMessage * sdp, gint idx)
GST_DEBUG_OBJECT (src, " control: %s", GST_STR_NULL (control_url));
if (control_url != NULL) {
/* FIXME, what if the control_url starts with a '/' or a non rtsp: protocol? */
/* check absolute/relative URL */
/* If the control_url starts with a '/' or a non rtsp: protocol we will most
* likely build a URL that the server will fail to understand, this is ok,
* we will fail then. */
if (g_str_has_prefix (control_url, "rtsp://"))
stream->setup_url = g_strdup (control_url);
else if (src->content_base)
@ -565,7 +571,7 @@ gst_rtspsrc_media_to_caps (gint pt, SDPMedia * media)
goto no_rtpmap;
}
caps = gst_caps_new_simple ("application/x-rtp",
caps = gst_caps_new_simple ("application/x-unknown",
"media", G_TYPE_STRING, media->media, "payload", G_TYPE_INT, pt, NULL);
s = gst_caps_get_structure (caps, 0);
@ -627,34 +633,34 @@ no_rtpmap:
}
static gboolean
gst_rtspsrc_stream_setup_rtp (GstRTSPStream * stream,
gst_rtspsrc_alloc_udp_ports (GstRTSPStream * stream,
gint * rtpport, gint * rtcpport)
{
GstStateChangeReturn ret;
GstRTSPSrc *src;
GstElement *tmp, *rtpsrc, *rtcpsrc;
GstStateChangeReturn ret;
GstElement *tmp, *udpsrc0, *udpsrc1;
gint tmp_rtp, tmp_rtcp;
guint count;
src = stream->parent;
tmp = NULL;
rtpsrc = NULL;
rtcpsrc = NULL;
udpsrc0 = NULL;
udpsrc1 = NULL;
count = 0;
/* try to allocate 2 UDP ports, the RTP port should be an even
* number and the RTCP port should be the next (uneven) port */
again:
rtpsrc = gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0:0", NULL);
if (rtpsrc == NULL)
goto no_udp_rtp_protocol;
udpsrc0 = gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0:0", NULL);
if (udpsrc0 == NULL)
goto no_udp_protocol;
ret = gst_element_set_state (rtpsrc, GST_STATE_PAUSED);
ret = gst_element_set_state (udpsrc0, GST_STATE_PAUSED);
if (ret == GST_STATE_CHANGE_FAILURE)
goto start_rtp_failure;
goto start_udp_failure;
g_object_get (G_OBJECT (rtpsrc), "port", &tmp_rtp, NULL);
g_object_get (G_OBJECT (udpsrc0), "port", &tmp_rtp, NULL);
GST_DEBUG_OBJECT (src, "got RTP port %d", tmp_rtp);
/* check if port is even */
@ -671,7 +677,7 @@ again:
gst_element_set_state (tmp, GST_STATE_NULL);
gst_object_unref (tmp);
}
tmp = rtpsrc;
tmp = udpsrc0;
GST_DEBUG_OBJECT (src, "retry %d", count);
goto again;
}
@ -683,50 +689,45 @@ again:
}
/* allocate port+1 for RTCP now */
rtcpsrc = gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0", NULL);
if (rtcpsrc == NULL)
udpsrc1 = gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0", NULL);
if (udpsrc1 == NULL)
goto no_udp_rtcp_protocol;
/* set port */
tmp_rtcp = tmp_rtp + 1;
g_object_set (G_OBJECT (rtcpsrc), "port", tmp_rtcp, NULL);
g_object_set (G_OBJECT (udpsrc1), "port", tmp_rtcp, NULL);
GST_DEBUG_OBJECT (src, "starting RTCP on port %d", tmp_rtcp);
ret = gst_element_set_state (rtcpsrc, GST_STATE_PAUSED);
ret = gst_element_set_state (udpsrc1, GST_STATE_PAUSED);
/* FIXME, this could fail if the next port is not free, we
* should retry with another port then */
if (ret == GST_STATE_CHANGE_FAILURE)
goto start_rtcp_failure;
/* all fine, do port check */
g_object_get (G_OBJECT (rtpsrc), "port", rtpport, NULL);
g_object_get (G_OBJECT (rtcpsrc), "port", rtcpport, NULL);
g_object_get (G_OBJECT (udpsrc0), "port", rtpport, NULL);
g_object_get (G_OBJECT (udpsrc1), "port", rtcpport, NULL);
/* this should not happen */
if (*rtpport != tmp_rtp || *rtcpport != tmp_rtcp)
goto port_error;
/* configure a timeout */
g_object_set (G_OBJECT (rtpsrc), "timeout", src->timeout, NULL);
/* we manage these elements, we set the caps in configure_transport */
stream->rtpsrc = rtpsrc;
stream->rtcpsrc = rtcpsrc;
gst_bin_add (GST_BIN_CAST (src), stream->rtpsrc);
gst_bin_add (GST_BIN_CAST (src), stream->rtcpsrc);
/* 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;
return TRUE;
/* ERRORS */
no_udp_rtp_protocol:
no_udp_protocol:
{
GST_DEBUG_OBJECT (src, "could not get UDP source for RTP");
GST_DEBUG_OBJECT (src, "could not get UDP source");
goto cleanup;
}
start_rtp_failure:
start_udp_failure:
{
GST_DEBUG_OBJECT (src, "could not start UDP source for RTP");
GST_DEBUG_OBJECT (src, "could not start UDP source");
goto cleanup;
}
no_ports:
@ -757,13 +758,13 @@ cleanup:
gst_element_set_state (tmp, GST_STATE_NULL);
gst_object_unref (tmp);
}
if (rtpsrc) {
gst_element_set_state (rtpsrc, GST_STATE_NULL);
gst_object_unref (rtpsrc);
if (udpsrc0) {
gst_element_set_state (udpsrc0, GST_STATE_NULL);
gst_object_unref (udpsrc0);
}
if (rtcpsrc) {
gst_element_set_state (rtcpsrc, GST_STATE_NULL);
gst_object_unref (rtcpsrc);
if (udpsrc1) {
gst_element_set_state (udpsrc1, GST_STATE_NULL);
gst_object_unref (udpsrc1);
}
return FALSE;
}
@ -774,115 +775,209 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
RTSPTransport * transport)
{
GstRTSPSrc *src;
GstPad *pad;
GstPad *outpad = NULL;
GstPadTemplate *template;
GstStateChangeReturn ret;
gchar *name;
GstStructure *s;
const gchar *mime, *manager;
RTSPResult res;
src = stream->parent;
GST_DEBUG ("configuring RTP transport for stream %p", stream);
GST_DEBUG_OBJECT (src, "configuring transport for stream %p", stream);
/* FIXME, the session manager needs to be shared with all the streams */
if (!(stream->rtpdec = gst_element_factory_make ("rtpdec", NULL)))
goto no_element;
s = gst_caps_get_structure (stream->caps, 0);
/* we manage this element */
gst_bin_add (GST_BIN_CAST (src), stream->rtpdec);
if ((res = rtsp_transport_get_mime (transport->trans, &mime)) < 0)
goto no_mime;
if (!mime)
goto no_mime;
ret = gst_element_set_state (stream->rtpdec, GST_STATE_PAUSED);
if (ret != GST_STATE_CHANGE_SUCCESS)
goto start_rtpdec_failure;
GST_DEBUG_OBJECT (src, "setting mime to %s", mime);
/* configure the final mime type */
gst_structure_set_name (s, mime);
stream->rtpdecrtp = gst_element_get_pad (stream->rtpdec, "sinkrtp");
stream->rtpdecrtcp = gst_element_get_pad (stream->rtpdec, "sinkrtcp");
if ((res = rtsp_transport_get_manager (transport->trans, &manager)) < 0)
goto no_manager;
if (manager) {
GST_DEBUG_OBJECT (src, "using manager %s", manager);
/* FIXME, the session manager needs to be shared with all the streams */
if (!(stream->sess = gst_element_factory_make (manager, NULL)))
goto no_element;
/* we manage this element */
gst_bin_add (GST_BIN_CAST (src), stream->sess);
ret = gst_element_set_state (stream->sess, GST_STATE_PAUSED);
if (ret != GST_STATE_CHANGE_SUCCESS)
goto start_session_failure;
stream->channelpad[0] = gst_element_get_pad (stream->sess, "sinkrtp");
stream->channelpad[1] = gst_element_get_pad (stream->sess, "sinkrtcp");
}
if (transport->lower_transport == RTSP_LOWER_TRANS_TCP) {
gint i;
/* configure for interleaved delivery, nothing needs to be done
* here, the loop function will call the chain functions of the
* RTP session manager. */
stream->rtpchannel = transport->interleaved.min;
stream->rtcpchannel = transport->interleaved.max;
GST_DEBUG ("stream %p on channels %d-%d", stream,
stream->rtpchannel, stream->rtcpchannel);
* session manager. */
stream->channel[0] = transport->interleaved.min;
stream->channel[1] = transport->interleaved.max;
GST_DEBUG_OBJECT (src, "stream %p on channels %d-%d", stream,
stream->channel[0], stream->channel[1]);
/* we can remove the allocated UDP ports now */
for (i = 0; i < 2; i++) {
if (stream->udpsrc[i]) {
gst_element_set_state (stream->udpsrc[i], GST_STATE_NULL);
gst_object_unref (stream->udpsrc[i]);
stream->udpsrc[i] = NULL;
}
}
/* no session manager, send data to srcpad directly */
if (!stream->channelpad[0]) {
GST_DEBUG_OBJECT (src, "no manager, creating pad");
name = g_strdup_printf ("stream%d", stream->id);
template = gst_static_pad_template_get (&rtptemplate);
stream->srcpad = 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);
stream->channelpad[0] = gst_object_ref (stream->srcpad);
gst_element_add_pad (GST_ELEMENT_CAST (src), stream->srcpad);
} else {
GST_DEBUG_OBJECT (src, "using manager source pad");
outpad = gst_element_get_pad (stream->sess, "srcrtp");
}
} else {
/* multicast was selected, create UDP sources and join the multicast
* group. */
if (transport->lower_transport == RTSP_LOWER_TRANS_UDP_MCAST) {
gchar *uri;
/* creating RTP source */
uri =
g_strdup_printf ("udp://%s:%d", transport->destination,
transport->port.min);
stream->rtpsrc = gst_element_make_from_uri (GST_URI_SRC, uri, NULL);
g_free (uri);
if (stream->rtpsrc == NULL)
goto no_element;
GST_DEBUG_OBJECT (src, "creating UDP sources for multicast");
/* creating RTCP source */
uri =
g_strdup_printf ("udp://%s:%d", transport->destination,
transport->port.max);
stream->rtcpsrc = gst_element_make_from_uri (GST_URI_SRC, uri, NULL);
g_free (uri);
if (stream->rtcpsrc == NULL)
goto no_element;
/* creating UDP source */
if (transport->port.min != -1) {
uri = g_strdup_printf ("udp://%s:%d", transport->destination,
transport->port.min);
stream->udpsrc[0] = gst_element_make_from_uri (GST_URI_SRC, uri, NULL);
g_free (uri);
if (stream->udpsrc[0] == NULL)
goto no_element;
/* change state */
gst_element_set_state (stream->rtpsrc, GST_STATE_PAUSED);
gst_element_set_state (stream->rtcpsrc, GST_STATE_PAUSED);
/* change state */
gst_element_set_state (stream->udpsrc[0], GST_STATE_PAUSED);
}
/* we manage these elements */
gst_bin_add (GST_BIN_CAST (src), stream->rtpsrc);
gst_bin_add (GST_BIN_CAST (src), stream->rtcpsrc);
/* creating another UDP source */
if (transport->port.max != -1) {
uri = g_strdup_printf ("udp://%s:%d", transport->destination,
transport->port.max);
stream->udpsrc[1] = gst_element_make_from_uri (GST_URI_SRC, uri, NULL);
g_free (uri);
if (stream->udpsrc[1] == NULL)
goto no_element;
gst_element_set_state (stream->udpsrc[1], GST_STATE_PAUSED);
}
}
/* set caps */
g_object_set (G_OBJECT (stream->rtpsrc), "caps", stream->caps, NULL);
/* 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;
/* configure for UDP delivery, we need to connect the UDP pads to
* the RTP session plugin. */
pad = gst_element_get_pad (stream->rtpsrc, "src");
gst_pad_link (pad, stream->rtpdecrtp);
gst_object_unref (pad);
gst_bin_add (GST_BIN_CAST (src), stream->udpsrc[0]);
pad = gst_element_get_pad (stream->rtcpsrc, "src");
gst_pad_link (pad, stream->rtpdecrtcp);
gst_object_unref (pad);
GST_DEBUG_OBJECT (src, "setting up UDP source");
/* set caps */
g_object_set (G_OBJECT (stream->udpsrc[0]), "caps", stream->caps, NULL);
/* configure a timeout on the UDP port */
g_object_set (G_OBJECT (stream->udpsrc[0]), "timeout", src->timeout,
NULL);
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);
outpad = gst_element_get_pad (stream->sess, "srcrtp");
} else {
GST_DEBUG_OBJECT (src, "using UDP src pad as output");
outpad = gst_element_get_pad (stream->udpsrc[0], "src");
}
}
if (stream->udpsrc[1]) {
gst_bin_add (GST_BIN_CAST (src), stream->udpsrc[1]);
if (stream->channelpad[1]) {
GstPad *pad;
GST_DEBUG_OBJECT (src, "connecting UDP source 1 to manager");
pad = gst_element_get_pad (stream->udpsrc[1], "src");
gst_pad_link (pad, stream->channelpad[1]);
gst_object_unref (pad);
}
}
}
pad = gst_element_get_pad (stream->rtpdec, "srcrtp");
if (stream->caps) {
gst_pad_use_fixed_caps (pad);
gst_pad_set_caps (pad, stream->caps);
if (outpad && !stream->srcpad) {
GST_DEBUG_OBJECT (src, "creating ghostpad");
gst_pad_use_fixed_caps (outpad);
gst_pad_set_caps (outpad, stream->caps);
/* create ghostpad */
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);
gst_object_unref (template);
g_free (name);
gst_object_unref (outpad);
/* and add */
gst_element_add_pad (GST_ELEMENT_CAST (src), stream->srcpad);
}
/* create ghostpad */
name = g_strdup_printf ("rtp_stream%d", stream->id);
template = gst_static_pad_template_get (&rtptemplate);
stream->srcpad = gst_ghost_pad_new_from_template (name, pad, template);
gst_object_unref (template);
g_free (name);
gst_object_unref (pad);
/* mark pad as ok */
stream->last_ret = GST_FLOW_OK;
/* and add */
gst_element_add_pad (GST_ELEMENT_CAST (src), stream->srcpad);
return TRUE;
/* ERRORS */
no_mime:
{
GST_DEBUG_OBJECT (src, "unknown transport");
return FALSE;
}
no_manager:
{
GST_DEBUG_OBJECT (src, "cannot get a session manager");
return FALSE;
}
no_element:
{
GST_DEBUG_OBJECT (src, "no rtpdec element found");
return FALSE;
}
start_rtpdec_failure:
start_session_failure:
{
GST_DEBUG_OBJECT (src, "could not start RTP session");
GST_DEBUG_OBJECT (src, "could not start session");
return FALSE;
}
}
@ -892,7 +987,7 @@ find_stream_by_channel (GstRTSPStream * stream, gconstpointer a)
{
gint channel = GPOINTER_TO_INT (a);
if (stream->rtpchannel == channel || stream->rtcpchannel == channel)
if (stream->channel[0] == channel || stream->channel[1] == channel)
return 0;
return -1;
@ -944,10 +1039,21 @@ gst_rtspsrc_push_event (GstRTSPSrc * src, GstEvent * event)
if (ostream->srcpad == NULL)
continue;
gst_event_ref (event);
gst_pad_push_event (ostream->rtpdecrtp, event);
gst_event_ref (event);
gst_pad_push_event (ostream->rtpdecrtcp, event);
if (ostream->channelpad[0]) {
gst_event_ref (event);
if (GST_PAD_IS_SRC (ostream->channelpad[0]))
gst_pad_push_event (ostream->channelpad[0], event);
else
gst_pad_send_event (ostream->channelpad[0], event);
}
if (ostream->channelpad[1]) {
gst_event_ref (event);
if (GST_PAD_IS_SRC (ostream->channelpad[1]))
gst_pad_push_event (ostream->channelpad[1], event);
else
gst_pad_send_event (ostream->channelpad[1], event);
}
}
gst_event_unref (event);
}
@ -984,11 +1090,11 @@ gst_rtspsrc_loop_interleaved (GstRTSPSrc * src)
goto unknown_stream;
stream = (GstRTSPStream *) lstream->data;
if (channel == stream->rtpchannel) {
outpad = stream->rtpdecrtp;
if (channel == stream->channel[0]) {
outpad = stream->channelpad[0];
caps = stream->caps;
} else if (channel == stream->rtcpchannel) {
outpad = stream->rtpdecrtcp;
} else if (channel == stream->channel[1]) {
outpad = stream->channelpad[1];
}
/* take a look at the body to figure out what we have */
@ -999,7 +1105,7 @@ gst_rtspsrc_loop_interleaved (GstRTSPSrc * src)
/* channels are not correct on some servers, do extra check */
if (data[1] >= 200 && data[1] <= 204) {
/* hmm RTCP message switch to the RTCP pad of the same stream. */
outpad = stream->rtpdecrtcp;
outpad = stream->channelpad[1];
}
/* we have no clue what this is, just ignore then. */
@ -1027,7 +1133,10 @@ gst_rtspsrc_loop_interleaved (GstRTSPSrc * src)
channel);
/* chain to the peer pad */
ret = gst_pad_chain (outpad, buf);
if (GST_PAD_IS_SINK (outpad))
ret = gst_pad_chain (outpad, buf);
else
ret = gst_pad_push (outpad, buf);
/* combine all stream flows */
ret = gst_rtspsrc_combine_flows (src, stream, ret);
@ -1311,6 +1420,136 @@ no_setup:
}
}
static RTSPResult
gst_rtspsrc_create_transports_string (GstRTSPSrc * src,
RTSPLowerTrans protocols, gchar ** transports)
{
gchar *result;
RTSPResult res;
*transports = NULL;
if (src->extension && src->extension->get_transports)
if ((res =
src->extension->get_transports (src->extension, protocols,
transports)) < 0)
goto failed;
/* extension listed transports, use those */
if (*transports != NULL)
return RTSP_OK;
result = g_strdup ("");
if (protocols & RTSP_LOWER_TRANS_UDP) {
gchar *new;
GST_DEBUG_OBJECT (src, "adding UDP unicast");
new =
g_strconcat (result, "RTP/AVP/UDP;unicast;client_port=%%u1-%%u2", NULL);
g_free (result);
result = new;
}
if (protocols & RTSP_LOWER_TRANS_UDP_MCAST) {
gchar *new;
GST_DEBUG_OBJECT (src, "adding UDP multicast");
/* we don't have to allocate any UDP ports yet, if the selected transport
* turns out to be multicast we can create them and join the multicast
* group indicated in the transport reply */
new = g_strconcat (result, result[0] ? "," : "",
"RTP/AVP/UDP;multicast", NULL);
g_free (result);
result = new;
}
if (protocols & RTSP_LOWER_TRANS_TCP) {
gchar *new;
GST_DEBUG_OBJECT (src, "adding TCP");
new = g_strconcat (result, result[0] ? "," : "",
"RTP/AVP/TCP;unicast;interleaved=%%i1-%%i2", NULL);
g_free (result);
result = new;
}
*transports = result;
return RTSP_OK;
/* ERRORS */
failed:
{
return res;
}
}
static RTSPResult
gst_rtspsrc_configure_transports (GstRTSPStream * stream, gchar ** transports)
{
GstRTSPSrc *src;
gint nr_udp, nr_int;
gchar *next, *p;
gint rtpport = 0, rtcpport = 0;
GString *str;
src = stream->parent;
/* find number of placeholders first */
if (strstr (*transports, "%%i2"))
nr_int = 2;
else if (strstr (*transports, "%%i1"))
nr_int = 1;
else
nr_int = 0;
if (strstr (*transports, "%%u2"))
nr_udp = 2;
else if (strstr (*transports, "%%u1"))
nr_udp = 1;
else
nr_udp = 0;
if (nr_udp == 0 && nr_int == 0)
goto done;
if (nr_udp > 0) {
if (!gst_rtspsrc_alloc_udp_ports (stream, &rtpport, &rtcpport))
goto failed;
}
str = g_string_new ("");
p = *transports;
while ((next = strstr (p, "%%"))) {
g_string_append_len (str, p, next - p);
if (next[2] == 'u') {
if (next[3] == '1')
g_string_append_printf (str, "%d", rtpport);
else if (next[3] == '2')
g_string_append_printf (str, "%d", rtcpport);
}
if (next[2] == 'i') {
if (next[3] == '1')
g_string_append_printf (str, "%d", src->free_channel);
else if (next[3] == '2')
g_string_append_printf (str, "%d", src->free_channel + 1);
}
p = next + 4;
}
g_free (*transports);
*transports = g_string_free (str, FALSE);
done:
return RTSP_OK;
/* ERRORS */
failed:
{
return RTSP_ERROR;
}
}
static gboolean
gst_rtspsrc_open (GstRTSPSrc * src)
{
@ -1321,7 +1560,7 @@ gst_rtspsrc_open (GstRTSPSrc * src)
guint size;
gint i, n_streams;
SDPMessage sdp = { 0 };
GstRTSPProto protocols;
RTSPLowerTrans protocols;
GstRTSPStream *stream = NULL;
gchar *respcont = NULL;
@ -1396,8 +1635,8 @@ gst_rtspsrc_open (GstRTSPSrc * src)
if (src->extension && src->extension->parse_sdp)
src->extension->parse_sdp (src->extension, &sdp);
/* we initially allow all configured protocols. based on the replies from the
* server we narrow them down. */
/* we initially allow all configured lower transports. based on the
* replies from the server we narrow them down. */
protocols = src->protocols;
/* setup streams */
@ -1440,54 +1679,15 @@ gst_rtspsrc_open (GstRTSPSrc * src)
if (res < 0)
goto create_request_failed;
transports = g_strdup ("");
if (protocols & GST_RTSP_PROTO_UDP_UNICAST) {
gchar *new;
gint rtpport, rtcpport;
gchar *trxparams;
/* create a string with all the transports */
res = gst_rtspsrc_create_transports_string (src, protocols, &transports);
if (res < 0)
goto setup_transport_failed;
/* allocate two UDP ports */
if (!gst_rtspsrc_stream_setup_rtp (stream, &rtpport, &rtcpport))
goto setup_rtp_failed;
GST_DEBUG_OBJECT (src, "setting up RTP ports %d-%d", rtpport, rtcpport);
trxparams = g_strdup_printf ("client_port=%d-%d", rtpport, rtcpport);
new = g_strconcat (transports, "RTP/AVP/UDP;unicast;", trxparams, NULL);
g_free (trxparams);
g_free (transports);
transports = new;
}
if (protocols & GST_RTSP_PROTO_UDP_MULTICAST) {
gchar *new;
GST_DEBUG_OBJECT (src, "setting up MULTICAST");
/* we don't hav to allocate any UDP ports yet, if the selected transport
* turns out to be multicast we can create them and join the multicast
* group indicated in the transport reply */
new =
g_strconcat (transports, transports[0] ? "," : "",
"RTP/AVP/UDP;multicast", NULL);
g_free (transports);
transports = new;
}
if (protocols & GST_RTSP_PROTO_TCP) {
gchar *new, *interleaved;
gint channel;
GST_DEBUG_OBJECT (src, "setting up TCP");
/* the channels for this stream is by default the next available number */
channel = i * 2;
interleaved = g_strdup_printf ("interleaved=%d-%d", channel, channel + 1);
new =
g_strconcat (transports, transports[0] ? "," : "",
"RTP/AVP/TCP;unicast;", interleaved, NULL);
g_free (interleaved);
g_free (transports);
transports = new;
}
/* 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);
@ -1506,7 +1706,8 @@ gst_rtspsrc_open (GstRTSPSrc * src)
goto no_transport;
/* parse transport */
rtsp_transport_parse (resptrans, &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
@ -1514,18 +1715,24 @@ gst_rtspsrc_open (GstRTSPSrc * src)
switch (transport.lower_transport) {
case RTSP_LOWER_TRANS_TCP:
GST_DEBUG_OBJECT (src, "stream %d as TCP interleaved", i);
protocols = GST_RTSP_PROTO_TCP;
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 = GST_RTSP_PROTO_UDP_MULTICAST;
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 = GST_RTSP_PROTO_UDP_UNICAST;
protocols = RTSP_LOWER_TRANS_UDP;
break;
default:
GST_DEBUG_OBJECT (src, "stream %d unknown transport %d", i,
@ -1612,10 +1819,10 @@ wrong_content_type:
("Server does not support SDP, got %s.", respcont));
goto cleanup_error;
}
setup_rtp_failed:
setup_transport_failed:
{
GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL),
("Could not setup rtp."));
("Could not setup transport."));
goto cleanup_error;
}
no_transport:
@ -1843,7 +2050,8 @@ gst_rtspsrc_handle_message (GstBin * bin, GstMessage * message)
GST_DEBUG_OBJECT (bin, "timeout on UDP port");
gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_RECONNECT);
/* FIXME, we post an error message now to inform the user
* that nothing happened. It's most likely a firewall thing. */
* that nothing happened. It's most likely a firewall thing. Ideally we
* notify the thread and redo the setup with only TCP. */
GST_ELEMENT_ERROR (rtspsrc, RESOURCE, READ, (NULL),
("Could not receive any UDP packets for %.4f seconds, maybe your "
"firewall is blocking it.",
@ -1870,14 +2078,21 @@ gst_rtspsrc_change_state (GstElement * element, GstStateChange transition)
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
/* the first free channel for interleaved mode */
rtspsrc->free_channel = 0;
rtspsrc->interleaved = FALSE;
gst_segment_init (&rtspsrc->segment, GST_FORMAT_TIME);
if (!gst_rtspsrc_open (rtspsrc))
goto open_failed;
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
rtsp_connection_flush (rtspsrc->connection, FALSE);
gst_rtspsrc_play (rtspsrc);
break;
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
case GST_STATE_CHANGE_PAUSED_TO_READY:
rtsp_connection_flush (rtspsrc->connection, TRUE);
break;
default:
break;
}

View file

@ -72,21 +72,6 @@ typedef struct _GstRTSPSrcClass GstRTSPSrcClass;
#define GST_RTSP_LOOP_WAIT(rtsp) (g_cond_wait(GST_RTSP_LOOP_GET_COND (rtsp), GST_OBJECT_GET_LOCK (rtsp)))
#define GST_RTSP_LOOP_SIGNAL(rtsp) (g_cond_signal(GST_RTSP_LOOP_GET_COND (rtsp)))
/**
* GstRTSPProto:
* @GST_RTSP_PROTO_UDP_UNICAST: Use unicast UDP transfer.
* @GST_RTSP_PROTO_UDP_MULTICAST: Use multicast UDP transfer
* @GST_RTSP_PROTO_TCP: Use TCP transfer.
*
* Flags with allowed protocols for the datatransfer.
*/
typedef enum
{
GST_RTSP_PROTO_UDP_UNICAST = (1 << 0),
GST_RTSP_PROTO_UDP_MULTICAST = (1 << 1),
GST_RTSP_PROTO_TCP = (1 << 2),
} GstRTSPProto;
typedef struct _GstRTSPStream GstRTSPStream;
struct _GstRTSPStream {
@ -99,21 +84,18 @@ struct _GstRTSPStream {
GstFlowReturn last_ret;
/* for interleaved mode */
gint rtpchannel;
gint rtcpchannel;
gint channel[2];
GstCaps *caps;
GstPad *channelpad[2];
/* our udp sources for RTP */
GstElement *rtpsrc;
GstElement *rtcpsrc;
/* our udp sources */
GstElement *udpsrc[2];
/* our udp sink back to the server */
GstElement *rtcpsink;
GstElement *udpsink;
/* the RTP decoder */
GstElement *rtpdec;
GstPad *rtpdecrtp;
GstPad *rtpdecrtcp;
/* the session manager */
GstElement *sess;
/* state */
gint pt;
@ -132,6 +114,7 @@ struct _GstRTSPSrc {
GStaticRecMutex *stream_rec_lock;
GstSegment segment;
gboolean running;
gint free_channel;
/* cond to signal loop */
GCond *loop_cond;
@ -144,7 +127,7 @@ struct _GstRTSPSrc {
gchar *location;
RTSPUrl *url;
gchar *content_base;
GstRTSPProto protocols;
RTSPLowerTrans protocols;
gboolean debug;
guint retry;
guint64 timeout;

View file

@ -753,3 +753,25 @@ rtsp_connection_free (RTSPConnection * conn)
return RTSP_OK;
}
RTSPResult
rtsp_connection_flush (RTSPConnection * conn, gboolean flush)
{
g_return_val_if_fail (conn != NULL, RTSP_EINVAL);
if (flush) {
SEND_COMMAND (conn, CONTROL_STOP);
} else {
while (TRUE) {
gchar command;
int res;
READ_COMMAND (conn, command, res);
if (res <= 0) {
/* no more commands */
break;
}
}
}
return RTSP_OK;
}

View file

@ -51,6 +51,7 @@ extern int h_errno;
static const gchar *rtsp_results[] = {
"OK",
/* errors */
"Generic error",
"Invalid parameter specified",
"Operation interrupted",
"Out of memory",

View file

@ -53,20 +53,21 @@ if (G_UNLIKELY ((res = (stmt)) != RTSP_OK)) goto label
typedef enum {
RTSP_OK = 0,
/* errors */
RTSP_EINVAL = -1,
RTSP_EINTR = -2,
RTSP_ENOMEM = -3,
RTSP_ERESOLV = -4,
RTSP_ENOTIMPL = -5,
RTSP_ESYS = -6,
RTSP_EPARSE = -7,
RTSP_EWSASTART = -8,
RTSP_EWSAVERSION = -9,
RTSP_EEOF = -10,
RTSP_ENET = -11,
RTSP_ENOTIP = -12,
RTSP_ERROR = -1,
RTSP_EINVAL = -2,
RTSP_EINTR = -3,
RTSP_ENOMEM = -4,
RTSP_ERESOLV = -5,
RTSP_ENOTIMPL = -6,
RTSP_ESYS = -7,
RTSP_EPARSE = -8,
RTSP_EWSASTART = -9,
RTSP_EWSAVERSION = -10,
RTSP_EEOF = -11,
RTSP_ENET = -12,
RTSP_ENOTIP = -13,
RTSP_ELAST = -13,
RTSP_ELAST = -14,
} RTSPResult;
typedef enum {

View file

@ -45,6 +45,7 @@
#include <glib.h>
#include "rtsptransport.h"
#include "sdp.h"
G_BEGIN_DECLS
@ -65,6 +66,8 @@ struct _RTSPExtensionCtx
RTSPResult (*parse_sdp) (RTSPExtensionCtx *ctx, SDPMessage *sdp);
RTSPResult (*setup_media) (RTSPExtensionCtx *ctx, SDPMedia *media);
RTSPResult (*get_transports) (RTSPExtensionCtx *ctx, RTSPLowerTrans protocols, gchar **transport);
RTSPResult (*stream_select) (RTSPExtensionCtx *ctx);
};

View file

@ -45,22 +45,72 @@
#include "gstrtspsrc.h"
#include "rtspextwms.h"
#define SERVER_PREFIX "WMServer/"
#define HEADER_PREFIX "data:application/vnd.ms.wms-hdr.asfv1;base64,"
typedef struct _RTSPExtWMSCtx RTSPExtWMSCtx;
struct _RTSPExtWMSCtx
{
RTSPExtensionCtx ctx;
gboolean active;
};
#define HEADER_PREFIX "data:application/vnd.ms.wms-hdr.asfv1;base64,"
static RTSPResult
rtsp_ext_wms_before_send (RTSPExtensionCtx * ctx, RTSPMessage * request)
{
RTSPExtWMSCtx *rext = (RTSPExtWMSCtx *) ctx;
switch (request->type_data.request.method) {
case RTSP_OPTIONS:
{
/* activate ourselves with the first request */
rext->active = TRUE;
break;
}
default:
break;
}
return RTSP_OK;
}
static RTSPResult
rtsp_ext_wms_after_send (RTSPExtensionCtx * ctx, RTSPMessage * req,
RTSPMessage * resp)
{
RTSPExtWMSCtx *rext = (RTSPExtWMSCtx *) ctx;
switch (req->type_data.request.method) {
case RTSP_OPTIONS:
{
gchar *server = NULL;
rtsp_message_get_header (resp, RTSP_HDR_SERVER, &server);
if (g_str_has_prefix (server, SERVER_PREFIX))
rext->active = TRUE;
else
rext->active = FALSE;
break;
}
default:
break;
}
return RTSP_OK;
}
static RTSPResult
rtsp_ext_wms_parse_sdp (RTSPExtensionCtx * ctx, SDPMessage * sdp)
{
GstRTSPSrc *src = (GstRTSPSrc *) ctx->src;
RTSPExtWMSCtx *rext = (RTSPExtWMSCtx *) ctx;
gchar *config, *maxps;
gint i;
if (!rext->active)
return RTSP_OK;
for (i = 0; (config = sdp_message_get_attribute_val_n (sdp, "pgmpu", i)); i++) {
if (g_str_has_prefix (config, HEADER_PREFIX)) {
config += strlen (HEADER_PREFIX);
@ -86,9 +136,9 @@ rtsp_ext_wms_parse_sdp (RTSPExtensionCtx * ctx, SDPMessage * sdp)
/* ERRORS */
no_config:
{
GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
("Could not find config SDP field."));
return RTSP_ENOTIMPL;
GST_DEBUG_OBJECT (src, "Could not find config SDP field, deactivating.");
rext->active = FALSE;
return RTSP_OK;
}
}
@ -99,6 +149,8 @@ rtsp_ext_wms_get_context (void)
res = g_new0 (RTSPExtWMSCtx, 1);
res->ctx.parse_sdp = rtsp_ext_wms_parse_sdp;
res->ctx.before_send = rtsp_ext_wms_before_send;
res->ctx.after_send = rtsp_ext_wms_after_send;
return (RTSPExtensionCtx *) res;
}

View file

@ -50,13 +50,14 @@ typedef struct
const gchar *name;
const RTSPTransMode mode;
const gchar *gst_mime;
const gchar *manager;
} RTSPTransMap;
static const RTSPTransMap transports[] = {
{"rtp", RTSP_TRANS_RTP, "application/x-rtp"},
{"x-real-rdt", RTSP_TRANS_RDT, "application/x-rdt"},
{"x-pn-tng", RTSP_TRANS_RDT, "application/x-rdt"},
{NULL, RTSP_TRANS_UNKNOWN, "application/x-unknown"}
{"rtp", RTSP_TRANS_RTP, "application/x-rtp", "rtpdec"},
{"x-real-rdt", RTSP_TRANS_RDT, "application/x-rdt", NULL},
{"x-pn-tng", RTSP_TRANS_RDT, "application/x-rdt", NULL},
{NULL, RTSP_TRANS_UNKNOWN, NULL, NULL}
};
typedef struct
@ -118,6 +119,36 @@ rtsp_transport_init (RTSPTransport * transport)
return RTSP_OK;
}
RTSPResult
rtsp_transport_get_mime (RTSPTransMode trans, const gchar ** mime)
{
gint i;
g_return_val_if_fail (mime != NULL, RTSP_EINVAL);
for (i = 0; transports[i].name; i++)
if (transports[i].mode == trans)
break;
*mime = transports[i].gst_mime;
return RTSP_OK;
}
RTSPResult
rtsp_transport_get_manager (RTSPTransMode trans, const gchar ** manager)
{
gint i;
g_return_val_if_fail (manager != NULL, RTSP_EINVAL);
for (i = 0; transports[i].name; i++)
if (transports[i].mode == trans)
break;
*manager = transports[i].manager;
return RTSP_OK;
}
static void
parse_mode (RTSPTransport * transport, gchar * str)
{
@ -163,11 +194,11 @@ rtsp_transport_parse (const gchar * str, RTSPTransport * transport)
if (strstr (split[0], transports[i].name))
break;
transport->trans = transports[i].mode;
for (i = 1; profiles[i].name; i++)
for (i = 0; profiles[i].name; i++)
if (strstr (split[0], profiles[i].name))
break;
transport->profile = profiles[i].profile;
for (i = 1; ltrans[i].name; i++)
for (i = 0; ltrans[i].name; i++)
if (strstr (split[0], ltrans[i].name))
break;
transport->lower_transport = ltrans[i].ltrans;

View file

@ -85,7 +85,7 @@ typedef struct _RTSPTransport {
gboolean append;
RTSPRange interleaved;
/* mulitcast specific */
/* multicast specific */
gint ttl;
/* UDP specific */
@ -97,12 +97,15 @@ typedef struct _RTSPTransport {
} RTSPTransport;
RTSPResult rtsp_transport_new (RTSPTransport **transport);
RTSPResult rtsp_transport_init (RTSPTransport *transport);
RTSPResult rtsp_transport_new (RTSPTransport **transport);
RTSPResult rtsp_transport_init (RTSPTransport *transport);
RTSPResult rtsp_transport_parse (const gchar *str, RTSPTransport *transport);
RTSPResult rtsp_transport_parse (const gchar *str, RTSPTransport *transport);
RTSPResult rtsp_transport_free (RTSPTransport *transport);
RTSPResult rtsp_transport_get_mime (RTSPTransMode trans, const gchar **mime);
RTSPResult rtsp_transport_get_manager (RTSPTransMode trans, const gchar **manager);
RTSPResult rtsp_transport_free (RTSPTransport *transport);
G_END_DECLS