Commit f5d33f52 authored by Josh Allmann's avatar Josh Allmann Committed by Martin Storsjö

Add RTSP tunneling over HTTP

Patch by Josh Allmann, joshua dot allmann at gmail dot com

Originally committed as revision 23536 to svn://svn.ffmpeg.org/ffmpeg/trunk
parent a26c3c21
......@@ -22,6 +22,7 @@
#include "libavutil/base64.h"
#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/random_seed.h"
#include "avformat.h"
#include <sys/time.h>
......@@ -32,6 +33,7 @@
#include "internal.h"
#include "network.h"
#include "os_support.h"
#include "http.h"
#include "rtsp.h"
#include "rtpdec.h"
......@@ -1008,8 +1010,11 @@ int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s,
int send_content_length)
{
RTSPState *rt = s->priv_data;
char buf[4096];
char buf[4096], *out_buf;
char base64buf[AV_BASE64_SIZE(sizeof(buf))];
/* Add in RTSP headers */
out_buf = buf;
rt->seq++;
snprintf(buf, sizeof(buf), "%s %s RTSP/1.0\r\n", method, url);
if (headers)
......@@ -1030,11 +1035,23 @@ int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s,
av_strlcatf(buf, sizeof(buf), "Content-Length: %d\r\n", send_content_length);
av_strlcat(buf, "\r\n", sizeof(buf));
/* base64 encode rtsp if tunneling */
if (rt->control_transport == RTSP_MODE_TUNNEL) {
av_base64_encode(base64buf, sizeof(base64buf), buf, strlen(buf));
out_buf = base64buf;
}
dprintf(s, "Sending:\n%s--\n", buf);
url_write(rt->rtsp_hd_out, buf, strlen(buf));
if (send_content_length > 0 && send_content)
url_write(rt->rtsp_hd_out, out_buf, strlen(out_buf));
if (send_content_length > 0 && send_content) {
if (rt->control_transport == RTSP_MODE_TUNNEL) {
av_log(s, AV_LOG_ERROR, "tunneling of RTSP requests "
"with content data not supported\n");
return AVERROR_PATCHWELCOME;
}
url_write(rt->rtsp_hd_out, send_content, send_content_length);
}
rt->last_cmd_time = av_gettime();
return 0;
......@@ -1485,6 +1502,7 @@ int ff_rtsp_connect(AVFormatContext *s)
if (!ff_network_init())
return AVERROR(EIO);
redirect:
rt->control_transport = RTSP_MODE_PLAIN;
/* extract hostname and port */
ff_url_split(NULL, 0, auth, sizeof(auth),
host, sizeof(host), &port, path, sizeof(path), s->filename);
......@@ -1514,6 +1532,9 @@ redirect:
lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
} else if (!strcmp(option, "tcp")) {
lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_TCP);
} else if(!strcmp(option, "http")) {
lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_TCP);
rt->control_transport = RTSP_MODE_TUNNEL;
} else {
/* Write options back into the buffer, using memmove instead
* of strcpy since the strings may overlap. */
......@@ -1533,7 +1554,7 @@ redirect:
/* Only UDP or TCP - UDP multicast isn't supported. */
lower_transport_mask &= (1 << RTSP_LOWER_TRANSPORT_UDP) |
(1 << RTSP_LOWER_TRANSPORT_TCP);
if (!lower_transport_mask) {
if (!lower_transport_mask || rt->control_transport == RTSP_MODE_TUNNEL) {
av_log(s, AV_LOG_ERROR, "Unsupported lower transport method, "
"only UDP and TCP are supported for output.\n");
err = AVERROR(EINVAL);
......@@ -1547,6 +1568,56 @@ redirect:
ff_url_join(rt->control_uri, sizeof(rt->control_uri), "rtsp", NULL,
host, port, "%s", path);
if (rt->control_transport == RTSP_MODE_TUNNEL) {
/* set up initial handshake for tunneling */
char httpname[1024];
char sessioncookie[17];
char headers[1024];
ff_url_join(httpname, sizeof(httpname), "http", NULL, host, port, "%s", path);
snprintf(sessioncookie, sizeof(sessioncookie), "%08x%08x",
av_get_random_seed(), av_get_random_seed());
/* GET requests */
if (url_open(&rtsp_hd, httpname, URL_RDONLY) < 0) {
err = AVERROR(EIO);
goto fail;
}
/* generate GET headers */
snprintf(headers, sizeof(headers),
"x-sessioncookie: %s\r\n"
"Accept: application/x-rtsp-tunnelled\r\n"
"Pragma: no-cache\r\n"
"Cache-Control: no-cache\r\n",
sessioncookie);
ff_http_set_headers(rtsp_hd, headers);
/* complete the connection */
if (url_read(rtsp_hd, NULL, 0)) {
err = AVERROR(EIO);
goto fail;
}
/* POST requests */
if (url_open(&rtsp_hd_out, httpname, URL_WRONLY) < 0 ) {
err = AVERROR(EIO);
goto fail;
}
/* generate POST headers */
snprintf(headers, sizeof(headers),
"x-sessioncookie: %s\r\n"
"Content-Type: application/x-rtsp-tunnelled\r\n"
"Pragma: no-cache\r\n"
"Cache-Control: no-cache\r\n"
"Content-Length: 32767\r\n"
"Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n",
sessioncookie);
ff_http_set_headers(rtsp_hd_out, headers);
ff_http_set_chunked_transfer_encoding(rtsp_hd_out, 0);
} else {
/* open the tcp connexion */
ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL);
if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0) {
......@@ -1554,6 +1625,7 @@ redirect:
goto fail;
}
rtsp_hd_out = rtsp_hd;
}
rt->rtsp_hd = rtsp_hd;
rt->rtsp_hd_out = rtsp_hd_out;
rt->seq = 0;
......
......@@ -49,6 +49,15 @@ enum RTSPTransport {
RTSP_TRANSPORT_NB
};
/**
* Transport mode for the RTSP data. This may be plain, or
* tunneled, which is done over HTTP.
*/
enum RTSPControlTransport {
RTSP_MODE_PLAIN, /**< Normal RTSP */
RTSP_MODE_TUNNEL /**< RTSP over HTTP (tunneling) */
};
#define RTSP_DEFAULT_PORT 554
#define RTSP_MAX_TRANSPORTS 8
#define RTSP_TCP_MAX_PACKET_SIZE 1472
......@@ -282,6 +291,9 @@ typedef struct RTSPState {
/** Additional output handle, used when input and output are done
* separately, eg for HTTP tunneling. */
URLContext *rtsp_hd_out;
/** RTSP transport mode, such as plain or tunneled. */
enum RTSPControlTransport control_transport;
} RTSPState;
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment