[FFmpeg-devel] [PATCH 2/7] lavu: new AVWriter API.

Stefano Sabatini stefasab at gmail.com
Thu Jun 16 02:22:48 EEST 2022


Preliminary quick review.

On date Wednesday 2021-04-21 14:27:01 +0200, Nicolas George wrote:
[...]
> --- /dev/null
> +++ b/libavutil/writer.c
> @@ -0,0 +1,443 @@
> +/*
> + * Copyright (c) 2021 Nicolas George
> + *
> + * 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 "avassert.h"
> +#include "log.h"
> +#include "writer.h"
> +
> +/***************************************************************************
> + * Generic API
> + ***************************************************************************/
> +
> +#define FIELDOK(st, f) ((char *)(&(st)->f + 1) <= (char *)(st) + (st)->self_size)
> +

> +#define methods_assert_abi(methods) av_assert1(FIELDOK(methods, flush))

"methods" is somehow confusing since it can contain also other things
(name, size, etc.). What about using something as class instead? Or
"implementation" to mean that the functionality is embedded there.

> +
> +static void printf_unchecked(AVWriter wr, const char *fmt, ...)
> +{
> +    va_list va;
> +
> +    va_start(va, fmt);
> +    wr.methods->vprintf(wr, fmt, va);
> +    va_end(va);
> +}
> +
> +static void write_or_discard(AVWriter wr, size_t buf_size, size_t write_size)
> +{
> +    av_assert1(wr.methods->advance_buffer);
> +    wr.methods->advance_buffer(wr, FFMIN(buf_size, write_size));
> +    if (write_size > buf_size && wr.methods->notify_discard)
> +        wr.methods->notify_discard(wr, write_size - buf_size);
> +}
> +

> +static void av_writer_impossible(AVWriter wr, const char *message)

you can possibly discard the av_ and rename to something as
notify_unsupported_operation() ("impossible" is confusing in this
context).

[...]
> +/***************************************************************************
> + * AVBufWriter - write to pre-allocated memory
> + ***************************************************************************/
> +
> +#define buf_writer_assert_abi(bwr) av_assert1(FIELDOK(bwr, pos))
> +

> +static size_t buf_writer_room(AVBufWriter *bwr)

inline?

> +{
> +    return bwr->pos < bwr->size ? bwr->size - bwr->pos - 1 : 0;
> +}
> +
> +static void buf_writer_write(AVWriter wr, const char *data, size_t size)
> +{

> +    AVBufWriter *bwr = wr.obj;
> +
> +    av_assert1(av_buf_writer_check(wr));
> +    buf_writer_assert_abi(bwr);

maybe factorize with a macro?

> +    size = FFMIN(buf_writer_room(bwr), size);
> +    memcpy(bwr->buf + bwr->pos, data, size);
> +    bwr->pos += size;
> +    bwr->buf[bwr->pos] = 0;
> +}
> +
> +static void buf_writer_vprintf(AVWriter wr, const char *fmt, va_list va)
> +{
> +    AVBufWriter *bwr = wr.obj;
> +    int ret;
> +
> +    av_assert1(av_buf_writer_check(wr));
> +    buf_writer_assert_abi(bwr);
> +    ret = vsnprintf(bwr->buf + bwr->pos, buf_writer_room(bwr) + 1, fmt, va);
> +    if (ret > 0)
> +        bwr->pos += ret;
> +}
> +
> +static char *buf_writer_get_buffer(AVWriter wr, size_t min_size, size_t *size)
> +{
> +    AVBufWriter *bwr = wr.obj;
> +
> +    av_assert1(av_buf_writer_check(wr));
> +    buf_writer_assert_abi(bwr);
> +    *size = bwr->size - 1 - bwr->pos;
> +    return bwr->buf + bwr->pos;
> +}
> +

> +static void buf_writer_advance_buffer(AVWriter wr, size_t size)
> +{
> +    AVBufWriter *bwr = wr.obj;
> +
> +    av_assert1(av_buf_writer_check(wr));
> +    buf_writer_assert_abi(bwr);
> +    bwr->pos += size;

> +    bwr->buf[bwr->pos] = 0;

buffer overflow, is this set needed?

> +}
> +
> +AV_WRITER_DEFINE_METHODS(/*public*/, AVBufWriter, av_buf_writer) {
> +    .self_size        = sizeof(AVWriterMethods),
> +    .name             = "AVBufWriter",
> +    .write            = buf_writer_write,
> +    .vprintf          = buf_writer_vprintf,
> +    .get_buffer       = buf_writer_get_buffer,
> +    .advance_buffer   = buf_writer_advance_buffer,
> +};
> +
> +AVBufWriter *av_buf_writer_init(AVBufWriter *bwr, char *buf, size_t size)
> +{
> +    buf_writer_assert_abi(bwr);
> +    bwr->buf       = buf;
> +    bwr->size      = size;
> +    bwr->pos       = 0;
> +    buf[0] = 0;
> +    return bwr;
> +}
> +

> +AVWriter av_buf_writer_wrap(AVBufWriter *bwr)
> +{
> +    AVWriter r = { av_buf_writer_get_methods(), bwr };

nit+++: wr for consistency

[...]

> +AVWriter av_dynbuf_writer_wrap(AVDynbufWriter *dwr)
> +{
> +    AVWriter r = { av_dynbuf_writer_get_methods(), dwr };
> +    dynbuf_writer_assert_abi(dwr);
> +    return r;
> +}

this also seems a common pattern and could be factorized:

#define AV_WRITER_DEFINE_WRAP(type_, name_) \
AVWriter av_##name_##_writer_wrap(type_, *wrimpl) \
{ \ 
    AVWriter wr = { av_##name_##_writer_get_methods(), wrimpl }; \
    type_##_writer_assert_abi(wrimpl); \
    return wr; \
}

[...]


More information about the ffmpeg-devel mailing list