[FFmpeg-devel] [PATCH 9/9] Add http multi-client example code

Nicolas George george at nsup.org
Fri Jul 3 12:32:10 CEST 2015


Le quintidi 15 messidor, an CCXXIII, Stephan Holljes a écrit :
> Signed-off-by: Stephan Holljes <klaxa1337 at googlemail.com>
> ---
>  doc/examples/Makefile           |   1 +
>  doc/examples/http_multiclient.c | 101 ++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 102 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..fdecab4
> --- /dev/null
> +++ b/doc/examples/http_multiclient.c
> @@ -0,0 +1,101 @@
> +/*
> + * copyright (c) 2015 Stephan Holljes
> + *
> + * 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
> + */
> +
> +/**
> + * @file
> + * libavformat multi-client network API usage example.
> + *
> + * @example http_multiclient.c
> + * This example will serve a file without decoding or demuxing it over http.

> + * Multiple clients can connect and will receive the same file.

Did you test several simultaneous clients? It should work, because fork() is
a strong isolation, so it if works for one it works for many, but testing is
always better.

> + */
> +
> +#include <libavformat/avformat.h>
> +#include <unistd.h>
> +
> +int main(int argc, char **argv)
> +{
> +    AVDictionary *options = NULL;
> +    AVIOContext *input = NULL, *client = NULL, *server = NULL;
> +    const char *in_uri, *out_uri;
> +    int ret, pid, n;
> +    uint8_t buf[1024];
> +
> +    if (argc < 3) {
> +        printf("usage: %s input http://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"

I suspect this sentence is outdated.

> +               "\n", argv[0]);
> +        return 1;
> +    }
> +
> +    in_uri = argv[1];
> +    out_uri = argv[2];
> +
> +    av_register_all();
> +    avformat_network_init();
> +
> +    if ((ret = av_dict_set(&options, "listen", "2", 0)) < 0)
> +        goto end;
> +    if ((ret = avio_open2(&server, out_uri, AVIO_FLAG_READ_WRITE, NULL, &options)) < 0)
> +        goto end;
> +    fprintf(stderr, "Entering main loop.\n");
> +    for(;;) {
> +        if ((ret = avio_accept(server, &client)) < 0)
> +            goto end;
> +        fprintf(stderr, "Accepted client, forking process.\n");

> +        pid = fork();

Zombie apocalypse ahead!

Well, setting SIGCHLD to SIG_IGN is hardly portable, double-fork is ugly and
proper waiting is painful, so I guess we can leave the zombies for now in a
simple testing program, but a comment stating it would be useful.

> +        if (pid == 0) {

Maybe also:

    if (pid < 0) {
	perror("Fork failed");
	err = AVERROR(errno);
	goto end;
    }

> +            fprintf(stderr, "Opening input file.\n");
> +            if ((ret = avio_open2(&input, in_uri, AVIO_FLAG_READ, NULL, NULL)) < 0)
> +                goto end;
> +            fprintf(stderr, "In child.\n");
> +            avio_handshake(client);
> +            fprintf(stderr, "Handshake performed.\n");

It would feel more natural to perform the handshake before opening the input
file. Note that since this is your code, you are free to reject this kind of
stylistic remark. In this particular case, it will be necessary when
extending the code, because avio_handshake() will be responsible for getting
the request file name from the client.

Also: since this is fork()ing instead of pthread_create()ing, you probably
should close the server context in the client process. And you definitely
must close the client context in the server process.

> +            for(;;) {
> +                n = avio_read(input, buf, sizeof(buf));
> +                fprintf(stderr, "Read %d bytes from input.\n", n);

> +                if (n <= 0) {
> +                    fprintf(stderr, "n is negative, abort.\n");
> +                    break;
> +                }

n == 0 is not possible and not supposed to be possible according to the API.

n < 0 is a proper error code, but the particular code AVERROR_EOF should not
be treated as an error, i.e. not print an alarming error message. It usually
goes something like that:

    if (n < 0) {
	if (n == AVERROR_EOF)
	    break;
	av_log(ctx, AV_LOG_ERROR, "Error reading from input: %s\n",
	       av_err2str(n));
	goto fail;
    }

> +                fprintf(stderr, "Writing %d bytes of data.\n", n);
> +                avio_write(client, buf, n);
> +                avio_flush(client);
> +            }
> +            fprintf(stderr, "Flushing client\n");
> +            avio_flush(client);
> +            fprintf(stderr, "Closing client\n");
> +            avio_close(client);
> +            break;
> +        }
> +    }
> +
> +end:

> +    if (ret)
> +        fprintf(stderr, "Some errors occured.\n");

av_err2str() to know what exactly. And knowing where the error happened
would be nicer.

> +    if (server)
> +        avio_close(server);
> +    if (input)
> +        avio_close(input);

No need to check, avio_close() works for NULL.

> +    if (ret < 0 && ret != AVERROR_EOF)
> +        return 1;
> +    return 0;
> +}

Regards,

-- 
  Nicolas George
-------------- 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/20150703/7650f0c3/attachment.asc>


More information about the ffmpeg-devel mailing list