diff --git a/girs/GstRtsp-1.0.gir b/girs/GstRtsp-1.0.gir
index daa63237a2..9bfcb58f3b 100644
--- a/girs/GstRtsp-1.0.gir
+++ b/girs/GstRtsp-1.0.gir
@@ -2997,6 +2997,9 @@ UTC times will be converted to nanoseconds since 1900.
no error
+
+ RTSP request is successful, but was redirected.
+
some unspecified error occurred
@@ -3094,6 +3097,12 @@ UTC times will be converted to nanoseconds since 1900.
+
+ RTSP request is temporarily redirected
+
+
+ RTSP request is permanently redirected
+
diff --git a/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspconnection.c b/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspconnection.c
index bd5c5d750a..a822d956b9 100644
--- a/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspconnection.c
+++ b/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspconnection.c
@@ -362,6 +362,26 @@ socket_client_event (GSocketClient * client, GSocketClientEvent event,
}
}
+static void
+stream0_reset (GstRTSPConnection * conn)
+{
+ if (conn->stream0) {
+ g_object_unref (conn->stream0);
+ conn->stream0 = NULL;
+ conn->socket0 = NULL;
+ }
+ conn->input_stream = NULL;
+ conn->output_stream = NULL;
+ g_free (conn->remote_ip);
+
+ conn->remote_ip = NULL;
+ conn->read_socket = NULL;
+ conn->write_socket = NULL;
+ conn->read_socket_used = FALSE;
+ conn->write_socket_used = FALSE;
+ conn->control_stream = NULL;
+}
+
/* transfer full */
static GCancellable *
get_cancellable (GstRTSPConnection * conn)
@@ -892,7 +912,7 @@ add_extra_headers (GstRTSPMessage * msg, GArray * headers)
}
static GstRTSPResult
-setup_tunneling (GstRTSPConnection * conn, gint64 timeout, gchar * uri,
+setup_tunneling (GstRTSPConnection * conn, gint64 timeout, gchar ** req_uri,
GstRTSPMessage * response)
{
gint i;
@@ -909,6 +929,10 @@ setup_tunneling (GstRTSPConnection * conn, gint64 timeout, gchar * uri,
gchar *request_uri = NULL;
gchar *host = NULL;
GCancellable *cancellable;
+ gchar *uri;
+
+ g_return_val_if_fail (req_uri != NULL, GST_RTSP_EINVAL);
+ uri = *req_uri;
url = conn->url;
@@ -952,10 +976,35 @@ setup_tunneling (GstRTSPConnection * conn, gint64 timeout, gchar * uri,
read_failed);
conn->manual_http = old_http;
- if (response->type != GST_RTSP_MESSAGE_HTTP_RESPONSE ||
- response->type_data.response.code != GST_RTSP_STS_OK)
+ if (response->type != GST_RTSP_MESSAGE_HTTP_RESPONSE)
goto wrong_result;
+ switch (response->type_data.response.code) {
+ case GST_RTSP_STS_OK:
+ break;
+ case GST_RTSP_STS_MOVED_PERMANENTLY:
+ case GST_RTSP_STS_MOVE_TEMPORARILY:
+ case GST_RTSP_STS_REDIRECT_TEMPORARILY:
+ case GST_RTSP_STS_REDIRECT_PERMANENTLY:
+ {
+ gchar *location_val = NULL;
+ gst_rtsp_message_get_header (response, GST_RTSP_HDR_LOCATION,
+ &location_val, 0);
+
+ if (location_val != NULL) {
+ GST_TRACE ("redirect (%d) to %s",
+ response->type_data.response.code, location_val);
+ g_free (uri);
+ uri = g_strdup (location_val);
+ *req_uri = uri;
+ res = GST_RTSP_OK_REDIRECT;
+ goto exit;
+ }
+ }
+ default:
+ goto wrong_result;
+ }
+
if (!conn->ignore_x_server_reply &&
gst_rtsp_message_get_header (response, GST_RTSP_HDR_X_SERVER_IP_ADDRESS,
&value, 0) == GST_RTSP_OK) {
@@ -1102,15 +1151,17 @@ GstRTSPResult
gst_rtsp_connection_connect_with_response_usec (GstRTSPConnection * conn,
gint64 timeout, GstRTSPMessage * response)
{
- GstRTSPResult res;
+ GstRTSPResult res = GST_RTSP_OK;
GSocketConnection *connection;
GSocket *socket;
GError *error = NULL;
- gchar *connection_uri, *request_uri, *remote_ip;
+ gchar *connection_uri, *request_uri, *remote_ip, *query = NULL, *path = NULL;
GstClockTime to;
guint16 url_port;
GstRTSPUrl *url;
GCancellable *cancellable;
+ guint redirect_cnt = 0;
+ GstUri *uri = NULL;
g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
g_return_val_if_fail (conn->url != NULL, GST_RTSP_EINVAL);
@@ -1130,54 +1181,110 @@ gst_rtsp_connection_connect_with_response_usec (GstRTSPConnection * conn,
connection_uri = gst_rtsp_url_get_request_uri (url);
}
- cancellable = get_cancellable (conn);
+ while (res == GST_RTSP_OK) {
+ cancellable = get_cancellable (conn);
- if (conn->proxy_host) {
- connection = g_socket_client_connect_to_host (conn->client,
- conn->proxy_host, conn->proxy_port, cancellable, &error);
- request_uri = g_strdup (connection_uri);
- } else {
- connection = g_socket_client_connect_to_uri (conn->client,
- connection_uri, url_port, cancellable, &error);
+ if (conn->proxy_host) {
+ connection = g_socket_client_connect_to_host (conn->client,
+ conn->proxy_host, conn->proxy_port, cancellable, &error);
+ request_uri = g_strdup (connection_uri);
+ } else {
+ if (uri != NULL) {
+ url_port = gst_uri_get_port (uri);
+ }
+ connection = g_socket_client_connect_to_uri (conn->client,
+ connection_uri, url_port, cancellable, &error);
- /* use the relative component of the uri for non-proxy connections */
- request_uri = g_strdup_printf ("%s%s%s", url->abspath,
- url->query ? "?" : "", url->query ? url->query : "");
- }
+ if (uri == NULL) {
+ /* Use the relative component of the uri for non-proxy connections.
+ * Note: request_uri is not a complete URI, it only contain path +
+ * query.*/
+ request_uri = g_strdup_printf ("%s%s%s", url->abspath,
+ url->query ? "?" : "", url->query ? url->query : "");
+ } else {
+ path = gst_uri_get_path (uri);
+ query = gst_uri_get_query_string (uri);
+ request_uri = g_strdup_printf ("%s%s%s",
+ path, query ? "?" : "", query ? query : "");
+ }
+ g_free (path);
+ g_free (query);
+ }
- g_clear_object (&cancellable);
+ g_clear_object (&cancellable);
- if (connection == NULL)
- goto connect_failed;
+ if (connection == NULL)
+ goto connect_failed;
- /* get remote address */
- socket = g_socket_connection_get_socket (connection);
+ /* get remote address */
+ socket = g_socket_connection_get_socket (connection);
- if (!collect_addresses (socket, &remote_ip, NULL, TRUE, &error))
- goto remote_address_failed;
+ if (!collect_addresses (socket, &remote_ip, NULL, TRUE, &error))
+ goto remote_address_failed;
- g_free (conn->remote_ip);
- conn->remote_ip = remote_ip;
- conn->stream0 = G_IO_STREAM (connection);
- conn->socket0 = socket;
- /* this is our read socket */
- conn->read_socket = conn->socket0;
- conn->write_socket = conn->socket0;
- conn->read_socket_used = FALSE;
- conn->write_socket_used = FALSE;
- conn->input_stream = g_io_stream_get_input_stream (conn->stream0);
- conn->output_stream = g_io_stream_get_output_stream (conn->stream0);
- conn->control_stream = NULL;
+ g_free (conn->remote_ip);
+ conn->remote_ip = remote_ip;
+ conn->stream0 = G_IO_STREAM (connection);
+ conn->socket0 = socket;
+ /* this is our read socket */
+ conn->read_socket = conn->socket0;
+ conn->write_socket = conn->socket0;
+ conn->read_socket_used = FALSE;
+ conn->write_socket_used = FALSE;
+ conn->input_stream = g_io_stream_get_input_stream (conn->stream0);
+ conn->output_stream = g_io_stream_get_output_stream (conn->stream0);
+ conn->control_stream = NULL;
- if (conn->tunneled) {
- res = setup_tunneling (conn, timeout, request_uri, response);
- if (res != GST_RTSP_OK)
- goto tunneling_failed;
+ if (conn->tunneled) {
+ res = setup_tunneling (conn, timeout, &request_uri, response);
+ if (res != GST_RTSP_OK) {
+ if (res == GST_RTSP_OK_REDIRECT) {
+ if (conn->proxy_host) {
+ GST_TRACE ("redirect behind proxy is not supported");
+ res = GST_RTSP_ERROR;
+ goto tunneling_failed;
+ }
+
+ GST_LOG ("redirect from %s to %s.", connection_uri, request_uri);
+
+ stream0_reset (conn);
+ connection_uri = request_uri;
+ gst_uri_unref (uri);
+
+ uri = gst_uri_from_string (connection_uri);
+ if (uri == NULL) {
+ GST_TRACE ("failed to parse redirect uri");
+ res = GST_RTSP_ERROR;
+ goto tunneling_failed;
+ }
+
+ conn->url->abspath = gst_uri_get_path (uri);
+ conn->url->host = g_strdup (gst_uri_get_host (uri));
+ conn->url->port = gst_uri_get_port (uri);
+ conn->url->query = gst_uri_get_query_string (uri);
+ res = GST_RTSP_OK;
+
+ /* at most allow 5 redirect */
+ if (redirect_cnt++ > 4) {
+ GST_TRACE ("redirect max reached");
+ res = GST_RTSP_ERROR;
+ goto tunneling_failed;
+ }
+ } else {
+ goto tunneling_failed;
+ }
+ } else {
+ /* Caller must be informed */
+ res = GST_RTSP_OK_REDIRECT;
+ }
+ } else {
+ break;
+ }
}
g_free (connection_uri);
g_free (request_uri);
- return GST_RTSP_OK;
+ return res;
/* ERRORS */
connect_failed:
diff --git a/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h b/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h
index 8a028c381b..83adb74c51 100644
--- a/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h
+++ b/subprojects/gst-plugins-base/gst-libs/gst/rtsp/gstrtspdefs.h
@@ -65,6 +65,7 @@ G_STMT_START { \
/**
* GstRTSPResult:
* @GST_RTSP_OK: no error
+ * @GST_RTSP_OK_REDIRECT: no error, but redirected
* @GST_RTSP_ERROR: some unspecified error occurred
* @GST_RTSP_EINVAL: invalid arguments were provided to a function
* @GST_RTSP_EINTR: an operation was canceled
@@ -87,6 +88,16 @@ G_STMT_START { \
*/
typedef enum {
GST_RTSP_OK = 0,
+
+ /**
+ * GST_RTSP_OK_REDIRECT:
+ *
+ * RTSP request is successful, but was redirected.
+ *
+ * Since: 1.24
+ */
+ GST_RTSP_OK_REDIRECT = 1,
+
/* errors */
GST_RTSP_ERROR = -1,
GST_RTSP_EINVAL = -2,
@@ -365,6 +376,25 @@ typedef enum {
GST_RTSP_STS_SEE_OTHER = 303,
GST_RTSP_STS_NOT_MODIFIED = 304,
GST_RTSP_STS_USE_PROXY = 305,
+
+ /**
+ * GST_RTSP_STS_REDIRECT_TEMPORARILY:
+ *
+ * RTSP request is temporarily redirected
+ *
+ * Since: 1.24
+ */
+ GST_RTSP_STS_REDIRECT_TEMPORARILY = 307,
+
+ /**
+ * GST_RTSP_STS_REDIRECT_PERMANENTLY:
+ *
+ * RTSP request is permanently redirected
+ *
+ * Since: 1.24
+ */
+ GST_RTSP_STS_REDIRECT_PERMANENTLY = 308,
+
GST_RTSP_STS_BAD_REQUEST = 400,
GST_RTSP_STS_UNAUTHORIZED = 401,
GST_RTSP_STS_PAYMENT_REQUIRED = 402,