From 499e437e501215849d24cdaa157e0edf4de097d0 Mon Sep 17 00:00:00 2001 From: Patricia Muscalu Date: Wed, 25 Jul 2018 15:33:18 +0200 Subject: [PATCH] stream: Choose the maximum ttl value provided by multicast clients The maximum ttl value provided so far by the multicast clients will be chosen and reported in the response to the current client request. https://bugzilla.gnome.org/show_bug.cgi?id=793441 --- gst/rtsp-server/rtsp-client.c | 59 ++++++-- gst/rtsp-server/rtsp-stream.c | 291 ++++++++++++++++++++++++++++++++++---- gst/rtsp-server/rtsp-stream.h | 13 ++ tests/check/gst/client.c | 319 ++++++++++++++++++++++++++++++++++++++---- tests/check/gst/stream.c | 91 ++++++++++++ 5 files changed, 706 insertions(+), 67 deletions(-) diff --git a/gst/rtsp-server/rtsp-client.c b/gst/rtsp-server/rtsp-client.c index 8f39ccd..d4c5211 100644 --- a/gst/rtsp-server/rtsp-client.c +++ b/gst/rtsp-server/rtsp-client.c @@ -1972,8 +1972,13 @@ default_configure_client_transport (GstRTSPClient * client, if ((ct->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) && gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_TRANSPORT_CLIENT_SETTINGS) && - (ct->destination != NULL)) + (ct->destination != NULL)) { + + if (!gst_rtsp_stream_verify_mcast_ttl (ctx->stream, ct->ttl)) + goto error_ttl; + use_client_settings = TRUE; + } /* We need to allocate the sockets for both families before starting * multiudpsink, otherwise multiudpsink won't accept new clients with @@ -1992,14 +1997,29 @@ default_configure_client_transport (GstRTSPClient * client, goto error_allocating_ports; if (ct->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) { - /* FIXME: the address has been successfully allocated, however, in - * the use_client_settings case we need to verify that the allocated - * address is the one requested by the client and if this address is - * an allowed destination. Verifying this via the address pool in not - * the proper way as the address pool should only be used for choosing - * the server-selected address/port pairs. */ - - if (!use_client_settings) { + if (use_client_settings) { + /* FIXME: the address has been successfully allocated, however, in + * the use_client_settings case we need to verify that the allocated + * address is the one requested by the client and if this address is + * an allowed destination. Verifying this via the address pool in not + * the proper way as the address pool should only be used for choosing + * the server-selected address/port pairs. */ + GSocket *rtp_socket; + guint ttl; + + rtp_socket = + gst_rtsp_stream_get_rtp_multicast_socket (ctx->stream, family); + if (rtp_socket == NULL) + goto no_socket; + ttl = g_socket_get_multicast_ttl (rtp_socket); + g_object_unref (rtp_socket); + if (ct->ttl < ttl) { + /* use the maximum ttl that is requested by multicast clients */ + GST_DEBUG ("requested ttl %u, but keeping ttl %u", ct->ttl, ttl); + ct->ttl = ttl; + } + + } else { GstRTSPAddress *addr = NULL; g_free (ct->destination); @@ -2012,6 +2032,11 @@ default_configure_client_transport (GstRTSPClient * client, ct->ttl = addr->ttl; gst_rtsp_address_free (addr); } + + if (!gst_rtsp_stream_add_multicast_client_address (ctx->stream, + ct->destination, ct->port.min, ct->port.max, family)) + goto error_mcast_transport; + } else { GstRTSPUrl *url; @@ -2064,6 +2089,12 @@ default_configure_client_transport (GstRTSPClient * client, return TRUE; /* ERRORS */ +error_ttl: + { + GST_ERROR_OBJECT (client, + "Failed to allocate UDP ports: invalid ttl value"); + return FALSE; + } error_allocating_ports: { GST_ERROR_OBJECT (client, "Failed to allocate UDP ports"); @@ -2074,6 +2105,16 @@ no_address: GST_ERROR_OBJECT (client, "Failed to acquire address for stream"); return FALSE; } +no_socket: + { + GST_ERROR_OBJECT (client, "Failed to get UDP socket"); + return FALSE; + } +error_mcast_transport: + { + GST_ERROR_OBJECT (client, "Failed to add multicast client transport"); + return FALSE; + } } static GstRTSPTransport * diff --git a/gst/rtsp-server/rtsp-stream.c b/gst/rtsp-server/rtsp-stream.c index 0d17718..65f0f5d 100644 --- a/gst/rtsp-server/rtsp-stream.c +++ b/gst/rtsp-server/rtsp-stream.c @@ -110,6 +110,7 @@ struct _GstRTSPStreamPrivate GstElement *mcast_udpsink[2]; GSocket *mcast_socket_v4[2]; GSocket *mcast_socket_v6[2]; + GList *mcast_clients; /* for TCP transport */ GstElement *appsrc[2]; @@ -292,6 +293,24 @@ gst_rtsp_stream_init (GstRTSPStream * stream) (GDestroyNotify) gst_caps_unref); } +typedef struct _UdpClientAddrInfo UdpClientAddrInfo; + +struct _UdpClientAddrInfo +{ + gchar *address; + guint rtp_port; + guint add_count; /* how often this address has been added */ +}; + +static void +free_mcast_client (gpointer data) +{ + UdpClientAddrInfo *client = data; + + g_free (client->address); + g_free (client); +} + static void gst_rtsp_stream_finalize (GObject * obj) { @@ -338,6 +357,7 @@ gst_rtsp_stream_finalize (GObject * obj) } g_free (priv->multicast_iface); + g_list_free_full (priv->mcast_clients, (GDestroyNotify) free_mcast_client); gst_object_unref (priv->payloader); if (priv->srcpad) @@ -1497,6 +1517,12 @@ again: g_clear_object (&inetaddr); + if (multicast && (ct->ttl > 0) && (ct->ttl <= priv->max_mcast_ttl)) { + GST_DEBUG ("setting mcast ttl to %d", ct->ttl); + g_socket_set_multicast_ttl (rtp_socket, ct->ttl); + g_socket_set_multicast_ttl (rtcp_socket, ct->ttl); + } + socket_out[0] = rtp_socket; socket_out[1] = rtcp_socket; *server_addr_out = addr; @@ -1579,6 +1605,100 @@ cleanup: } } +/* must be called with lock */ +static gboolean +add_mcast_client_addr (GstRTSPStream * stream, const gchar * destination, + guint rtp_port, guint rtcp_port) +{ + GstRTSPStreamPrivate *priv; + GList *walk; + UdpClientAddrInfo *client; + GInetAddress *inet; + + priv = stream->priv; + + if (destination == NULL) + return FALSE; + + inet = g_inet_address_new_from_string (destination); + if (inet == NULL) + goto invalid_address; + + if (!g_inet_address_get_is_multicast (inet)) { + g_object_unref (inet); + goto invalid_address; + } + g_object_unref (inet); + + for (walk = priv->mcast_clients; walk; walk = g_list_next (walk)) { + UdpClientAddrInfo *cli = walk->data; + + if ((g_strcmp0 (cli->address, destination) == 0) && + (cli->rtp_port == rtp_port)) { + GST_DEBUG ("requested destination already exists: %s:%u-%u", + destination, rtp_port, rtcp_port); + cli->add_count++; + return TRUE; + } + } + + client = g_new0 (UdpClientAddrInfo, 1); + client->address = g_strdup (destination); + client->rtp_port = rtp_port; + client->add_count = 1; + priv->mcast_clients = g_list_prepend (priv->mcast_clients, client); + + GST_DEBUG ("added mcast client %s:%u-%u", destination, rtp_port, rtcp_port); + + return TRUE; + +invalid_address: + { + GST_WARNING_OBJECT (stream, "Multicast address is invalid: %s", + destination); + return FALSE; + } +} + +/* must be called with lock */ +static gboolean +remove_mcast_client_addr (GstRTSPStream * stream, const gchar * destination, + guint rtp_port, guint rtcp_port) +{ + GstRTSPStreamPrivate *priv; + GList *walk; + + priv = stream->priv; + + if (destination == NULL) + goto no_destination; + + for (walk = priv->mcast_clients; walk; walk = g_list_next (walk)) { + UdpClientAddrInfo *cli = walk->data; + + if ((g_strcmp0 (cli->address, destination) == 0) && + (cli->rtp_port == rtp_port)) { + cli->add_count--; + + if (!cli->add_count) { + priv->mcast_clients = g_list_remove (priv->mcast_clients, cli); + free_mcast_client (cli); + } + return TRUE; + } + } + + GST_WARNING_OBJECT (stream, "Address not found"); + return FALSE; + +no_destination: + { + GST_WARNING_OBJECT (stream, "No destination has been provided"); + return FALSE; + } +} + + /** * gst_rtsp_stream_allocate_udp_sockets: * @stream: a #GstRTSPStream @@ -2006,6 +2126,29 @@ gst_rtsp_stream_get_max_mcast_ttl (GstRTSPStream * stream) return ttl; } +/** + * gst_rtsp_stream_verify_mcast_ttl: + * @stream: a #GstRTSPStream + * @ttl: a requested multicast ttl + * + * Check if the requested multicast ttl value is allowed. + * + * Returns: TRUE if the requested ttl value is allowed. + * + */ +gboolean +gst_rtsp_stream_verify_mcast_ttl (GstRTSPStream * stream, guint ttl) +{ + gboolean res = FALSE; + + g_mutex_lock (&stream->priv->lock); + if ((ttl > 0) && (ttl <= stream->priv->max_mcast_ttl)) + res = TRUE; + g_mutex_unlock (&stream->priv->lock); + + return res; +} + /* executed from streaming thread */ static void caps_notify (GstPad * pad, GParamSpec * unused, GstRTSPStream * stream) @@ -3339,38 +3482,29 @@ udpsrc_error: } static gboolean -check_mcast_part_for_transport (GstRTSPStream * stream, - const GstRTSPTransport * tr) +check_mcast_client_addr (GstRTSPStream * stream, const GstRTSPTransport * tr) { GstRTSPStreamPrivate *priv = stream->priv; - GInetAddress *inetaddr; - GSocketFamily family; - GstRTSPAddress *mcast_addr; + GList *walk; - /* Check if it's a ipv4 or ipv6 transport */ - inetaddr = g_inet_address_new_from_string (tr->destination); - family = g_inet_address_get_family (inetaddr); - g_object_unref (inetaddr); + if (priv->mcast_clients == NULL) + goto no_addr; - /* Select fields corresponding to the family */ - if (family == G_SOCKET_FAMILY_IPV4) { - mcast_addr = priv->mcast_addr_v4; - } else { - mcast_addr = priv->mcast_addr_v6; - } + if (tr == NULL) + goto no_transport; - /* We support only one mcast group per family, make sure this transport - * matches it. */ - if (!mcast_addr) - goto no_addr; + if (tr->destination == NULL) + goto no_destination; - if (g_ascii_strcasecmp (tr->destination, mcast_addr->address) != 0 || - tr->port.min != mcast_addr->port || - tr->port.max != mcast_addr->port + mcast_addr->n_ports - 1 || - tr->ttl != mcast_addr->ttl) - goto wrong_addr; + for (walk = priv->mcast_clients; walk; walk = g_list_next (walk)) { + UdpClientAddrInfo *cli = walk->data; - return TRUE; + if ((g_strcmp0 (cli->address, tr->destination) == 0) && + (cli->rtp_port == tr->port.min)) + return TRUE; + } + + return FALSE; no_addr: { @@ -3378,7 +3512,13 @@ no_addr: "has been reserved"); return FALSE; } -wrong_addr: +no_transport: + { + GST_WARNING_OBJECT (stream, "Adding mcast transport, but no transport " + "has been provided"); + return FALSE; + } +no_destination: { GST_WARNING_OBJECT (stream, "Adding mcast transport, but it doesn't match " "the reserved address"); @@ -4055,10 +4195,11 @@ update_transport (GstRTSPStream * stream, GstRTSPStreamTransport * trans, if (add) { GST_INFO ("adding %s:%d-%d", dest, min, max); - if (!check_mcast_part_for_transport (stream, tr)) + if (!check_mcast_client_addr (stream, tr)) goto mcast_error; + add_client (priv->mcast_udpsink[0], priv->mcast_udpsink[1], dest, min, + max); - /* FIXME: Is it ok to set ttl-mc if media is shared? */ if (tr->ttl > 0) { GST_INFO ("setting ttl-mc %d", tr->ttl); if (priv->mcast_udpsink[0]) @@ -4068,11 +4209,12 @@ update_transport (GstRTSPStream * stream, GstRTSPStreamTransport * trans, g_object_set (G_OBJECT (priv->mcast_udpsink[1]), "ttl-mc", tr->ttl, NULL); } - add_client (priv->mcast_udpsink[0], priv->mcast_udpsink[1], dest, min, - max); priv->transports = g_list_prepend (priv->transports, trans); } else { GST_INFO ("removing %s:%d-%d", dest, min, max); + if (!remove_mcast_client_addr (stream, dest, min, max)) + GST_WARNING_OBJECT (stream, + "Failed to remove multicast address: %s:%d-%d", dest, min, max); remove_client (priv->mcast_udpsink[0], priv->mcast_udpsink[1], dest, min, max); priv->transports = g_list_remove (priv->transports, trans); @@ -4423,6 +4565,95 @@ gst_rtsp_stream_get_rtcp_multicast_socket (GstRTSPStream * stream, } /** + * gst_rtsp_stream_add_multicast_client_address: + * @stream: a #GstRTSPStream + * @destination: (transfer none): a multicast address to add + * @rtp_port: RTP port + * @rtcp_port: RTCP port + * @family: socket family + * + * Add multicast client address to stream. At this point, the sockets that + * will stream RTP and RTCP data to @destination are supposed to be + * allocated. + * + * Returns: %TRUE if @destination can be addedd and handled by @stream. + */ +gboolean +gst_rtsp_stream_add_multicast_client_address (GstRTSPStream * stream, + const gchar * destination, guint rtp_port, guint rtcp_port, + GSocketFamily family) +{ + GstRTSPStreamPrivate *priv; + + g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE); + g_return_val_if_fail (destination != NULL, FALSE); + + priv = stream->priv; + g_mutex_lock (&priv->lock); + if ((family == G_SOCKET_FAMILY_IPV4) && (priv->mcast_socket_v4[0] == NULL)) + goto socket_error; + else if ((family == G_SOCKET_FAMILY_IPV6) && + (priv->mcast_socket_v6[0] == NULL)) + goto socket_error; + + if (!add_mcast_client_addr (stream, destination, rtp_port, rtcp_port)) + goto add_addr_error; + g_mutex_unlock (&priv->lock); + + return TRUE; + +socket_error: + { + GST_WARNING_OBJECT (stream, + "Failed to add multicast address: no udp socket"); + g_mutex_unlock (&priv->lock); + return FALSE; + } +add_addr_error: + { + GST_WARNING_OBJECT (stream, + "Failed to add multicast address: invalid address"); + g_mutex_unlock (&priv->lock); + return FALSE; + } +} + +/** + * gst_rtsp_stream_get_multicast_client_addresses + * @stream: a #GstRTSPStream + * + * Get all multicast client addresses that RTP data will be sent to + * + * Returns: A comma separated list of host:port pairs with destinations + */ +gchar * +gst_rtsp_stream_get_multicast_client_addresses (GstRTSPStream * stream) +{ + GstRTSPStreamPrivate *priv; + GString *str; + GList *clients; + + g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL); + + priv = stream->priv; + str = g_string_new (""); + + g_mutex_lock (&priv->lock); + clients = priv->mcast_clients; + while (clients != NULL) { + UdpClientAddrInfo *client; + + client = (UdpClientAddrInfo *) clients->data; + clients = g_list_next (clients); + g_string_append_printf (str, "%s:%d%s", client->address, client->rtp_port, + (clients != NULL ? "," : "")); + } + g_mutex_unlock (&priv->lock); + + return g_string_free (str, FALSE); +} + +/** * gst_rtsp_stream_set_seqnum: * @stream: a #GstRTSPStream * @seqnum: a new sequence number diff --git a/gst/rtsp-server/rtsp-stream.h b/gst/rtsp-server/rtsp-stream.h index 1ee146d..0c6804d 100644 --- a/gst/rtsp-server/rtsp-stream.h +++ b/gst/rtsp-server/rtsp-stream.h @@ -234,6 +234,16 @@ GSocket * gst_rtsp_stream_get_rtcp_multicast_socket (GstRTSPStream *stre GSocketFamily family); GST_RTSP_SERVER_API +gboolean gst_rtsp_stream_add_multicast_client_address (GstRTSPStream * stream, + const gchar * destination, + guint rtp_port, + guint rtcp_port, + GSocketFamily family); + +GST_RTSP_SERVER_API +gchar * gst_rtsp_stream_get_multicast_client_addresses (GstRTSPStream * stream); + +GST_RTSP_SERVER_API gboolean gst_rtsp_stream_update_crypto (GstRTSPStream * stream, guint ssrc, GstCaps * crypto); @@ -299,6 +309,9 @@ GST_RTSP_SERVER_API guint gst_rtsp_stream_get_max_mcast_ttl (GstRTSPStream *stream); GST_RTSP_SERVER_API +gboolean gst_rtsp_stream_verify_mcast_ttl (GstRTSPStream *stream, guint ttl); + +GST_RTSP_SERVER_API gboolean gst_rtsp_stream_complete_stream (GstRTSPStream * stream, const GstRTSPTransport * transport); GST_RTSP_SERVER_API diff --git a/tests/check/gst/client.c b/tests/check/gst/client.c index d4fdd9e..d890b82 100644 --- a/tests/check/gst/client.c +++ b/tests/check/gst/client.c @@ -542,6 +542,35 @@ test_setup_response_200_multicast (GstRTSPClient * client, } static gboolean +test_setup_response_461 (GstRTSPClient * client, + GstRTSPMessage * response, gboolean close, gpointer user_data) +{ + GstRTSPStatusCode code; + const gchar *reason; + GstRTSPVersion version; + gchar *str; + + fail_unless (expected_transport == NULL); + + fail_unless (gst_rtsp_message_get_type (response) == + GST_RTSP_MESSAGE_RESPONSE); + + fail_unless (gst_rtsp_message_parse_response (response, &code, &reason, + &version) + == GST_RTSP_OK); + fail_unless (code == GST_RTSP_STS_UNSUPPORTED_TRANSPORT); + fail_unless (g_str_equal (reason, "Unsupported transport")); + fail_unless (version == GST_RTSP_VERSION_1_0); + + fail_unless (gst_rtsp_message_get_header (response, GST_RTSP_HDR_CSEQ, &str, + 0) == GST_RTSP_OK); + fail_unless (atoi (str) == cseq++); + + + return TRUE; +} + +static gboolean test_teardown_response_200 (GstRTSPClient * client, GstRTSPMessage * response, gboolean close, gpointer user_data) { @@ -584,7 +613,7 @@ send_teardown (GstRTSPClient * client) } static GstRTSPClient * -setup_multicast_client (void) +setup_multicast_client (guint max_ttl) { GstRTSPClient *client; GstRTSPSessionPool *session_pool; @@ -611,6 +640,7 @@ setup_multicast_client (void) "media.factory.construct", G_TYPE_BOOLEAN, TRUE, NULL); gst_rtsp_mount_points_add_factory (mount_points, "/test", factory); gst_rtsp_client_set_mount_points (client, mount_points); + gst_rtsp_media_factory_set_max_mcast_ttl (factory, max_ttl); thread_pool = gst_rtsp_thread_pool_new (); gst_rtsp_client_set_thread_pool (client, thread_pool); @@ -629,7 +659,7 @@ GST_START_TEST (test_client_multicast_transport_404) GstRTSPMessage request = { 0, }; gchar *str; - client = setup_multicast_client (); + client = setup_multicast_client (1); /* simple SETUP for non-existing url */ fail_unless (gst_rtsp_message_init_request (&request, GST_RTSP_SETUP, @@ -662,7 +692,7 @@ GST_START_TEST (test_client_multicast_transport) GstRTSPMessage request = { 0, }; gchar *str; - client = setup_multicast_client (); + client = setup_multicast_client (1); expected_session_timeout = 20; g_signal_connect (G_OBJECT (client), "new-session", @@ -699,7 +729,7 @@ GST_START_TEST (test_client_multicast_ignore_transport_specific) GstRTSPMessage request = { 0, }; gchar *str; - client = setup_multicast_client (); + client = setup_multicast_client (1); /* simple SETUP with a valid URI and multicast and a specific dest, * but ignore it */ @@ -735,7 +765,7 @@ multicast_transport_specific (void) GstRTSPSessionPool *session_pool; GstRTSPContext ctx = { NULL }; - client = setup_multicast_client (); + client = setup_multicast_client (1); ctx.client = client; ctx.auth = gst_rtsp_auth_new (); @@ -950,9 +980,12 @@ GST_START_TEST (test_client_sdp_with_no_bitrate_tags) GST_END_TEST; static void -mcast_transport_specific_two_clients (gboolean shared) +mcast_transport_two_clients (gboolean shared, const gchar * transport1, + const gchar * expected_transport1, const gchar * addr1, + const gchar * transport2, const gchar * expected_transport2, + const gchar * addr2) { - GstRTSPClient *client, *client2; + GstRTSPClient *client1, *client2; GstRTSPMessage request = { 0, }; gchar *str; GstRTSPSessionPool *session_pool; @@ -963,6 +996,7 @@ mcast_transport_specific_two_clients (gboolean shared) GstRTSPAddressPool *address_pool; GstRTSPThreadPool *thread_pool; gchar *session_id1; + gchar *client_addr = NULL; mount_points = gst_rtsp_mount_points_new (); factory = gst_rtsp_media_factory_new (); @@ -983,12 +1017,12 @@ mcast_transport_specific_two_clients (gboolean shared) thread_pool = gst_rtsp_thread_pool_new (); /* first multicast client with transport specific request */ - client = gst_rtsp_client_new (); - gst_rtsp_client_set_session_pool (client, session_pool); - gst_rtsp_client_set_mount_points (client, mount_points); - gst_rtsp_client_set_thread_pool (client, thread_pool); + client1 = gst_rtsp_client_new (); + gst_rtsp_client_set_session_pool (client1, session_pool); + gst_rtsp_client_set_mount_points (client1, mount_points); + gst_rtsp_client_set_thread_pool (client1, thread_pool); - ctx.client = client; + ctx.client = client1; ctx.auth = gst_rtsp_auth_new (); ctx.token = gst_rtsp_token_new (GST_RTSP_TOKEN_TRANSPORT_CLIENT_SETTINGS, @@ -996,20 +1030,18 @@ mcast_transport_specific_two_clients (gboolean shared) "user", NULL); gst_rtsp_context_push_current (&ctx); - expected_transport = "RTP/AVP;multicast;destination=233.252.0.1;" - "ttl=1;port=5000-5001;mode=\"PLAY\""; + expected_transport = expected_transport1; /* send SETUP request */ fail_unless (gst_rtsp_message_init_request (&request, GST_RTSP_SETUP, "rtsp://localhost/test/stream=0") == GST_RTSP_OK); str = g_strdup_printf ("%d", cseq); gst_rtsp_message_take_header (&request, GST_RTSP_HDR_CSEQ, str); - gst_rtsp_message_add_header (&request, GST_RTSP_HDR_TRANSPORT, - expected_transport); + gst_rtsp_message_add_header (&request, GST_RTSP_HDR_TRANSPORT, transport1); - gst_rtsp_client_set_send_func (client, test_setup_response_200_multicast, + gst_rtsp_client_set_send_func (client1, test_setup_response_200_multicast, NULL, NULL); - fail_unless (gst_rtsp_client_handle_message (client, + fail_unless (gst_rtsp_client_handle_message (client1, &request) == GST_RTSP_OK); gst_rtsp_message_unset (&request); expected_transport = NULL; @@ -1020,10 +1052,17 @@ mcast_transport_specific_two_clients (gboolean shared) str = g_strdup_printf ("%d", cseq); gst_rtsp_message_take_header (&request, GST_RTSP_HDR_CSEQ, str); gst_rtsp_message_add_header (&request, GST_RTSP_HDR_SESSION, session_id); - gst_rtsp_client_set_send_func (client, test_response_200, NULL, NULL); - fail_unless (gst_rtsp_client_handle_message (client, + gst_rtsp_client_set_send_func (client1, test_response_200, NULL, NULL); + fail_unless (gst_rtsp_client_handle_message (client1, &request) == GST_RTSP_OK); gst_rtsp_message_unset (&request); + + /* check address */ + client_addr = gst_rtsp_stream_get_multicast_client_addresses (ctx.stream); + fail_if (client_addr == NULL); + fail_unless (g_str_equal (client_addr, addr1)); + g_free (client_addr); + gst_rtsp_context_pop_current (&ctx); session_id1 = session_id; @@ -1042,16 +1081,14 @@ mcast_transport_specific_two_clients (gboolean shared) "user", NULL); gst_rtsp_context_push_current (&ctx2); - expected_transport = "RTP/AVP;multicast;destination=233.252.0.2;" - "ttl=1;port=5002-5003;mode=\"PLAY\""; + expected_transport = expected_transport2; /* send SETUP request */ fail_unless (gst_rtsp_message_init_request (&request, GST_RTSP_SETUP, "rtsp://localhost/test/stream=0") == GST_RTSP_OK); str = g_strdup_printf ("%d", cseq); gst_rtsp_message_take_header (&request, GST_RTSP_HDR_CSEQ, str); - gst_rtsp_message_add_header (&request, GST_RTSP_HDR_TRANSPORT, - expected_transport); + gst_rtsp_message_add_header (&request, GST_RTSP_HDR_TRANSPORT, transport2); gst_rtsp_client_set_send_func (client2, test_setup_response_200_multicast, NULL, NULL); @@ -1071,15 +1108,31 @@ mcast_transport_specific_two_clients (gboolean shared) &request) == GST_RTSP_OK); gst_rtsp_message_unset (&request); + /* check addresses */ + client_addr = gst_rtsp_stream_get_multicast_client_addresses (ctx2.stream); + fail_if (client_addr == NULL); + if (shared) { + if (g_str_equal (addr1, addr2)) { + fail_unless (g_str_equal (client_addr, addr1)); + } else { + gchar *addr_str = g_strdup_printf ("%s,%s", addr2, addr1); + fail_unless (g_str_equal (client_addr, addr_str)); + g_free (addr_str); + } + } else { + fail_unless (g_str_equal (client_addr, addr2)); + } + g_free (client_addr); + send_teardown (client2); gst_rtsp_context_pop_current (&ctx2); gst_rtsp_context_push_current (&ctx); session_id = session_id1; - send_teardown (client); + send_teardown (client1); gst_rtsp_context_pop_current (&ctx); - teardown_client (client); + teardown_client (client1); teardown_client (client2); g_object_unref (ctx.auth); g_object_unref (ctx2.auth); @@ -1095,7 +1148,19 @@ mcast_transport_specific_two_clients (gboolean shared) * CASE: media is shared */ GST_START_TEST (test_client_multicast_transport_specific_two_clients_shared_media) { - mcast_transport_specific_two_clients (TRUE); + const gchar *transport_client_1 = "RTP/AVP;multicast;destination=233.252.0.1;" + "ttl=1;port=5000-5001;mode=\"PLAY\""; + const gchar *expected_transport_1 = transport_client_1; + const gchar *addr_client_1 = "233.252.0.1:5000"; + + const gchar *transport_client_2 = "RTP/AVP;multicast;destination=233.252.0.2;" + "ttl=1;port=5002-5003;mode=\"PLAY\""; + const gchar *expected_transport_2 = transport_client_2; + const gchar *addr_client_2 = "233.252.0.2:5002"; + + mcast_transport_two_clients (TRUE, transport_client_1, + expected_transport_1, addr_client_1, transport_client_2, + expected_transport_2, addr_client_2); } GST_END_TEST; @@ -1104,7 +1169,195 @@ GST_END_TEST; * CASE: media is not shared */ GST_START_TEST (test_client_multicast_transport_specific_two_clients) { - mcast_transport_specific_two_clients (FALSE); + const gchar *transport_client_1 = "RTP/AVP;multicast;destination=233.252.0.1;" + "ttl=1;port=5000-5001;mode=\"PLAY\""; + const gchar *expected_transport_1 = transport_client_1; + const gchar *addr_client_1 = "233.252.0.1:5000"; + + const gchar *transport_client_2 = "RTP/AVP;multicast;destination=233.252.0.2;" + "ttl=1;port=5002-5003;mode=\"PLAY\""; + const gchar *expected_transport_2 = transport_client_2; + const gchar *addr_client_2 = "233.252.0.2:5002"; + + mcast_transport_two_clients (FALSE, transport_client_1, + expected_transport_1, addr_client_1, transport_client_2, + expected_transport_2, addr_client_2); +} + +GST_END_TEST; + +/* test if two multicast clients can choose the same transport settings. + * CASE: media is shared */ +GST_START_TEST + (test_client_multicast_transport_specific_two_clients_shared_media_same_transport) +{ + + const gchar *transport_client_1 = "RTP/AVP;multicast;destination=233.252.0.1;" + "ttl=1;port=5000-5001;mode=\"PLAY\""; + const gchar *expected_transport_1 = transport_client_1; + const gchar *addr_client_1 = "233.252.0.1:5000"; + + const gchar *transport_client_2 = transport_client_1; + const gchar *expected_transport_2 = expected_transport_1; + const gchar *addr_client_2 = addr_client_1; + + mcast_transport_two_clients (TRUE, transport_client_1, + expected_transport_1, addr_client_1, transport_client_2, + expected_transport_2, addr_client_2); +} + +GST_END_TEST; + +/* test if two multicast clients get the same transport settings without + * requesting specific transport. + * CASE: media is shared */ +GST_START_TEST (test_client_multicast_two_clients_shared_media) +{ + const gchar *transport_client_1 = "RTP/AVP;multicast;mode=\"PLAY\""; + const gchar *expected_transport_1 = + "RTP/AVP;multicast;destination=233.252.0.1;" + "ttl=1;port=5000-5001;mode=\"PLAY\""; + const gchar *addr_client_1 = "233.252.0.1:5000"; + + const gchar *transport_client_2 = transport_client_1; + const gchar *expected_transport_2 = expected_transport_1; + const gchar *addr_client_2 = addr_client_1; + + mcast_transport_two_clients (TRUE, transport_client_1, + expected_transport_1, addr_client_1, transport_client_2, + expected_transport_2, addr_client_2); +} + +GST_END_TEST; + +/* test if two multicast clients get the different transport settings: the first client + * requests the specific transport configuration while the second client lets + * the server select the multicast address and the ports. + * CASE: media is shared */ +GST_START_TEST + (test_client_multicast_two_clients_first_specific_transport_shared_media) { + const gchar *transport_client_1 = "RTP/AVP;multicast;destination=233.252.0.1;" + "ttl=1;port=5000-5001;mode=\"PLAY\""; + const gchar *expected_transport_1 = transport_client_1; + const gchar *addr_client_1 = "233.252.0.1:5000"; + + const gchar *transport_client_2 = "RTP/AVP;multicast;mode=\"PLAY\""; + const gchar *expected_transport_2 = expected_transport_1; + const gchar *addr_client_2 = addr_client_1; + + mcast_transport_two_clients (TRUE, transport_client_1, + expected_transport_1, addr_client_1, transport_client_2, + expected_transport_2, addr_client_2); +} + +GST_END_TEST; +/* test if two multicast clients get the different transport settings: the first client lets + * the server select the multicast address and the ports while the second client requests + * the specific transport configuration. + * CASE: media is shared */ +GST_START_TEST + (test_client_multicast_two_clients_second_specific_transport_shared_media) { + const gchar *transport_client_1 = "RTP/AVP;multicast;mode=\"PLAY\""; + const gchar *expected_transport_1 = + "RTP/AVP;multicast;destination=233.252.0.1;" + "ttl=1;port=5000-5001;mode=\"PLAY\""; + const gchar *addr_client_1 = "233.252.0.1:5000"; + + const gchar *transport_client_2 = "RTP/AVP;multicast;destination=233.252.0.2;" + "ttl=2;port=5004-5005;mode=\"PLAY\""; + const gchar *expected_transport_2 = transport_client_2; + const gchar *addr_client_2 = "233.252.0.2:5004"; + + mcast_transport_two_clients (TRUE, transport_client_1, + expected_transport_1, addr_client_1, transport_client_2, + expected_transport_2, addr_client_2); +} + +GST_END_TEST; + +/* test if the maximum ttl multicast value is chosen by the server + * CASE: the first client provides the highest ttl value */ +GST_START_TEST (test_client_multicast_max_ttl_first_client) +{ + const gchar *transport_client_1 = "RTP/AVP;multicast;destination=233.252.0.1;" + "ttl=3;port=5000-5001;mode=\"PLAY\""; + const gchar *expected_transport_1 = transport_client_1; + const gchar *addr_client_1 = "233.252.0.1:5000"; + + const gchar *transport_client_2 = "RTP/AVP;multicast;destination=233.252.0.2;" + "ttl=1;port=5002-5003;mode=\"PLAY\""; + const gchar *expected_transport_2 = + "RTP/AVP;multicast;destination=233.252.0.2;" + "ttl=3;port=5002-5003;mode=\"PLAY\""; + const gchar *addr_client_2 = "233.252.0.2:5002"; + + mcast_transport_two_clients (TRUE, transport_client_1, + expected_transport_1, addr_client_1, transport_client_2, + expected_transport_2, addr_client_2); +} + +GST_END_TEST; + +/* test if the maximum ttl multicast value is chosen by the server + * CASE: the second client provides the highest ttl value */ +GST_START_TEST (test_client_multicast_max_ttl_second_client) +{ + const gchar *transport_client_1 = "RTP/AVP;multicast;destination=233.252.0.1;" + "ttl=2;port=5000-5001;mode=\"PLAY\""; + const gchar *expected_transport_1 = transport_client_1; + const gchar *addr_client_1 = "233.252.0.1:5000"; + + const gchar *transport_client_2 = "RTP/AVP;multicast;destination=233.252.0.2;" + "ttl=4;port=5002-5003;mode=\"PLAY\""; + const gchar *expected_transport_2 = transport_client_2; + const gchar *addr_client_2 = "233.252.0.2:5002"; + + mcast_transport_two_clients (TRUE, transport_client_1, + expected_transport_1, addr_client_1, transport_client_2, + expected_transport_2, addr_client_2); +} + +GST_END_TEST; +GST_START_TEST (test_client_multicast_invalid_ttl) +{ + GstRTSPClient *client; + GstRTSPMessage request = { 0, }; + gchar *str; + GstRTSPSessionPool *session_pool; + GstRTSPContext ctx = { NULL }; + + client = setup_multicast_client (3); + + ctx.client = client; + ctx.auth = gst_rtsp_auth_new (); + ctx.token = + gst_rtsp_token_new (GST_RTSP_TOKEN_TRANSPORT_CLIENT_SETTINGS, + G_TYPE_BOOLEAN, TRUE, GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING, + "user", NULL); + gst_rtsp_context_push_current (&ctx); + + /* simple SETUP with an invalid ttl=0 */ + fail_unless (gst_rtsp_message_init_request (&request, GST_RTSP_SETUP, + "rtsp://localhost/test/stream=0") == GST_RTSP_OK); + str = g_strdup_printf ("%d", cseq); + gst_rtsp_message_take_header (&request, GST_RTSP_HDR_CSEQ, str); + gst_rtsp_message_add_header (&request, GST_RTSP_HDR_TRANSPORT, + "RTP/AVP;multicast;destination=233.252.0.1;ttl=0;port=5000-5001;"); + + gst_rtsp_client_set_send_func (client, test_setup_response_461, NULL, NULL); + fail_unless (gst_rtsp_client_handle_message (client, + &request) == GST_RTSP_OK); + gst_rtsp_message_unset (&request); + + session_pool = gst_rtsp_client_get_session_pool (client); + fail_unless (session_pool != NULL); + fail_unless (gst_rtsp_session_pool_get_n_sessions (session_pool) == 0); + g_object_unref (session_pool); + + teardown_client (client); + g_object_unref (ctx.auth); + gst_rtsp_token_unref (ctx.token); + gst_rtsp_context_pop_current (&ctx); } GST_END_TEST; @@ -1133,7 +1386,17 @@ rtspclient_suite (void) test_client_multicast_transport_specific_two_clients_shared_media); tcase_add_test (tc, test_client_multicast_transport_specific_two_clients); tcase_add_test (tc, + test_client_multicast_transport_specific_two_clients_shared_media_same_transport); + tcase_add_test (tc, test_client_multicast_two_clients_shared_media); + tcase_add_test (tc, + test_client_multicast_two_clients_first_specific_transport_shared_media); + tcase_add_test (tc, + test_client_multicast_two_clients_second_specific_transport_shared_media); + tcase_add_test (tc, test_client_multicast_transport_specific_no_address_in_pool); + tcase_add_test (tc, test_client_multicast_max_ttl_first_client); + tcase_add_test (tc, test_client_multicast_max_ttl_second_client); + tcase_add_test (tc, test_client_multicast_invalid_ttl); return s; } diff --git a/tests/check/gst/stream.c b/tests/check/gst/stream.c index 7b59c0a..d71f193 100644 --- a/tests/check/gst/stream.c +++ b/tests/check/gst/stream.c @@ -486,6 +486,95 @@ GST_START_TEST (test_tcp_transport) GST_END_TEST; +static void +check_multicast_client_address (const gchar * destination, guint port, + const gchar * expected_addr_str, gboolean expected_res) +{ + GstPad *srcpad; + GstElement *pay; + GstRTSPStream *stream; + GstBin *bin; + GstElement *rtpbin; + GstRTSPTransport *transport; + GstRTSPRange ports = { 0 }; + gchar *addr_str = NULL; + + srcpad = gst_pad_new ("testsrcpad", GST_PAD_SRC); + fail_unless (srcpad != NULL); + gst_pad_set_active (srcpad, TRUE); + pay = gst_element_factory_make ("rtpgstpay", "testpayloader"); + fail_unless (pay != NULL); + stream = gst_rtsp_stream_new (0, pay, srcpad); + fail_unless (stream != NULL); + gst_object_unref (pay); + gst_object_unref (srcpad); + rtpbin = gst_element_factory_make ("rtpbin", "testrtpbin"); + fail_unless (rtpbin != NULL); + bin = GST_BIN (gst_bin_new ("testbin")); + fail_unless (bin != NULL); + fail_unless (gst_bin_add (bin, rtpbin)); + + fail_unless (gst_rtsp_stream_join_bin (stream, bin, rtpbin, GST_STATE_NULL)); + + fail_unless (gst_rtsp_transport_new (&transport) == GST_RTSP_OK); + transport->lower_transport = GST_RTSP_LOWER_TRANS_UDP_MCAST; + transport->destination = g_strdup (destination); + transport->ttl = 1; + ports.min = port; + ports.max = port + 1; + transport->port = ports; + + /* allocate ports */ + fail_unless (gst_rtsp_stream_allocate_udp_sockets (stream, + G_SOCKET_FAMILY_IPV4, transport, TRUE) == expected_res); + + fail_unless (gst_rtsp_stream_add_multicast_client_address (stream, + destination, ports.min, ports.max, + G_SOCKET_FAMILY_IPV4) == expected_res); + + fail_unless (gst_rtsp_stream_complete_stream (stream, + transport) == expected_res); + + fail_unless (gst_rtsp_transport_free (transport) == GST_RTSP_OK); + addr_str = gst_rtsp_stream_get_multicast_client_addresses (stream); + + fail_unless (g_str_equal (addr_str, expected_addr_str)); + g_free (addr_str); + + fail_unless (gst_rtsp_stream_leave_bin (stream, bin, rtpbin)); + + gst_object_unref (bin); + gst_object_unref (stream); +} + +/* test if the provided transport destination is correct. + * CASE: valid multicast address */ +GST_START_TEST (test_multicast_client_address) +{ + const gchar *addr = "233.252.0.1"; + guint port = 50000; + const gchar *expected_addr_str = "233.252.0.1:50000"; + gboolean expected_res = TRUE; + + check_multicast_client_address (addr, port, expected_addr_str, expected_res); +} + +GST_END_TEST; + +/* test if the provided transport destination is correct. + * CASE: invalid multicast address */ +GST_START_TEST (test_multicast_client_address_invalid) +{ + const gchar *addr = "1.2.3.4"; + guint port = 50000; + const gchar *expected_addr_str = ""; + gboolean expected_res = FALSE; + + check_multicast_client_address (addr, port, expected_addr_str, expected_res); +} + +GST_END_TEST; + static Suite * rtspstream_suite (void) { @@ -501,6 +590,8 @@ rtspstream_suite (void) tcase_add_test (tc, test_allocate_udp_ports_multicast); tcase_add_test (tc, test_allocate_udp_ports_client_settings); tcase_add_test (tc, test_tcp_transport); + tcase_add_test (tc, test_multicast_client_address); + tcase_add_test (tc, test_multicast_client_address_invalid); return s; } -- 2.7.4