[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