]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/rtsp.c
Cosmetics: Change connexion to connection in code comments
[ffmpeg] / libavformat / rtsp.c
index 6dbd796d31bf6bc09ac163323df42f0f193ac8b8..e762f61e8910f4b21db3df37f999b3eb94252428 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"
@@ -271,9 +273,7 @@ int ff_rtsp_next_attr_and_value(const char **p, char *attr, int attr_size,
 static void sdp_parse_fmtp(AVStream *st, const char *p)
 {
     char attr[256];
-    /* Vorbis setup headers can be up to 12KB and are sent base64
-     * encoded, giving a 12KB * (4/3) = 16KB FMTP line. */
-    char value[16384];
+    char value[4096];
     int i;
     RTSPStream *rtsp_st = st->priv_data;
     AVCodecContext *codec = st->codec;
@@ -539,7 +539,8 @@ static int sdp_parse(AVFormatContext *s, const char *content)
      * "rulebooks" describing their properties. Therefore, the SDP line
      * buffer is large.
      *
-     * The Vorbis FMTP line can be up to 16KB - see sdp_parse_fmtp. */
+     * The Vorbis FMTP line can be up to 16KB - see xiph_parse_sdp_line
+     * in rtpdec_xiph.c. */
     char buf[16384], *q;
     SDPParseState sdp_parse_state, *s1 = &sdp_parse_state;
 
@@ -1001,15 +1002,18 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply,
     return 0;
 }
 
-void ff_rtsp_send_cmd_with_content_async(AVFormatContext *s,
-                                         const char *method, const char *url,
-                                         const char *headers,
-                                         const unsigned char *send_content,
-                                         int send_content_length)
+int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s,
+                                        const char *method, const char *url,
+                                        const char *headers,
+                                        const unsigned char *send_content,
+                                        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,49 +1034,69 @@ void 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, buf, strlen(buf));
-    if (send_content_length > 0 && send_content)
-        url_write(rt->rtsp_hd, send_content, send_content_length);
+    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;
 }
 
-void ff_rtsp_send_cmd_async(AVFormatContext *s, const char *method,
-                            const char *url, const char *headers)
+int ff_rtsp_send_cmd_async(AVFormatContext *s, const char *method,
+                           const char *url, const char *headers)
 {
-    ff_rtsp_send_cmd_with_content_async(s, method, url, headers, NULL, 0);
+    return ff_rtsp_send_cmd_with_content_async(s, method, url, headers, NULL, 0);
 }
 
-void ff_rtsp_send_cmd(AVFormatContext *s, const char *method, const char *url,
-                      const char *headers, RTSPMessageHeader *reply,
-                      unsigned char **content_ptr)
+int ff_rtsp_send_cmd(AVFormatContext *s, const char *method, const char *url,
+                     const char *headers, RTSPMessageHeader *reply,
+                     unsigned char **content_ptr)
 {
-    ff_rtsp_send_cmd_with_content(s, method, url, headers, reply,
-                                  content_ptr, NULL, 0);
+    return ff_rtsp_send_cmd_with_content(s, method, url, headers, reply,
+                                         content_ptr, NULL, 0);
 }
 
