Hi guys,

First attempt at $subj. Feng works, but Darwin still hangs.

First three are related to http, the last adds actual tunnelling
support to rtsp.

Josh
diff --git a/libavformat/http.c b/libavformat/http.c
index e697578..33a303b 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -26,6 +26,7 @@
 #include "internal.h"
 #include "network.h"
 #include "os_support.h"
+#include "http.h"
 #include "httpauth.h"
 
 /* XXX: POST protocol is not completely implemented because ffmpeg uses
@@ -53,7 +54,7 @@ static int http_write(URLContext *h, uint8_t *buf, int size);
 
 
 /* return non zero if error */
-static int http_open_cnx(URLContext *h)
+int ff_http_connect(URLContext *h)
 {
     const char *path, *proxy_path;
     char hostname[1024], hoststr[1024];
@@ -138,10 +139,13 @@ static int http_open(URLContext *h, const char *uri, int flags)
     memset(&s->auth_state, 0, sizeof(s->auth_state));
     av_strlcpy(s->location, uri, URL_SIZE);
 
-    ret = http_open_cnx(h);
-    if (ret != 0)
-        av_free (s);
-    return ret;
+    if (!(flags & URL_DELAYOPEN)) {
+        ret = ff_http_connect(h);
+        if (ret != 0)
+            av_free (s);
+        return ret;
+    }
+    return 0;
 }
 static int http_getc(HTTPContext *s)
 {
@@ -425,7 +429,7 @@ static int64_t http_seek(URLContext *h, int64_t off, int whence)
     s->off = off;
 
     /* if it fails, continue on old connection */
-    if (http_open_cnx(h) < 0) {
+    if (ff_http_connect(h) < 0) {
         memcpy(s->buffer, old_buf, old_buf_size);
         s->buf_ptr = s->buffer;
         s->buf_end = s->buffer + old_buf_size;
diff --git a/libavformat/http.h b/libavformat/http.h
new file mode 100644
index 0000000..9ef421c
--- /dev/null
+++ b/libavformat/http.h
@@ -0,0 +1,33 @@
+/*
+ * HTTP definitions
+ * Copyright (c) 2002 Fabrice Bellard
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef AVFORMAT_HTTP_H
+#define AVFORMAT_HTTP_H
+
+/**
+ * Opens the connection using an initialized HTTP context.
+ *
+ * @param h URL context for this HTTP connecton
+ *
+ * @return nonzero if error, 0 on success.
+ */
+int ff_http_connect(URLContext *h);
+
+#endif
diff --git a/libavformat/internal.h b/libavformat/internal.h
index 358959c..d40b6e0 100644
--- a/libavformat/internal.h
+++ b/libavformat/internal.h
@@ -29,6 +29,8 @@ typedef struct AVCodecTag {
     unsigned int tag;
 } AVCodecTag;
 
+#define URL_DELAYOPEN 4 /* follows URL_* scheme defined in avio.h */
+
 void ff_dynarray_add(intptr_t **tab_ptr, int *nb_ptr, intptr_t elem);
 
 #ifdef __GNUC__
diff --git a/libavformat/http.c b/libavformat/http.c
index f76e995..f4ddcae 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -46,12 +46,18 @@ typedef struct {
     int64_t off, filesize;
     char location[URL_SIZE];
     HTTPAuthState auth_state;
+    unsigned char headers[BUFFER_SIZE];
 } HTTPContext;
 
 static int http_connect(URLContext *h, const char *path, const char *hoststr,
                         const char *auth, int *new_location);
 static int http_write(URLContext *h, uint8_t *buf, int size);
 
+void ff_http_set_headers(URLContext *h, const char *headers)
+{
+    HTTPContext *s = h->priv_data;
+    snprintf(s->headers, sizeof(s->headers), "%s", headers);
+}
 
 /* return non zero if error */
 int ff_http_connect(URLContext *h)
@@ -135,6 +141,7 @@ int ff_http_init(URLContext *h, const char *uri, int flags)
     s->filesize = -1;
     s->chunksize = -1;
     s->off = 0;
+    *s->headers = '\0';
     memset(&s->auth_state, 0, sizeof(s->auth_state));
     av_strlcpy(s->location, uri, URL_SIZE);
 
@@ -272,8 +279,9 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr,
     post = h->flags & URL_WRONLY;
     authstr = ff_http_auth_create_response(&s->auth_state, auth, path,
                                         post ? "POST" : "GET");
-    snprintf(s->buffer, sizeof(s->buffer),
-             "%s %s HTTP/1.1\r\n"
+    if(!strlen(s->headers))
+    {
+    snprintf(s->headers, sizeof(s->headers),
              "User-Agent: %s\r\n"
              "Accept: */*\r\n"
              "Range: bytes=%"PRId64"-\r\n"
@@ -282,13 +290,20 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr,
              "Connection: close\r\n"
              "%s"
              "\r\n",
-             post ? "POST" : "GET",
-             path,
              LIBAVFORMAT_IDENT,
              s->off,
              hoststr,
              authstr ? authstr : "",
              post ? "Transfer-Encoding: chunked\r\n" : "");
+    }
+
+    snprintf(s->buffer, sizeof(s->buffer),
+             "%s %s HTTP/1.1\r\n"
+             "%s"
+             "\r\n",
+             post ? "POST" : "GET",
+             path,
+             s->headers);
 
     av_freep(&authstr);
     if (http_write(h, s->buffer, strlen(s->buffer)) < 0)
diff --git a/libavformat/http.h b/libavformat/http.h
index 25ae18b..1e78ec7 100644
--- a/libavformat/http.h
+++ b/libavformat/http.h
@@ -41,4 +41,12 @@ int ff_http_init(URLContext *h, const char *uri, int flags);
  */
 int ff_http_connect(URLContext *h);
 
+/**
+ * Sets custom HTTP headers. Note this overwrites all the default options.
+ *
+ * @param h URL context for this HTTP connection
+ * @param headers New headers to use for future requests.
+ */
+void ff_http_set_headers(URLContext *h, const char *headers);
+
 #endif
diff --git a/libavformat/http.c b/libavformat/http.c
index 55cd3a4..e9326de 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -47,6 +47,7 @@ typedef struct {
     char location[URL_SIZE];
     HTTPAuthState auth_state;
     unsigned char headers[BUFFER_SIZE];
+    int is_chunked;
 } HTTPContext;
 
 static int http_connect(URLContext *h, const char *path, const char *hoststr,
@@ -59,6 +60,11 @@ void ff_http_set_headers(URLContext *h, const char *headers)
     snprintf(s->headers, sizeof(s->headers), "%s", headers);
 }
 
+void ff_http_set_chunked_transfer_encoding(URLContext *h, int is_chunked)
+{
+    ((HTTPContext*)h->priv_data)->is_chunked = is_chunked;
+}
+
 /* return non zero if error */
 int ff_http_connect(URLContext *h)
 {
@@ -141,6 +147,7 @@ static int http_open(URLContext *h, const char *uri, int flags)
     h->priv_data = s;
     s->filesize = -1;
     s->chunksize = -1;
+    s->is_chunked = 1;
     s->off = 0;
     *s->headers = '\0';
     memset(&s->auth_state, 0, sizeof(s->auth_state));
@@ -393,6 +400,7 @@ static int http_write(URLContext *h, uint8_t *buf, int size)
      * signal EOF */
     if (size > 0) {
         /* upload data using chunked encoding */
+        if(s->is_chunked)
         snprintf(temp, sizeof(temp), "%x\r\n", size);
 
         if ((ret = url_write(s->hd, temp, strlen(temp))) < 0 ||
diff --git a/libavformat/http.h b/libavformat/http.h
index 96250b8..d98839d 100644
--- a/libavformat/http.h
+++ b/libavformat/http.h
@@ -38,4 +38,12 @@ int ff_http_connect(URLContext *h);
  */
 void ff_http_set_headers(URLContext *h, const char *headers);
 
+/**
+ * Enables or disables chunked transfer encoding. (default is enabled)
+ *
+ * @param h URL context for this HTTP connection
+ * @param is_chunked 0 to disable chunking, nonzero otherwise.
+ */
+void ff_http_set_chunked_transfer_encoding(URLContext *h, int is_chunked);
+
 #endif
diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c
index 6dbd796..e97d629 100644
--- a/libavformat/rtsp.c
+++ b/libavformat/rtsp.c
@@ -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 @@ void 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[(sizeof(buf)+2)/3*4+1];
 
+    /* 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,20 @@ 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 tunnelling */
+    if (rt->rtsp_hd_out != rt->rtsp_hd) {
+        if (!av_base64_encode(base64buf, sizeof(base64buf), buf, strlen(buf))) {
+            av_log(s, AV_LOG_ERROR, "Unable to base64 encode RTSP.\n");
+            return;
+        }
+        out_buf = base64buf;
+    }
+
     dprintf(s, "Sending:\n%s--\n", buf);
 
-    url_write(rt->rtsp_hd, buf, strlen(buf));
+    url_write(rt->rtsp_hd_out, out_buf, strlen(out_buf));
     if (send_content_length > 0 && send_content)
-        url_write(rt->rtsp_hd, send_content, send_content_length);
+        url_write(rt->rtsp_hd_out, send_content, send_content_length);
     rt->last_cmd_time = av_gettime();
 }
 
@@ -1454,18 +1468,25 @@ static int rtsp_setup_output_streams(AVFormatContext *s, const char *addr)
     return 0;
 }
 
+static inline void close_cxn(RTSPState *rt)
+{
+    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;
     char real_challenge[64];
     struct sockaddr_storage peer;
     socklen_t peer_len = sizeof(peer);
+    int is_http = 0;
 
     if (!ff_network_init())
         return AVERROR(EIO);
@@ -1499,6 +1520,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);
+                is_http = 1;
             } else {
                 /* Write options back into the buffer, using memmove instead
                  * of strcpy since the strings may overlap. */
@@ -1532,13 +1556,75 @@ redirect:
     ff_url_join(rt->control_uri, sizeof(rt->control_uri), "rtsp", NULL,
                 host, port, "%s", path);
 
+    if (is_http) {
+        /* set up initial handshake for tunnelling */
+        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 | URL_DELAYOPEN) < 0) {
+            err = AVERROR(EIO);
+            goto fail;
+        }
+
+        /* generate GET headers */
+        snprintf(headers, sizeof(headers),
+                 "User-Agent: %s\r\n"
+                 "x-sessioncookie: %s\r\n"
+                 "Accept: application/x-rtsp-tunnelled\r\n"
+                 "Pragma: no-cache\r\n"
+                 "Cache-Control: no-cache\r\n",
+                 LIBAVFORMAT_IDENT,
+                 sessioncookie);
+        ff_http_set_headers(rtsp_hd, headers);
+
+        /* complete the connection */
+        if (ff_http_connect(rtsp_hd)) {
+            err = AVERROR(EIO);
+            goto fail;
+        }
+
+        /* POST requests */
+        if (url_open(&rtsp_hd_out, httpname, URL_WRONLY | URL_DELAYOPEN) < 0 ) {
+            err = AVERROR(EIO);
+            goto fail;
+        }
+
+        /* generate POST headers */
+        snprintf(headers, sizeof(headers),
+                 "User-Agent: %s\r\n"
+                 "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",
+                 LIBAVFORMAT_IDENT,
+                 sessioncookie);
+        ff_http_set_headers(rtsp_hd_out, headers);
+        ff_http_set_chunked_transfer_encoding(rtsp_hd_out, 0);
+
+        /* complete the connection */
+        if (ff_http_connect(rtsp_hd_out)) {
+            err = AVERROR(EIO);
+            goto fail;
+        }
+    } 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) {
         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 +1698,7 @@ redirect:
     return 0;
  fail:
     ff_rtsp_close_streams(s);
-    url_close(rt->rtsp_hd);
+    close_cxn(rt);
     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",
@@ -1641,7 +1727,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);
+            close_cxn(rt);
             return AVERROR_INVALIDDATA;
         }
     }
@@ -1986,7 +2072,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);
+    close_cxn(rt);
     ff_network_close();
     return 0;
 }
diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h
index 357d3bf..1cbbec1 100644
--- a/libavformat/rtsp.h
+++ b/libavformat/rtsp.h
@@ -278,6 +278,10 @@ typedef struct RTSPState {
 
     /** The synchronized start time of the output streams. */
     int64_t start_time;
+
+    /** Additional output handler, used when input and output are done
+     * separately, eg for HTTP tunnelling. */
+    URLContext *rtsp_hd_out;
 } RTSPState;
 
 /**
_______________________________________________
FFmpeg-soc mailing list
[email protected]
https://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-soc

Reply via email to