Mercurial > libavformat.hg
changeset 6112:3e5c6c575f69 libavformat
Add RTSP tunneling over HTTP
Patch by Josh Allmann, joshua dot allmann at gmail dot com
author | mstorsjo |
---|---|
date | Tue, 08 Jun 2010 12:40:34 +0000 |
parents | fcbafbe31721 |
children | 6f9d084d5888 |
files | rtsp.c rtsp.h |
diffstat | 2 files changed, 88 insertions(+), 4 deletions(-) [+] |
line wrap: on
line diff
--- a/rtsp.c Tue Jun 08 12:26:51 2010 +0000 +++ b/rtsp.c Tue Jun 08 12:40:34 2010 +0000 @@ -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 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 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ 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 @@ goto fail; } rtsp_hd_out = rtsp_hd; + } rt->rtsp_hd = rtsp_hd; rt->rtsp_hd_out = rtsp_hd_out; rt->seq = 0;
--- a/rtsp.h Tue Jun 08 12:26:51 2010 +0000 +++ b/rtsp.h Tue Jun 08 12:40:34 2010 +0000 @@ -49,6 +49,15 @@ 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 @@ /** 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; /**