[FFmpeg-devel] [GSoC] Proof-of-concept HTTP Server

Stephan Holljes klaxa1337 at googlemail.com
Sat Mar 21 23:00:09 CET 2015


Hi,

this is a patch for a proof-of-concept for an http server.

Usage on the server side:

ffmpeg -i test.mp3 -c copy -listen 1 -f mp3 http://0.0.0.0

Usage on the client side:

ffplay http://localhost:8080

I looked at tls.c and tcp.c and copied parts of the code.
Please comment.

Regards,
Stephan Holljes

---
 libavformat/http.c |  113
++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 83 insertions(+), 30 deletions(-)

diff --git a/libavformat/http.c b/libavformat/http.c
index da3c9be..d61e4e2 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -96,8 +96,12 @@ typedef struct HTTPContext {
     int send_expect_100;
     char *method;
     int reconnect;
+    int listen;
+    int fd;
+    int header_sent;
 } HTTPContext;

+
 #define OFFSET(x) offsetof(HTTPContext, x)
 #define D AV_OPT_FLAG_DECODING_PARAM
 #define E AV_OPT_FLAG_ENCODING_PARAM
@@ -127,6 +131,7 @@ static const AVOption options[] = {
     { "end_offset", "try to limit the request to bytes preceding this
offset", OFFSET(end_off), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D
},
     { "method", "Override the HTTP method", OFFSET(method),
AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
     { "reconnect", "auto reconnect after disconnect before EOF",
OFFSET(reconnect), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, D },
+    { "listen", "listen on HTTP", OFFSET(listen), AV_OPT_TYPE_INT, { .i64
= 0 }, 0, 1, D },
     { NULL }
 };

@@ -299,8 +304,10 @@ int ff_http_averror(int status_code, int
default_averror)
 static int http_open(URLContext *h, const char *uri, int flags,
                      AVDictionary **options)
 {
+    struct addrinfo hints = { 0 }, *ai;
     HTTPContext *s = h->priv_data;
-    int ret;
+    int ret = -1, fd;
+    char portstr[] = "8080"; // allow non-root users for now

     if( s->seekable == 1 )
         h->is_streamed = 0;
@@ -320,11 +327,39 @@ static int http_open(URLContext *h, const char *uri,
int flags,
             av_log(h, AV_LOG_WARNING,
                    "No trailing CRLF found in HTTP header.\n");
     }
+    if (s->listen) {
+        hints.ai_family = AF_UNSPEC;
+        hints.ai_socktype = SOCK_STREAM;
+        hints.ai_flags |= AI_PASSIVE;
+        ret = getaddrinfo(NULL, portstr, &hints, &ai);
+        if (ret) {
+            av_log(h, AV_LOG_ERROR, "borked");
+            return AVERROR(EIO);
+        }
+        fd = ff_socket(ai->ai_family,
+                       ai->ai_socktype,
+                       ai->ai_protocol);
+        if (fd < 0) {
+            ret = ff_neterrno();
+            freeaddrinfo(ai);
+            return -1;
+        }

-    ret = http_open_cnx(h, options);
-    if (ret < 0)
-        av_dict_free(&s->chained_options);
-    return ret;
+        fd = ff_listen_bind(fd, ai->ai_addr, ai->ai_addrlen, -1, h);
+        if (fd < 0) {
+            freeaddrinfo(ai);
+            return fd;
+        }
+        h->is_streamed = 1;
+        s->fd = fd;
+        freeaddrinfo(ai);
+        return 0;
+    } else {
+        ret = http_open_cnx(h, options);
+        if (ret < 0)
+            av_dict_free(&s->chained_options);
+        return ret;
+    }
 }

 static int http_getc(HTTPContext *s)
@@ -1102,25 +1137,40 @@ static int http_write(URLContext *h, const uint8_t
*buf, int size)
     char temp[11] = "";  /* 32-bit hex + CRLF + nul */
     int ret;
     char crlf[] = "\r\n";
+    char header[] = "HTTP 200 OK\r\n\r\n";
     HTTPContext *s = h->priv_data;
+    if (!s->listen) {
+        if (!s->chunked_post) {
+            /* non-chunked data is sent without any special encoding */
+            return ffurl_write(s->hd, buf, size);
+        }

-    if (!s->chunked_post) {
-        /* non-chunked data is sent without any special encoding */
-        return ffurl_write(s->hd, buf, size);
-    }
-
-    /* silently ignore zero-size data since chunk encoding that would
-     * signal EOF */
-    if (size > 0) {
-        /* upload data using chunked encoding */
-        snprintf(temp, sizeof(temp), "%x\r\n", size);
+        /* silently ignore zero-size data since chunk encoding that would
+         * signal EOF */
+        if (size > 0) {
+            /* upload data using chunked encoding */
+            snprintf(temp, sizeof(temp), "%x\r\n", size);

-        if ((ret = ffurl_write(s->hd, temp, strlen(temp))) < 0 ||
-            (ret = ffurl_write(s->hd, buf, size)) < 0          ||
-            (ret = ffurl_write(s->hd, crlf, sizeof(crlf) - 1)) < 0)
-            return ret;
+            if ((ret = ffurl_write(s->hd, temp, strlen(temp))) < 0 ||
+                (ret = ffurl_write(s->hd, buf, size)) < 0          ||
+                (ret = ffurl_write(s->hd, crlf, sizeof(crlf) - 1)) < 0)
+                return ret;
+        }
+        return size;
+    } else {
+        if (!s->header_sent) {
+            ret = send(s->fd, header, sizeof(header), MSG_NOSIGNAL);
+            s->header_sent = 1;
+            return ret < 0 ? ff_neterrno() : ret;
+        }
+        if (size > 0) {
+            ret = send(s->fd, buf, size, MSG_NOSIGNAL);
+            return ret < 0 ? ff_neterrno() : ret;
+        } else {
+            ret = -1;
+        }
+        return ret;
     }
-    return size;
 }

 static int http_shutdown(URLContext *h, int flags)
@@ -1143,20 +1193,23 @@ static int http_close(URLContext *h)
 {
     int ret = 0;
     HTTPContext *s = h->priv_data;
-
+    if (!s->listen) {
 #if CONFIG_ZLIB
-    inflateEnd(&s->inflate_stream);
-    av_freep(&s->inflate_buffer);
+        inflateEnd(&s->inflate_stream);
+        av_freep(&s->inflate_buffer);
 #endif /* CONFIG_ZLIB */

-    if (!s->end_chunked_post)
-        /* Close the write direction by sending the end of chunked
encoding. */
-        ret = http_shutdown(h, h->flags);
+        if (!s->end_chunked_post)
+            /* Close the write direction by sending the end of chunked
encoding. */
+            ret = http_shutdown(h, h->flags);

-    if (s->hd)
-        ffurl_closep(&s->hd);
-    av_dict_free(&s->chained_options);
-    return ret;
+        if (s->hd)
+            ffurl_closep(&s->hd);
+        av_dict_free(&s->chained_options);
+        return ret;
+    } else {
+        return shutdown(s->fd, SHUT_RDWR);
+    }
 }

 static int64_t http_seek_internal(URLContext *h, int64_t off, int whence,
int force_reconnect)


More information about the ffmpeg-devel mailing list