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;
GSocketAddress *rtp_sockaddr = NULL;
GSocketAddress *rtcp_sockaddr = NULL;
GstRTSPAddressPool *pool;
+ gboolean transport_settings_defined = FALSE;
pool = priv->pool;
count = 0;
/* 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)
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);
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;
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);
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");
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");
/* 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 */
/* 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);
GST_END_TEST;
-GST_START_TEST (test_client_multicast_transport_specific)
+static void
+multicast_transport_specific (void)
{
GstRTSPClient *client;
GstRTSPMessage request = { 0, };
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);
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
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);
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)
{
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;
}