-void ff_rtsp_send_cmd_with_content(AVFormatContext *s,
-                                   const char *method, const char *url,
-                                   const char *header,
-                                   RTSPMessageHeader *reply,
-                                   unsigned char **content_ptr,
-                                   const unsigned char *send_content,
-                                   int send_content_length)
+int ff_rtsp_send_cmd_with_content(AVFormatContext *s,
+                                  const char *method, const char *url,
+                                  const char *header,
+                                  RTSPMessageHeader *reply,
+                                  unsigned char **content_ptr,
+                                  const unsigned char *send_content,
+                                  int send_content_length)
 {
     RTSPState *rt = s->priv_data;
     HTTPAuthType cur_auth_type;
+    int ret;
 
 retry:
     cur_auth_type = rt->auth_state.auth_type;
-    ff_rtsp_send_cmd_with_content_async(s, method, url, header,
-                                        send_content, send_content_length);
+    if ((ret = ff_rtsp_send_cmd_with_content_async(s, method, url, header,
+                                                   send_content,
+                                                   send_content_length)))
+        return ret;
 
-    ff_rtsp_read_reply(s, reply, content_ptr, 0);
+    if ((ret = ff_rtsp_read_reply(s, reply, content_ptr, 0) ) < 0)
+        return ret;
 
     if (reply->status_code == 401 && cur_auth_type == HTTP_AUTH_NONE &&
         rt->auth_state.auth_type != HTTP_AUTH_NONE)
         goto retry;
+
+    return 0;
 }
 
 /**
@@ -1454,12 +1478,19 @@ static int rtsp_setup_output_streams(AVFormatContext *s, const char *addr)
     return 0;
 }
 
+void ff_rtsp_close_connections(AVFormatContext *s)
+{
+    RTSPState *rt = s->priv_data;
+    if (rt->rtsp_hd_out != rt->rtsp_hd) url_close(rt->rtsp_hd_out);
+    url_close(rt->rtsp_hd);
+}
+
 int ff_rtsp_connect(AVFormatContext *s)
 {
     RTSPState *rt = s->priv_data;
     char host[1024], path[1024], tcpname[1024], cmd[2048], auth[128];
     char *option_list, *option, *filename;
-    URLContext *rtsp_hd;
+    URLContext *rtsp_hd, *rtsp_hd_out;
     int port, err, tcp_fd;
     RTSPMessageHeader reply1 = {}, *reply = &reply1;
     int lower_transport_mask = 0;
@@ -1470,6 +1501,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);
@@ -1499,6 +1531,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. */
@@ -1518,7 +1553,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);
@@ -1532,13 +1567,66 @@ redirect:
     ff_url_join(rt->control_uri, sizeof(rt->control_uri), "rtsp", NULL,
                 host, port, "%s", path);
 
-    /* open the tcp connexion */
-    ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL);
-    if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0) {
-        err = AVERROR(EIO);
-        goto fail;
+    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 connection */
+        ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL);
+        if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0) {
+            err = AVERROR(EIO);
+            goto fail;
+        }
+        rtsp_hd_out = rtsp_hd;
     }
     rt->rtsp_hd = rtsp_hd;
+    rt->rtsp_hd_out = rtsp_hd_out;
     rt->seq = 0;
 
     tcp_fd = url_get_file_handle(rtsp_hd);
@@ -1612,7 +1700,7 @@ redirect:
     return 0;
  fail:
     ff_rtsp_close_streams(s);
-    url_close(rt->rtsp_hd);
+    ff_rtsp_close_connections(s);
     if (reply->status_code >=300 && reply->status_code < 400 && s->iformat) {
         av_strlcpy(s->filename, reply->location, sizeof(s->filename));
         av_log(s, AV_LOG_INFO, "Status %d: Redirecting to %s\n",
@@ -1629,7 +1717,6 @@ redirect:
 static int rtsp_read_header(AVFormatContext *s,
                             AVFormatParameters *ap)
 {
-    RTSPState *rt = s->priv_data;
     int ret;
 
     ret = ff_rtsp_connect(s);
@@ -1641,7 +1728,7 @@ static int rtsp_read_header(AVFormatContext *s,
     } else {
          if (rtsp_read_play(s) < 0) {
             ff_rtsp_close_streams(s);
-            url_close(rt->rtsp_hd);
+            ff_rtsp_close_connections(s);
             return AVERROR_INVALIDDATA;
         }
     }
@@ -1986,7 +2073,7 @@ static int rtsp_read_close(AVFormatContext *s)
     ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL);
 
     ff_rtsp_close_streams(s);
-    url_close(rt->rtsp_hd);
+    ff_rtsp_close_connections(s);
     ff_network_close();
     return 0;
 }