[FFmpeg-devel] GSoC update

Nicolas George george at nsup.org
Tue Jun 23 17:25:59 CEST 2015


This has better go to the mailing list.

Le quintidi 5 messidor, an CCXXIII, Stephan Holljes a écrit :
> So far I have split ff_listen_bind() into ff_listen_bind() and
> ff_accept() and added an url_accept field to URLProtocol (see attached
> patch).

I do not believe this is the correct approach. ff_listen_bind() is an
internal helper that works specifically for sockets.

> I read our past exchanges for a number of times and I am somewhat at a
> loss as to where to allocate my additional Client(-Contexts).
> No sourcefile I have looked at stores multiple AVIOContexts or
> AVFormatContexts (or I have not seen it).
> I also could not find out how ffmpeg would even accept multiple
> clients. The ff_listen_bind() call is blocking on accept() and it is
> only called once.
> Maybe I'm also overlooking some documentation. Maybe I am also looking
> in the wrong place. The files I have looked at the most have been:
> url.h network.c/h, avio.c/h aviobuf.c and tcp.c.

You need to understand very well how it happens for plain Unix TCP clients.

First, the server create and prepares the server socket s, with socket(),
bind() and mostly listen(). There is also a list of client sockets c[i],
initially empty.

Then, indefinitely and in parallel:

  * call accept() on s: this returns a new c[i] added to the list;

  * perform read() / write() on each c[i], remove them from the list if they
    are finished.

Here, "in parallel" means either threads, forked processes, or emulated
parallelism with poll() and non-blocking operations. For lavf, non-blocking
operations do not work, so you can assume parallelism between multiple
clients is achieved with threads; although I very much would like that all
new networking code is input-driven and therefore ready for non-blocking.

Now, as for how to transpose it into lavf terms:

* Creating and preparing the server socket: avio_open2() with various
  options for the address and the "listen" option set.

* Accepting a new client: a new function that you have to design, probably
  avio_accept().

There are a few caveats for avio_accept():

* Creating a new AVFormatContext, like accept() creates a new socket. That
  is not really difficult, IMHO. It just means the API will look somewhat
  like that:

  int avio_accept(AVIOContext *s, AVIOContext **c, ...);

  The c parameter is just like the s parameter to avio_open2(): it allocates
  a new context if necessary and populates it.

* Except, the current API accepts a single client by replacing the server
  context. This must still be possible after the change. Maybe just
  overwriting the server context will work, but it must be considered.

* If you remember, a full TCP accept() requires three handshake packets: SYN
  from the client, ACK-SYN from the server and then ACK from the client. The
  kernel handles this in the background and accept() returns a new client
  only when the full handshake is done. In lavf, we do not have a kernel
  working in the background, so it must be explicit.

  We can look at how non-blocking connect() works: first call connect() on
  the socket, it returns EINPROGRESS immediately, the repeatedly call
  getsockopt(SO_ERROR) to check the status of the connection until it stops
  returning EAGAIN.

  I believe we can do the same: avio_accept(s, &c) to initiate the accept,
  and then avio_accept_connect(c) or something (feel free to suggest better
  names) to finish the handshake.

  For the particular case of the HTTP server, the avio_accept() part would
  call avio_accept() on the underlying socket (or TLS context), and the
  avio_accept_connect() would call avio_accept_connect() on the underlying
  context and then perform the handshake.

  In terms of application, the code would probably look something like this
  (error checks and stuff removed):

  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);
      };
  }

Hope this helps.

Regards,

-- 
  Nicolas George

> From 234199a18e0c3bfede5f04a9ade0a11c71061285 Mon Sep 17 00:00:00 2001
> From: Stephan Holljes <klaxa1337 at googlemail.com>
> Date: Tue, 23 Jun 2015 16:41:51 +0200
> Subject: [PATCH] lavf: Split ff_listen_bind into ff_listen_bind and ff_accept
>  and implement its usage in tcp.
> 
> Signed-off-by: Stephan Holljes <klaxa1337 at googlemail.com>
> ---
>  libavformat/network.c |  7 ++++---
>  libavformat/network.h | 12 +++++++++++-
>  libavformat/tcp.c     | 10 ++++++++++
>  libavformat/url.h     |  1 +
>  4 files changed, 26 insertions(+), 4 deletions(-)
> 
> diff --git a/libavformat/network.c b/libavformat/network.c
> index 47ade8c..8d9434f 100644
> --- a/libavformat/network.c
> +++ b/libavformat/network.c

> @@ -204,10 +204,11 @@ int ff_listen_bind(int fd, const struct sockaddr *addr,
>      if (ret)
>          return ff_neterrno();
>  
> -    ret = ff_poll_interrupt(&lp, 1, timeout, &h->interrupt_callback);
> -    if (ret < 0)
> -        return ret;
> +    return ff_poll_interrupt(&lp, 1, timeout, &h->interrupt_callback);
> +}

ff_poll_interrupt() belongs in ff_accept(), not ff_listen_bind(), since it
is blocking and required for each client.

>  
> +int ff_accept(int fd) {
> +    int ret;
>      ret = accept(fd, NULL, NULL);
>      if (ret < 0)
>          return ff_neterrno();
> diff --git a/libavformat/network.h b/libavformat/network.h
> index 86fb656..810319d 100644
> --- a/libavformat/network.h
> +++ b/libavformat/network.h

> @@ -247,7 +247,7 @@ int ff_is_multicast_address(struct sockaddr *addr);
>   * @param timeout Polling timeout in milliseconds.
>   * @param h       URLContext providing interrupt check
>   *                callback and logging context.
> - * @return        A non-blocking file descriptor on success
> + * @return        0 on success
>   *                or an AVERROR on failure.
>   */

This looks strange.

>  int ff_listen_bind(int fd, const struct sockaddr *addr,
> @@ -255,6 +255,16 @@ int ff_listen_bind(int fd, const struct sockaddr *addr,
>                     URLContext *h);
>  
>  /**
> + * Accept a filedescriptor on a server socket.
> + *
> + * @param fd      Server socket filedescriptor.
> + *
> + * @return        A non-blocking file descriptor on success
> + *                or an AVERROR on failure.
> + */
> +int ff_accept(int fd);
> +
> +/**
>   * Connect to a file descriptor and poll for result.
>   *
>   * @param fd       First argument of connect(),
> diff --git a/libavformat/tcp.c b/libavformat/tcp.c
> index f24cad2..168e5c5 100644
> --- a/libavformat/tcp.c
> +++ b/libavformat/tcp.c
> @@ -130,6 +130,9 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
>                                    s->listen_timeout, h)) < 0) {
>              goto fail1;
>          }
> +        if ((ret = ff_accept(fd)) < 0) {
> +            goto fail;
> +        }
>          fd = ret;
>      } else {
>          if ((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
> @@ -163,6 +166,12 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
>      return ret;
>  }
>  
> +static int tcp_accept(URLContext *h)
> +{
> +    TCPContext *s = h->priv_data;
> +    return ff_accept(s->fd);
> +}
> +
>  static int tcp_read(URLContext *h, uint8_t *buf, int size)
>  {
>      TCPContext *s = h->priv_data;
> @@ -223,6 +232,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,
> diff --git a/libavformat/url.h b/libavformat/url.h
> index 99a3201..5c82117 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);

If you do never access this field, there is no use to adding it yet.

>  
>      /**
>       * Read data from the protocol.
> -- 
> 2.1.0
> 

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20150623/7579ec6e/attachment.asc>


More information about the ffmpeg-devel mailing list