mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2025-12-18 13:00:00 +01:00
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@hotmail.com>
232 lines
6.2 KiB
C
232 lines
6.2 KiB
C
/*
|
|
* 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;
|
|
|
|
|
|
#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);
|
|
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)
|
|
{
|
|
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;
|
|
}
|