Hi,

On Sun, Jun 28, 2015 at 10:06 PM, Nicolas George <geo...@nsup.org> wrote:
> Le decadi 10 messidor, an CCXXIII, Stephan Holljes a écrit :
>> Hi,
>> attached patches are the current state of work.
>> It's probably still a bit rough around the edges, but I think I made
>> some progress.
>> The sample code in doc/examples does not write any data as of yet, but
>> the HTTP handshake works.
>
> A few quick remarks, without entering into the fine details since the patch
> series will likely evolve still quite a bit.
>
>> From b43aeaa27f6ca7df476aa194b2f78aa1b49516d0 Mon Sep 17 00:00:00 2001
>> From: Stephan Holljes <klaxa1...@googlemail.com>
>> Date: Sun, 28 Jun 2015 06:06:16 +0200
>> Subject: [PATCH 01/10] lavf/network: split ff_listen_bind into ff_listen and
>>  ff_accept
>>
>> Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
>> ---
>>  libavformat/network.c | 27 +++++++++++++++++++++------
>>  libavformat/network.h |  4 ++++
>>  2 files changed, 25 insertions(+), 6 deletions(-)
>
> This one looks good to me, except a small doxy would be nice for the two new
> functions.
>
>> From 39faa1ea315bb51452446e291fd5d93d7eb3a988 Mon Sep 17 00:00:00 2001
>> From: Stephan Holljes <klaxa1...@googlemail.com>
>> Date: Sun, 28 Jun 2015 06:12:37 +0200
>> Subject: [PATCH 02/10] lavf/avio: add ffurl_accept and ffurl_handshake
>>
>> Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
>> ---
>>  libavformat/avio.c | 15 +++++++++++++++
>>  libavformat/url.h  | 12 ++++++++++++
>>  2 files changed, 27 insertions(+)
>
> ffurl_accept() requires two arguments, but I suspect ffurl_handshake() only
> requires one, the client. A doxy would be nice for ffurl_handshake().
>
> Also, the client argument of ffurl_accept() and url_accept() should probably
> a pointer to pointer, like ffurl_alloc(), so that it can allocate the
> context itself.

Fixed here and in all instances. This took me a lot longer than I want to admit.

