You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-11-23 21:54:53 +02:00
Reviewed-by: softworkz . <softworkz-at-hotmail.com@ffmpeg.org> Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
197 lines
5.4 KiB
C
197 lines
5.4 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 "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;
|
|
|
|
static ResourceManagerContext resman_ctx = { .class = &resman_class };
|
|
|
|
|
|
#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
|
|
|
|
void ff_resman_uninit(void)
|
|
{
|
|
ff_mutex_lock(&mutex);
|
|
|
|
av_dict_free(&resman_ctx.resource_dic);
|
|
|
|
ff_mutex_unlock(&mutex);
|
|
}
|
|
|
|
|
|
char *ff_resman_get_string(FFResourceId resource_id)
|
|
{
|
|
ResourceManagerContext *ctx = &resman_ctx;
|
|
FFResourceDefinition resource_definition = { 0 };
|
|
AVDictionaryEntry *dic_entry;
|
|
char *res = 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;
|
|
}
|
|
}
|
|
|
|
av_assert1(resource_definition.name);
|
|
|
|
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(ctx, 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(ctx, 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(ctx, 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(ctx, 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;
|
|
}
|