[FFmpeg-devel] GSoC update

Stephan Holljes klaxa1337 at googlemail.com
Thu Jun 25 21:09:21 CEST 2015


On Thu, Jun 25, 2015 at 11:12 AM, Nicolas George <george at nsup.org> wrote:
> Le sextidi 6 messidor, an CCXXIII, Stephan Holljes a écrit :
>> > * Creating and preparing the server socket: avio_open2() with various
>> >   options for the address and the "listen" option set.
>> Am I correct to understand that this has to return immediately,
>> because if avio_open2() does not return, I have no (server)
>> AVIOContext to call avio_accept() with?
>
> Yes. This is more or less equivalent to the socket(), bind() and listen()
> system calls, which are all instantaneous.
>
>> The HTTPContext will then not have any clients connected to it, which
>> is different from the current behaviour.
>
> Exactly.
>
>> >   av_dict_set(&options, "listen", "1");
>> >   avio_open2(&server, "http://:8080", &options);
>> >   while (1) {
>> >       avio_accept(server, &client);
>> >       thread_run {
>> >           avio_accept_connect(client);
>> >           communicate_with_the_client(client);
>> >           avio_close(client);
>> >       };
>> >   }
>> Would this still be in http.c? If my thinking is correct, this should
>> be in http_open() then, but only if http_listen() returns without
>> accepting an initial client.
>
> The code snippet above is an example for an application that will wish to
> use the API.
>
>> A lot of this I still can't wrap my head around. I am still not sure
>> about a lot of things, for example how and where to use threading to
>> serve clients in parallel,
>
> The threading to serve clients in parallel is for the applications. From
> your point of view, you only need to make sure that the contexts returned
> for separate clients are completely separate structures that do not share
> any data; this is the natural thing to do anyways.
>
>>                            or why exactly lavf has to do the
>> TCP-handshake manually and how.
>
> Not the TCP handshake, this was for explaining. The TCP handshake is handled
> by the kernel: this is the reason the socket API can do with a single
> accept() call. But the HTTP handshake needs to be done by the library.
>
>> Is the general idea to split the API at that point the right way to go?
>> In my current version this is also split.
>
> Not exactly at that point, but yes, you probably need to split that
> function.
>
>> From 280162f00ef407b3c85a332f45825bbdd558b32d Mon Sep 17 00:00:00 2001
>> From: Stephan Holljes <klaxa1337 at googlemail.com>
>> Date: Wed, 24 Jun 2015 07:27:40 +0200
>> Subject: [PATCH] lavf: (Incomplete) avio_accept
>>
>> Signed-off-by: Stephan Holljes <klaxa1337 at googlemail.com>
>> ---
>>  libavformat/avio.c    | 15 +++++++++++++++
>>  libavformat/aviobuf.c | 12 ++++++++++++
>>  libavformat/http.c    | 29 ++++++++++++++++++++++++++++-
>>  libavformat/network.c | 22 +++++++++++++++++-----
>>  libavformat/network.h |  5 +++++
>>  libavformat/tcp.c     |  6 ++++++
>>  libavformat/url.h     |  6 ++++++
>>  7 files changed, 89 insertions(+), 6 deletions(-)
>>
>> diff --git a/libavformat/avio.c b/libavformat/avio.c
>> index bd32944..e67588d 100644
>> --- a/libavformat/avio.c
>> +++ b/libavformat/avio.c
>> @@ -211,6 +211,21 @@ int ffurl_connect(URLContext *uc, AVDictionary **options)
>>      return 0;
>>  }
>>
>
>> +int ffurl_accept(URLContext *uc)
>
> You need to have two URLContext structures: one for the server, one for the
> new client.
>
>> +{
>> +    int err = uc->prot->url_accept(uc);
>> +    if (err)
>> +        return err;
>> +    uc->is_connected = 1;
>
>> +    /* We must be careful here as ffurl_seek() could be slow,
>> +     * for example for http */
>> +    if ((uc->flags & AVIO_FLAG_WRITE) || !strcmp(uc->prot->name, "file"))
>> +        if (!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0)
>> +            uc->is_streamed = 1;
>
> I do not understand the point of this code.
>
>> +    return 0;
>> +
>> +}
>> +
>>  #define URL_SCHEME_CHARS                        \
>>      "abcdefghijklmnopqrstuvwxyz"                \
>>      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"                \
>> diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c
>> index ff85081..84e85ff 100644
>> --- a/libavformat/aviobuf.c
>> +++ b/libavformat/aviobuf.c
>> @@ -932,6 +932,18 @@ int avio_open2(AVIOContext **s, const char *filename, int flags,
>>      return 0;
>>  }
>>
>> +/*int avio_accept(AVIOContext *s, AVIOContext **c, char *filename, int flags,
>> +                const AVIOInterruptCB *int_cb, AVDictionary **options)
>> +{
>> +    URLContext *h;
>> +    int err;
>> +
>> +    err = ffurl_open(&h, filename, flags, int_cb, options);
>> +    if (err < 0)
>> +        return err;
>> +    err = ffio_fdopen(s, h);
>> +} */
>> +
>>  int ffio_open2_wrapper(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags,
>>                         const AVIOInterruptCB *int_cb, AVDictionary **options)
>>  {
>> diff --git a/libavformat/http.c b/libavformat/http.c
>> index 676bfd5..14a639a 100644
>> --- a/libavformat/http.c
>> +++ b/libavformat/http.c
>> @@ -348,6 +348,27 @@ fail:
>>      return ret;
>>  }
>>
>> +static int http_accept(URLContext *h, const char *uri) {
>> +    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;
>> +    s->chunked_post = 1;
>> +    av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port,
>> +                 NULL, 0, uri);
>> +    if (!strcmp(proto, "https"))
>> +        lower_proto = "tls";
>> +    ff_url_join(lower_url, sizeof(lower_url), lower_proto, NULL, hostname, port,
>> +                NULL);
>
> All this is wrong: you do not recompute the address and lower-protocol on a
> per-client basis.
>
>> +    if ((ret = ffurl_accept(&s->hd)) < 0)
>> +        return ret;
>> +
>> +
>> +}
>> +
>>  static int http_open(URLContext *h, const char *uri, int flags,
>>                       AVDictionary **options)
>>  {
>> @@ -374,7 +395,12 @@ static int http_open(URLContext *h, const char *uri, int flags,
>>      }
>>
>>      if (s->listen) {
>> -        return http_listen(h, uri, flags, options);
>> +        int ret;
>> +        if ((ret = http_listen(h, uri, flags, options)) < 0)
>> +            return ret;
>
>> +        while (1) {
>> +            http_accept(h);
>> +        }
>
> Not here! The whole point of the multi-client API is to separate accept()
> from open()!
>
>>      }
>>      ret = http_open_cnx(h, options);
>>      if (ret < 0)
>> @@ -1477,6 +1503,7 @@ 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_read            = http_buf_read,
>>      .url_write           = http_proxy_write,
>>      .url_close           = http_proxy_close,
>> diff --git a/libavformat/network.c b/libavformat/network.c
>> index 47ade8c..381aafc 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 timeout, URLContext *h)
>>  {
>>      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,7 +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)
>>          return ret;
>> @@ -212,14 +217,21 @@ int ff_listen_bind(int fd, const struct sockaddr *addr,
>>      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, timeout, h)) < 0)
>> +        return ret;
>> +    return ff_accept(fd, timeout, h);
>> +}
>
> This looks correct at first glance.
>
>> +
>>  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..d090e8b 100644
>> --- a/libavformat/network.h
>> +++ b/libavformat/network.h
>> @@ -238,6 +238,11 @@ int ff_is_multicast_address(struct sockaddr *addr);
>>
>>  #define POLLING_TIME 100 /// Time in milliseconds between interrupt check
>>
>> +int ff_listen(int fd, const struct sockaddr *addr,
>> +              socklen_t addrlen, int timeout,
>> +              URLContext *h);
>> +int ff_accept(int fd, int timeout, URLContext *h);
>> +
>>  /**
>>   * Bind to a file descriptor and poll for a connection.
>>   *
>> diff --git a/libavformat/tcp.c b/libavformat/tcp.c
>> index f24cad2..a3392aa 100644
>> --- a/libavformat/tcp.c
>> +++ b/libavformat/tcp.c
>> @@ -163,6 +163,12 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
>>      return ret;
>>  }
>>
>
>> +static int tcp_accept(URLContext *h)
>
> Again: you need two context structures.
>
>> +{
>> +    TCPContext *s = h->priv_data;
>> +    return ff_accept(s->fd, s->listen_timeout, h);
>> +}
>> +
>>  static int tcp_read(URLContext *h, uint8_t *buf, int size)
>>  {
>>      TCPContext *s = h->priv_data;
>> diff --git a/libavformat/url.h b/libavformat/url.h
>> index 99a3201..714858e 100644
>> --- a/libavformat/url.h
>> +++ b/libavformat/url.h
>> @@ -58,6 +58,7 @@ typedef struct URLProtocol {
>>       * for those nested protocols.
>>       */
>>      int     (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
>> +    int     (*url_accept)(URLContext *h);
>>
>>      /**
>>       * Read data from the protocol.
>> @@ -140,6 +141,11 @@ int ffurl_open(URLContext **puc, const char *filename, int flags,
>>                 const AVIOInterruptCB *int_cb, AVDictionary **options);
>>
>>  /**
>> + * Accept a client socket on the URLContext
>> + */
>> +int ffurl_accept(URLContext *h);
>> +
>> +/**
>>   * Read up to size bytes from the resource accessed by h, and store
>>   * the read bytes in buf.
>>   *
>
> Regards,
>
> --
>   Nicolas George
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>

Thanks, I understand the datastructures and their interaction a lot
better now. I discussed it with a friend yesterday too and there a lot
of the things started to make more sense.
I'm currently working on the implementation, when questions arise I
will ask again.

Regards,
Stephan


More information about the ffmpeg-devel mailing list