>
>> From 8b473ba9acffecf98f8252eeccb413bcfbbf38c5 Mon Sep 17 00:00:00 2001
>> From: Stephan Holljes <klaxa1...@googlemail.com>
>> Date: Sun, 28 Jun 2015 06:13:36 +0200
>> Subject: [PATCH 03/10] lavf/avio: add avio_accept and avio_handshake
>>
>> Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
>> ---
>>  libavformat/avio.h    |  2 ++
>>  libavformat/aviobuf.c | 21 +++++++++++++++++++++
>>  2 files changed, 23 insertions(+)
>
> That part looks mostly good.
>
>> From 8ba3d1ef528cdd9209764b0f696b8df81ea46870 Mon Sep 17 00:00:00 2001
>> From: Stephan Holljes <klaxa1...@googlemail.com>
>> Date: Sun, 28 Jun 2015 06:16:02 +0200
>> Subject: [PATCH 04/10] lavf/http: add http_accept and http_handshake
>>
>> Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
>> ---
>>  libavformat/http.c | 38 ++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 38 insertions(+)
>
> I believe this patch can not come before the one for TCP.
>
>>
>> diff --git a/libavformat/http.c b/libavformat/http.c
>> index 676bfd5..7219f08 100644
>> --- a/libavformat/http.c
>> +++ b/libavformat/http.c
>> @@ -382,6 +382,38 @@ static int http_open(URLContext *h, const char *uri, 
>> int flags,
>>      return ret;
>>  }
>>
>> +static int http_accept(URLContext *s, URLContext *c)
>> +{
>> +    int ret;
>> +    HTTPContext *sh = s->priv_data;
>> +    HTTPContext *ch = c->priv_data;
>> +    URLContext *sl = sh->hd;
>> +    URLContext *cl;
>
>> +    if ((ret = ffurl_alloc(&cl, sl->filename, AVIO_FLAG_READ_WRITE, 
>> &sl->interrupt_callback)) < 0)
>> +        goto fail;
>> +    if ((ret = ffurl_accept(sl, cl)) < 0)
>> +        goto fail;
>> +    ch->hd = cl;
>> +fail:
>> +    return ret;
>> +}
>
> This looks mostly correct, but I suspect it would be more convenient to make
> url_accept() responsible for allocating the client context. Otherwise, the
> application is responsible for allocating a client context with the correct
> protocol and settings, this is fragile.
>
>> +
>> +static int http_handshake(URLContext *s, URLContext *c) {
>> +    int ret, err, new_location;
>> +    HTTPContext *sh = s->priv_data;
>> +    HTTPContext *ch = c->priv_data;
>> +    URLContext *sl = sh->hd;
>> +    URLContext *cl = ch->hd;
>> +    static const char header[] = "HTTP/1.1 200 OK\r\nContent-Type: 
>> application/octet-stream\r\nTransfer-Encoding: chunked\r\n\r\n";
>> +    if ((err = http_read_header(c, &new_location)) < 0)
>> +        goto fail;
>> +    if ((ret = ffurl_write(cl, header, strlen(header))) < 0)
>> +        goto fail;
>> +fail:
>> +    handle_http_errors(c, err);
>> +    return ret;
>> +}
>
> As you can see, the s argument is never used.
>
> Also, it should probably call ffurl_handshake() on the underlying socket:
> for TCP it is a nop, but for TLS, for example, it is blocking.
>
>> +
>>  static int http_getc(HTTPContext *s)
>>  {
>>      int len;
>> @@ -1346,6 +1378,8 @@ HTTP_CLASS(http);
>>  URLProtocol ff_http_protocol = {
>>      .name                = "http",
>>      .url_open2           = http_open,
>> +    .url_accept          = http_accept,
>> +    .url_handshake       = http_handshake,
>>      .url_read            = http_read,
>>      .url_write           = http_write,
>>      .url_seek            = http_seek,
>> @@ -1364,6 +1398,8 @@ HTTP_CLASS(https);
>>  URLProtocol ff_https_protocol = {
>>      .name                = "https",
>>      .url_open2           = http_open,
>> +    .url_accept          = http_accept,
>> +    .url_handshake       = http_handshake,
>>      .url_read            = http_read,
>>      .url_write           = http_write,
>>      .url_seek            = http_seek,
>> @@ -1477,6 +1513,8 @@ static int http_proxy_write(URLContext *h, const 
>> uint8_t *buf, int size)
>>  URLProtocol ff_httpproxy_protocol = {
>>      .name                = "httpproxy",
>>      .url_open            = http_proxy_open,
>> +    .url_accept          = http_accept,
>> +    .url_handshake       = http_handshake,
>>      .url_read            = http_buf_read,
>>      .url_write           = http_proxy_write,
>>      .url_close           = http_proxy_close,
>> --
>> 2.1.0
>>
>
>> From cfd27b21cf9fae39d881608a3ba379e6fb75848c Mon Sep 17 00:00:00 2001
>> From: Stephan Holljes <klaxa1...@googlemail.com>
>> Date: Sun, 28 Jun 2015 06:17:12 +0200
>> Subject: [PATCH 05/10] lavf/tcp: make tcp_open return with a listening socket
>>  without calling accept()
>>
>> ---
>>  libavformat/tcp.c | 4 +---
>>  1 file changed, 1 insertion(+), 3 deletions(-)
>
> This would break existing code. You suggested using a different value for
> listen, what happened to that idea?

This is implemented now, rather elegantly I think even.

>
>> From dd197651d205b2dece97798e933974ecef3a2b7f Mon Sep 17 00:00:00 2001
>> From: Stephan Holljes <klaxa1...@googlemail.com>
>> Date: Sun, 28 Jun 2015 06:20:17 +0200
>> Subject: [PATCH 06/10] lavf/tcp: add tcp_accept
>>
>> Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
>> ---
>>  libavformat/tcp.c | 14 ++++++++++++++
>>  1 file changed, 14 insertions(+)
>
> No extra remark for that patch, but the suggestion of having url_accept()
> responsible for allocating the client context applies here too.
>
>> From 5ab3661637c1ba571bc7f7bf365e3f3c8bc4ae89 Mon Sep 17 00:00:00 2001
>> From: Stephan Holljes <klaxa1...@googlemail.com>
>> Date: Sun, 28 Jun 2015 06:25:35 +0200
>> Subject: [PATCH 07/10] lavf/http: remove connection logic from http_listen()
>>
>> Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
>> ---
>>  libavformat/http.c | 10 +---------
>>  1 file changed, 1 insertion(+), 9 deletions(-)
>
> No remark for this for now.
>
>> From b835a248bba5004ad8f8a598992fa959881b2376 Mon Sep 17 00:00:00 2001
>> From: Stephan Holljes <klaxa1...@googlemail.com>
>> Date: Sun, 28 Jun 2015 06:26:43 +0200
>> Subject: [PATCH 08/10] lavf/http: ignore 0 in handle_http_errors()
>>
>> Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
>> ---
>>  libavformat/http.c | 2 ++
>>  1 file changed, 2 insertions(+)
>
> It should probably be merged with the patch that makes it necessary.
>
>> From 127b0ef456d203bc295ef017737019c0f8329515 Mon Sep 17 00:00:00 2001
>> From: Stephan Holljes <klaxa1...@googlemail.com>
>> Date: Sun, 28 Jun 2015 06:39:13 +0200
>> Subject: [PATCH 09/10] lavf/http: only shut down the connection when it's a
>>  client.
>>
>> Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
>> ---
>>  libavformat/http.c | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> Ditto.
>
>> From df4b466693b8d8627cca59a17d9e7ab5fd5e843e Mon Sep 17 00:00:00 2001
>> From: Stephan Holljes <klaxa1...@googlemail.com>
>> Date: Sun, 28 Jun 2015 06:39:56 +0200
>> Subject: [PATCH 10/10] doc/examples: WIP: add http_multiclient example code.
>>
>> Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
>> ---
>>  doc/examples/Makefile           |  1 +
>>  doc/examples/http_multiclient.c | 60 
>> +++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 61 insertions(+)
>>  create mode 100644 doc/examples/http_multiclient.c
>>
>> diff --git a/doc/examples/Makefile b/doc/examples/Makefile
>> index 9699f11..8c9501b 100644
>> --- a/doc/examples/Makefile
>> +++ b/doc/examples/Makefile
>> @@ -18,6 +18,7 @@ EXAMPLES=       avio_list_dir                      \
>>                  extract_mvs                        \
>>                  filtering_video                    \
>>                  filtering_audio                    \
>> +                http_multiclient                   \
>>                  metadata                           \
>>                  muxing                             \
>>                  remuxing                           \
>> diff --git a/doc/examples/http_multiclient.c 
>> b/doc/examples/http_multiclient.c
>> new file mode 100644
>> index 0000000..215a8bb
>> --- /dev/null
>> +++ b/doc/examples/http_multiclient.c
>> @@ -0,0 +1,60 @@
>> +#include <libavformat/avformat.h>
>> +
>> +int main(int argc, char **argv)
>> +{
>
>> +    AVOutputFormat *ofmt = NULL;
>> +    AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
>
> You could probably dispense with the muxers and demuxers and work directly
> with bytes. That would make the code much simpler.

This might be a stupid question, but how would I go about that? Just
use open() and read() from stdio.h or are there structs that allow me
to do that even more easily?

>
>> +    AVDictionary *options = NULL;
>> +    AVPacket pkt;
>> +    AVIOContext *c = NULL;
>> +    const char *in_filename, *out_uri;
>> +    int ret;
>> +
>> +    if (argc < 3) {
>> +        printf("usage: %s input http[s]://hostname[:port]\n"
>> +               "API example program to serve http to multiple clients.\n"
>> +               "The output format is guessed according to the input file 
>> extension.\n"
>> +               "\n", argv[0]);
>> +        return 1;
>> +    }
>> +
>> +    in_filename = argv[1];
>> +    out_uri = argv[2];
>> +
>> +    av_register_all();
>> +    avformat_network_init();
>> +
>> +    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
>> +        fprintf(stderr, "Could not open input file '%s'", in_filename);
>> +        goto end;
>> +    }
>> +
>> +    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
>> +        fprintf(stderr, "Failed to retrieve input stream information");
>> +        goto end;
>> +    }
>> +
>> +    avformat_alloc_output_context2(&ofmt_ctx, NULL, 
>> ifmt_ctx->iformat->name, out_uri);
>> +    if (!ofmt_ctx) {
>> +        fprintf(stderr, "Could not create output context\n");
>> +        ret = AVERROR_UNKNOWN;
>> +        goto end;
>> +    }
>> +
>> +    ofmt = ofmt_ctx->oformat;
>> +    av_dict_set(&options, "listen", "1", 0);
>> +    ret = avio_open2(&ofmt_ctx->pb, out_uri, AVIO_FLAG_READ_WRITE, NULL, 
>> &options);
>
>> +    avio_accept(ofmt_ctx->pb, &c);
>> +    avio_handshake(ofmt_ctx->pb, c);
>> +    avio_close(c);
>
> To test the multi-client API, you need to accept several clients. You can
> probably just put that in a while(1) loop, but it would be better to fork a
> thread for each client.
>
>> +
>> +end:
>> +    avformat_close_input(&ifmt_ctx);
>> +
>> +    if (ofmt_ctx)
>> +        avio_closep(&ofmt_ctx->pb);
>> +    avformat_free_context(ofmt_ctx);
>> +    if (ret < 0 && ret != AVERROR_EOF)
>> +        return 1;
>> +    return 0;
>> +}
>
> Regards,
>
> --
>   Nicolas George
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>

Thanks again for the detailed review!
Regards,
Stephan
From b0f0caa700b8cbd0352304de703d8191bf0ac922 Mon Sep 17 00:00:00 2001
From: Stephan Holljes <klaxa1...@googlemail.com>
Date: Tue, 30 Jun 2015 07:42:14 +0200
Subject: [PATCH 1/8] lavf/network: split ff_listen_bind into ff_listen and
 ff_accept

Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
---
 libavformat/network.c | 27 +++++++++++++++++++++------
 libavformat/network.h | 21 +++++++++++++++++++++
 2 files changed, 42 insertions(+), 6 deletions(-)

diff --git a/libavformat/network.c b/libavformat/network.c
index 47ade8c..8d61746 100644
--- a/libavformat/network.c
+++ b/libavformat/network.c
@@ -187,12 +187,11 @@ int ff_socket(int af, int type, int proto)
     return fd;
 }
 
-int ff_listen_bind(int fd, const struct sockaddr *addr,
-                   socklen_t addrlen, int timeout, URLContext *h)
+int ff_listen(int fd, const struct sockaddr *addr,
+              socklen_t addrlen)
 {
     int ret;
     int reuse = 1;
-    struct pollfd lp = { fd, POLLIN, 0 };
     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) {
         av_log(NULL, AV_LOG_WARNING, "setsockopt(SO_REUSEADDR) failed\n");
     }
@@ -203,6 +202,13 @@ int ff_listen_bind(int fd, const struct sockaddr *addr,
     ret = listen(fd, 1);
     if (ret)
         return ff_neterrno();
+    return ret;
+}
+
+int ff_accept(int fd, int timeout, URLContext *h)
+{
+    int ret;
+    struct pollfd lp = { fd, POLLIN, 0 };
 
     ret = ff_poll_interrupt(&lp, 1, timeout, &h->interrupt_callback);
     if (ret < 0)
@@ -211,15 +217,24 @@ int ff_listen_bind(int fd, const struct sockaddr *addr,
     ret = accept(fd, NULL, NULL);
     if (ret < 0)
         return ff_neterrno();
-
-    closesocket(fd);
-
     if (ff_socket_nonblock(ret, 1) < 0)
         av_log(NULL, AV_LOG_DEBUG, "ff_socket_nonblock failed\n");
 
     return ret;
 }
 
+int ff_listen_bind(int fd, const struct sockaddr *addr,
+                   socklen_t addrlen, int timeout, URLContext *h)
+{
+    int ret;
+    if ((ret = ff_listen(fd, addr, addrlen)) < 0)
+        return ret;
+    ret = ff_accept(fd, timeout, h);
+    closesocket(fd);
+    return ret;
+
+}
+
 int ff_listen_connect(int fd, const struct sockaddr *addr,
                       socklen_t addrlen, int timeout, URLContext *h,
                       int will_try_next)
diff --git a/libavformat/network.h b/libavformat/network.h
index 86fb656..e684787 100644
--- a/libavformat/network.h
+++ b/libavformat/network.h
@@ -255,6 +255,27 @@ int ff_listen_bind(int fd, const struct sockaddr *addr,
                    URLContext *h);
 
 /**
+ * Bind to a file descriptor and return a listening socket without accepting connections.
+ * @param fd      First argument of bind().
+ * @param addr    Second argument of bind().
+ * @param addrlen Third argument of bind().
+ * @return        A blocking file descriptor on success
+ *                or an AVERROR on failure.
+ */
+int ff_listen(int fd, const struct sockaddr *addr, socklen_t addrlen);
+
+/**
+ * Poll for a single connection on the passed file descriptor.
+ * @param fd      The listening socket file descriptor.
+ * @param timeout Polling timeout in milliseconds.
+ * @param h       URLContext providing interrupt check
+ *                callback and logging context.
+ * @return        A non-blocking file descriptor on success
+ *                or an AVERROR on failure.
+ */
+int ff_accept(int fd, int timeout, URLContext *h);
+
+/**
  * Connect to a file descriptor and poll for result.
  *
  * @param fd       First argument of connect(),
-- 
2.1.0

From 330c0eedaede961e52a4a4d93b2211156bc15a69 Mon Sep 17 00:00:00 2001
From: Stephan Holljes <klaxa1...@googlemail.com>
Date: Tue, 30 Jun 2015 08:16:37 +0200
Subject: [PATCH 2/8] lavf/avio: add ffurl_accept and ffurl_handshake

Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
---
 libavformat/avio.c | 17 +++++++++++++++++
 libavformat/url.h  | 18 ++++++++++++++++++
 2 files changed, 35 insertions(+)

diff --git a/libavformat/avio.c b/libavformat/avio.c
index c188adc..63c8b75 100644
--- a/libavformat/avio.c
+++ b/libavformat/avio.c
@@ -211,6 +211,23 @@ int ffurl_connect(URLContext *uc, AVDictionary **options)
     return 0;
 }
 
+int ffurl_accept(URLContext *s, URLContext **c)
+{
+    int ret;
+    if ((ret = s->prot->url_accept(s, c)) < 0)
+        return ret;
+    (*c)->is_connected = 1;
+    return ret;
+
+}
+
+int ffurl_handshake(URLContext *c)
+{
+    if (c->prot->url_handshake)
+        return c->prot->url_handshake(c);
+    return 0;
+}
+
 #define URL_SCHEME_CHARS                        \
     "abcdefghijklmnopqrstuvwxyz"                \
     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"                \
diff --git a/libavformat/url.h b/libavformat/url.h
index 99a3201..4cf6892 100644
--- a/libavformat/url.h
+++ b/libavformat/url.h
@@ -58,6 +58,8 @@ typedef struct URLProtocol {
      * for those nested protocols.
      */
     int     (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
+    int     (*url_accept)(URLContext *s, URLContext **c);
+    int     (*url_handshake)(URLContext *c);
 
     /**
      * Read data from the protocol.
@@ -140,6 +142,22 @@ int ffurl_open(URLContext **puc, const char *filename, int flags,
                const AVIOInterruptCB *int_cb, AVDictionary **options);
 
 /**
+ * Accept an URLContext c on an URLContext s
+ * @param  s server context
+ * @param  c client context
+ * @return the accepted filedescriptor on success, ff_neterrno() on failure.
+ */
+int ffurl_accept(URLContext *s, URLContext **c);
+
+/**
+ * Performs a protocl handshake on the passed client context.
+ * @param  c the client context
+ * @return >= 0 on success or a negative value corresponding
+ *         to an AVERROR code on failure
+ */
+int ffurl_handshake(URLContext *c);
+
+/**
  * Read up to size bytes from the resource accessed by h, and store
  * the read bytes in buf.
  *
-- 
2.1.0

From 8d8cb5e32a12aedb489a36cf9a26d961244d36a0 Mon Sep 17 00:00:00 2001
From: Stephan Holljes <klaxa1...@googlemail.com>
Date: Tue, 30 Jun 2015 08:24:09 +0200
Subject: [PATCH 3/8] lavf/avio: add avio_accept and avio_handshake

Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
---
 libavformat/avio.h    | 16 ++++++++++++++++
 libavformat/aviobuf.c | 17 +++++++++++++++++
 2 files changed, 33 insertions(+)

diff --git a/libavformat/avio.h b/libavformat/avio.h
index d3d9bbd..667e3c5 100644
--- a/libavformat/avio.h
+++ b/libavformat/avio.h
@@ -648,4 +648,20 @@ struct AVBPrint;
  */
 int avio_read_to_bprint(AVIOContext *h, struct AVBPrint *pb, size_t max_size);
 
+/**
+ * Accepts and allocates a client context on a server context.
+ * @param  s the server context
+ * @param  c the client context
+ * @return   >= 0 on success or a negative value corresponding
+ *           to an AVERROR on failure
+ */
+int avio_accept(AVIOContext *s, AVIOContext **c);
+
+/**
+ * Performs a protocol dependent handshake
+ * @param  c the client context to perform the handshake on
+ * @return   >= 0 on success or a negative value corresponding
+ *           to an AVERROR on failure
+ */
+int avio_handshake(AVIOContext *c);
 #endif /* AVFORMAT_AVIO_H */
diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c
index ff85081..0591ba5 100644
--- a/libavformat/aviobuf.c
+++ b/libavformat/aviobuf.c
@@ -1021,6 +1021,23 @@ int avio_read_to_bprint(AVIOContext *h, AVBPrint *pb, size_t max_size)
     return 0;
 }
 
+int avio_accept(AVIOContext *s, AVIOContext **c)
+{
+    int ret;
+    URLContext *sc = s->opaque;
+    URLContext *cc;
+    ret = ffurl_accept(sc, &cc);
+    if (ret > 0)
+        ret = ffio_fdopen(c, cc);
+    return ret;
+}
+
+int avio_handshake(AVIOContext *c)
+{
+    URLContext *cc = c->opaque;
+    return ffurl_handshake(cc);
+}
+
 /* output in a dynamic buffer */
 
 typedef struct DynBuffer {
-- 
2.1.0

From b8071e7b6416c37fb05cfae29dd4a29ea71bc41e Mon Sep 17 00:00:00 2001
From: Stephan Holljes <klaxa1...@googlemail.com>
Date: Tue, 30 Jun 2015 09:28:48 +0200
Subject: [PATCH 4/8] lavf/tcp: add tcp_accept

Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
---
 libavformat/tcp.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/libavformat/tcp.c b/libavformat/tcp.c
index f24cad2..ddaecc9 100644
--- a/libavformat/tcp.c
+++ b/libavformat/tcp.c
@@ -163,6 +163,22 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
     return ret;
 }
 
+static int tcp_accept(URLContext *s, URLContext **c)
+{
+    TCPContext *sc = s->priv_data;
+    TCPContext *cc;
+    int ret;
+    if ((ret = ffurl_alloc(c, s->filename, AVIO_FLAG_READ_WRITE, &s->interrupt_callback)) < 0)
+        return ret;
+    cc = (*c)->priv_data;
+    ret = accept(sc->fd, NULL, NULL);
+    if (ret < 0) {
+        return ff_neterrno();
+    }
+    cc->fd = ret;
+    return ret;
+}
+
 static int tcp_read(URLContext *h, uint8_t *buf, int size)
 {
     TCPContext *s = h->priv_data;
@@ -223,6 +239,7 @@ static int tcp_get_file_handle(URLContext *h)
 URLProtocol ff_tcp_protocol = {
     .name                = "tcp",
     .url_open            = tcp_open,
+    .url_accept          = tcp_accept,
     .url_read            = tcp_read,
     .url_write           = tcp_write,
     .url_close           = tcp_close,
-- 
2.1.0

From e493cad7d123025a27f04721ba236593ea44738b Mon Sep 17 00:00:00 2001
From: Stephan Holljes <klaxa1...@googlemail.com>
Date: Tue, 30 Jun 2015 09:29:16 +0200
Subject: [PATCH 5/8] lavf/tcp: increase range for listen and call the
 underlying socket operations accordingly

Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
---
 libavformat/tcp.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/libavformat/tcp.c b/libavformat/tcp.c
index ddaecc9..a7a5d74 100644
--- a/libavformat/tcp.c
+++ b/libavformat/tcp.c
@@ -44,7 +44,7 @@ typedef struct TCPContext {
 #define D AV_OPT_FLAG_DECODING_PARAM
 #define E AV_OPT_FLAG_ENCODING_PARAM
 static const AVOption options[] = {
-    { "listen",          "Listen for incoming connections",  OFFSET(listen),         AV_OPT_TYPE_INT, { .i64 = 0 },     0,       1,       .flags = D|E },
+    { "listen",          "Listen for incoming connections",  OFFSET(listen),         AV_OPT_TYPE_INT, { .i64 = 0 },     0,       2,       .flags = D|E },
     { "timeout",     "set timeout (in microseconds) of socket I/O operations", OFFSET(rw_timeout),     AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
     { "listen_timeout",  "Connection awaiting timeout (in milliseconds)",      OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
     { NULL }
@@ -125,12 +125,17 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
         goto fail;
     }
 
-    if (s->listen) {
-        if ((ret = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
+    if (s->listen == 2) {
+        // multi-client
+        if ((ret = ff_listen(fd, cur_ai->ai_addr, cur_ai->ai_addrlen)) < 0) {
+            goto fail1;
+        }
+    } else if (s->listen == 1) {
+        // single client
+        if ((fd = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
                                   s->listen_timeout, h)) < 0) {
             goto fail1;
         }
-        fd = ret;
     } else {
         if ((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
                                      s->open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) {
-- 
2.1.0

From c8622b35795ea52dc0b25ce1d5d196d1d561e250 Mon Sep 17 00:00:00 2001
From: Stephan Holljes <klaxa1...@googlemail.com>
Date: Tue, 30 Jun 2015 09:58:09 +0200
Subject: [PATCH 6/8] lavf/http: add http_accept and http_handshake

Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
---
 libavformat/http.c | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/libavformat/http.c b/libavformat/http.c
index 676bfd5..9b67b0b 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -316,6 +316,21 @@ static void handle_http_errors(URLContext *h, int error)
     }
 }
 
+static int http_handshake(URLContext *c) {
+    int ret, err, new_location;
+    HTTPContext *ch = c->priv_data;
+    URLContext *cl = ch->hd;
+    static const char header[] = "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nTransfer-Encoding: chunked\r\n\r\n";
+    ffurl_handshake(cl);
+    if ((err = http_read_header(c, &new_location)) < 0)
+        goto fail;
+    if ((ret = ffurl_write(cl, header, strlen(header))) < 0)
+        goto fail;
+fail:
+    handle_http_errors(c, err);
+    return ret;
+}
+
 static int http_listen(URLContext *h, const char *uri, int flags,
                        AVDictionary **options) {
     HTTPContext *s = h->priv_data;
@@ -382,6 +397,23 @@ static int http_open(URLContext *h, const char *uri, int flags,
     return ret;
 }
 
+static int http_accept(URLContext *s, URLContext **c)
+{
+    int ret;
+    HTTPContext *sc = s->priv_data;
+    HTTPContext *cc;
+    URLContext *sl = sc->hd;
+    URLContext *cl;
+    if ((ret = ffurl_alloc(c, s->filename, AVIO_FLAG_READ_WRITE, &sl->interrupt_callback)) < 0)
+        goto fail;
+    cc = (*c)->priv_data;
+    if ((ret = ffurl_accept(sl, &cl)) < 0)
+        goto fail;
+    cc->hd = cl;
+fail:
+    return ret;
+}
+
 static int http_getc(HTTPContext *s)
 {
     int len;
@@ -1346,6 +1378,8 @@ HTTP_CLASS(http);
 URLProtocol ff_http_protocol = {
     .name                = "http",
     .url_open2           = http_open,
+    .url_accept          = http_accept,
+    .url_handshake       = http_handshake,
     .url_read            = http_read,
     .url_write           = http_write,
     .url_seek            = http_seek,
@@ -1364,6 +1398,8 @@ HTTP_CLASS(https);
 URLProtocol ff_https_protocol = {
     .name                = "https",
     .url_open2           = http_open,
+    .url_accept          = http_accept,
+    .url_handshake       = http_handshake,
     .url_read            = http_read,
     .url_write           = http_write,
     .url_seek            = http_seek,
@@ -1477,6 +1513,8 @@ static int http_proxy_write(URLContext *h, const uint8_t *buf, int size)
 URLProtocol ff_httpproxy_protocol = {
     .name                = "httpproxy",
     .url_open            = http_proxy_open,
+    .url_accept          = http_accept,
+    .url_handshake       = http_handshake,
     .url_read            = http_buf_read,
     .url_write           = http_proxy_write,
     .url_close           = http_proxy_close,
-- 
2.1.0

From b183b01c2bd1978d922f9516f59b200415285823 Mon Sep 17 00:00:00 2001
From: Stephan Holljes <klaxa1...@googlemail.com>
Date: Tue, 30 Jun 2015 11:12:15 +0200
Subject: [PATCH 7/8] lavf/http: increase range for listen, add http_handshake
 and move handshake logic there

Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
---
 libavformat/http.c | 24 +++++++++++-------------
 1 file changed, 11 insertions(+), 13 deletions(-)

diff --git a/libavformat/http.c b/libavformat/http.c
index 9b67b0b..f6a6620 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -128,7 +128,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 or set the expected HTTP method from a client", OFFSET(method), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | 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 | E },
+    { "listen", "listen on HTTP", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, D | E },
     { NULL }
 };
 
@@ -306,6 +306,8 @@ static void handle_http_errors(URLContext *h, int error)
     HTTPContext *s = h->priv_data;
     if (h->is_connected) {
         switch(error) {
+            case 0:
+                break;
             case AVERROR_HTTP_BAD_REQUEST:
                 ffurl_write(s->hd, bad_request, strlen(bad_request));
                 break;
@@ -335,11 +337,10 @@ static int http_listen(URLContext *h, const char *uri, int flags,
                        AVDictionary **options) {
     HTTPContext *s = h->priv_data;
     int ret;
-    static const char header[] = "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nTransfer-Encoding: chunked\r\n\r\n";
     char hostname[1024], proto[10];
     char lower_url[100];
     const char *lower_proto = "tcp";
-    int port, new_location;
+    int port;
     s->chunked_post = 1;
     av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port,
                  NULL, 0, uri);
@@ -347,20 +348,17 @@ static int http_listen(URLContext *h, const char *uri, int flags,
         lower_proto = "tls";
     ff_url_join(lower_url, sizeof(lower_url), lower_proto, NULL, hostname, port,
                 NULL);
-    av_dict_set(options, "listen", "1", 0);
+    av_dict_set_int(options, "listen", s->listen, 0);
     if ((ret = ffurl_open(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
                           &h->interrupt_callback, options)) < 0)
         goto fail;
-    if ((ret = http_read_header(h, &new_location)) < 0)
-         goto fail;
-    if ((ret = ffurl_write(s->hd, header, strlen(header))) < 0)
-         goto fail;
-    return 0;
-
+    if (s->listen == 1) {
+        // single client
+        ret = http_handshake(h);
+    }
 fail:
-    handle_http_errors(h, ret);
     av_dict_free(&s->chained_options);
-    return ret;
+    return 0;
 }
 
 static int http_open(URLContext *h, const char *uri, int flags,
@@ -1295,7 +1293,7 @@ static int http_close(URLContext *h)
     av_freep(&s->inflate_buffer);
 #endif /* CONFIG_ZLIB */
 
-    if (!s->end_chunked_post)
+    if ((s->listen != 2 && !s->end_chunked_post))
         /* Close the write direction by sending the end of chunked encoding. */
         ret = http_shutdown(h, h->flags);
 
-- 
2.1.0

From 803a41609cf37ee44763ada91ae31142873a845a Mon Sep 17 00:00:00 2001
From: Stephan Holljes <klaxa1...@googlemail.com>
Date: Tue, 30 Jun 2015 11:12:58 +0200
Subject: [PATCH 8/8] doc/protocols: document experimental mutli-client api

Signed-off-by: Stephan Holljes <klaxa1...@googlemail.com>
---
 doc/protocols.texi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/doc/protocols.texi b/doc/protocols.texi
index 453dbcf..39a132a 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -292,6 +292,8 @@ autodetection in the future.
 If set to 1 enables experimental HTTP server. This can be used to send data when
 used as an output option, or read data from a client with HTTP POST when used as
 an input option.
+If set to 2 enables experimental mutli-client HTTP server. This is not yet implemented
+in ffmpeg.c or ffserver.c and thus must not be used as a command line option.
 @example
 # Server side (sending):
 ffmpeg -i somefile.ogg -c copy -listen 1 -f ogg http://@var{server}:@var{port}
-- 
2.1.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to