Add RTSP tunneling over HTTP
authorJosh Allmann <joshua.allmann@gmail.com>
Tue, 8 Jun 2010 12:40:34 +0000 (12:40 +0000)
committerMartin Storsjö <martin@martin.st>
Tue, 8 Jun 2010 12:40:34 +0000 (12:40 +0000)
Patch by Josh Allmann, joshua dot allmann at gmail dot com

Originally committed as revision 23536 to svn://svn.ffmpeg.org/ffmpeg/trunk

libavformat/rtsp.c
libavformat/rtsp.h

index 0b55742..aa8f31b 100644 (file)
@@ -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;
index a28bb94..d646045 100644 (file)
@@ -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;
 
 /**