[FFmpeg-devel] [PATCH] libavformat: add "capture:" protocol using AVIO
Nicolas George
george at nsup.org
Tue Apr 11 12:19:53 EEST 2017
Le duodi 22 germinal, an CCXXV, Timothy Lee a écrit :
> Capture is an input stream capture protocol that dumps the input stream to a
> file. The default name of the output file is "capture.dat", but it can be
> changed using the "capture_file" option.
> ---
> Changelog | 1 +
> doc/protocols.texi | 23 ++++++++++
> libavformat/Makefile | 1 +
> libavformat/capture.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++
> libavformat/protocols.c | 1 +
> 5 files changed, 138 insertions(+)
> create mode 100644 libavformat/capture.c
Thanks, I like this version much better. See comments below.
>
> diff --git a/Changelog b/Changelog
> index e76b324f61..3c06e84185 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -36,6 +36,7 @@ version 3.3:
> - MPEG-7 Video Signature filter
> - Removed asyncts filter (use af_aresample instead)
> - Intel QSV-accelerated VP8 video decoding
> +- capture protocol
>
>
> version 3.2:
> diff --git a/doc/protocols.texi b/doc/protocols.texi
> index a7968ff56e..89a1f2afa8 100644
> --- a/doc/protocols.texi
> +++ b/doc/protocols.texi
> @@ -103,6 +103,29 @@ Cache the input stream to temporary file. It brings seeking capability to live s
> cache:@var{URL}
> @end example
>
> + at section capture
> +
> +Input stream capturing protocol.
> +
> +A capture URL has the form:
> + at example
> +capture:@var{URL}
> + at end example
> +
> +This protocol accepts the following option:
> + at table @option
> +
> + at item capture_file
> +Name of the capture file.
> +If not specified, the input stream will be written into @file{capture.dat}.
> +
> + at end table
> +
> +For example, to capture the input stream as @file{stream.sav} during playback:
> + at example
> +ffplay -capture_file stream.sav capture:@var{URL}
> + at end example
> +
> @section concat
>
> Physical concatenation protocol.
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index a1dae894fe..08d23baf95 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -549,6 +549,7 @@ OBJS-$(CONFIG_ASYNC_PROTOCOL) += async.o
> OBJS-$(CONFIG_APPLEHTTP_PROTOCOL) += hlsproto.o
> OBJS-$(CONFIG_BLURAY_PROTOCOL) += bluray.o
> OBJS-$(CONFIG_CACHE_PROTOCOL) += cache.o
> +OBJS-$(CONFIG_CAPTURE_PROTOCOL) += capture.o
> OBJS-$(CONFIG_CONCAT_PROTOCOL) += concat.o
> OBJS-$(CONFIG_CRYPTO_PROTOCOL) += crypto.o
> OBJS-$(CONFIG_DATA_PROTOCOL) += data_uri.o
> diff --git a/libavformat/capture.c b/libavformat/capture.c
> new file mode 100644
> index 0000000000..90daf40877
> --- /dev/null
> +++ b/libavformat/capture.c
> @@ -0,0 +1,112 @@
> +/*
> + * Input capture protocol.
> + * Copyright (c) 2017 Timothy Lee
> + *
> + * 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
> + */
> +
> +#include "libavutil/avstring.h"
> +#include "libavutil/opt.h"
> +#include "avformat.h"
> +#include "avio.h"
> +#include <fcntl.h>
> +#if HAVE_IO_H
> +#include <io.h>
> +#endif
> +#if HAVE_UNISTD_H
> +#include <unistd.h>
> +#endif
> +#include <sys/stat.h>
> +#include <stdlib.h>
> +#include "os_support.h"
> +#include "url.h"
> +
> +#ifndef O_BINARY
> +# define O_BINARY 0
> +#endif
Is there a reason you use direct I/O, and have to suffer all that
boilerplate code? You could achieve the same result with
avio_open2(&c->capture, c->capture_file), with extra features and less
code.
> +
> +typedef struct Context {
> + AVClass *class;
> + int fd;
> + int64_t count;
> + AVIOContext *io;
> + const char *capture_file;
> +} Context;
> +
> +static int capture_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
> +{
> + Context *c= h->priv_data;
> + c->fd = avpriv_open(c->capture_file, O_WRONLY|O_BINARY|O_TRUNC|O_CREAT, 0666);
> + if (c->fd < 0)
> + av_log(h, AV_LOG_ERROR, "Failed to create capture file\n");
I think it would be better to return an error than test fd everywhere.
> + av_strstart(arg, "capture:", &arg);
> + return avio_open2(&c->io, arg, flags, &h->interrupt_callback, options);
> +}
> +
> +static int capture_read(URLContext *h, unsigned char *buf, int size)
> +{
> + Context *c = h->priv_data;
> + int r = avio_read(c->io, buf, size);
> + if ((r > 0) && (c->fd >= 0))
> + {
Nit: braces on the same line.
> + if (write(c->fd, buf, r) == r)
> + c->count += r;
Depending on the kind of capture file, you may need to loop over write()
to avoid short writes and EINTR. Using avio takes care of it.
> + else
> + av_log(h, AV_LOG_ERROR, "Cannot write to capture file\n");
> + }
> + return r;
> +}
> +
> +static int64_t capture_seek(URLContext *h, int64_t pos, int whence)
> +{
> + Context *c = h->priv_data;
> + return avio_seek(c->io, pos, whence);
Maybe seek in the capture file too?
> +}
> +
> +static int capture_close(URLContext *h)
> +{
> + Context *c = h->priv_data;
> + av_log(h, AV_LOG_INFO, "Captured %"PRId64" bytes\n", c->count);
> + if (c->fd >= 0)
> + close(c->fd);
> + return avio_closep(&c->io);
> +}
> +
> +#define OFFSET(x) offsetof(Context, x)
> +#define D AV_OPT_FLAG_DECODING_PARAM
> +
> +static const AVOption options[] = {
> + { "capture_file", "Name of capture file", OFFSET(capture_file), AV_OPT_TYPE_STRING, { .str = "capture.dat" }, CHAR_MIN, CHAR_MAX, D },
> + {NULL},
> +};
> +
> +static const AVClass capture_context_class = {
> + .class_name = "Capture",
> + .item_name = av_default_item_name,
> + .option = options,
> + .version = LIBAVUTIL_VERSION_INT,
> +};
> +
> +const URLProtocol ff_capture_protocol = {
> + .name = "capture",
> + .url_open2 = capture_open,
> + .url_read = capture_read,
> + .url_seek = capture_seek,
> + .url_close = capture_close,
> + .priv_data_size = sizeof(Context),
> + .priv_data_class = &capture_context_class,
> +};
> diff --git a/libavformat/protocols.c b/libavformat/protocols.c
> index 8d3555ed52..0855588740 100644
> --- a/libavformat/protocols.c
> +++ b/libavformat/protocols.c
> @@ -26,6 +26,7 @@
> extern const URLProtocol ff_async_protocol;
> extern const URLProtocol ff_bluray_protocol;
> extern const URLProtocol ff_cache_protocol;
> +extern const URLProtocol ff_capture_protocol;
> extern const URLProtocol ff_concat_protocol;
> extern const URLProtocol ff_crypto_protocol;
> extern const URLProtocol ff_data_protocol;
Regards,
--
Nicolas George
More information about the ffmpeg-devel
mailing list