[FFmpeg-devel] [FFmpeg-cvslog] fftools/resources: Add resource manager files with build-time compression

Ramiro Polla ramiro.polla at gmail.com
Fri May 30 14:23:40 EEST 2025


On Thu, May 15, 2025 at 11:11 PM softworkz <git at videolan.org> wrote:
>
> ffmpeg | branch: master | softworkz <softworkz at hotmail.com> | Thu May 15 23:07:54 2025 +0200| [517a8055655798970d94a4c5ea912511362520ea] | committer: softworkz
>
> fftools/resources: Add resource manager files with build-time compression
>
> Compression requires zlib to be available, otherwise resources will
> be included uncompressed - in either case via BIN2C.
>
> It can also be disabled via
>
> ./configure --disable-resource-compression
>
> Size figures:
>
> graph.css         7752
> graph.css.min     6655 (css is always minified)
> graph.html        2153
>
> No Compression
>
> graph.css.c      40026
> graph.css.o       9344 (6688)
> graph.html.c     13016
> graph.html.o      4848 (2186)
>
> With Compression
>
> graph.css.c      10206
> graph.css.o       4368 (1718)
> graph.html.c      5725
> graph.html.o      3632 (971)
>
> Numbers in brackets: .rodata size from 'size -Ax -d *.o'
>
> Signed-off-by: softworkz <softworkz at hotmail.com>
>
> > http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=517a8055655798970d94a4c5ea912511362520ea
> ---
>
>  configure                    |   5 +
>  ffbuild/common.mak           |  43 +++++-
>  fftools/Makefile             |   3 +-
>  fftools/resources/.gitignore |   4 +
>  fftools/resources/Makefile   |  13 ++
>  fftools/resources/graph.css  | 353 +++++++++++++++++++++++++++++++++++++++++++
>  fftools/resources/graph.html |  86 +++++++++++
>  fftools/resources/resman.c   | 231 ++++++++++++++++++++++++++++
>  fftools/resources/resman.h   |  50 ++++++
>  9 files changed, 785 insertions(+), 3 deletions(-)

[...]

> diff --git a/fftools/resources/resman.c b/fftools/resources/resman.c
> new file mode 100644
> index 0000000000..a9e21626fa
> --- /dev/null
> +++ b/fftools/resources/resman.c
> @@ -0,0 +1,231 @@
> +/*
> + * Copyright (c) 2025 - softworkz
> + *
> + * 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
> + * output writers for filtergraph details
> + */
> +
> +#include "config.h"
> +
> +#include <string.h>
> +
> +#if CONFIG_RESOURCE_COMPRESSION
> +#include <zlib.h>
> +#endif
> +
> +#include "resman.h"
> +#include "fftools/ffmpeg_filter.h"
> +#include "libavutil/avassert.h"
> +#include "libavutil/pixdesc.h"
> +#include "libavutil/dict.h"
> +#include "libavutil/common.h"
> +
> +extern const unsigned char ff_graph_html_data[];
> +extern const unsigned int ff_graph_html_len;
> +
> +extern const unsigned char ff_graph_css_data[];
> +extern const unsigned ff_graph_css_len;
> +
> +static const FFResourceDefinition resource_definitions[] = {
> +    [FF_RESOURCE_GRAPH_CSS]   = { FF_RESOURCE_GRAPH_CSS,   "graph.css",   &ff_graph_css_data[0],   &ff_graph_css_len   },
> +    [FF_RESOURCE_GRAPH_HTML]  = { FF_RESOURCE_GRAPH_HTML,  "graph.html",  &ff_graph_html_data[0],  &ff_graph_html_len  },
> +};
> +
> +
> +static const AVClass resman_class = {
> +    .class_name = "ResourceManager",
> +};
> +
> +typedef struct ResourceManagerContext {
> +    const AVClass *class;
> +    AVDictionary *resource_dic;
> +} ResourceManagerContext;
> +
> +static AVMutex mutex = AV_MUTEX_INITIALIZER;
> +
> +ResourceManagerContext *resman_ctx = NULL;

static

> +#if CONFIG_RESOURCE_COMPRESSION
> +
> +static int decompress_gzip(ResourceManagerContext *ctx, uint8_t *in, unsigned in_len, char **out, size_t *out_len)
> +{
> +    z_stream strm;
> +    unsigned chunk = 65534;
> +    int ret;
> +    uint8_t *buf;
> +
> +    *out = NULL;
> +    memset(&strm, 0, sizeof(strm));
> +
> +    // Allocate output buffer with extra byte for null termination
> +    buf = (uint8_t *)av_mallocz(chunk + 1);

Unnecessary cast.

> +    if (!buf) {
> +        av_log(ctx, AV_LOG_ERROR, "Failed to allocate decompression buffer\n");
> +        return AVERROR(ENOMEM);
> +    }
> +
> +    // 15 + 16 tells zlib to detect GZIP or zlib automatically
> +    ret = inflateInit2(&strm, 15 + 16);
> +    if (ret != Z_OK) {
> +        av_log(ctx, AV_LOG_ERROR, "Error during zlib initialization: %s\n", strm.msg);
> +        av_free(buf);
> +        return AVERROR(ENOSYS);
> +    }
> +
> +    strm.avail_in  = in_len;
> +    strm.next_in   = in;
> +    strm.avail_out = chunk;
> +    strm.next_out  = buf;
> +
> +    ret = inflate(&strm, Z_FINISH);
> +    if (ret != Z_OK && ret != Z_STREAM_END) {
> +        av_log(ctx, AV_LOG_ERROR, "Inflate failed: %d, %s\n", ret, strm.msg);
> +        inflateEnd(&strm);
> +        av_free(buf);
> +        return (ret == Z_STREAM_END) ? Z_OK : ((ret == Z_OK) ? Z_BUF_ERROR : ret);
> +    }
> +
> +    if (strm.avail_out == 0) {
> +        // TODO: Error or loop decoding?

^^

> +        av_log(ctx, AV_LOG_WARNING, "Decompression buffer may be too small\n");
> +    }
> +
> +    *out_len = chunk - strm.avail_out;
> +    buf[*out_len] = 0; // Ensure null termination
> +
> +    inflateEnd(&strm);
> +    *out = (char *)buf;
> +    return Z_OK;
> +}
> +#endif
> +
> +static ResourceManagerContext *get_resman_context(void)
> +{
> +    ResourceManagerContext *res = resman_ctx;
> +
> +    ff_mutex_lock(&mutex);
> +
> +    if (res)
> +        goto end;
> +
> +    res = av_mallocz(sizeof(ResourceManagerContext));
> +    if (!res) {
> +        av_log(NULL, AV_LOG_ERROR, "Failed to allocate resource manager context\n");
> +        goto end;
> +    }
> +
> +    res->class = &resman_class;
> +    resman_ctx = res;
> +
> +end:
> +    ff_mutex_unlock(&mutex);
> +    return res;
> +}
> +
> +
> +void ff_resman_uninit(void)
> +{
> +    ff_mutex_lock(&mutex);
> +
> +    if (resman_ctx) {
> +        if (resman_ctx->resource_dic)
> +            av_dict_free(&resman_ctx->resource_dic);
> +        av_freep(&resman_ctx);
> +    }
> +
> +    ff_mutex_unlock(&mutex);
> +}
> +
> +
> +char *ff_resman_get_string(FFResourceId resource_id)

