rtspstream: handle both ipv4 and ipv6 clients

Fixes https://bugzilla.gnome.org/show_bug.cgi?id=701129
This commit is contained in:
Alexander Schrab 2013-05-30 08:07:48 +02:00 committed by Wim Taymans
parent 17b07d1c0e
commit 3e119be829
3 changed files with 116 additions and 49 deletions

View file

@ -1133,6 +1133,8 @@ make_server_transport (GstRTSPClient * client, GstRTSPClientState * state,
GstRTSPTransport * ct)
{
GstRTSPTransport *st;
GInetAddress *addr;
GSocketFamily family;
/* prepare the server transport */
gst_rtsp_transport_new (&st);
@ -1141,10 +1143,22 @@ make_server_transport (GstRTSPClient * client, GstRTSPClientState * state,
st->profile = ct->profile;
st->lower_transport = ct->lower_transport;
addr = g_inet_address_new_from_string (ct->destination);
if (!addr) {
GST_ERROR ("failed to get inet addr from client destination");
family = G_SOCKET_FAMILY_IPV4;
} else {
family = g_inet_address_get_family (addr);
g_object_unref (addr);
addr = NULL;
}
switch (st->lower_transport) {
case GST_RTSP_LOWER_TRANS_UDP:
st->client_port = ct->client_port;
gst_rtsp_stream_get_server_port (state->stream, &st->server_port);
gst_rtsp_stream_get_server_port (state->stream, &st->server_port,
family);
break;
case GST_RTSP_LOWER_TRANS_UDP_MCAST:
st->port = ct->port;

View file

@ -36,7 +36,6 @@ struct _GstRTSPStreamPrivate
guint idx;
GstPad *srcpad;
GstElement *payloader;
gboolean is_ipv6;
guint buffer_size;
gboolean is_joined;
@ -48,10 +47,16 @@ struct _GstRTSPStreamPrivate
/* the RTPSession object */
GObject *session;
/* sinks used for sending and receiving RTP and RTCP, they share
/* sinks used for sending and receiving RTP and RTCP over ipv4, they share
* sockets */
GstElement *udpsrc[2];
GstElement *udpsrc_v4[2];
/* sinks used for sending and receiving RTP and RTCP over ipv6, they share
* sockets */
GstElement *udpsrc_v6[2];
GstElement *udpsink[2];
/* for TCP transport */
GstElement *appsrc[2];
GstElement *appqueue[2];
@ -60,9 +65,13 @@ struct _GstRTSPStreamPrivate
GstElement *tee[2];
GstElement *funnel[2];
/* server ports for sending/receiving */
GstRTSPRange server_port;
GstRTSPAddress *server_addr;
/* server ports for sending/receiving over ipv4 */
GstRTSPRange server_port_v4;
GstRTSPAddress *server_addr_v4;
/* server ports for sending/receiving over ipv6 */
GstRTSPRange server_port_v6;
GstRTSPAddress *server_addr_v6;
/* multicast addresses */
GstRTSPAddressPool *pool;
@ -137,8 +146,10 @@ gst_rtsp_stream_finalize (GObject * obj)
if (priv->addr)
gst_rtsp_address_free (priv->addr);
if (priv->server_addr)
gst_rtsp_address_free (priv->server_addr);
if (priv->server_addr_v4)
gst_rtsp_address_free (priv->server_addr_v4);
if (priv->server_addr_v6)
gst_rtsp_address_free (priv->server_addr_v6);
if (priv->pool)
g_object_unref (priv->pool);
gst_object_unref (priv->payloader);
@ -428,11 +439,12 @@ different_address:
}
}
/* must be called with lock */
static gboolean
alloc_ports (GstRTSPStream * stream)
alloc_ports_one_family (GstRTSPAddressPool * pool, gint buffer_size,
GSocketFamily family, GstElement * udpsrc_out[2],
GstElement * udpsink_out[2], GstRTSPRange * server_port_out,
GstRTSPAddress ** server_addr_out)
{
GstRTSPStreamPrivate *priv = stream->priv;
GstStateChangeReturn ret;
GstElement *udpsrc0, *udpsrc1;
GstElement *udpsink0, *udpsink1;
@ -443,10 +455,14 @@ alloc_ports (GstRTSPStream * stream)
gint rtpport, rtcpport;
GList *rejected_addresses = NULL;
GstRTSPAddress *addr = NULL;
GSocketFamily family;
GInetAddress *inetaddr = NULL;
GSocketAddress *rtp_sockaddr = NULL;
GSocketAddress *rtcp_sockaddr = NULL;
const gchar *multisink_socket = "socket";
if (family == G_SOCKET_FAMILY_IPV6) {
multisink_socket = "socket-v6";
}
udpsrc0 = NULL;
udpsrc1 = NULL;
@ -457,19 +473,13 @@ alloc_ports (GstRTSPStream * stream)
/* Start with random port */
tmp_rtp = 0;
if (priv->is_ipv6) {
family = G_SOCKET_FAMILY_IPV6;
} else {
family = G_SOCKET_FAMILY_IPV4;
}
rtcp_socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM,
G_SOCKET_PROTOCOL_UDP, NULL);
if (!rtcp_socket)
goto no_udp_protocol;
if (priv->server_addr)
gst_rtsp_address_free (priv->server_addr);
if (*server_addr_out)
gst_rtsp_address_free (*server_addr_out);
/* try to allocate 2 UDP ports, the RTP port should be an even
* number and the RTCP port should be the next (uneven) port */
@ -482,19 +492,19 @@ again:
goto no_udp_protocol;
}
if (priv->pool && gst_rtsp_address_pool_has_unicast_addresses (priv->pool)) {
if (pool && gst_rtsp_address_pool_has_unicast_addresses (pool)) {
GstRTSPAddressFlags flags;
if (addr)
rejected_addresses = g_list_prepend (rejected_addresses, addr);
flags = GST_RTSP_ADDRESS_FLAG_EVEN_PORT | GST_RTSP_ADDRESS_FLAG_UNICAST;
if (priv->is_ipv6)
if (family == G_SOCKET_FAMILY_IPV6)
flags |= GST_RTSP_ADDRESS_FLAG_IPV6;
else
flags |= GST_RTSP_ADDRESS_FLAG_IPV4;
addr = gst_rtsp_address_pool_acquire_address (priv->pool, flags, 2);
addr = gst_rtsp_address_pool_acquire_address (pool, flags, 2);
if (addr == NULL)
goto no_ports;
@ -576,23 +586,31 @@ again:
if (rtpport != tmp_rtp || rtcpport != tmp_rtcp)
goto port_error;
udpsink0 = gst_element_factory_make ("multiudpsink", NULL);
if (udpsink_out[0])
udpsink0 = udpsink_out[0];
else
udpsink0 = gst_element_factory_make ("multiudpsink", NULL);
if (!udpsink0)
goto no_udp_protocol;
g_object_set (G_OBJECT (udpsink0), "close-socket", FALSE, NULL);
g_object_set (G_OBJECT (udpsink0), "socket", rtp_socket, NULL);
g_object_set (G_OBJECT (udpsink0), multisink_socket, rtp_socket, NULL);
if (udpsink_out[1])
udpsink1 = udpsink_out[1];
else
udpsink1 = gst_element_factory_make ("multiudpsink", NULL);
udpsink1 = gst_element_factory_make ("multiudpsink", NULL);
if (!udpsink1)
goto no_udp_protocol;
g_object_set (G_OBJECT (udpsink0), "send-duplicates", FALSE, NULL);
g_object_set (G_OBJECT (udpsink1), "send-duplicates", FALSE, NULL);
g_object_set (G_OBJECT (udpsink0), "buffer-size", priv->buffer_size, NULL);
g_object_set (G_OBJECT (udpsink0), "buffer-size", buffer_size, NULL);
g_object_set (G_OBJECT (udpsink1), "close-socket", FALSE, NULL);
g_object_set (G_OBJECT (udpsink1), "socket", rtcp_socket, NULL);
g_object_set (G_OBJECT (udpsink1), multisink_socket, rtcp_socket, NULL);
g_object_set (G_OBJECT (udpsink1), "sync", FALSE, NULL);
g_object_set (G_OBJECT (udpsink1), "async", FALSE, NULL);
g_object_set (G_OBJECT (udpsink0), "auto-multicast", FALSE, NULL);
@ -602,14 +620,14 @@ again:
/* we keep these elements, we will further configure them when the
* client told us to really use the UDP ports. */
priv->udpsrc[0] = udpsrc0;
priv->udpsrc[1] = udpsrc1;
priv->udpsink[0] = udpsink0;
priv->udpsink[1] = udpsink1;
priv->server_port.min = rtpport;
priv->server_port.max = rtcpport;
udpsrc_out[0] = udpsrc0;
udpsrc_out[1] = udpsrc1;
udpsink_out[0] = udpsink0;
udpsink_out[1] = udpsink1;
server_port_out->min = rtpport;
server_port_out->max = rtcpport;
priv->server_addr = addr;
*server_addr_out = addr;
g_list_free_full (rejected_addresses, (GDestroyNotify) gst_rtsp_address_free);
g_object_unref (rtp_socket);
@ -670,6 +688,20 @@ cleanup:
}
}
/* must be called with lock */
static gboolean
alloc_ports (GstRTSPStream * stream)
{
GstRTSPStreamPrivate *priv = stream->priv;
return alloc_ports_one_family (priv->pool, priv->buffer_size,
G_SOCKET_FAMILY_IPV4, priv->udpsrc_v4, priv->udpsink,
&priv->server_port_v4, &priv->server_addr_v4) &&
alloc_ports_one_family (priv->pool, priv->buffer_size,
G_SOCKET_FAMILY_IPV6, priv->udpsrc_v6, priv->udpsink,
&priv->server_port_v6, &priv->server_addr_v6);
}
/**
* gst_rtsp_stream_get_server_port:
* @stream: a #GstRTSPStream
@ -680,7 +712,7 @@ cleanup:
*/
void
gst_rtsp_stream_get_server_port (GstRTSPStream * stream,
GstRTSPRange * server_port)
GstRTSPRange * server_port, GSocketFamily family)
{
GstRTSPStreamPrivate *priv;
@ -689,8 +721,13 @@ gst_rtsp_stream_get_server_port (GstRTSPStream * stream,
g_return_if_fail (priv->is_joined);
g_mutex_lock (&priv->lock);
if (server_port)
*server_port = priv->server_port;
if (family == G_SOCKET_FAMILY_IPV4) {
if (server_port)
*server_port = priv->server_port_v4;
} else {
if (server_port)
*server_port = priv->server_port_v6;
}
g_mutex_unlock (&priv->lock);
}
@ -1104,13 +1141,23 @@ gst_rtsp_stream_join_bin (GstRTSPStream * stream, GstBin * bin,
/* we set and keep these to playing so that they don't cause NO_PREROLL return
* values */
gst_element_set_state (priv->udpsrc[i], GST_STATE_PLAYING);
gst_element_set_locked_state (priv->udpsrc[i], TRUE);
gst_element_set_state (priv->udpsrc_v4[i], GST_STATE_PLAYING);
gst_element_set_state (priv->udpsrc_v6[i], GST_STATE_PLAYING);
gst_element_set_locked_state (priv->udpsrc_v4[i], TRUE);
gst_element_set_locked_state (priv->udpsrc_v6[i], TRUE);
/* add udpsrc */
gst_bin_add (bin, priv->udpsrc[i]);
/* and link to the funnel */
gst_bin_add (bin, priv->udpsrc_v4[i]);
gst_bin_add (bin, priv->udpsrc_v6[i]);
/* and link to the funnel v4 */
selpad = gst_element_get_request_pad (priv->funnel[i], "sink_%u");
pad = gst_element_get_static_pad (priv->udpsrc[i], "src");
pad = gst_element_get_static_pad (priv->udpsrc_v4[i], "src");
gst_pad_link (pad, selpad);
gst_object_unref (pad);
gst_object_unref (selpad);
/* and link to the funnel v6 */
selpad = gst_element_get_request_pad (priv->funnel[i], "sink_%u");
pad = gst_element_get_static_pad (priv->udpsrc_v6[i], "src");
gst_pad_link (pad, selpad);
gst_object_unref (pad);
gst_object_unref (selpad);
@ -1213,12 +1260,15 @@ gst_rtsp_stream_leave_bin (GstRTSPStream * stream, GstBin * bin,
gst_element_set_state (priv->funnel[i], GST_STATE_NULL);
gst_element_set_state (priv->appsrc[i], GST_STATE_NULL);
/* and set udpsrc to NULL now before removing */
gst_element_set_locked_state (priv->udpsrc[i], FALSE);
gst_element_set_state (priv->udpsrc[i], GST_STATE_NULL);
gst_element_set_locked_state (priv->udpsrc_v4[i], FALSE);
gst_element_set_state (priv->udpsrc_v4[i], GST_STATE_NULL);
gst_element_set_locked_state (priv->udpsrc_v6[i], FALSE);
gst_element_set_state (priv->udpsrc_v6[i], GST_STATE_NULL);
/* removing them should also nicely release the request
* pads when they finalize */
gst_bin_remove (bin, priv->udpsrc[i]);
gst_bin_remove (bin, priv->udpsrc_v4[i]);
gst_bin_remove (bin, priv->udpsrc_v6[i]);
gst_bin_remove (bin, priv->udpsink[i]);
gst_bin_remove (bin, priv->appsrc[i]);
gst_bin_remove (bin, priv->appsink[i]);
@ -1230,7 +1280,8 @@ gst_rtsp_stream_leave_bin (GstRTSPStream * stream, GstBin * bin,
gst_object_unref (priv->recv_sink[i]);
priv->recv_sink[i] = NULL;
priv->udpsrc[i] = NULL;
priv->udpsrc_v4[i] = NULL;
priv->udpsrc_v6[i] = NULL;
priv->udpsink[i] = NULL;
priv->appsrc[i] = NULL;
priv->appsink[i] = NULL;

View file

@ -20,6 +20,7 @@
#include <gst/gst.h>
#include <gst/rtsp/gstrtsprange.h>
#include <gst/rtsp/gstrtspurl.h>
#include <gio/gio.h>
#ifndef __GST_RTSP_STREAM_H__
#define __GST_RTSP_STREAM_H__
@ -88,7 +89,8 @@ gboolean gst_rtsp_stream_leave_bin (GstRTSPStream *stream,
GstBin *bin, GstElement *rtpbin);
void gst_rtsp_stream_get_server_port (GstRTSPStream *stream,
GstRTSPRange *server_port);
GstRTSPRange *server_port,
GSocketFamily family);
void gst_rtsp_stream_get_ssrc (GstRTSPStream *stream,
guint *ssrc);