diff options
Diffstat (limited to 'gst')
-rw-r--r-- | gst/rtsp/gstrtspsrc.c | 587 | ||||
-rw-r--r-- | gst/rtsp/gstrtspsrc.h | 35 | ||||
-rw-r--r-- | gst/rtsp/rtspconnection.c | 22 | ||||
-rw-r--r-- | gst/rtsp/rtspdefs.c | 1 | ||||
-rw-r--r-- | gst/rtsp/rtspdefs.h | 29 | ||||
-rw-r--r-- | gst/rtsp/rtspext.h | 3 | ||||
-rw-r--r-- | gst/rtsp/rtspextwms.c | 60 | ||||
-rw-r--r-- | gst/rtsp/rtsptransport.c | 43 | ||||
-rw-r--r-- | gst/rtsp/rtsptransport.h | 13 |
9 files changed, 552 insertions, 241 deletions
diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index 0ea88313..70c6aa07 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -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); + + s = gst_caps_get_structure (stream->caps, 0); + + 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_structure_set_name (s, mime); + + if ((res = rtsp_transport_get_manager (transport->trans, &manager)) < 0) + goto no_manager; - /* FIXME, the session manager needs to be shared with all the streams */ - if (!(stream->rtpdec = gst_element_factory_make ("rtpdec", NULL))) - goto no_element; + 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->rtpdec); + /* we manage this element */ + gst_bin_add (GST_BIN_CAST (src), stream->sess); - ret = gst_element_set_state (stream->rtpdec, GST_STATE_PAUSED); - if (ret != GST_STATE_CHANGE_SUCCESS) - goto start_rtpdec_failure; + ret = gst_element_set_state (stream->sess, GST_STATE_PAUSED); + if (ret != GST_STATE_CHANGE_SUCCESS) + goto start_session_failure; - stream->rtpdecrtp = gst_element_get_pad (stream->rtpdec, "sinkrtp"); - stream->rtpdecrtcp = gst_element_get_pad (stream->rtpdec, "sinkrtcp"); + 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; - - /* 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; - - /* change state */ - gst_element_set_state (stream->rtpsrc, GST_STATE_PAUSED); - gst_element_set_state (stream->rtcpsrc, 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); + GST_DEBUG_OBJECT (src, "creating UDP sources for multicast"); + + /* 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->udpsrc[0], GST_STATE_PAUSED); + } + + /* 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"); - pad = gst_element_get_pad (stream->rtpdec, "srcrtp"); - if (stream->caps) { - gst_pad_use_fixed_caps (pad); - gst_pad_set_caps (pad, stream->caps); + /* 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); + } + } } - /* 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); + if (outpad && !stream->srcpad) { + GST_DEBUG_OBJECT (src, "creating ghostpad"); - gst_object_unref (pad); + 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); + } /* 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; - - /* 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); + /* create a string with all the transports */ + res = gst_rtspsrc_create_transports_string (src, protocols, &transports); + if (res < 0) + goto setup_transport_failed; - 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; } diff --git a/gst/rtsp/gstrtspsrc.h b/gst/rtsp/gstrtspsrc.h index 8a9c74fe..94b9ed3d 100644 --- a/gst/rtsp/gstrtspsrc.h +++ b/gst/rtsp/gstrtspsrc.h @@ -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; diff --git a/gst/rtsp/rtspconnection.c b/gst/rtsp/rtspconnection.c index e939a55d..27cc3ebb 100644 --- a/gst/rtsp/rtspconnection.c +++ b/gst/rtsp/rtspconnection.c @@ -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; +} diff --git a/gst/rtsp/rtspdefs.c b/gst/rtsp/rtspdefs.c index 706e5aad..0138d792 100644 --- a/gst/rtsp/rtspdefs.c +++ b/gst/rtsp/rtspdefs.c @@ -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", diff --git a/gst/rtsp/rtspdefs.h b/gst/rtsp/rtspdefs.h index 4067488c..229fc42a 100644 --- a/gst/rtsp/rtspdefs.h +++ b/gst/rtsp/rtspdefs.h @@ -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_ELAST = -13, + 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 = -14, } RTSPResult; typedef enum { diff --git a/gst/rtsp/rtspext.h b/gst/rtsp/rtspext.h index 779c109b..3f41f310 100644 --- a/gst/rtsp/rtspext.h +++ b/gst/rtsp/rtspext.h @@ -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); }; diff --git a/gst/rtsp/rtspextwms.c b/gst/rtsp/rtspextwms.c index 3769fcdb..81964e1e 100644 --- a/gst/rtsp/rtspextwms.c +++ b/gst/rtsp/rtspextwms.c @@ -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; } diff --git a/gst/rtsp/rtsptransport.c b/gst/rtsp/rtsptransport.c index f9848c48..e833080a 100644 --- a/gst/rtsp/rtsptransport.c +++ b/gst/rtsp/rtsptransport.c @@ -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; diff --git a/gst/rtsp/rtsptransport.h b/gst/rtsp/rtsptransport.h index 32a347b7..84c0996e 100644 --- a/gst/rtsp/rtsptransport.h +++ b/gst/rtsp/rtsptransport.h @@ -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 |