const char *

> +{
> +    ResourceManagerContext *ctx               = get_resman_context();
> +    FFResourceDefinition resource_definition = { 0 };
> +    AVDictionaryEntry *dic_entry;
> +    char *res = NULL;
> +
> +    if (!ctx)
> +        return NULL;
> +
> +    for (unsigned i = 0; i < FF_ARRAY_ELEMS(resource_definitions); ++i) {
> +        FFResourceDefinition def = resource_definitions[i];
> +        if (def.resource_id == resource_id) {
> +            resource_definition = def;
> +            break;
> +        }
> +    }
> +
> +    if (!resource_definition.name) {
> +        av_log(ctx, AV_LOG_ERROR, "Unable to find resource with ID %d\n", resource_id);
> +        return NULL;
> +    }
> +
> +    ff_mutex_lock(&mutex);
> +
> +    dic_entry = av_dict_get(ctx->resource_dic, resource_definition.name, NULL, 0);
> +
> +    if (!dic_entry) {
> +        int dict_ret;
> +
> +#if CONFIG_RESOURCE_COMPRESSION
> +
> +        char *out = NULL;
> +        size_t out_len;
> +
> +        int ret = decompress_gzip(ctx, (uint8_t *)resource_definition.data, *resource_definition.data_len, &out, &out_len);
> +
> +        if (ret) {
> +            av_log(NULL, AV_LOG_ERROR, "Unable to decompress the resource with ID %d\n", resource_id);
> +            goto end;
> +        }
> +
> +        dict_ret = av_dict_set(&ctx->resource_dic, resource_definition.name, out, 0);
> +        if (dict_ret < 0) {
> +            av_log(NULL, AV_LOG_ERROR, "Failed to store decompressed resource in dictionary: %d\n", dict_ret);
> +            av_freep(&out);
> +            goto end;
> +        }
> +
> +        av_freep(&out);
> +#else
> +
> +        dict_ret = av_dict_set(&ctx->resource_dic, resource_definition.name, (const char *)resource_definition.data, 0);
> +        if (dict_ret < 0) {
> +            av_log(NULL, AV_LOG_ERROR, "Failed to store resource in dictionary: %d\n", dict_ret);
> +            goto end;
> +        }
> +
> +#endif
> +        dic_entry = av_dict_get(ctx->resource_dic, resource_definition.name, NULL, 0);
> +
> +        if (!dic_entry) {
> +            av_log(NULL, AV_LOG_ERROR, "Failed to retrieve resource from dictionary after storing it\n");
> +            goto end;
> +        }
> +    }
> +
> +    res = dic_entry->value;
> +
> +end:
> +    ff_mutex_unlock(&mutex);
> +    return res;
> +}
> diff --git a/fftools/resources/resman.h b/fftools/resources/resman.h
> new file mode 100644
> index 0000000000..6485db5091
> --- /dev/null
> +++ b/fftools/resources/resman.h
> @@ -0,0 +1,50 @@
> +/*
> + * Copyright (c) 2025 - softworkz
> + *
> + * 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
> + */
> +
> +#ifndef FFTOOLS_RESOURCES_RESMAN_H
> +#define FFTOOLS_RESOURCES_RESMAN_H
> +
> +#include <stdint.h>
> +
> +#include "config.h"
> +#include "fftools/ffmpeg.h"
> +#include "libavutil/avutil.h"
> +#include "libavutil/bprint.h"
> +#include "fftools/textformat/avtextformat.h"

All includes in this file are unnecessary.

> +
> +typedef enum {
> +    FF_RESOURCE_GRAPH_CSS,
> +    FF_RESOURCE_GRAPH_HTML,
> +} FFResourceId;
> +
> +typedef struct FFResourceDefinition {
> +    FFResourceId resource_id;
> +    const char *name;
> +
> +    const unsigned char *data;
> +    const unsigned *data_len;
> +
> +} FFResourceDefinition;
> +
> +void ff_resman_uninit(void);
> +
> +char *ff_resman_get_string(FFResourceId resource_id);
> +
> +#endif /* FFTOOLS_RESOURCES_RESMAN_H */


More information about the ffmpeg-devel mailing list