summaryrefslogtreecommitdiffstats
path: root/gst/rtsp/gstrtspsrc.c
diff options
context:
space:
mode:
authorEric Zhang <chao.zhang@access-company.com>2008-11-13 16:11:16 +0000
committerWim Taymans <wim.taymans@gmail.com>2008-11-13 16:11:16 +0000
commitbe3906c91892cb9ecf56782615b301762d67ea4f (patch)
treeb32e323a107849235cdc161b7f3d35a42afb8bdc /gst/rtsp/gstrtspsrc.c
parentbfb73112af20309fabbb56c2c9cd536e7a276662 (diff)
gst/rtsp/gstrtspsrc.*: Add property to configure NAT traversal method.
Original commit message from CVS: Based on patch by: Eric Zhang <chao.zhang at access-company dot com> * gst/rtsp/gstrtspsrc.c: (gst_rtsp_nat_method_get_type), (gst_rtspsrc_class_init), (gst_rtspsrc_set_property), (gst_rtspsrc_get_property), (gst_rtspsrc_create_stream), (gst_rtspsrc_stream_free), (gst_rtspsrc_stream_configure_udp_sinks), (gst_rtspsrc_stream_configure_transport), (gst_rtspsrc_send_dummy_packets), (gst_rtspsrc_create_transports_string), (gst_rtspsrc_handle_message), (gst_rtspsrc_change_state): * gst/rtsp/gstrtspsrc.h: Add property to configure NAT traversal method. Ignore EOS from the internal sinks. Implement sending dummy packets as a (simple) method to open up some firewalls. Send PLAY request to the server after we started the udp sources. Fixes #559545.
Diffstat (limited to 'gst/rtsp/gstrtspsrc.c')
-rw-r--r--gst/rtsp/gstrtspsrc.c268
1 files changed, 194 insertions, 74 deletions
diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c
index d31bee74..109d74df 100644
--- a/gst/rtsp/gstrtspsrc.c
+++ b/gst/rtsp/gstrtspsrc.c
@@ -152,6 +152,7 @@ enum
#define DEFAULT_TCP_TIMEOUT 20000000
#define DEFAULT_LATENCY_MS 3000
#define DEFAULT_CONNECTION_SPEED 0
+#define DEFAULT_NAT_METHOD GST_RTSP_NAT_DUMMY
enum
{
@@ -163,7 +164,9 @@ enum
PROP_TIMEOUT,
PROP_TCP_TIMEOUT,
PROP_LATENCY,
- PROP_CONNECTION_SPEED
+ PROP_CONNECTION_SPEED,
+ PROP_NAT_METHOD,
+ PROP_LAST
};
#define GST_TYPE_RTSP_LOWER_TRANS (gst_rtsp_lower_trans_get_type())
@@ -185,6 +188,24 @@ gst_rtsp_lower_trans_get_type (void)
return rtsp_lower_trans_type;
}
+#define GST_TYPE_RTSP_NAT_METHOD (gst_rtsp_nat_method_get_type())
+static GType
+gst_rtsp_nat_method_get_type (void)
+{
+ static GType rtsp_nat_method_type = 0;
+ static const GEnumValue rtsp_nat_method[] = {
+ {GST_RTSP_NAT_NONE, "None", "none"},
+ {GST_RTSP_NAT_DUMMY, "Send Dummy packets", "dummy"},
+ {0, NULL, NULL},
+ };
+
+ if (!rtsp_nat_method_type) {
+ rtsp_nat_method_type =
+ g_enum_register_static ("GstRTSPNatMethod", rtsp_nat_method);
+ }
+ return rtsp_nat_method_type;
+}
+
static void gst_rtspsrc_base_init (gpointer g_class);
static void gst_rtspsrc_finalize (GObject * object);
@@ -315,6 +336,12 @@ gst_rtspsrc_class_init (GstRTSPSrcClass * klass)
0, G_MAXINT / 1000, DEFAULT_CONNECTION_SPEED,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (gobject_class, PROP_NAT_METHOD,
+ g_param_spec_enum ("nat-method", "NAT Method",
+ "Method to use for traversing firewalls and NAT",
+ GST_TYPE_RTSP_NAT_METHOD, DEFAULT_NAT_METHOD,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
gstelement_class->change_state = gst_rtspsrc_change_state;
gstbin_class->handle_message = gst_rtspsrc_handle_message;
@@ -431,6 +458,9 @@ gst_rtspsrc_set_property (GObject * object, guint prop_id, const GValue * value,
case PROP_CONNECTION_SPEED:
rtspsrc->connection_speed = g_value_get_uint (value);
break;
+ case PROP_NAT_METHOD:
+ rtspsrc->nat_method = g_value_get_enum (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -476,6 +506,9 @@ gst_rtspsrc_get_property (GObject * object, guint prop_id, GValue * value,
case PROP_CONNECTION_SPEED:
g_value_set_uint (value, rtspsrc->connection_speed);
break;
+ case PROP_NAT_METHOD:
+ g_value_set_enum (value, rtspsrc->nat_method);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -656,7 +689,6 @@ gst_rtspsrc_create_stream (GstRTSPSrc * src, GstSDPMessage * sdp, gint idx)
* the RTP-Info header field returned from PLAY. */
control_url = gst_sdp_media_get_attribute_val (media, "control");
-
GST_DEBUG_OBJECT (src, "stream %d, (%p)", stream->id, stream);
GST_DEBUG_OBJECT (src, " pt: %d", stream->pt);
GST_DEBUG_OBJECT (src, " container: %d", stream->container);
@@ -703,19 +735,17 @@ gst_rtspsrc_stream_free (GstRTSPSrc * src, GstRTSPStream * stream)
g_free (stream->setup_url);
for (i = 0; i < 2; i++) {
- GstElement *udpsrc = stream->udpsrc[i];
-
- if (udpsrc) {
+ if (stream->udpsrc[i]) {
GstPad *pad;
/* unlink the pad */
- pad = gst_element_get_static_pad (udpsrc, "src");
+ pad = gst_element_get_static_pad (stream->udpsrc[i], "src");
if (stream->channelpad[i]) {
gst_pad_unlink (pad, stream->channelpad[i]);
}
- gst_element_set_state (udpsrc, GST_STATE_NULL);
- gst_bin_remove (GST_BIN_CAST (src), udpsrc);
+ gst_element_set_state (stream->udpsrc[i], GST_STATE_NULL);
+ gst_bin_remove (GST_BIN_CAST (src), stream->udpsrc[i]);
gst_object_unref (stream->udpsrc[i]);
stream->udpsrc[i] = NULL;
}
@@ -723,12 +753,18 @@ gst_rtspsrc_stream_free (GstRTSPSrc * src, GstRTSPStream * stream)
gst_object_unref (stream->channelpad[i]);
stream->channelpad[i] = NULL;
}
+ if (stream->udpsink[i]) {
+ gst_element_set_state (stream->udpsink[i], GST_STATE_NULL);
+ gst_bin_remove (GST_BIN_CAST (src), stream->udpsink[i]);
+ gst_object_unref (stream->udpsink[i]);
+ stream->udpsink[i] = NULL;
+ }
}
- if (stream->udpsink) {
- gst_element_set_state (stream->udpsink, GST_STATE_NULL);
- gst_bin_remove (GST_BIN_CAST (src), stream->udpsink);
- gst_object_unref (stream->udpsink);
- stream->udpsink = NULL;
+ if (stream->fakesrc) {
+ gst_element_set_state (stream->fakesrc, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN_CAST (src), stream->fakesrc);
+ gst_object_unref (stream->fakesrc);
+ stream->fakesrc = NULL;
}
if (stream->srcpad) {
gst_pad_set_active (stream->srcpad, FALSE);
@@ -2052,28 +2088,22 @@ gst_rtspsrc_stream_configure_udp (GstRTSPSrc * src, GstRTSPStream * stream,
/* configure the UDP sink back to the server for status reports */
static gboolean
-gst_rtspsrc_stream_configure_udp_sink (GstRTSPSrc * src, GstRTSPStream * stream,
- GstRTSPTransport * transport)
+gst_rtspsrc_stream_configure_udp_sinks (GstRTSPSrc * src,
+ GstRTSPStream * stream, GstRTSPTransport * transport)
{
GstPad *pad;
- gint port, sockfd = -1;
+ gint rtp_port, rtcp_port, sockfd = -1;
const gchar *destination;
gchar *uri, *name;
- /* no session, we're done */
- if (src->session == NULL)
- return TRUE;
-
/* get host and port */
- if (transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST)
- port = transport->port.max;
- else
- port = transport->server_port.max;
-
- /* it's possible that the server does not want us to send RTCP in which case
- * the port is -1 */
- if (port == -1)
- goto no_port;
+ if (transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
+ rtp_port = transport->port.min;
+ rtcp_port = transport->port.max;
+ } else {
+ rtp_port = transport->server_port.min;
+ rtcp_port = transport->server_port.max;
+ }
/* first take the source, then the endpoint to figure out where to send
* the RTCP. */
@@ -2081,51 +2111,108 @@ gst_rtspsrc_stream_configure_udp_sink (GstRTSPSrc * src, GstRTSPStream * stream,
if (destination == NULL)
destination = gst_rtsp_connection_get_ip (src->connection);
- GST_DEBUG_OBJECT (src, "configure UDP sink for %s:%d", destination, port);
+ /* try to construct the fakesrc to the RTP port of the server to open up any
+ * NAT firewalls */
+ if (rtp_port != -1) {
+ GST_DEBUG_OBJECT (src, "configure RTP UDP sink for %s:%d", destination,
+ rtp_port);
- uri = g_strdup_printf ("udp://%s:%d", destination, port);
- stream->udpsink = gst_element_make_from_uri (GST_URI_SINK, uri, NULL);
- g_free (uri);
- if (stream->udpsink == NULL)
- goto no_sink_element;
+ uri = g_strdup_printf ("udp://%s:%d", destination, rtp_port);
+ stream->udpsink[0] = gst_element_make_from_uri (GST_URI_SINK, uri, NULL);
+ g_free (uri);
+ if (stream->udpsink[0] == NULL)
+ goto no_sink_element;
- /* no sync needed */
- g_object_set (G_OBJECT (stream->udpsink), "sync", FALSE, NULL);
- /* no async state changes needed */
- g_object_set (G_OBJECT (stream->udpsink), "async", FALSE, NULL);
+ /* no sync or async state changes needed */
+ g_object_set (G_OBJECT (stream->udpsink[0]), "sync", FALSE, "async", FALSE,
+ NULL);
- if (stream->udpsrc[1]) {
- /* configure socket, we give it the same UDP socket as the udpsrc for RTCP
- * because some servers check the port number of where it sends RTCP to identify
- * the RTCP packets it receives */
- g_object_get (G_OBJECT (stream->udpsrc[1]), "sock", &sockfd, NULL);
- GST_DEBUG_OBJECT (src, "UDP src has sock %d", sockfd);
- /* configure socket and make sure udpsink does not close it when shutting
- * down, it belongs to udpsrc after all. */
- g_object_set (G_OBJECT (stream->udpsink), "sockfd", sockfd, NULL);
- g_object_set (G_OBJECT (stream->udpsink), "closefd", FALSE, NULL);
+ if (stream->udpsrc[0]) {
+ /* configure socket, we give it the same UDP socket as the udpsrc for RTP
+ * so that NAT firewalls will open a hole for us */
+ g_object_get (G_OBJECT (stream->udpsrc[0]), "sock", &sockfd, NULL);
+ GST_DEBUG_OBJECT (src, "RTP UDP src has sock %d", sockfd);
+ /* configure socket and make sure udpsink does not close it when shutting
+ * down, it belongs to udpsrc after all. */
+ g_object_set (G_OBJECT (stream->udpsink[0]), "sockfd", sockfd, NULL);
+ g_object_set (G_OBJECT (stream->udpsink[0]), "closefd", FALSE, NULL);
+ }
+
+ /* the source for the dummy packets to open up NAT */
+ stream->fakesrc = gst_element_factory_make ("fakesrc", NULL);
+ if (stream->fakesrc == NULL)
+ goto no_fakesrc_element;
+
+ /* random data in 5 buffers, a size of 200 bytes should be fine */
+ g_object_set (G_OBJECT (stream->fakesrc), "filltype", 3, "num-buffers", 5,
+ NULL);
+ g_object_set (G_OBJECT (stream->fakesrc), "sizetype", 2, "sizemax", 200,
+ NULL);
+
+ /* we don't want to consider this a sink */
+ GST_OBJECT_FLAG_UNSET (stream->udpsink[0], GST_ELEMENT_IS_SINK);
+
+ /* keep everything locked */
+ gst_element_set_locked_state (stream->udpsink[0], TRUE);
+ gst_element_set_locked_state (stream->fakesrc, TRUE);
+
+ gst_object_ref (stream->udpsink[0]);
+ gst_bin_add (GST_BIN_CAST (src), stream->udpsink[0]);
+ gst_object_ref (stream->fakesrc);
+ gst_bin_add (GST_BIN_CAST (src), stream->fakesrc);
+
+ gst_element_link (stream->fakesrc, stream->udpsink[0]);
}
+ /* it's possible that the server does not want us to send RTCP in which case
+ * the port is -1 */
+ if (rtcp_port != -1 && src->session != NULL) {
+ GST_DEBUG_OBJECT (src, "configure RTCP UDP sink for %s:%d", destination,
+ rtcp_port);
- /* we don't want to consider this a sink */
- GST_OBJECT_FLAG_UNSET (stream->udpsink, GST_ELEMENT_IS_SINK);
+ uri = g_strdup_printf ("udp://%s:%d", destination, rtcp_port);
+ stream->udpsink[1] = gst_element_make_from_uri (GST_URI_SINK, uri, NULL);
+ g_free (uri);
+ if (stream->udpsink[1] == NULL)
+ goto no_sink_element;
+
+ /* no sync needed */
+ g_object_set (G_OBJECT (stream->udpsink[1]), "sync", FALSE, NULL);
+ /* no async state changes needed */
+ g_object_set (G_OBJECT (stream->udpsink[1]), "async", FALSE, NULL);
+
+ if (stream->udpsrc[1]) {
+ /* configure socket, we give it the same UDP socket as the udpsrc for RTCP
+ * because some servers check the port number of where it sends RTCP to identify
+ * the RTCP packets it receives */
+ g_object_get (G_OBJECT (stream->udpsrc[1]), "sock", &sockfd, NULL);
+ GST_DEBUG_OBJECT (src, "RTCP UDP src has sock %d", sockfd);
+ /* configure socket and make sure udpsink does not close it when shutting
+ * down, it belongs to udpsrc after all. */
+ g_object_set (G_OBJECT (stream->udpsink[1]), "sockfd", sockfd, NULL);
+ g_object_set (G_OBJECT (stream->udpsink[1]), "closefd", FALSE, NULL);
+ }
- /* we keep this playing always */
- gst_element_set_locked_state (stream->udpsink, TRUE);
- gst_element_set_state (stream->udpsink, GST_STATE_PLAYING);
+ /* we don't want to consider this a sink */
+ GST_OBJECT_FLAG_UNSET (stream->udpsink[1], GST_ELEMENT_IS_SINK);
- gst_object_ref (stream->udpsink);
- gst_bin_add (GST_BIN_CAST (src), stream->udpsink);
+ /* we keep this playing always */
+ gst_element_set_locked_state (stream->udpsink[1], TRUE);
+ gst_element_set_state (stream->udpsink[1], GST_STATE_PLAYING);
- stream->rtcppad = gst_element_get_static_pad (stream->udpsink, "sink");
+ gst_object_ref (stream->udpsink[1]);
+ gst_bin_add (GST_BIN_CAST (src), stream->udpsink[1]);
- /* get session RTCP pad */
- name = g_strdup_printf ("send_rtcp_src_%d", stream->id);
- pad = gst_element_get_request_pad (src->session, name);
- g_free (name);
+ stream->rtcppad = gst_element_get_static_pad (stream->udpsink[1], "sink");
+
+ /* get session RTCP pad */
+ name = g_strdup_printf ("send_rtcp_src_%d", stream->id);
+ pad = gst_element_get_request_pad (src->session, name);
+ g_free (name);
- /* and link */
- if (pad)
- gst_pad_link (pad, stream->rtcppad);
+ /* and link */
+ if (pad)
+ gst_pad_link (pad, stream->rtcppad);
+ }
return TRUE;
@@ -2135,10 +2222,10 @@ no_sink_element:
GST_DEBUG_OBJECT (src, "no UDP sink element found");
return FALSE;
}
-no_port:
+no_fakesrc_element:
{
- GST_DEBUG_OBJECT (src, "no valid port, ignoring RTCP for this stream");
- return TRUE;
+ GST_DEBUG_OBJECT (src, "no fakesrc element found");
+ return FALSE;
}
}
@@ -2192,8 +2279,9 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
case GST_RTSP_LOWER_TRANS_UDP:
if (!gst_rtspsrc_stream_configure_udp (src, stream, transport, &outpad))
goto transport_failed;
- /* configure udpsink back to the server for RTCP messages. */
- if (!gst_rtspsrc_stream_configure_udp_sink (src, stream, transport))
+ /* configure udpsinks back to the server for RTCP messages and for the
+ * dummy RTP messages to open NAT. */
+ if (!gst_rtspsrc_stream_configure_udp_sinks (src, stream, transport))
goto transport_failed;
break;
default:
@@ -2240,6 +2328,31 @@ no_manager:
}
}
+/* send a couple of dummy random packets on the receiver RTP port to the server,
+ * this should make a firewall think we initiated the data transfer and
+ * hopefully allow packets to go from the sender port to our RTP receiver port */
+static gboolean
+gst_rtspsrc_send_dummy_packets (GstRTSPSrc * src)
+{
+ GList *walk;
+
+ if (!src->nat_method != GST_RTSP_NAT_DUMMY)
+ return TRUE;
+
+ for (walk = src->streams; walk; walk = g_list_next (walk)) {
+ GstRTSPStream *stream = (GstRTSPStream *) walk->data;
+
+ if (stream->fakesrc && stream->udpsink[0]) {
+ GST_DEBUG_OBJECT (src, "sending dummy packet to stream %p", stream);
+ gst_element_set_state (stream->udpsink[0], GST_STATE_NULL);
+ gst_element_set_state (stream->fakesrc, GST_STATE_NULL);
+ gst_element_set_state (stream->udpsink[0], GST_STATE_PLAYING);
+ gst_element_set_state (stream->fakesrc, GST_STATE_PLAYING);
+ }
+ }
+ return TRUE;
+}
+
/* Adds the source pads of all configured streams to the element.
* This code is performed when we detected dataflow.
*
@@ -4698,6 +4811,9 @@ gst_rtspsrc_handle_message (GstBin * bin, GstMessage * message)
rtspsrc = GST_RTSPSRC (bin);
switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_EOS:
+ gst_message_unref (message);
+ break;
case GST_MESSAGE_ELEMENT:
{
const GstStructure *s = gst_message_get_structure (message);
@@ -4788,9 +4904,9 @@ gst_rtspsrc_change_state (GstElement * element, GstStateChange transition)
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
GST_DEBUG_OBJECT (rtspsrc, "PAUSED->PLAYING: stop connection flush");
gst_rtsp_connection_flush (rtspsrc->connection, FALSE);
- /* FIXME, the server might send UDP packets before we activate the UDP
- * ports */
- gst_rtspsrc_play (rtspsrc, &rtspsrc->segment);
+ /* send some dummy packets before we chain up to the parent to activate
+ * the receive in the udp sources */
+ gst_rtspsrc_send_dummy_packets (rtspsrc);
break;
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
case GST_STATE_CHANGE_PAUSED_TO_READY:
@@ -4806,13 +4922,17 @@ gst_rtspsrc_change_state (GstElement * element, GstStateChange transition)
goto done;
switch (transition) {
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- ret = GST_STATE_CHANGE_NO_PREROLL;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ /* chained up to parent so the udp sources are activated and receiving */
+ gst_rtspsrc_play (rtspsrc, &rtspsrc->segment);
break;
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
gst_rtspsrc_pause (rtspsrc);
ret = GST_STATE_CHANGE_NO_PREROLL;
break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ ret = GST_STATE_CHANGE_NO_PREROLL;
+ break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
gst_rtspsrc_close (rtspsrc);
break;