diff options
-rw-r--r-- | gst/rtsp-server/rtsp-stream.c | 173 | ||||
-rw-r--r-- | tests/check/gst/client.c | 51 |
2 files changed, 169 insertions, 55 deletions
diff --git a/gst/rtsp-server/rtsp-stream.c b/gst/rtsp-server/rtsp-stream.c index 1a68fd5..0d17718 100644 --- a/gst/rtsp-server/rtsp-stream.c +++ b/gst/rtsp-server/rtsp-stream.c @@ -1326,11 +1326,11 @@ error: static gboolean alloc_ports_one_family (GstRTSPStream * stream, GSocketFamily family, GSocket * socket_out[2], GstRTSPAddress ** server_addr_out, - gboolean multicast, GstRTSPTransport * ct) + gboolean multicast, GstRTSPTransport * ct, gboolean use_transport_settings) { GstRTSPStreamPrivate *priv = stream->priv; GSocket *rtp_socket = NULL; - GSocket *rtcp_socket; + GSocket *rtcp_socket = NULL; gint tmp_rtp, tmp_rtcp; guint count; GList *rejected_addresses = NULL; @@ -1339,6 +1339,7 @@ alloc_ports_one_family (GstRTSPStream * stream, GSocketFamily family, GSocketAddress *rtp_sockaddr = NULL; GSocketAddress *rtcp_sockaddr = NULL; GstRTSPAddressPool *pool; + gboolean transport_settings_defined = FALSE; pool = priv->pool; count = 0; @@ -1346,6 +1347,30 @@ alloc_ports_one_family (GstRTSPStream * stream, GSocketFamily family, /* Start with random port */ tmp_rtp = 0; + if (use_transport_settings) { + if (!multicast) + goto no_mcast; + + if (ct == NULL) + goto no_transport; + + /* multicast and transport specific case */ + if (ct->destination != NULL) { + tmp_rtp = ct->port.min; + tmp_rtcp = ct->port.max; + inetaddr = g_inet_address_new_from_string (ct->destination); + if (inetaddr == NULL) + goto destination_error; + if (!g_inet_address_get_is_multicast (inetaddr)) + goto destination_no_mcast; + g_object_unref (inetaddr); + inetaddr = g_inet_address_new_any (family); + + GST_DEBUG_OBJECT (stream, "use transport settings"); + transport_settings_defined = TRUE; + } + } + rtcp_socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, NULL); if (!rtcp_socket) @@ -1364,55 +1389,60 @@ again: g_socket_set_multicast_loopback (rtp_socket, FALSE); } - if ((pool && gst_rtsp_address_pool_has_unicast_addresses (pool)) || multicast) { - GstRTSPAddressFlags flags; + if (!transport_settings_defined) { + if ((pool && gst_rtsp_address_pool_has_unicast_addresses (pool)) + || multicast) { + GstRTSPAddressFlags flags; - if (addr) - rejected_addresses = g_list_prepend (rejected_addresses, addr); + if (addr) + rejected_addresses = g_list_prepend (rejected_addresses, addr); - if (!pool) - goto no_pool; + if (!pool) + goto no_pool; - flags = GST_RTSP_ADDRESS_FLAG_EVEN_PORT; - if (multicast) - flags |= GST_RTSP_ADDRESS_FLAG_MULTICAST; - else - flags |= GST_RTSP_ADDRESS_FLAG_UNICAST; + flags = GST_RTSP_ADDRESS_FLAG_EVEN_PORT; + if (multicast) + flags |= GST_RTSP_ADDRESS_FLAG_MULTICAST; + else + flags |= GST_RTSP_ADDRESS_FLAG_UNICAST; - if (family == G_SOCKET_FAMILY_IPV6) - flags |= GST_RTSP_ADDRESS_FLAG_IPV6; - else - flags |= GST_RTSP_ADDRESS_FLAG_IPV4; + 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 (pool, flags, 2); + addr = gst_rtsp_address_pool_acquire_address (pool, flags, 2); - if (addr == NULL) - goto no_address; + if (addr == NULL) + goto no_address; - tmp_rtp = addr->port; + tmp_rtp = addr->port; - g_clear_object (&inetaddr); - /* FIXME: Does it really work with the IP_MULTICAST_ALL socket option and - * socket control message set in udpsrc? */ - if (multicast) - inetaddr = g_inet_address_new_any (family); - else - inetaddr = g_inet_address_new_from_string (addr->address); - } else { - if (tmp_rtp != 0) { - tmp_rtp += 2; - if (++count > 20) - goto no_ports; - } + g_clear_object (&inetaddr); + /* FIXME: Does it really work with the IP_MULTICAST_ALL socket option and + * socket control message set in udpsrc? */ + if (multicast) + inetaddr = g_inet_address_new_any (family); + else + inetaddr = g_inet_address_new_from_string (addr->address); + } else { + if (tmp_rtp != 0) { + tmp_rtp += 2; + if (++count > 20) + goto no_ports; + } - if (inetaddr == NULL) - inetaddr = g_inet_address_new_any (family); + if (inetaddr == NULL) + inetaddr = g_inet_address_new_any (family); + } } rtp_sockaddr = g_inet_socket_address_new (inetaddr, tmp_rtp); if (!g_socket_bind (rtp_socket, rtp_sockaddr, FALSE, NULL)) { GST_DEBUG_OBJECT (stream, "rtp bind() failed, will try again"); g_object_unref (rtp_sockaddr); + if (transport_settings_defined) + goto transport_settings_error; goto again; } g_object_unref (rtp_sockaddr); @@ -1423,17 +1453,22 @@ again: goto socket_error; } - tmp_rtp = - g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (rtp_sockaddr)); - g_object_unref (rtp_sockaddr); - - /* check if port is even */ - if ((tmp_rtp & 1) != 0) { - /* port not even, close and allocate another */ - tmp_rtp++; - g_clear_object (&rtp_socket); - goto again; + if (!transport_settings_defined) { + tmp_rtp = + g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (rtp_sockaddr)); + + /* check if port is even. RFC 3550 encorages the use of an even/odd port + * pair, however it's not a strict requirement so this check is not done + * for the client selected ports. */ + if ((tmp_rtp & 1) != 0) { + /* port not even, close and allocate another */ + tmp_rtp++; + g_object_unref (rtp_sockaddr); + g_clear_object (&rtp_socket); + goto again; + } } + g_object_unref (rtp_sockaddr); /* set port */ tmp_rtcp = tmp_rtp + 1; @@ -1443,15 +1478,21 @@ again: GST_DEBUG_OBJECT (stream, "rctp bind() failed, will try again"); g_object_unref (rtcp_sockaddr); g_clear_object (&rtp_socket); + if (transport_settings_defined) + goto transport_settings_error; goto again; } g_object_unref (rtcp_sockaddr); if (!addr) { addr = g_slice_new0 (GstRTSPAddress); - addr->address = g_inet_address_to_string (inetaddr); addr->port = tmp_rtp; addr->n_ports = 2; + if (transport_settings_defined) + addr->address = g_strdup (ct->destination); + else + addr->address = g_inet_address_to_string (inetaddr); + addr->ttl = ct->ttl; } g_clear_object (&inetaddr); @@ -1468,6 +1509,28 @@ again: return TRUE; /* ERRORS */ +no_mcast: + { + GST_ERROR_OBJECT (stream, "failed to allocate UDP ports: wrong transport"); + goto cleanup; + } +no_transport: + { + GST_ERROR_OBJECT (stream, "failed to allocate UDP ports: no transport"); + goto cleanup; + } +destination_error: + { + GST_ERROR_OBJECT (stream, + "failed to allocate UDP ports: destination error"); + goto cleanup; + } +destination_no_mcast: + { + GST_ERROR_OBJECT (stream, + "failed to allocate UDP ports: destination not multicast address"); + goto cleanup; + } no_udp_protocol: { GST_WARNING_OBJECT (stream, "failed to allocate UDP ports: protocol error"); @@ -1489,6 +1552,12 @@ no_ports: GST_WARNING_OBJECT (stream, "failed to allocate UDP ports: no ports"); goto cleanup; } +transport_settings_error: + { + GST_ERROR_OBJECT (stream, + "failed to allocate UDP ports with requested transport settings"); + goto cleanup; + } socket_error: { GST_WARNING_OBJECT (stream, "failed to allocate UDP ports: socket error"); @@ -1563,12 +1632,13 @@ gst_rtsp_stream_allocate_udp_sockets (GstRTSPStream * stream, /* UDP unicast */ GST_DEBUG_OBJECT (stream, "GST_RTSP_LOWER_TRANS_UDP, ipv4"); ret = alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV4, - priv->socket_v4, &priv->server_addr_v4, FALSE, ct); + priv->socket_v4, &priv->server_addr_v4, FALSE, ct, FALSE); } else { /* multicast */ GST_DEBUG_OBJECT (stream, "GST_RTSP_LOWER_TRANS_MCAST_UDP, ipv4"); ret = alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV4, - priv->mcast_socket_v4, &priv->mcast_addr_v4, TRUE, ct); + priv->mcast_socket_v4, &priv->mcast_addr_v4, TRUE, ct, + use_transport_settings); } } else { /* IPv6 */ @@ -1576,13 +1646,14 @@ gst_rtsp_stream_allocate_udp_sockets (GstRTSPStream * stream, /* unicast */ GST_DEBUG_OBJECT (stream, "GST_RTSP_LOWER_TRANS_UDP, ipv6"); ret = alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV6, - priv->socket_v6, &priv->server_addr_v6, FALSE, ct); + priv->socket_v6, &priv->server_addr_v6, FALSE, ct, FALSE); } else { /* multicast */ GST_DEBUG_OBJECT (stream, "GST_RTSP_LOWER_TRANS_MCAST_UDP, ipv6"); ret = alloc_ports_one_family (stream, G_SOCKET_FAMILY_IPV6, - priv->mcast_socket_v6, &priv->mcast_addr_v6, TRUE, ct); + priv->mcast_socket_v6, &priv->mcast_addr_v6, TRUE, ct, + use_transport_settings); } } g_mutex_unlock (&priv->lock); diff --git a/tests/check/gst/client.c b/tests/check/gst/client.c index ab95509..d4fdd9e 100644 --- a/tests/check/gst/client.c +++ b/tests/check/gst/client.c @@ -726,7 +726,8 @@ GST_START_TEST (test_client_multicast_ignore_transport_specific) GST_END_TEST; -GST_START_TEST (test_client_multicast_transport_specific) +static void +multicast_transport_specific (void) { GstRTSPClient *client; GstRTSPMessage request = { 0, }; @@ -760,7 +761,6 @@ GST_START_TEST (test_client_multicast_transport_specific) fail_unless (gst_rtsp_client_handle_message (client, &request) == GST_RTSP_OK); gst_rtsp_message_unset (&request); - expected_transport = NULL; gst_rtsp_client_set_send_func (client, test_setup_response_200_multicast, NULL, NULL); @@ -769,14 +769,44 @@ GST_START_TEST (test_client_multicast_transport_specific) fail_unless (gst_rtsp_session_pool_get_n_sessions (session_pool) == 1); g_object_unref (session_pool); - send_teardown (client); + /* send PLAY request */ + fail_unless (gst_rtsp_message_init_request (&request, GST_RTSP_PLAY, + "rtsp://localhost/test") == 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_SESSION, session_id); + gst_rtsp_client_set_send_func (client, test_response_200, NULL, NULL); + fail_unless (gst_rtsp_client_handle_message (client, + &request) == GST_RTSP_OK); + gst_rtsp_message_unset (&request); + send_teardown (client); teardown_client (client); g_object_unref (ctx.auth); gst_rtsp_token_unref (ctx.token); gst_rtsp_context_pop_current (&ctx); } +/* CASE: multicast address requested by the client exists in the address pool */ +GST_START_TEST (test_client_multicast_transport_specific) +{ + expected_transport = "RTP/AVP;multicast;destination=233.252.0.1;" + "ttl=1;port=5000-5001;mode=\"PLAY\""; + multicast_transport_specific (); + expected_transport = NULL; +} + +GST_END_TEST; + +/* CASE: multicast address requested by the client does not exist in the address pool */ +GST_START_TEST (test_client_multicast_transport_specific_no_address_in_pool) +{ + expected_transport = "RTP/AVP;multicast;destination=234.252.0.3;" + "ttl=1;port=6000-6001;mode=\"PLAY\""; + multicast_transport_specific (); + expected_transport = NULL; +} + GST_END_TEST; static gboolean @@ -1061,7 +1091,8 @@ mcast_transport_specific_two_clients (gboolean shared) g_object_unref (thread_pool); } -/* test if two multicast clients can choose different transport settings */ +/* test if two multicast clients can choose different transport settings + * CASE: media is shared */ GST_START_TEST (test_client_multicast_transport_specific_two_clients_shared_media) { mcast_transport_specific_two_clients (TRUE); @@ -1069,6 +1100,15 @@ GST_START_TEST GST_END_TEST; +/* test if two multicast clients can choose different transport settings + * CASE: media is not shared */ +GST_START_TEST (test_client_multicast_transport_specific_two_clients) +{ + mcast_transport_specific_two_clients (FALSE); +} + +GST_END_TEST; + static Suite * rtspclient_suite (void) { @@ -1091,6 +1131,9 @@ rtspclient_suite (void) tcase_add_test (tc, test_client_sdp_with_no_bitrate_tags); tcase_add_test (tc, 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_no_address_in_pool); return s; } |